diff --git a/umu/umu_test.py b/umu/umu_test.py index 1e01a619..483cc2b8 100644 --- a/umu/umu_test.py +++ b/umu/umu_test.py @@ -20,6 +20,7 @@ ) from unittest.mock import MagicMock, Mock, patch +import vdf from Xlib.display import Display from Xlib.error import DisplayConnectionError from Xlib.protocol.rq import Event @@ -75,6 +76,7 @@ def setUp(self): # Proton verb # Used when testing build_command self.test_verb = "waitforexitandrun" + self.test_verb_as_arg = "--verb=waitforexitandrun" # Test directory self.test_file = "./tmp.WMYQiPb9A" # Executable @@ -130,6 +132,18 @@ def setUp(self): Path(self.test_user_share, "run").touch() Path(self.test_user_share, "run-in-sniper").touch() Path(self.test_user_share, "umu").touch() + with Path(self.test_user_share, "toolmanifest.vdf").open( + "w" + ) as toolmanifest: + vdf.dump( + { + "manifest": { + "commandline": "/_v2-entry-point --verb=%verb% --", + "compatmanager_layer_name": "container-runtime", + } + }, + toolmanifest, + ) # Mock pressure vessel Path(self.test_user_share, "pressure-vessel", "bin").mkdir( @@ -142,6 +156,34 @@ def setUp(self): # Mock the proton file in the dir self.test_proton_dir.joinpath("proton").touch(exist_ok=True) + with Path(self.test_proton_dir, "toolmanifest.vdf").open( + "w" + ) as toolmanifest: + vdf.dump( + { + "manifest": { + "commandline": "/proton %verb%", + "require_tool_appid": "1628350", + "compatmanager_layer_name": "proton", + } + }, + toolmanifest, + ) + with Path(self.test_proton_dir, "compatibilitytool.vdf").open( + "w" + ) as compatibilitytool: + vdf.dump( + { + "compatibilitytools": { + "compat_tools": { + "Proton": { + "display_name": "Proton", + } + } + } + }, + compatibilitytool, + ) # Mock the release downloaded in the cache: # tmp.5HYdpddgvs/umu-Proton-5HYdpddgvs.tar.gz @@ -1783,7 +1825,7 @@ def test_game_drive_empty(self): def test_build_command_linux_exe(self): """Test build_command when running a Linux executable. - UMU_NO_PROTON=1 disables Proton, running the executable directly in the + UMU_NO_TOOL=1 skips using a tool, running the executable directly in the Steam Linux Runtime. """ result_args = None @@ -1800,7 +1842,7 @@ def test_build_command_linux_exe(self): os.environ["PROTONPATH"] = self.test_file os.environ["GAMEID"] = self.test_file os.environ["STORE"] = self.test_file - os.environ["UMU_NO_PROTON"] = "1" + os.environ["UMU_NO_TOOL"] = "1" # Args result_args = __main__.parse_args() # Config @@ -1844,6 +1886,10 @@ def test_build_command_linux_exe(self): Path(self.test_user_share, "umu"), Path(self.test_local_share, "umu"), ) + copy( + Path(self.test_user_share, "toolmanifest.vdf"), + Path(self.test_local_share, "toolmanifest.vdf"), + ) # Build test_command = umu_run.build_command(self.env, self.test_local_share) @@ -1852,18 +1898,17 @@ def test_build_command_linux_exe(self): ) self.assertEqual( len(test_command), - 5, - f"Expected 5 element, received {len(test_command)}", + 4, + f"Expected 4 element, received {len(test_command)}", ) - entry_point, opt, verb, sep, exe = [*test_command] + entry_point, verb, sep, exe = [*test_command] self.assertEqual( - entry_point, - self.test_local_share / "umu", + Path(entry_point), + Path(self.test_local_share / "umu"), "Expected an entry point", ) - self.assertEqual(opt, "--verb", "Expected --verb") - self.assertEqual(verb, "waitforexitandrun", "Expected PROTON_VERB") + self.assertEqual(verb, self.test_verb_as_arg, "Expected PROTON_VERB") self.assertEqual(sep, "--", "Expected --") self.assertEqual(exe, self.env["EXE"], "Expected the EXE") @@ -1881,12 +1926,16 @@ def test_build_command_nopv(self): # Mock the proton file Path(self.test_file, "proton").touch() + # Mock the shim file + shim_path = Path(self.test_local_share, "umu-shim") + shim_path.touch() + with ( patch("sys.argv", ["", self.test_exe]), ThreadPoolExecutor() as thread_pool, ): os.environ["WINEPREFIX"] = self.test_file - os.environ["PROTONPATH"] = self.test_file + os.environ["PROTONPATH"] = self.test_proton_dir.as_posix() os.environ["GAMEID"] = self.test_file os.environ["STORE"] = self.test_file os.environ["UMU_NO_RUNTIME"] = "1" @@ -1931,6 +1980,10 @@ def test_build_command_nopv(self): Path(self.test_user_share, "umu"), Path(self.test_local_share, "umu"), ) + copy( + Path(self.test_user_share, "toolmanifest.vdf"), + Path(self.test_local_share, "toolmanifest.vdf"), + ) os.environ |= self.env @@ -1941,15 +1994,15 @@ def test_build_command_nopv(self): ) self.assertEqual( len(test_command), - 3, - f"Expected 3 elements, received {len(test_command)}", + 4, + f"Expected 4 elements, received {len(test_command)}", ) - proton, verb, exe, *_ = [*test_command] + _, proton, verb, exe, *_ = [*test_command] self.assertIsInstance( - proton, os.PathLike, "Expected proton to be PathLike" + Path(proton), os.PathLike, "Expected proton to be PathLike" ) self.assertEqual( - proton, + Path(proton), Path(self.env["PROTONPATH"], "proton"), "Expected PROTONPATH", ) @@ -2016,7 +2069,7 @@ def test_build_command(self): ThreadPoolExecutor() as thread_pool, ): os.environ["WINEPREFIX"] = self.test_file - os.environ["PROTONPATH"] = self.test_file + os.environ["PROTONPATH"] = self.test_proton_dir.as_posix() os.environ["GAMEID"] = self.test_file os.environ["STORE"] = self.test_file # Args @@ -2063,6 +2116,10 @@ def test_build_command(self): Path(self.test_user_share, "umu"), Path(self.test_local_share, "umu"), ) + copy( + Path(self.test_user_share, "toolmanifest.vdf"), + Path(self.test_local_share, "toolmanifest.vdf"), + ) # Build test_command = umu_run.build_command(self.env, self.test_local_share) @@ -2071,29 +2128,28 @@ def test_build_command(self): ) self.assertEqual( len(test_command), - 8, - f"Expected 8 elements, received {len(test_command)}", + 7, + f"Expected 7 elements, received {len(test_command)}", ) - entry_point, opt1, verb, opt2, shim, proton, verb2, exe = [ - *test_command - ] + entry_point, verb, sep, shim, proton, verb2, exe = [*test_command] # The entry point dest could change. Just check if there's a value self.assertTrue(entry_point, "Expected an entry point") self.assertIsInstance( - entry_point, os.PathLike, "Expected entry point to be PathLike" + Path(entry_point), + os.PathLike, + "Expected entry point to be PathLike", ) - self.assertEqual(opt1, "--verb", "Expected --verb") - self.assertEqual(verb, self.test_verb, "Expected a verb") - self.assertEqual(opt2, "--", "Expected --") + self.assertEqual(verb, self.test_verb_as_arg, "Expected a verb") + self.assertEqual(sep, "--", "Expected --") self.assertIsInstance( - shim, os.PathLike, "Expected shim to be PathLike" + Path(shim), os.PathLike, "Expected shim to be PathLike" ) - self.assertEqual(shim, shim_path, "Expected the shim file") + self.assertEqual(Path(shim), shim_path, "Expected the shim file") self.assertIsInstance( - proton, os.PathLike, "Expected proton to be PathLike" + Path(proton), os.PathLike, "Expected proton to be PathLike" ) self.assertEqual( - proton, + Path(proton), Path(self.env["PROTONPATH"], "proton"), "Expected the proton file", ) diff --git a/umu/umu_test_plugins.py b/umu/umu_test_plugins.py index 7b620cec..446f4086 100644 --- a/umu/umu_test_plugins.py +++ b/umu/umu_test_plugins.py @@ -9,6 +9,7 @@ from shutil import copy, copytree, rmtree from unittest.mock import MagicMock, patch +import vdf from tomllib import TOMLDecodeError sys.path.append(str(Path(__file__).parent.parent)) @@ -47,6 +48,7 @@ def setUp(self): # Proton verb # Used when testing build_command self.test_verb = "waitforexitandrun" + self.test_verb_as_arg = "--verb=waitforexitandrun" # Test directory self.test_file = "./tmp.AKN6tnueyO" # Executable @@ -91,6 +93,18 @@ def setUp(self): Path(self.test_user_share, "run").touch() Path(self.test_user_share, "run-in-sniper").touch() Path(self.test_user_share, "umu").touch() + with Path(self.test_user_share, "toolmanifest.vdf").open( + "w" + ) as toolmanifest: + vdf.dump( + { + "manifest": { + "commandline": "/_v2-entry-point --verb=%verb% --", + "compatmanager_layer_name": "container-runtime", + } + }, + toolmanifest, + ) # Mock pressure vessel Path(self.test_user_share, "pressure-vessel").mkdir() @@ -105,6 +119,34 @@ def setUp(self): # Mock the proton file in the dir self.test_proton_dir.joinpath("proton").touch(exist_ok=True) + with Path(self.test_proton_dir, "toolmanifest.vdf").open( + "w" + ) as toolmanifest: + vdf.dump( + { + "manifest": { + "commandline": "/proton %verb%", + "require_tool_appid": "1628350", + "compatmanager_layer_name": "proton", + } + }, + toolmanifest, + ) + with Path(self.test_proton_dir, "compatibilitytool.vdf").open( + "w" + ) as compatibilitytool: + vdf.dump( + { + "compatibilitytools": { + "compat_tools": { + "Proton": { + "display_name": "Proton", + } + } + } + }, + compatibilitytool, + ) Path(self.test_file).mkdir(exist_ok=True) Path(self.test_exe).touch() @@ -305,12 +347,18 @@ def test_build_command_proton(self): Path(self.test_user_share, "umu"), Path(self.test_local_share, "umu"), ) + copy( + Path(self.test_user_share, "toolmanifest.vdf"), + Path(self.test_local_share, "toolmanifest.vdf"), + ) for key, val in self.env.items(): os.environ[key] = val # Build - with self.assertRaisesRegex(FileNotFoundError, "proton"): + with self.assertRaisesRegex( + FileNotFoundError, "proton|toolmanifest.vdf|compatibilitytool.vdf" + ): umu_run.build_command( self.env, self.test_local_share, test_command ) @@ -325,7 +373,7 @@ def test_build_command_toml(self): toml_str = f""" [umu] prefix = "{self.test_file}" - proton = "{self.test_file}" + proton = "{self.test_proton_dir}" game_id = "{self.test_file}" launch_args = ["{self.test_file}", "{self.test_file}"] exe = "{self.test_exe}" @@ -390,6 +438,10 @@ def test_build_command_toml(self): Path(self.test_user_share, "umu"), Path(self.test_local_share, "umu"), ) + copy( + Path(self.test_user_share, "toolmanifest.vdf"), + Path(self.test_local_share, "toolmanifest.vdf"), + ) for key, val in self.env.items(): os.environ[key] = val @@ -398,26 +450,25 @@ def test_build_command_toml(self): test_command = umu_run.build_command(self.env, self.test_local_share) # Verify contents of the command - entry_point, opt1, verb, opt2, shim, proton, verb2, exe = [ - *test_command - ] + entry_point, verb, sep, shim, proton, verb2, exe = [*test_command] # The entry point dest could change. Just check if there's a value self.assertTrue(entry_point, "Expected an entry point") self.assertIsInstance( - entry_point, os.PathLike, "Expected entry point to be PathLike" + Path(entry_point), + os.PathLike, + "Expected entry point to be PathLike", ) - self.assertEqual(opt1, "--verb", "Expected --verb") - self.assertEqual(verb, self.test_verb, "Expected a verb") - self.assertEqual(opt2, "--", "Expected --") + self.assertEqual(verb, self.test_verb_as_arg, "Expected a verb") + self.assertEqual(sep, "--", "Expected --") self.assertIsInstance( - shim, os.PathLike, "Expected shim to be PathLike" + Path(shim), os.PathLike, "Expected shim to be PathLike" ) - self.assertEqual(shim, shim_path, "Expected the shim file") + self.assertEqual(Path(shim), shim_path, "Expected the shim file") self.assertIsInstance( - proton, os.PathLike, "Expected proton to be PathLike" + Path(proton), os.PathLike, "Expected proton to be PathLike" ) self.assertEqual( - proton, + Path(proton), Path(self.env["PROTONPATH"], "proton"), "Expected the proton file", )