fix: name the save folder the chosen run writes

The save-folder line came from the check run only: after Choose systems the result named it for an unticked system, and the Confirm screen left it out for a kept one. The confirm, preview and install runs now carry their own saves event.
This commit is contained in:
Abdessamad Derraz committed 2026-10-23 23:26:31 +02:00
1 parent 5c02196b27
commit 359c260e98
2 files changed
+64 -8

No files matched your search

+20 -8
View File
@@ -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]] = {}
+44
View File
@@ -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/<core>"}
LINE = "1 file read from RetroArch's save directory goes to ~/MySaves/<core>"
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):