diff --git a/install.py b/install.py index 30921d9a6..f73c0d7f4 100644 --- a/install.py +++ b/install.py @@ -8905,8 +8905,9 @@ def _open_from_folders(folders: list[Path]) -> None: other_drives=[str(drive) for drive in index.other_drives]) -def _refuse_closed(dest: Path, named: str) -> None: - """End the run, with its name, on a folder above dest this user cannot open.""" +def _refuse_closed(dest: Path, named: str, then: str = "") -> None: + """End the run, with its name, on a folder above dest this user cannot + open. then is a line said after the error, which it continues.""" closed = closed_folder(dest) if closed is None: return @@ -8915,6 +8916,8 @@ def _refuse_closed(dest: Path, named: str) -> None: print(f"Error: {screen_text(message)}. Give this user access to it, run as " f"its owner, or choose another folder than the one {named}.", file=sys.stderr) + if then: + print(f" {then}", file=sys.stderr) sys.exit(1) @@ -9366,13 +9369,13 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None: # A system_directory under /root read from retroarch.cfg, run without # sudo: the case probe raised PermissionError before any line. for name, path in platforms: - if closed_folder(path) is not None and len(platforms) > 1: - print(" Nothing was installed. Name one platform with --platform " - "to install it alone.", file=sys.stderr) - _refuse_closed(path, ( - f"taken as {display_name(name)}'s default, {display_name(name)} " - "not being found here" if path in assumed_dests - else "detected here") + " (name another with --dest)") + found_in = _retroarch_config_at(path) + named = (f"used by default, since {display_name(name)} was not found" + if path in assumed_dests + else f"{found_in[0]} names" if found_in else "detected here") + _refuse_closed(path, f"{named} (name another with --dest)", + "Nothing was installed. Name one platform with --platform " + "to install it alone." if len(platforms) > 1 else "") total_downloaded = 0 total_up_to_date = 0 diff --git a/tests/test_install_experience.py b/tests/test_install_experience.py index e2e79da53..00a3cffb2 100644 --- a/tests/test_install_experience.py +++ b/tests/test_install_experience.py @@ -1119,5 +1119,58 @@ class NotFoundWithAClosedFolder(unittest.TestCase): self.assertIn("could not be opened", asked[0]) +@unittest.skipIf(os.name == "nt" or os.geteuid() == 0, + "a folder without search permission is POSIX") +class ADetectedClosedFolderSaysWhereItCameFrom(unittest.TestCase): + """The refusal said "the one detected here" for a folder retroarch.cfg + names, and with two setups found the line saying nothing was installed + came above the error it continues.""" + + def run_detected(self, emudeck: bool) -> subprocess.CompletedProcess: + with FakeRepository() as repo: + home = repo.root / "home" + lock = repo.root / "lock" + cfg = home / ".config" / "retroarch" / "retroarch.cfg" + cfg.parent.mkdir(parents=True) + (lock / "system").mkdir(parents=True) + cfg.write_text(f'system_directory = "{lock / "system"}"\n') + if emudeck: + (home / "Emulation" / "bios").mkdir(parents=True) + env = {**repo.env(), "HOME": str(home), "XDG_CONFIG_HOME": str(home / ".config"), + "RETROBIOS_OS": "linux"} + os.chmod(lock, 0o000) + try: + proc = subprocess.run( + [sys.executable, str(REPO_ROOT / "install.py"), "--check", "--no-input"], + env=env, capture_output=True, text=True, timeout=60, + stdin=subprocess.DEVNULL) + finally: + os.chmod(lock, 0o755) + self.cfg = cfg + return proc + + def test_the_config_is_named(self): + proc = self.run_detected(False) + self.assertEqual(proc.returncode, 1, proc.stdout + proc.stderr) + said = " ".join(proc.stderr.split()) + self.assertIn(f"than the one {self.cfg} names (name another with --dest)", said) + + def test_nothing_installed_comes_after_the_error(self): + proc = self.run_detected(True) + self.assertEqual(proc.returncode, 1, proc.stdout + proc.stderr) + error = proc.stderr.index("Error: ") + self.assertGreater(proc.stderr.index("Nothing was installed."), error, proc.stderr) + + def test_an_assumed_default_reads_plainly(self): + self.assertIn("used by default, since {display_name(name)} was not found", + _source_of(install._run_text)) + + +def _source_of(function) -> str: + import inspect + + return " ".join(inspect.getsource(function).split()) + + if __name__ == "__main__": unittest.main()