From 2d9db59c73641a939b38435b465a4bbb03bb40c2 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sat, 24 Oct 2026 03:56:53 +0200 Subject: [PATCH] fix: let A pass over system and recycle folders --- install.py | 29 +++++++++++++++++++++++- tests/test_install_tui.py | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/install.py b/install.py index 57a39c40b..41381f47e 100644 --- a/install.py +++ b/install.py @@ -11056,6 +11056,33 @@ def _browse_rows(folder: Path | None, zips: bool = False) -> tuple[list[TuiRow], return rows, "" +# The folders of a system root that hold no one's files: A went into +# /bin from /, the first name in order. +_POSIX_ROOT_SYSTEM = frozenset(( + "bin", "boot", "dev", "efi", "etc", "lib", "lib32", "lib64", "libx32", "lost+found", + "proc", "root", "run", "sbin", "snap", "srv", "sys", "tmp", "usr", "var", +)) +# FILE_ATTRIBUTE_HIDDEN and FILE_ATTRIBUTE_SYSTEM: Explorer shows neither. +_WINDOWS_HIDDEN = 0x2 | 0x4 + + +def _system_folder(path: Path, folder: Path) -> bool: + """A folder of the system, not one a person keeps files in: the + recycle bin and restore points of a Windows drive ($RECYCLE.BIN, System + Volume Information, hidden by Explorer), and /bin, /etc, /usr and the + like at the root of a Unix system.""" + name = path.name + if name.startswith(("$", ".")) or name.casefold() == "system volume information": + return True + try: + attributes = getattr(os.stat(path, follow_symlinks=False), "st_file_attributes", 0) + except OSError: + attributes = 0 + if attributes & _WINDOWS_HIDDEN: + return True + return folder == Path("/") and name in _POSIX_ROOT_SYSTEM + + def _browse_to(state: TuiState, folder: Path | None, came_from: Path | None = None) -> None: state.browse_dir = folder state.browse_rows, state.status = _browse_rows(folder, state.browse_for == "from") @@ -11070,7 +11097,7 @@ def _browse_to(state: TuiState, folder: Path | None, came_from: Path | None = No focus = next((index for index, row in enumerate(state.browse_rows) if row.action == "dir" and isinstance(row.value, Path) and row.label == f"{row.value.name}/" - and not row.value.name.startswith(".")), 0) + and not _system_folder(row.value, folder)), 0) if not focus and not state.status: state.status = ("This is your home folder: no setup reads its BIOS files " "here. Go back to type a path." if folder == state.home diff --git a/tests/test_install_tui.py b/tests/test_install_tui.py index 6dfde1c36..45f0173a7 100644 --- a/tests/test_install_tui.py +++ b/tests/test_install_tui.py @@ -3843,5 +3843,52 @@ class TheFolderScreenSaysWhatItOffers(unittest.TestCase): self.assertEqual(rows[state.focus["folder"]].action, "browse") +class AFocusesNoSystemFolder(unittest.TestCase): + """At / A went into bin/, and at a drive root holding $RECYCLE.BIN into + the recycle bin, which Use this folder then took as the BIOS folder.""" + + def setUp(self): + self.home = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp")) + self.addCleanup(shutil.rmtree, self.home, True) + self.state = install.TuiState(os_type="linux", home=self.home) + self.state.platform = "retroarch" + self.state.stack = ["folder", "browse"] + + def focused(self) -> install.TuiRow: + return self.state.browse_rows[self.state.focus["browse"]] + + def test_the_recycle_bin_and_restore_points_are_passed_over(self): + for name in ("$RECYCLE.BIN", "System Volume Information", "Games"): + (self.home / name).mkdir() + with mock.patch.object(install, "_mounted_cards", return_value=[]): + install._browse_to(self.state, self.home) + self.assertEqual(self.focused().label, "Games/") + + def test_explorer_hidden_folders_are_passed_over(self): + for name in ("Boot", "Games"): + (self.home / name).mkdir() + real_stat = os.stat + + def stat(path, *args, **kwargs): + result = real_stat(path, *args, **kwargs) + hidden = 0x2 | 0x4 if Path(path).name == "Boot" else 0 + return types.SimpleNamespace(st_file_attributes=hidden, st_mode=result.st_mode) + + with mock.patch.object(install, "_mounted_cards", return_value=[]), \ + mock.patch.object(install.os, "stat", stat): + install._browse_to(self.state, self.home) + self.assertEqual(self.focused().label, "Games/") + + def test_at_the_root_no_system_folder_is_focused(self): + with mock.patch.object(install, "_mounted_cards", return_value=[]): + install._browse_to(self.state, Path("/")) + row = self.focused() + if row.action == "dir": + self.assertNotIn(row.value.name, install._POSIX_ROOT_SYSTEM) + names = {r.value.name for r in self.state.browse_rows if r.action == "dir"} + if "bin" in names and names - install._POSIX_ROOT_SYSTEM - {""}: + self.assertEqual(row.action, "dir") + + if __name__ == "__main__": unittest.main()