mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 04:39:06 -05:00
feat: name the save folder and the pack on screen
The plan and the result now say how many files go to the save folder and where, from a new saves event. With --from-pack they say the files come from the pack, not a download. A stop asked for (Start+Select, SIGHUP) ends on the plain outcome, without the log's last lines.
This commit is contained in:
1 parent
e003828e26
commit
3db8ddae2b
4 files changed
+104
-4
No files matched your search
+38
-3
@@ -546,6 +546,11 @@ EVENT_KEYS: dict[str, tuple[str, ...]] = {
|
||||
# size of a file no copy was found for, or of no known size (a folder,
|
||||
# a ZIP that would not open). member as in found, reason in words.
|
||||
"unreadable": ("platform", "folder", "path", "member", "reason"),
|
||||
# The files of the list read from the frontend's save directory, and
|
||||
# where they go (<system> and <core> stand for a folder per system or
|
||||
# per core), once that directory is known. Not written when it is not:
|
||||
# the summary's skipped then counts them (no_save_directory).
|
||||
"saves": ("platform", "files", "folder"),
|
||||
"download": ("platform", "files", "bytes"),
|
||||
"progress": ("files_done", "files_total", "bytes_done", "bytes_total"),
|
||||
# Right after each progress event, and once when the files of a
|
||||
@@ -4074,6 +4079,8 @@ def place_save_entries(
|
||||
where += f"{os.sep}<core>"
|
||||
print(f" {_plural(len(saved), 'file')} read from {display}'s save directory "
|
||||
f"go to {where}")
|
||||
EVENTS.emit("saves", platform=str(manifest.get("platform", "")),
|
||||
files=len(saved), folder=where)
|
||||
return files, []
|
||||
print(f" {_plural(len(saved), 'file')} read from {display}'s save directory "
|
||||
f"left out: {place}")
|
||||
@@ -10081,6 +10088,8 @@ class TuiState:
|
||||
self.idle_closed = False
|
||||
# The check run's summary: what it left out and why.
|
||||
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 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
|
||||
@@ -10634,7 +10643,7 @@ def _start_run(state: TuiState, run: str) -> list[tuple]:
|
||||
# dropped the --system the command line gave.
|
||||
state.check, state.files, state.systems, state.reading = None, [], [], None
|
||||
state.found, state.scanned, state.unreadable = [], [], []
|
||||
state.check_summary = None
|
||||
state.check_summary = state.saves = None
|
||||
state.checked_flags = tui_flags(state)
|
||||
elif run in ("confirm", "preview"):
|
||||
state.dry, state.dry_files, state.dry_summary = None, [], None
|
||||
@@ -11259,6 +11268,8 @@ def _take_event(state: TuiState, event: dict) -> None:
|
||||
state.scanned.append(event)
|
||||
elif kind == "unreadable" and state.run == "check":
|
||||
state.unreadable.append(event)
|
||||
elif kind == "saves" and state.run == "check":
|
||||
state.saves = event
|
||||
elif kind == "found" and state.run == "install":
|
||||
state.local_dests[event["dest"]] = (event["folder"], event["reason"])
|
||||
elif state.run == "check":
|
||||
@@ -11590,6 +11601,11 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list:
|
||||
elif need and fetch and offline:
|
||||
headline = (f"{_plural(fetch, 'file')} {'needs' if fetch == 1 else 'need'} the "
|
||||
"internet, which --offline does not use")
|
||||
elif need and fetch and LOCAL.pack is not None:
|
||||
# Nothing comes from the network: "to download" on a handheld with
|
||||
# no Wi-Fi read as a failure on its way.
|
||||
headline = (f"{_plural(fetch, 'file')} to install from the pack, "
|
||||
f"{format_size(fetch_bytes)}")
|
||||
elif need and fetch:
|
||||
headline = f"{_plural(fetch, 'file')} to download, {format_size(fetch_bytes)}"
|
||||
elif need:
|
||||
@@ -11614,6 +11630,8 @@ 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)]
|
||||
found_in = _RETROARCH_CONFIGS.get(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"))
|
||||
@@ -11651,6 +11669,18 @@ 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]:
|
||||
"""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"):
|
||||
return []
|
||||
name = display_name(state.platform) if state.platform else "the frontend"
|
||||
count = state.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)}"]
|
||||
|
||||
|
||||
def _unplaced_lines(summary: dict | None, state: TuiState) -> list[str]:
|
||||
"""The files read from the frontend's save directory that the run left
|
||||
out, that directory being unknown: said, never silent."""
|
||||
@@ -12102,7 +12132,8 @@ def tui_outcome(state: TuiState) -> tuple[str, list[str]]:
|
||||
head = f"The installer stopped with code {state.code}."
|
||||
elif installed:
|
||||
title = "Done"
|
||||
head = f"{_plural(installed, 'file')} installed."
|
||||
source = " from the pack" if LOCAL.pack is not None else ""
|
||||
head = f"{_plural(installed, 'file')} installed{source}."
|
||||
if up_to_date:
|
||||
head += f" {_plural(up_to_date, 'file')} already in place."
|
||||
else:
|
||||
@@ -12113,6 +12144,8 @@ def tui_outcome(state: TuiState) -> tuple[str, list[str]]:
|
||||
if sources:
|
||||
lines.append(sources)
|
||||
lines.append(f"BIOS folder: {state.dest}")
|
||||
if installed + up_to_date:
|
||||
lines += _saves_lines(state)
|
||||
if space:
|
||||
lines.append("Free some space, or run again and choose another folder.")
|
||||
causes: dict[str, list[str]] = {}
|
||||
@@ -12131,7 +12164,9 @@ def tui_outcome(state: TuiState) -> tuple[str, list[str]]:
|
||||
shown += 1
|
||||
if shown > 20:
|
||||
lines.append(f" ... and {shown - 20} more")
|
||||
if not summary and not space:
|
||||
if not summary and not space and state.code != 130:
|
||||
# A stop asked for (Ctrl-C, Start+Select, the window closed) is said
|
||||
# by the line above: the log's last lines are for a run that died.
|
||||
lines += [f" {line}" for line in state.tail[-8:]]
|
||||
run = Invocation()
|
||||
run.os_type = state.os_type
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"declared",
|
||||
"found",
|
||||
"unreadable",
|
||||
"saves",
|
||||
"download",
|
||||
"progress",
|
||||
"transfer",
|
||||
@@ -64,6 +65,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/unreadable"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/saves"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/download"
|
||||
},
|
||||
@@ -486,6 +490,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"saves": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"event",
|
||||
"platform",
|
||||
"files",
|
||||
"folder"
|
||||
],
|
||||
"properties": {
|
||||
"event": {
|
||||
"const": "saves"
|
||||
},
|
||||
"platform": {
|
||||
"type": "string"
|
||||
},
|
||||
"files": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"folder": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"download": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -2205,6 +2205,33 @@ class AuditRoundThree(unittest.TestCase):
|
||||
|
||||
# P2-1: a card too small for the download.
|
||||
|
||||
def test_a_stop_asked_for_says_only_the_outcome(self):
|
||||
"""Start+Select (SIGTERM) or SIGHUP stops the child before it writes
|
||||
a summary: the farewell then listed the log's last lines."""
|
||||
state = checked_state()
|
||||
state.download = {"files": 50}
|
||||
state.finished = [("scph5501.bin", True, "")]
|
||||
state.tail = [" [2/50] scph5501.bin ok",
|
||||
" Stopping. Files already installed are kept."]
|
||||
for code, shown in ((130, False), (1, True)):
|
||||
with self.subTest(code=code):
|
||||
state.code = code
|
||||
_title, lines = install.tui_outcome(state)
|
||||
self.assertEqual(any("[2/50]" in line for line in lines), shown, lines)
|
||||
|
||||
def test_a_pack_installs_from_the_pack_not_the_network(self):
|
||||
"""With --from-pack and no Wi-Fi, the plan said "to download", which
|
||||
on a handheld with no network read as a failure on its way."""
|
||||
state = checked_state()
|
||||
with mock.patch.object(install.LOCAL, "pack", object()):
|
||||
headline = str(install.tui_view(state, 80, 24)[0])
|
||||
plan = "\n".join(str(line) for line in install.tui_view(state, 80, 24))
|
||||
state.summary = {"installed": 3, "failed": [], "up_to_date": 0}
|
||||
title, lines = install.tui_outcome(state)
|
||||
self.assertIn("to install from the pack", plan, headline)
|
||||
self.assertNotIn("to download", plan)
|
||||
self.assertEqual(lines[0], "3 files installed from the pack.")
|
||||
|
||||
def test_p2_1_another_folder_comes_first_when_space_is_short(self):
|
||||
state = checked_state({"free": 1024, "writable": True, "cores": []})
|
||||
self.assertEqual([row.label for row in install.tui_rows(state)[:2]],
|
||||
@@ -2974,8 +3001,13 @@ class EndToEnd(unittest.TestCase):
|
||||
term.send("\r")
|
||||
screen = term.wait("files to download")
|
||||
self.assertIn("6 files to download", screen)
|
||||
# The second folder the run writes to, named before
|
||||
# anything is written, and again on the result.
|
||||
self.assertIn("1 file read from RetroArch's save directory goes to "
|
||||
"~/MySaves", screen)
|
||||
term.send("\r")
|
||||
term.wait("6 files installed.")
|
||||
screen = term.wait("6 files installed.")
|
||||
self.assertIn("save directory goes to ~/MySaves", screen)
|
||||
term.send("\r")
|
||||
self.assertEqual(term.close(), 0)
|
||||
results.append(written(repo, saves))
|
||||
@@ -3074,6 +3106,9 @@ class EndToEnd(unittest.TestCase):
|
||||
after = term.after()
|
||||
self.assertIn(f"Stopped: {on_disk} of 6 installed, run again to finish.", after)
|
||||
self.assertNotIn("Nothing was installed", after)
|
||||
# The plain outcome only: the log's last lines are for a run that died.
|
||||
self.assertIsNone(re.search(r"\[\d+/6\]", after), after)
|
||||
self.assertNotIn("Stopping.", after)
|
||||
time.sleep(1)
|
||||
self.assertEqual(len(repo.installed()), on_disk)
|
||||
|
||||
|
||||
@@ -1304,6 +1304,7 @@ other schemas under `api/v1/schemas/`.
|
||||
| `declared` | `platform`, `files`, `present`, `missing`, `wrong_hash`, `bytes_needed`, `dests` | Right after `check`, the same counts over the files the platform's own list names (`cores: null`), and their `dests` in the order of the `file` events. A setup that checks its own list reads its answer here, with or without `--declared-only` |
|
||||
| `found` | `platform`, `dest`, `folder`, `path`, `member`, `reason` | Each file to install that this machine already holds, after `check` and before anything is written, in `--check` and `--dry-run` too. `folder` is the `--from` folder as given, or the `bios_dir` of the check when the BIOS folder holds the file at another place, and then `reason` says where in words (`nested folder: ...` for a misplaced file, `the same file is at ...` for one the list installs there too). `path` is the file, and `member` the ZIP member holding the bytes when `path` is an archive, an empty string otherwise. Its copy is reported by a `downloaded` event |
|
||||
| `unreadable` | `platform`, `folder`, `path`, `member`, `reason` | Each file in a `--from` folder that could not be read and may be one the run still needs, right after the `found` events: its size is that of a file no copy was found for, or unknown (a folder that could not be listed, a ZIP that would not open). `member` names the ZIP member, or is empty. `reason` says why in words (`Permission denied`, `it is encrypted, a password is needed`) |
|
||||
| `saves` | `platform`, `files`, `folder` | After `detect` and before `checking`, when the list holds files read from the frontend's save directory and that directory is known: how many, and the `folder` they go to, where `<system>` and `<core>` stand for one folder per system or per core. When it is not known, the files are counted in the summary's `skipped` instead (`no_save_directory`) |
|
||||
| `download` | `platform`, `files`, `bytes` | Before the downloads start. Counts the files copied from this machine too |
|
||||
| `progress` | `files_done`, `files_total`, `bytes_done`, `bytes_total` | At most every half second while bytes arrive |
|
||||
| `transfer` | `bytes_local`, `bytes_network` | Right after each `progress` event, and once when the files of a `download` event are done. It splits `bytes_done`: `bytes_local` came from this machine (a copy found by `--from` or in the BIOS folder, the pack, a partial copy resumed), `bytes_network` from the network. A download speed is `bytes_network` over the time elapsed |
|
||||
|
||||
Reference in new issue
Block a user