diff --git a/install.py b/install.py index 60f49e22..07b512e6 100644 --- a/install.py +++ b/install.py @@ -10165,6 +10165,10 @@ class TuiState: self.check_summary: dict | None = None # The saves event of the check: the files that go to the save folder. self.saves: dict | None = None + # The same of the confirm or preview run, and of the install: a + # choice of systems can leave the save file out, or keep it. + self.dry_saves: dict | None = None + self.done_saves: dict | None = None # The confirm screen waits for A even when the plan is unchanged: # it was opened by Space, which never installs by itself. self.must_confirm: bool = False @@ -10722,7 +10726,9 @@ def _start_run(state: TuiState, run: str) -> list[tuple]: state.checked_flags = tui_flags(state) elif run in ("confirm", "preview"): state.dry, state.dry_files, state.dry_summary = None, [], None + state.dry_saves = None else: + state.done_saves = None state.download = state.progress = state.reading = None state.samples, state.local_dests, state.network = [], {}, 0 state.finished, state.summary, state.code, state.stopping = [], None, None, False @@ -11365,6 +11371,10 @@ def _take_event(state: TuiState, event: dict) -> None: state.unreadable.append(event) elif kind == "saves" and state.run == "check": state.saves = event + elif kind == "saves" and state.run in ("confirm", "preview"): + state.dry_saves = event + elif kind == "saves" and state.run == "install": + state.done_saves = event elif kind == "found" and state.run == "install": state.local_dests[event["dest"]] = (event["folder"], event["reason"]) elif state.run == "check": @@ -11725,8 +11735,9 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list: lines.append("You cannot write to this folder: choose another one.") lines.append(f"{check['present']} in place, {check['missing']} missing, " f"{wrong} wrong {'copy' if wrong == 1 else 'copies'}") - if check is state.check: - lines += [_styled(line, "path") for line in _saves_lines(state)] + saves = (state.saves if check is state.check + else state.dry_saves if check is state.dry else None) + lines += [_styled(line, "path") for line in _saves_lines(state, saves)] found_in = _retroarch_config_at(dest) if dest is not None else None if found_in: lines.append(_styled(f"Named by {_short_path(Path(found_in[0]), state.home)}", "path")) @@ -11764,16 +11775,17 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list: return lines[:2] + offline_lines + lines[2:] -def _saves_lines(state: TuiState) -> list[str]: +def _saves_lines(state: TuiState, saves: dict | None) -> list[str]: """Where the files read from the frontend's save directory go: a second - folder this run writes to, named before anything is written.""" - if not state.saves or not state.saves.get("files"): + folder this run writes to, named before anything is written. saves is + the event of the run the screen shows.""" + if not saves or not saves.get("files"): return [] name = display_name(state.platform) if state.platform else "the frontend" - count = state.saves["files"] + count = saves["files"] return [f"{_plural(count, 'file')} read from {name}'s save directory " f"{'goes' if count == 1 else 'go'} to " - f"{_short_path(Path(state.saves['folder']), state.home)}"] + f"{_short_path(Path(saves['folder']), state.home)}"] def _unplaced_lines(summary: dict | None, state: TuiState) -> list[str]: @@ -12241,7 +12253,7 @@ def tui_outcome(state: TuiState) -> tuple[str, list[str]]: lines.append(sources) lines.append(f"BIOS folder: {state.dest}") if installed + up_to_date: - lines += _saves_lines(state) + lines += _saves_lines(state, state.done_saves) if space: lines.append("Free some space, or run again and choose another folder.") causes: dict[str, list[str]] = {} diff --git a/tests/test_install_tui.py b/tests/test_install_tui.py index 76ad6e34..a3adb6f4 100644 --- a/tests/test_install_tui.py +++ b/tests/test_install_tui.py @@ -2778,6 +2778,50 @@ class Crash(unittest.TestCase): self.assertNotIn("Traceback", err.getvalue() + out.getvalue()) +class TheSaveFolderLineFollowsTheChoice(unittest.TestCase): + """The save-folder line was the check's: with the system owning the + save file unticked, the result named a folder nothing was written to, + and kept, the Confirm screen did not name it.""" + + SAVES = {"event": "saves", "platform": "retroarch", "files": 1, + "folder": "/home/user/MySaves/"} + LINE = "1 file read from RetroArch's save directory goes to ~/MySaves/" + + def said(self, lines) -> str: + return " ".join(" ".join(lines).split()) + + def run_to_result(self, confirm_saves: bool, install_saves: bool) -> install.TuiState: + state = checked_state() + state.saves = dict(self.SAVES) + state.must_confirm = True + state.stack.append("confirm") + install._start_run(state, "confirm") + dry = {**CHECK, "files": 3, "missing": 3, "wrong_hash": 0} + for event in [*([self.SAVES] if confirm_saves else []), dry]: + install.tui_receive(state, ("event", state.run_id, event)) + install.tui_receive(state, ("ended", state.run_id, 1, [])) + self.confirm = self.said(install.tui_view(state, 200, 40)) + install._start_run(state, "install") + summary = {"event": "summary", "mode": "install", "installed": 3, "up_to_date": 0, + "pending": 0, "failed": [], "skipped": [], "left_out": [], "kept": [], + "interrupted": False} + for event in [*([self.SAVES] if install_saves else []), summary]: + install.tui_receive(state, ("event", state.run_id, event)) + install.tui_receive(state, ("ended", state.run_id, 0, [])) + return state + + def test_unticked_it_is_named_nowhere(self): + state = self.run_to_result(confirm_saves=False, install_saves=False) + self.assertNotIn("save directory", self.confirm) + self.assertEqual(state.screen, "result") + self.assertNotIn("save directory", self.said(install.tui_outcome(state)[1])) + + def test_kept_the_confirm_screen_and_the_result_name_it(self): + state = self.run_to_result(confirm_saves=True, install_saves=True) + self.assertIn(self.LINE, self.confirm) + self.assertIn(self.LINE, self.said(install.tui_outcome(state)[1])) + + @unittest.skipIf(os.name == "nt" or os.geteuid() == 0, "a folder without search permission is POSIX") class AFolderThisUserCannotOpen(unittest.TestCase):