diff --git a/umu/umu_test.py b/umu/umu_test.py index 1e01a619..84864566 100644 --- a/umu/umu_test.py +++ b/umu/umu_test.py @@ -20,10 +20,11 @@ ) from unittest.mock import MagicMock, Mock, patch +import vdf +from Xlib.X import CreateNotify from Xlib.display import Display from Xlib.error import DisplayConnectionError from Xlib.protocol.rq import Event -from Xlib.X import CreateNotify from Xlib.xobject.drawable import Window sys.path.append(str(Path(__file__).parent.parent)) @@ -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,16 @@ 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 +154,30 @@ 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 +1819,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 +1836,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 +1880,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 +1892,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 +1920,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 +1974,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 +1988,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 +2063,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 +2110,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 +2122,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 = [ + 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..b8cea6f5 100644 --- a/umu/umu_test_plugins.py +++ b/umu/umu_test_plugins.py @@ -7,9 +7,10 @@ from argparse import Namespace from pathlib import Path from shutil import copy, copytree, rmtree +from tomllib import TOMLDecodeError from unittest.mock import MagicMock, patch -from tomllib import TOMLDecodeError +import vdf 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,16 @@ 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 +117,30 @@ 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 +341,16 @@ 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 +365,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 +430,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 +442,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 = [ + 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", )