From 2a84ec5bad6fc651700e8cebe10c84194e303408 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Tue, 9 Jul 2024 00:12:49 +0100 Subject: [PATCH] Add broken test for game support issue --- app/logic/game_support.py | 10 +++- app/tests/unit/logic/test_game_support.py | 70 +++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/app/logic/game_support.py b/app/logic/game_support.py index 11e2d407..ec7f677a 100644 --- a/app/logic/game_support.py +++ b/app/logic/game_support.py @@ -13,7 +13,7 @@ # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . - +import sys from typing import List, Dict, Optional, Tuple import sqlalchemy @@ -90,6 +90,7 @@ class GSPackage: return self.user_unsupported_games def add_error(self, error: str): + print(f"ERROR {self.name}: {error}") return self.errors.add(error) @@ -135,10 +136,12 @@ class GameSupport: return [package for package in self.packages.values() if modname in package.depends] def _get_supported_games_for_modname(self, depend: str, visited: list[str]): + print(f"_get_supported_games_for_modname {depend} visited {', '.join(visited)}", file=sys.stderr) dep_supports_all = False for_dep = set() for provider in self.get_all_that_provide(depend): found_in = self._get_supported_games(provider, visited) + print(f" - provider for {depend}: {provider.name}: {found_in}", file=sys.stderr) if found_in is None: # Unsupported, keep going pass @@ -151,6 +154,7 @@ class GameSupport: return dep_supports_all, for_dep def _get_supported_games_for_deps(self, package: GSPackage, visited: list[str]) -> Optional[set[str]]: + print(f"_get_supported_games_for_deps package {package.name} visited {', '.join(visited)}", file=sys.stderr) ret = set() for depend in package.depends: @@ -173,6 +177,7 @@ class GameSupport: return ret def _get_supported_games(self, package: GSPackage, visited: list[str]) -> Optional[set[str]]: + print(f"_get_supported_games package {package.name} visited {', '.join(visited)}", file=sys.stderr) if package.id_ in visited: first_idx = visited.index(package.id_) visited = visited[first_idx:] @@ -183,8 +188,10 @@ class GameSupport: return None if package.type == PackageType.GAME: + print(f"_get_supported_games package {package.name} is game", file=sys.stderr) return {package.name} elif package.is_confirmed: + print(f"_get_supported_games package {package.name} is confirmed", file=sys.stderr) return package.supported_games visited = visited.copy() @@ -221,6 +228,7 @@ class GameSupport: while len(to_update) > 0: current_package = to_update.pop() + print(f"on_update package {current_package.name}", file=sys.stderr) if current_package.id_ in self.packages and current_package.type != PackageType.GAME: self._get_supported_games(current_package, []) diff --git a/app/tests/unit/logic/test_game_support.py b/app/tests/unit/logic/test_game_support.py index c14f81e6..e9b7f8e0 100644 --- a/app/tests/unit/logic/test_game_support.py +++ b/app/tests/unit/logic/test_game_support.py @@ -217,6 +217,14 @@ def test_cycle_fails_safely(): """ A dependency cycle shouldn't completely break the graph if a mod is available elsewhere + + a -> d + game has d + + cycle: + d -> b + b -> c + c -> b """ support = GameSupport() support.add(make_game("game1", ["default", "mod_d"])) @@ -241,6 +249,68 @@ def test_cycle_fails_safely(): } +def test_cycle_not_fulfill_with_conflict(): + """ + Test that cycles aren't fulfilled by installing a mod multiple times, which would conflict + + a -> b -> a + game1 has a + + b should be {game1} + a should be unfulfilled + """ + support = GameSupport() + support.add(make_game("game1", ["default", "mod_a"])) + modB = support.add(make_mod("mod_b", ["mod_b"], ["mod_a"])) + modA = support.add(make_mod("mod_a", ["mod_a"], ["mod_b"])) + support.on_first_run() + + assert modB.is_confirmed + assert modB.detected_supported_games == {"game1"} + + # Can't install mod_a and game1 at the same time + assert not modA.is_confirmed + assert modA.detected_supported_games == {} + + assert support.all_errors == { + "author/mod_a: Dependency cycle detected: author/mod_b -> author/mod_a -> author/mod_b", + "author/mod_b: Dependency cycle detected: author/mod_b -> author/mod_a -> author/mod_b", + } + + +def test_cycle_not_fulfill_with_conflict2(): + """ + Test that cycles aren't fulfilled by installing a mod multiple times, which would conflict + + a -> b -> a + game1 has a + + b should be {game1} + a should be unfulfilled + """ + support = GameSupport() + support.add(make_game("game1", ["default"])) + modB = support.add(make_mod("mod_b", ["mod_b"], ["mod_a"])) + modA2 = support.add(make_mod("mod_a", ["mod_a"], ["default"])) + modA = support.add(make_mod("mod_a", ["mod_a"], ["mod_b"])) + support.on_first_run() + + assert modB.is_confirmed + assert modB.detected_supported_games == {"game1"} + + assert modA2.is_confirmed + assert modA2.detected_supported_games == {"game1"} + + # Can't install modA and modA2 at the same time + assert not modA.is_confirmed + assert modA.detected_supported_games == {} + + assert support.all_errors == { + "author/mod_a: Dependency cycle detected: author/mod_b -> author/mod_a -> author/mod_b", + "author/mod_b: Dependency cycle detected: author/mod_b -> author/mod_a -> author/mod_b", + } + + def test_update(): """ Test updating a mod will update mods that depend on it