mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-01 00:27:20 -05:00
fix: offer only what an install can do
Back from the systems list with nothing ticked keeps the ticks it opened with, and None says First system once nothing is left to untick. Confirm under --offline with nothing on hand has no Install, and the line mode no longer asks to download what --offline never fetches. The line mode counts the data files before its question. Screen words: the checking clock starts at zero, a pack installs from the pack, the copies found are said on a line of their own, the screen of misplaced files says the copies stay, the save folder gets a line of its own, the shared note is one line, a size keeps its unit, the same save folder remedy on both screens, A Tick on the Miyoo bar, no Home key in the help of a pad, and a system name said once with a pack.
This commit is contained in:
1 parent
44f94ab703
commit
fe1e98917b
8 files changed
+284
-52
No files matched your search
+1
-1
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||||
$defaultInstallSha256 = "8833256124ffdfa38865e7b065f2720ec20997e1a91ba340eab4067e31b20727"
|
$defaultInstallSha256 = "d59defaa5802d2d7b73b4b02e693e9592618803b308eb625a6f436b8a5388464"
|
||||||
$maximumInstallerBytes = 2MB
|
$maximumInstallerBytes = 2MB
|
||||||
$installerExit = 0
|
$installerExit = 0
|
||||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||||
|
|||||||
+100
-25
@@ -4220,7 +4220,7 @@ def place_save_entries(
|
|||||||
if place.by_core:
|
if place.by_core:
|
||||||
where += f"{os.sep}<core>"
|
where += f"{os.sep}<core>"
|
||||||
print(f" {_plural(len(saved), 'file')} read from {display}'s save directory "
|
print(f" {_plural(len(saved), 'file')} read from {display}'s save directory "
|
||||||
f"go to {where}")
|
f"{'goes' if len(saved) == 1 else 'go'} to {where}")
|
||||||
EVENTS.emit("saves", platform=str(manifest.get("platform", "")),
|
EVENTS.emit("saves", platform=str(manifest.get("platform", "")),
|
||||||
files=len(saved), folder=where)
|
files=len(saved), folder=where)
|
||||||
return files, []
|
return files, []
|
||||||
@@ -6512,7 +6512,7 @@ INSTALLED_ONLY = "installed"
|
|||||||
def _prompt_custom_selection(
|
def _prompt_custom_selection(
|
||||||
plat: str, files: list[dict], omitted: list[dict],
|
plat: str, files: list[dict], omitted: list[dict],
|
||||||
installed: tuple[list[dict], list[dict]] | None = None,
|
installed: tuple[list[dict], list[dict]] | None = None,
|
||||||
name: str = "", dry_run: bool = False,
|
name: str = "", dry_run: bool = False, data_trees: list[dict] | None = None,
|
||||||
) -> tuple[list[str], list[str], list[str]] | str | None:
|
) -> tuple[list[str], list[str], list[str]] | str | None:
|
||||||
"""Offer to narrow the install by system, core and region.
|
"""Offer to narrow the install by system, core and region.
|
||||||
|
|
||||||
@@ -6521,7 +6521,15 @@ def _prompt_custom_selection(
|
|||||||
installed read) holds, or the three choices. Each empty list keeps
|
installed read) holds, or the three choices. Each empty list keeps
|
||||||
everything on its axis.
|
everything on its axis.
|
||||||
"""
|
"""
|
||||||
print(f"\n{name or display_name(plat)}: {_sized(files)}.")
|
trees = data_trees or []
|
||||||
|
data = sum(len(entry.get("files") or {}) for entry in trees)
|
||||||
|
data_size = sum(meta.get("size", 0) for entry in trees
|
||||||
|
for meta in (entry.get("files") or {}).values())
|
||||||
|
print(f"\n{name or display_name(plat)}: {_sized(files)}"
|
||||||
|
+ (f", and {_plural(data, 'data file')} ({format_size(data_size)})" if data else "")
|
||||||
|
+ ".")
|
||||||
|
if _offline_only():
|
||||||
|
print(" --offline: only the files found on this machine are installed.")
|
||||||
# A dry run installs nothing: "install everything" was asked before it.
|
# A dry run installs nothing: "install everything" was asked before it.
|
||||||
print(" Enter) list everything, nothing is written" if dry_run
|
print(" Enter) list everything, nothing is written" if dry_run
|
||||||
else " Enter) install everything")
|
else " Enter) install everything")
|
||||||
@@ -8234,7 +8242,7 @@ def _check_copy_lines(
|
|||||||
if fixed:
|
if fixed:
|
||||||
verb = "is" if fixed == 1 else "are"
|
verb = "is" if fixed == 1 else "are"
|
||||||
them = "it" if fixed == 1 else "them"
|
them = "it" if fixed == 1 else "them"
|
||||||
lines.append(f" {fixed} of these files {verb} in the BIOS folder at the wrong place: "
|
lines.append(f" {fixed} of these files {verb} in the BIOS folder in the wrong place: "
|
||||||
f"a run without --check copies {them} where the cores open {them}, and "
|
f"a run without --check copies {them} where the cores open {them}, and "
|
||||||
f"leaves your {'copy' if fixed == 1 else 'copies'} as "
|
f"leaves your {'copy' if fixed == 1 else 'copies'} as "
|
||||||
f"{'it is' if fixed == 1 else 'they are'}.")
|
f"{'it is' if fixed == 1 else 'they are'}.")
|
||||||
@@ -10189,6 +10197,7 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None:
|
|||||||
selection = _prompt_custom_selection(
|
selection = _prompt_custom_selection(
|
||||||
plat_name, files, omitted_files, installed,
|
plat_name, files, omitted_files, installed,
|
||||||
display_name(plat_name, manifest), args.dry_run,
|
display_name(plat_name, manifest), args.dry_run,
|
||||||
|
manifest.get("data_directories") or [],
|
||||||
)
|
)
|
||||||
if selection is None:
|
if selection is None:
|
||||||
smaller = (" Nothing downloaded. To install less, run the same command "
|
smaller = (" Nothing downloaded. To install less, run the same command "
|
||||||
@@ -10387,9 +10396,23 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if to_download:
|
if to_download:
|
||||||
dl_size = sum(f.get("size", 0) for f in to_download)
|
|
||||||
fetched = [f for f in to_download if id(f) not in copies]
|
fetched = [f for f in to_download if id(f) not in copies]
|
||||||
|
if interactive and not args.yes and _offline_only() and fetched:
|
||||||
|
# The screen offers no Install for these: the question asked
|
||||||
|
# to download 6.6 GB that --offline never fetches.
|
||||||
|
print(f"\n {_plural(len(fetched), 'file')} "
|
||||||
|
f"{'needs' if len(fetched) == 1 else 'need'} the internet, which "
|
||||||
|
"--offline does not use. Copy them from your old card or a folder "
|
||||||
|
"(--from), or run the installer again with a pack (--from-pack) or "
|
||||||
|
"without --offline.")
|
||||||
|
files_pending += len(fetched)
|
||||||
|
to_download = [f for f in to_download if id(f) in copies]
|
||||||
|
fetched = []
|
||||||
|
dl_size = sum(f.get("size", 0) for f in to_download)
|
||||||
fetch_size = _bytes(fetched)
|
fetch_size = _bytes(fetched)
|
||||||
|
if not to_download:
|
||||||
|
total_up_to_date += len(up_to_date)
|
||||||
|
continue
|
||||||
if (
|
if (
|
||||||
interactive
|
interactive
|
||||||
and not args.yes
|
and not args.yes
|
||||||
@@ -10818,6 +10841,21 @@ def _split_word(word: str, room: int) -> list[str]:
|
|||||||
return pieces + [word] if word else pieces
|
return pieces + [word] if word else pieces
|
||||||
|
|
||||||
|
|
||||||
|
_UNIT = re.compile(r"(B|KB|MB|GB)\b")
|
||||||
|
|
||||||
|
|
||||||
|
def _glued_words(text: str) -> list[str]:
|
||||||
|
"""The words of text, a size kept with its unit: "5.1" / "KB" was
|
||||||
|
broken across two lines of a 40-column headline."""
|
||||||
|
words: list[str] = []
|
||||||
|
for word in text.split():
|
||||||
|
if words and _UNIT.match(word) and re.fullmatch(r"\(?\d+(\.\d+)?", words[-1]):
|
||||||
|
words[-1] += f" {word}"
|
||||||
|
else:
|
||||||
|
words.append(word)
|
||||||
|
return words
|
||||||
|
|
||||||
|
|
||||||
def _wrap(text: str, width: int, indent: str = " ") -> list[str]:
|
def _wrap(text: str, width: int, indent: str = " ") -> list[str]:
|
||||||
"""Lines of at most width cells, the ones after the first indented."""
|
"""Lines of at most width cells, the ones after the first indented."""
|
||||||
if cell_width(text) <= width:
|
if cell_width(text) <= width:
|
||||||
@@ -10825,7 +10863,7 @@ def _wrap(text: str, width: int, indent: str = " ") -> list[str]:
|
|||||||
lead = text[:len(text) - len(text.lstrip(" "))]
|
lead = text[:len(text) - len(text.lstrip(" "))]
|
||||||
lines: list[str] = []
|
lines: list[str] = []
|
||||||
line = ""
|
line = ""
|
||||||
for word in text.split():
|
for word in _glued_words(text):
|
||||||
for piece in _split_word(word, width - len(lead + indent)):
|
for piece in _split_word(word, width - len(lead + indent)):
|
||||||
prefix = lead + indent if lines else lead
|
prefix = lead + indent if lines else lead
|
||||||
longer = f"{line} {piece}" if line else piece
|
longer = f"{line} {piece}" if line else piece
|
||||||
@@ -10884,7 +10922,9 @@ class TuiState:
|
|||||||
self.status: str = ""
|
self.status: str = ""
|
||||||
self.cols: int = 80
|
self.cols: int = 80
|
||||||
self.rows: int = 24
|
self.rows: int = 24
|
||||||
self.now: float = 0.0
|
# A check started before the loop's first tick counted the
|
||||||
|
# machine's uptime.
|
||||||
|
self.now: float = time.monotonic()
|
||||||
# (platform, folder, label) for each setup found here.
|
# (platform, folder, label) for each setup found here.
|
||||||
self.setups: list = []
|
self.setups: list = []
|
||||||
self.notes: list = []
|
self.notes: list = []
|
||||||
@@ -10964,6 +11004,9 @@ class TuiState:
|
|||||||
self.must_confirm: bool = False
|
self.must_confirm: bool = False
|
||||||
# Choices, each one an option of the command line.
|
# Choices, each one an option of the command line.
|
||||||
self.ticked: set = set()
|
self.ticked: set = set()
|
||||||
|
# The ticks the systems list opened with: Back with none ticked
|
||||||
|
# brings them back, nothing having been chosen.
|
||||||
|
self.ticked_before: set | None = None
|
||||||
self.cli_systems: list = []
|
self.cli_systems: list = []
|
||||||
self.regions: list = []
|
self.regions: list = []
|
||||||
self.cli_regions: list = []
|
self.cli_regions: list = []
|
||||||
@@ -11375,6 +11418,9 @@ def tui_rows(state: TuiState) -> list[TuiRow]:
|
|||||||
if screen == "confirm":
|
if screen == "confirm":
|
||||||
if state.running or state.dry is None:
|
if state.running or state.dry is None:
|
||||||
return []
|
return []
|
||||||
|
if _offline_only() and not _dry_copies(state):
|
||||||
|
# The plan's rule: Install would only fail every file.
|
||||||
|
return [TuiRow("back", "Back")]
|
||||||
return [TuiRow("install", "Install"), TuiRow("back", "Back")]
|
return [TuiRow("install", "Install"), TuiRow("back", "Back")]
|
||||||
if screen == "error":
|
if screen == "error":
|
||||||
rows = ([TuiRow("retry", "Try again")]
|
rows = ([TuiRow("retry", "Try again")]
|
||||||
@@ -11429,6 +11475,13 @@ def _plan_copies(state: TuiState, check: dict) -> list[str]:
|
|||||||
return [dest for dest in local if dest in sizes]
|
return [dest for dest in local if dest in sizes]
|
||||||
|
|
||||||
|
|
||||||
|
def _dry_copies(state: TuiState) -> list[str]:
|
||||||
|
"""The files the confirm or preview run would install that this
|
||||||
|
machine holds."""
|
||||||
|
local = {event["dest"] for event in state.found}
|
||||||
|
return [event["dest"] for event in state.dry_files if event["dest"] in local]
|
||||||
|
|
||||||
|
|
||||||
def _short_of_space(state: TuiState, check: dict) -> bool:
|
def _short_of_space(state: TuiState, check: dict) -> bool:
|
||||||
free = state.facts.get("free")
|
free = state.facts.get("free")
|
||||||
return free is not None and check.get("bytes_needed", 0) > free
|
return free is not None and check.get("bytes_needed", 0) > free
|
||||||
@@ -11566,6 +11619,10 @@ def _back(state: TuiState) -> list[tuple]:
|
|||||||
state.stack.append("quit")
|
state.stack.append("quit")
|
||||||
state.focus["quit"] = 0
|
state.focus["quit"] = 0
|
||||||
return effects
|
return effects
|
||||||
|
if state.screen == "systems" and not state.ticked and state.ticked_before:
|
||||||
|
# Enter on None, then Esc: the plan kept its headline over an
|
||||||
|
# Install the run refused.
|
||||||
|
state.ticked = set(state.ticked_before)
|
||||||
state.stack.pop()
|
state.stack.pop()
|
||||||
if state.screen == "checking" and not state.running:
|
if state.screen == "checking" and not state.running:
|
||||||
# Stay after Esc on a check started from the command line: a
|
# Stay after Esc on a check started from the command line: a
|
||||||
@@ -11995,6 +12052,8 @@ def _activate(state: TuiState, row: TuiRow) -> list[tuple]:
|
|||||||
return _open_from_browser(state)
|
return _open_from_browser(state)
|
||||||
if action in ("misplaced", "unreadable"):
|
if action in ("misplaced", "unreadable"):
|
||||||
return _push(state, action)
|
return _push(state, action)
|
||||||
|
if action == "systems":
|
||||||
|
state.ticked_before = set(state.ticked)
|
||||||
if action == "systems" and len(state.systems) > TUI_LONG_LIST:
|
if action == "systems" and len(state.systems) > TUI_LONG_LIST:
|
||||||
# On None: picking one system of 66 began with Up, Up, A.
|
# On None: picking one system of 66 began with Up, Up, A.
|
||||||
return _push(state, action, 1)
|
return _push(state, action, 1)
|
||||||
@@ -12687,6 +12746,9 @@ def _route_hint(state: TuiState, parts: tuple) -> tuple:
|
|||||||
named.append(key + sep + what)
|
named.append(key + sep + what)
|
||||||
if state.pad == "muxterm" and state.screen == "path":
|
if state.pad == "muxterm" and state.screen == "path":
|
||||||
named.append("Select Keyboard")
|
named.append("Select Keyboard")
|
||||||
|
if state.pad == "miyoo" and state.screen in _CHECKLISTS:
|
||||||
|
tick = [entry for entry in named if entry.startswith("A ")]
|
||||||
|
named = named[:1] + tick + [entry for entry in named[1:] if entry not in tick]
|
||||||
return tuple(named)
|
return tuple(named)
|
||||||
|
|
||||||
|
|
||||||
@@ -12724,6 +12786,7 @@ _ROW_VERBS = {
|
|||||||
"jobs": "Change", "retry": "Try again", "back": "Back", "stay": "Stay",
|
"jobs": "Change", "retry": "Try again", "back": "Back", "stay": "Stay",
|
||||||
"fromcard": "Choose", "from": "Add a folder", "misplaced": "Show", "zip": "Copy from it",
|
"fromcard": "Choose", "from": "Add a folder", "misplaced": "Show", "zip": "Copy from it",
|
||||||
"unreadable": "Show", "exit": "Exit", "addapp": "Put it on the card",
|
"unreadable": "Show", "exit": "Exit", "addapp": "Put it on the card",
|
||||||
|
"cardsystem": "Choose", "rescan": "Look again",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -12955,15 +13018,18 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list:
|
|||||||
lines.append("You cannot write to this folder: choose another one.")
|
lines.append("You cannot write to this folder: choose another one.")
|
||||||
counts = (f"{check['present']} in place, {check['missing']} missing, "
|
counts = (f"{check['present']} in place, {check['missing']} missing, "
|
||||||
f"{wrong} wrong {'copy' if wrong == 1 else 'copies'}")
|
f"{wrong} wrong {'copy' if wrong == 1 else 'copies'}")
|
||||||
|
lines.append(counts)
|
||||||
if copied and fetch:
|
if copied and fetch:
|
||||||
# 47 to download above 49 missing and 1 wrong copy: the 3 found on
|
# 47 to download above 49 missing and 1 wrong copy: the 3 found on
|
||||||
# this machine are the difference, said where the sum is read.
|
# this machine are the difference, on a line of its own ("0 wrong
|
||||||
counts += (f": {len(copied)} of them {'is' if len(copied) == 1 else 'are'} "
|
# copies: 2 of them" read as two of the zero).
|
||||||
f"copied from this machine, {fetch} downloaded")
|
lines.append(f"{need} to install: {len(copied)} copied from this machine, {fetch} "
|
||||||
lines.append(counts)
|
+ ("from the pack" if LOCAL.pack is not None else "downloaded"))
|
||||||
saves = (state.saves if check is state.check
|
after_counts = len(lines)
|
||||||
|
saves =(state.saves if check is state.check
|
||||||
else state.dry_saves if check is state.dry else None)
|
else state.dry_saves if check is state.dry else None)
|
||||||
lines += [_styled(line, "path") for line in _saves_lines(state, saves)]
|
saves_lines = _saves_lines(state, saves)
|
||||||
|
lines += [_styled(line, "path" if index else "") for index, line in enumerate(saves_lines)]
|
||||||
found_in = _retroarch_config_at(dest) if dest is not None else None
|
found_in = _retroarch_config_at(dest) if dest is not None else None
|
||||||
if found_in:
|
if found_in:
|
||||||
lines.append(_styled(f"Named by {_short_path(Path(found_in[0]), state.home)}", "path"))
|
lines.append(_styled(f"Named by {_short_path(Path(found_in[0]), state.home)}", "path"))
|
||||||
@@ -12980,7 +13046,7 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list:
|
|||||||
f"{'one' if wrong == 1 else 'ones'} kept in a backup folder.")
|
f"{'one' if wrong == 1 else 'ones'} kept in a backup folder.")
|
||||||
misplaced = _tui_misplaced(state) if check is state.check else []
|
misplaced = _tui_misplaced(state) if check is state.check else []
|
||||||
if misplaced:
|
if misplaced:
|
||||||
lines.insert(3, f"{_plural(len(misplaced), 'file')} "
|
lines.insert(after_counts, f"{_plural(len(misplaced), 'file')} "
|
||||||
f"{'is' if len(misplaced) == 1 else 'are'} in the wrong place, "
|
f"{'is' if len(misplaced) == 1 else 'are'} in the wrong place, "
|
||||||
f"{'it' if len(misplaced) == 1 else 'they'} will be copied where the "
|
f"{'it' if len(misplaced) == 1 else 'they'} will be copied where the "
|
||||||
"emulators read "
|
"emulators read "
|
||||||
@@ -13003,7 +13069,7 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list:
|
|||||||
lines += _unavailable_lines(state)
|
lines += _unavailable_lines(state)
|
||||||
if check is state.check:
|
if check is state.check:
|
||||||
lines += _unplaced_lines(state.check_summary, state,
|
lines += _unplaced_lines(state.check_summary, state,
|
||||||
"Choose it in Options, Save folder.")
|
"Choose it in Options, Save folder (--save-dir).")
|
||||||
return lines[:2] + offline_lines + lines[2:]
|
return lines[:2] + offline_lines + lines[2:]
|
||||||
|
|
||||||
|
|
||||||
@@ -13015,13 +13081,16 @@ def _saves_lines(state: TuiState, saves: dict | None) -> list[str]:
|
|||||||
return []
|
return []
|
||||||
name = display_name(state.platform) if state.platform else "the frontend"
|
name = display_name(state.platform) if state.platform else "the frontend"
|
||||||
count = saves["files"]
|
count = saves["files"]
|
||||||
|
# The folder alone is cut in the middle: cut with the sentence, it
|
||||||
|
# lost "directory goes to" even at 80 columns.
|
||||||
return [f"{_plural(count, 'file')} read from {name}'s save directory "
|
return [f"{_plural(count, 'file')} read from {name}'s save directory "
|
||||||
f"{'goes' if count == 1 else 'go'} to "
|
f"{'goes' if count == 1 else 'go'} to:",
|
||||||
f"{_short_path(Path(saves['folder']), state.home)}"]
|
_short_path(Path(saves['folder']), state.home)]
|
||||||
|
|
||||||
|
|
||||||
def _unplaced_lines(summary: dict | None, state: TuiState,
|
def _unplaced_lines(summary: dict | None, state: TuiState,
|
||||||
remedy: str = "Name it with --save-dir.") -> list[str]:
|
remedy: str = "Choose it in Options, Save folder (--save-dir), "
|
||||||
|
"next time.") -> list[str]:
|
||||||
"""The files read from the frontend's save directory that the run left
|
"""The files read from the frontend's save directory that the run left
|
||||||
out, that directory being unknown: said, never silent."""
|
out, that directory being unknown: said, never silent."""
|
||||||
count = sum(group.get("files", 0) for group in (summary or {}).get("skipped") or []
|
count = sum(group.get("files", 0) for group in (summary or {}).get("skipped") or []
|
||||||
@@ -13244,8 +13313,7 @@ def _view_systems(state: TuiState, width: int, height: int) -> tuple:
|
|||||||
footer.insert(0, OTHER_HELP)
|
footer.insert(0, OTHER_HELP)
|
||||||
elif "shared" in focused.right:
|
elif "shared" in focused.right:
|
||||||
count = _shared_needed(state)[focused.value][0]
|
count = _shared_needed(state)[focused.value][0]
|
||||||
footer.insert(0, f"{count} of these files are shared with other systems, "
|
footer.insert(0, f"+{count} shared: counted once")
|
||||||
"counted once in the total.")
|
|
||||||
needle = state.filters.get("systems", "")
|
needle = state.filters.get("systems", "")
|
||||||
after = [f"No system matches '{needle}'."] if len(rows) == 3 and needle else []
|
after = [f"No system matches '{needle}'."] if len(rows) == 3 and needle else []
|
||||||
if state.status:
|
if state.status:
|
||||||
@@ -13301,7 +13369,9 @@ _STATE_WORDS = {"missing": "missing", "wrong_hash": "wrong copy"}
|
|||||||
def _pager_items(state: TuiState) -> list:
|
def _pager_items(state: TuiState) -> list:
|
||||||
"""The text of a screen that scrolls, the same for its view and its keys."""
|
"""The text of a screen that scrolls, the same for its view and its keys."""
|
||||||
if state.screen == "misplaced":
|
if state.screen == "misplaced":
|
||||||
return [f"{event['dest']}: {event['reason']}" for event in _tui_misplaced(state)]
|
return ["They are copied where the emulators read them. Your copies stay where "
|
||||||
|
"they are.", ""] + [f"{event['dest']}: {event['reason']}"
|
||||||
|
for event in _tui_misplaced(state)]
|
||||||
if state.screen == "unreadable":
|
if state.screen == "unreadable":
|
||||||
return [f"{event['path']}" + (f", {event['member']}" if event["member"] else "")
|
return [f"{event['path']}" + (f", {event['member']}" if event["member"] else "")
|
||||||
+ f": {event['reason']}" for event in state.unreadable]
|
+ f": {event['reason']}" for event in state.unreadable]
|
||||||
@@ -13338,7 +13408,7 @@ def _view_unreadable(state: TuiState, width: int, height: int) -> tuple:
|
|||||||
|
|
||||||
|
|
||||||
def _view_misplaced(state: TuiState, width: int, height: int) -> tuple:
|
def _view_misplaced(state: TuiState, width: int, height: int) -> tuple:
|
||||||
return ("Files in the wrong place", _pager(state, width, height, _pager_items(state)),
|
return ("Files in the wrong place", _pager(state, width, height, _pager_items(state), 2),
|
||||||
("A/Enter Back", "Up/Down Scroll"))
|
("A/Enter Back", "Up/Down Scroll"))
|
||||||
|
|
||||||
|
|
||||||
@@ -13451,7 +13521,8 @@ def _tui_sources(state: TuiState, installed: int, failed: int) -> str:
|
|||||||
name = _last_name(folder)
|
name = _last_name(folder)
|
||||||
folders[name] = folders.get(name, 0) + 1
|
folders[name] = folders.get(name, 0) + 1
|
||||||
copied = fixed + sum(folders.values())
|
copied = fixed + sum(folders.values())
|
||||||
return sources_line(list(folders.items()), fixed, max(0, installed - copied), failed)
|
return sources_line(list(folders.items()), fixed, max(0, installed - copied), failed,
|
||||||
|
"from the pack" if LOCAL.pack is not None else "downloaded")
|
||||||
|
|
||||||
|
|
||||||
def _unavailable(state: TuiState) -> tuple[list[dict], int]:
|
def _unavailable(state: TuiState) -> tuple[list[dict], int]:
|
||||||
@@ -13551,7 +13622,7 @@ def tui_outcome(state: TuiState) -> tuple[str, list[str]]:
|
|||||||
ok = {dest for dest, good, _why in state.finished if good}
|
ok = {dest for dest, good, _why in state.finished if good}
|
||||||
if any(reason and dest in ok for dest, (_folder, reason) in state.local_dests.items()):
|
if any(reason and dest in ok for dest, (_folder, reason) in state.local_dests.items()):
|
||||||
# A fix copies: the misplaced files are the user's, never moved.
|
# A fix copies: the misplaced files are the user's, never moved.
|
||||||
lines.append("Your copies at the wrong place were left as they are.")
|
lines.append("Your copies in the wrong place were left as they are.")
|
||||||
lines.append(f"BIOS folder: {state.dest}")
|
lines.append(f"BIOS folder: {state.dest}")
|
||||||
if installed + up_to_date:
|
if installed + up_to_date:
|
||||||
lines += _saves_lines(state, state.done_saves)
|
lines += _saves_lines(state, state.done_saves)
|
||||||
@@ -13716,7 +13787,8 @@ def _help_lines(state: TuiState) -> list[tuple[str, str]]:
|
|||||||
keys = [(names["move"], "Move")]
|
keys = [(names["move"], "Move")]
|
||||||
if len(tui_rows(state)) > TUI_LONG_LIST or screen in _PAGERS:
|
if len(tui_rows(state)) > TUI_LONG_LIST or screen in _PAGERS:
|
||||||
keys += [*([(names["page"], "Page")] if names["page"] else []),
|
keys += [*([(names["page"], "Page")] if names["page"] else []),
|
||||||
("Home/End, g/G", "First, last")]
|
*([("Home/End, g/G", "First, last")] if state.pad in ("both", "keys")
|
||||||
|
else [])]
|
||||||
primary = _primary(state) or _TUI_VIEWS[screen](state, 80, 24)[2][0]
|
primary = _primary(state) or _TUI_VIEWS[screen](state, 80, 24)[2][0]
|
||||||
ok = names["ok"]
|
ok = names["ok"]
|
||||||
if state.pad == "miyoo" and screen in _CHECKLISTS:
|
if state.pad == "miyoo" and screen in _CHECKLISTS:
|
||||||
@@ -13755,6 +13827,9 @@ def _primary(state: TuiState) -> str | None:
|
|||||||
verb = _ROW_VERBS.get(row.action)
|
verb = _ROW_VERBS.get(row.action)
|
||||||
if state.screen == "systems" and row.action == "system" and not state.ticked:
|
if state.screen == "systems" and row.action == "system" and not state.ticked:
|
||||||
verb = "Only this system"
|
verb = "Only this system"
|
||||||
|
if state.screen == "systems" and row.action == "none" and not state.ticked:
|
||||||
|
# Nothing left to untick: A goes to the first system.
|
||||||
|
verb = "First system"
|
||||||
return f"A/Enter {verb}" if verb else None
|
return f"A/Enter {verb}" if verb else None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
|||||||
TEMP_INSTALLER=""
|
TEMP_INSTALLER=""
|
||||||
TEMP_DIRECTORY=""
|
TEMP_DIRECTORY=""
|
||||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||||
DEFAULT_INSTALL_SHA256="8833256124ffdfa38865e7b065f2720ec20997e1a91ba340eab4067e31b20727"
|
DEFAULT_INSTALL_SHA256="d59defaa5802d2d7b73b4b02e693e9592618803b308eb625a6f436b8a5388464"
|
||||||
MAX_INSTALLER_BYTES=2097152
|
MAX_INSTALLER_BYTES=2097152
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
|
|||||||
@@ -27,8 +27,8 @@
|
|||||||
{
|
{
|
||||||
"dest": "roms/ports/retrobios/install.py",
|
"dest": "roms/ports/retrobios/install.py",
|
||||||
"repo_path": "install.py",
|
"repo_path": "install.py",
|
||||||
"size": 646794,
|
"size": 650749,
|
||||||
"sha256": "8833256124ffdfa38865e7b065f2720ec20997e1a91ba340eab4067e31b20727",
|
"sha256": "d59defaa5802d2d7b73b4b02e693e9592618803b308eb625a6f436b8a5388464",
|
||||||
"executable": false
|
"executable": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -71,8 +71,8 @@
|
|||||||
"muos": {
|
"muos": {
|
||||||
"archive": {
|
"archive": {
|
||||||
"dest": "ARCHIVE/RetroBIOS.muxapp",
|
"dest": "ARCHIVE/RetroBIOS.muxapp",
|
||||||
"size": 699343,
|
"size": 703298,
|
||||||
"sha256": "42226d45d6a31c86b726b1af321e043f9894f9993d8c6de0f16cc84c04a2a1ed"
|
"sha256": "bb54c96f4ae4850f997cc925b2958b21477be655a658b6bc3551d1aef68fdbb5"
|
||||||
},
|
},
|
||||||
"members": [
|
"members": [
|
||||||
{
|
{
|
||||||
@@ -92,8 +92,8 @@
|
|||||||
{
|
{
|
||||||
"name": "RetroBIOS/install.py",
|
"name": "RetroBIOS/install.py",
|
||||||
"repo_path": "install.py",
|
"repo_path": "install.py",
|
||||||
"size": 646794,
|
"size": 650749,
|
||||||
"sha256": "8833256124ffdfa38865e7b065f2720ec20997e1a91ba340eab4067e31b20727",
|
"sha256": "d59defaa5802d2d7b73b4b02e693e9592618803b308eb625a6f436b8a5388464",
|
||||||
"executable": false
|
"executable": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -166,8 +166,8 @@
|
|||||||
{
|
{
|
||||||
"dest": ".config/modules/retrobios/install.py",
|
"dest": ".config/modules/retrobios/install.py",
|
||||||
"repo_path": "install.py",
|
"repo_path": "install.py",
|
||||||
"size": 646794,
|
"size": 650749,
|
||||||
"sha256": "8833256124ffdfa38865e7b065f2720ec20997e1a91ba340eab4067e31b20727",
|
"sha256": "d59defaa5802d2d7b73b4b02e693e9592618803b308eb625a6f436b8a5388464",
|
||||||
"executable": false
|
"executable": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Vendored
+6
-6
@@ -550,7 +550,7 @@
|
|||||||
|
|
||||||
|
|
||||||
2 of 3 systems: 4 files, 1.0 MB
|
2 of 3 systems: 4 files, 1.0 MB
|
||||||
Start Continue R2 Back A Tick
|
Start Continue A Tick R2 Back
|
||||||
=== systems 40x15
|
=== systems 40x15
|
||||||
RetroBIOS: Choose systems
|
RetroBIOS: Choose systems
|
||||||
|
|
||||||
@@ -566,7 +566,7 @@
|
|||||||
|
|
||||||
|
|
||||||
2 of 3 systems: 4 files, 1.0 MB
|
2 of 3 systems: 4 files, 1.0 MB
|
||||||
Start Continue R2 Back A Tick
|
Start Continue A Tick R2 Back
|
||||||
=== systems 80x24
|
=== systems 80x24
|
||||||
RetroBIOS: Choose systems
|
RetroBIOS: Choose systems
|
||||||
|
|
||||||
@@ -591,7 +591,7 @@
|
|||||||
|
|
||||||
|
|
||||||
2 of 3 systems: 4 files, 1.0 MB
|
2 of 3 systems: 4 files, 1.0 MB
|
||||||
Start Continue R2 Back A Tick
|
Start Continue A Tick R2 Back
|
||||||
=== options 40x12
|
=== options 40x12
|
||||||
RetroBIOS: Options
|
RetroBIOS: Options
|
||||||
|
|
||||||
@@ -712,7 +712,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Start Filter R2 Back A Tick
|
Start Filter A Tick R2 Back
|
||||||
=== cores 40x15
|
=== cores 40x15
|
||||||
RetroBIOS: Cores
|
RetroBIOS: Cores
|
||||||
|
|
||||||
@@ -728,7 +728,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Start Filter R2 Back A Tick
|
Start Filter A Tick R2 Back
|
||||||
=== cores 80x24
|
=== cores 80x24
|
||||||
RetroBIOS: Cores
|
RetroBIOS: Cores
|
||||||
|
|
||||||
@@ -753,7 +753,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Start Filter R2 Back A Tick
|
Start Filter A Tick R2 Back
|
||||||
=== preview 40x12
|
=== preview 40x12
|
||||||
RetroBIOS: Preview
|
RetroBIOS: Preview
|
||||||
|
|
||||||
|
|||||||
@@ -438,7 +438,7 @@ class MisplacedInTheBiosFolder(Scratch):
|
|||||||
for name, data in PAYLOAD.items():
|
for name, data in PAYLOAD.items():
|
||||||
self.place(f"system/{name}", data)
|
self.place(f"system/{name}", data)
|
||||||
out = self.check()
|
out = self.check()
|
||||||
self.assertIn("4 of these files are in the BIOS folder at the wrong place: a run "
|
self.assertIn("4 of these files are in the BIOS folder in the wrong place: a run "
|
||||||
"without --check copies them where the cores open them, and leaves "
|
"without --check copies them where the cores open them, and leaves "
|
||||||
"your copies as they are.", out)
|
"your copies as they are.", out)
|
||||||
self.assertFalse((self.dest / "scph5501.bin").exists())
|
self.assertFalse((self.dest / "scph5501.bin").exists())
|
||||||
|
|||||||
+166
-9
@@ -2044,7 +2044,7 @@ class CopyFromAnOldCard(unittest.TestCase):
|
|||||||
state.finished = [("scph5500.bin", True, ""), ("gba_bios.bin", True, "")]
|
state.finished = [("scph5500.bin", True, ""), ("gba_bios.bin", True, "")]
|
||||||
text = " ".join(" ".join(install.tui_outcome(state)[1]).split())
|
text = " ".join(" ".join(install.tui_outcome(state)[1]).split())
|
||||||
self.assertIn("1 file fixed from copies already in the BIOS folder, 1 downloaded. "
|
self.assertIn("1 file fixed from copies already in the BIOS folder, 1 downloaded. "
|
||||||
"Your copies at the wrong place were left as they are.", text)
|
"Your copies in the wrong place were left as they are.", text)
|
||||||
|
|
||||||
def test_a_local_copy_is_no_download_speed(self):
|
def test_a_local_copy_is_no_download_speed(self):
|
||||||
state = checked_state()
|
state = checked_state()
|
||||||
@@ -2432,7 +2432,7 @@ class AuditRoundThree(unittest.TestCase):
|
|||||||
self.assertEqual(rows[three].right, "1 file, 100 B + 2 shared")
|
self.assertEqual(rows[three].right, "1 file, 100 B + 2 shared")
|
||||||
state.focus["systems"] = three
|
state.focus["systems"] = three
|
||||||
text = " ".join(" ".join(install.tui_view(state, 80, 24)).split())
|
text = " ".join(" ".join(install.tui_view(state, 80, 24)).split())
|
||||||
self.assertIn("2 of these files are shared with other systems, counted once", text)
|
self.assertIn("+2 shared: counted once", text)
|
||||||
# The line mode counts the same files apart.
|
# The line mode counts the same files apart.
|
||||||
self.assertIn("and 2 files, 500 B shared with other systems", install._system_sized([
|
self.assertIn("and 2 files, 500 B shared with other systems", install._system_sized([
|
||||||
{"dest": "panafz1.bin", "size": 100, "systems": ["3do"]},
|
{"dest": "panafz1.bin", "size": 100, "systems": ["3do"]},
|
||||||
@@ -2767,11 +2767,12 @@ class AuditRoundFour(unittest.TestCase):
|
|||||||
plan = " ".join(" ".join(install.tui_view(state, 120, 40)).split())
|
plan = " ".join(" ".join(install.tui_view(state, 120, 40)).split())
|
||||||
# The plan names the row that sets it, the result the option a
|
# The plan names the row that sets it, the result the option a
|
||||||
# command carries.
|
# command carries.
|
||||||
self.assertIn(line + "Choose it in Options, Save folder.", plan)
|
self.assertIn(line + "Choose it in Options, Save folder (--save-dir).", plan)
|
||||||
state.stack.append("result")
|
state.stack.append("result")
|
||||||
state.code = 0
|
state.code = 0
|
||||||
state.summary = {**summary, "mode": "install", "installed": 5}
|
state.summary = {**summary, "mode": "install", "installed": 5}
|
||||||
self.assertIn(line + "Name it with --save-dir.", install.tui_outcome(state)[1])
|
self.assertIn(line + "Choose it in Options, Save folder (--save-dir), next time.",
|
||||||
|
install.tui_outcome(state)[1])
|
||||||
|
|
||||||
|
|
||||||
class TheSaveFolderHasARow(unittest.TestCase):
|
class TheSaveFolderHasARow(unittest.TestCase):
|
||||||
@@ -3441,6 +3442,162 @@ class OfflineInTheInterface(unittest.TestCase):
|
|||||||
self.assertIn("--offline", install.tui_command(state))
|
self.assertIn("--offline", install.tui_command(state))
|
||||||
|
|
||||||
|
|
||||||
|
class AuditFive(unittest.TestCase):
|
||||||
|
"""tui_audit_5 P2-1, P2-2 and its P3 points."""
|
||||||
|
|
||||||
|
def long_list(self) -> install.TuiState:
|
||||||
|
state = OneSystemInFewPresses().long_list("keys")
|
||||||
|
state.cols, state.rows = 80, 24
|
||||||
|
return state
|
||||||
|
|
||||||
|
def test_back_from_none_keeps_the_ticks_it_opened_with(self):
|
||||||
|
state = self.long_list()
|
||||||
|
everything = set(state.ticked)
|
||||||
|
press(state, "down", "enter")
|
||||||
|
self.assertEqual(install.tui_rows(state)[state.focus["systems"]].action, "none")
|
||||||
|
press(state, "enter")
|
||||||
|
self.assertEqual(state.ticked, set())
|
||||||
|
press(state, "up", "up")
|
||||||
|
self.assertEqual(install._primary(state), "A/Enter First system")
|
||||||
|
press(state, "esc")
|
||||||
|
self.assertEqual(state.screen, "plan")
|
||||||
|
self.assertEqual(state.ticked, everything)
|
||||||
|
text = " ".join(install.tui_view(state, 80, 24))
|
||||||
|
self.assertNotIn("No system is ticked", text)
|
||||||
|
|
||||||
|
def test_back_after_a_choice_keeps_the_choice(self):
|
||||||
|
state = self.long_list()
|
||||||
|
press(state, "down", "enter", "enter")
|
||||||
|
press(state, " ")
|
||||||
|
chosen = set(state.ticked)
|
||||||
|
press(state, "esc")
|
||||||
|
self.assertEqual((state.screen, state.ticked), ("plan", chosen))
|
||||||
|
|
||||||
|
def test_confirm_offline_with_nothing_on_hand_has_no_install(self):
|
||||||
|
state = checked_state()
|
||||||
|
state.stack.append("confirm")
|
||||||
|
state.dry = dict(CHECK)
|
||||||
|
state.dry_files = [dict(event, state="missing") for event in FILES]
|
||||||
|
with mock.patch.dict(os.environ, {install.OFFLINE_VARIABLE: "1"}):
|
||||||
|
rows = [row.action for row in install.tui_rows(state)]
|
||||||
|
text = " ".join(" ".join(install.tui_view(state, 120, 40)).split())
|
||||||
|
self.assertEqual(rows, ["back"])
|
||||||
|
self.assertIn("Copy them from your old card or a folder", text)
|
||||||
|
state.found = [{"event": "found", "platform": "retroarch",
|
||||||
|
"dest": FILES[0]["dest"], "folder": "/media/old",
|
||||||
|
"path": "/media/old/x", "member": "", "reason": ""}]
|
||||||
|
self.assertEqual(install.tui_rows(state)[0].action, "install")
|
||||||
|
|
||||||
|
def test_the_checking_clock_starts_at_zero(self):
|
||||||
|
state = install.TuiState()
|
||||||
|
self.assertLess(abs(state.now - time.monotonic()), 5)
|
||||||
|
|
||||||
|
def test_a_pack_installs_from_the_pack(self):
|
||||||
|
state = checked_state()
|
||||||
|
state.found = [{"event": "found", "platform": "retroarch", "dest": FILES[0]["dest"],
|
||||||
|
"folder": "/media/old", "path": "/media/old/x", "member": "",
|
||||||
|
"reason": "misplaced"}]
|
||||||
|
with mock.patch.object(install.LOCAL, "pack", mock.Mock()):
|
||||||
|
text = " ".join(" ".join(install.tui_view(state, 120, 40)).split())
|
||||||
|
self.assertIn("1 copied from this machine,", text)
|
||||||
|
self.assertIn("from the pack", text)
|
||||||
|
self.assertNotIn("downloaded", text)
|
||||||
|
self.assertNotIn("of them", text)
|
||||||
|
state.finished = [(FILES[0]["dest"], True, ""), (FILES[1]["dest"], True, "")]
|
||||||
|
state.local_dests = {FILES[0]["dest"]: ("/media/old", "misplaced")}
|
||||||
|
self.assertIn("1 from the pack", install._tui_sources(state, 2, 0))
|
||||||
|
|
||||||
|
def test_the_wrong_place_screen_says_the_copies_stay(self):
|
||||||
|
state = checked_state()
|
||||||
|
state.stack.append("misplaced")
|
||||||
|
self.assertEqual(install._pager_items(state)[0],
|
||||||
|
"They are copied where the emulators read them. Your copies stay "
|
||||||
|
"where they are.")
|
||||||
|
|
||||||
|
def test_the_save_folder_is_a_line_of_its_own(self):
|
||||||
|
state = checked_state()
|
||||||
|
saves = {"files": 1, "folder": str(HOME / ".config" / "retroarch" / "saves" / "<core>")}
|
||||||
|
lines = install._saves_lines(state, saves)
|
||||||
|
self.assertEqual(lines, ["1 file read from RetroArch's save directory goes to:",
|
||||||
|
"~/.config/retroarch/saves/<core>"])
|
||||||
|
|
||||||
|
def test_the_shared_note_is_one_line(self):
|
||||||
|
state = self.long_list()
|
||||||
|
state.cols, state.rows = 40, 12
|
||||||
|
state.files = [{"event": "file", "dest": "mame.zip", "size": 10,
|
||||||
|
"systems": ["sys-00", "sys-01"]}]
|
||||||
|
state.stack.append("systems")
|
||||||
|
state.focus["systems"] = 3
|
||||||
|
lines = install.tui_view(state, 40, 12)
|
||||||
|
self.assertIn(" +1 shared: counted once", lines)
|
||||||
|
|
||||||
|
def test_the_save_folder_remedy_is_the_same_on_both_screens(self):
|
||||||
|
summary = {"skipped": [{"reason": "no_save_directory", "files": 1}]}
|
||||||
|
state = checked_state()
|
||||||
|
lines = install._unplaced_lines(summary, state)
|
||||||
|
self.assertIn("Options, Save folder (--save-dir)", lines[0])
|
||||||
|
|
||||||
|
def test_a_size_keeps_its_unit(self):
|
||||||
|
for width in range(20, 45):
|
||||||
|
for line in install._wrap("2 files to install from the pack, 5.1 KB", width):
|
||||||
|
self.assertNotEqual(line.strip(), "KB")
|
||||||
|
|
||||||
|
def test_the_miyoo_bar_names_the_tick(self):
|
||||||
|
state = self.long_list()
|
||||||
|
state.pad = "miyoo"
|
||||||
|
press(state, "down", "enter")
|
||||||
|
state.ticked = set()
|
||||||
|
state.focus["systems"] = 3
|
||||||
|
bar = install.tui_view(state, 40, 15)[-1]
|
||||||
|
self.assertIn("A Tick", bar)
|
||||||
|
self.assertTrue(bar.strip().startswith("Start Only this system"))
|
||||||
|
|
||||||
|
def test_muxterm_help_names_no_home_key(self):
|
||||||
|
state = self.long_list()
|
||||||
|
state.pad = "muxterm"
|
||||||
|
press(state, "down", "enter")
|
||||||
|
keys = [key for key, _what in install._help_lines(state)]
|
||||||
|
self.assertNotIn("Home/End, g/G", keys)
|
||||||
|
state.pad = "keys"
|
||||||
|
self.assertIn("Home/End, g/G", [key for key, _what in install._help_lines(state)])
|
||||||
|
|
||||||
|
|
||||||
|
class LineModeOffline(unittest.TestCase):
|
||||||
|
"""tui_audit_5 P2-3 and P3-8.4: the line mode under --offline asked to
|
||||||
|
download, and said nothing of the data files."""
|
||||||
|
|
||||||
|
def test_nothing_is_offered_that_cannot_come(self):
|
||||||
|
with FakeRepository() as repo:
|
||||||
|
env = {**repo.env(), "TERM": "xterm", install.OFFLINE_VARIABLE: "1"}
|
||||||
|
term = Pty(["--plain", "--manifest", str(repo.root / "install" / "retroarch.json"),
|
||||||
|
"--dest", str(repo.dest), "--offline"], env)
|
||||||
|
term.wait("q) quit")
|
||||||
|
term.send("\r")
|
||||||
|
term.close()
|
||||||
|
out = term.raw.decode("utf-8", "replace")
|
||||||
|
self.assertIn("--offline: only the files found on this machine are installed.", out)
|
||||||
|
self.assertIn("5 files need the internet, which --offline does not use.", out)
|
||||||
|
self.assertNotIn("Downloading", out)
|
||||||
|
self.assertNotIn("failed", out.lower().replace("not failed", ""))
|
||||||
|
|
||||||
|
def test_the_data_files_are_counted_before_the_question(self):
|
||||||
|
files = [{"dest": "a.bin", "size": 1024}]
|
||||||
|
trees = [{"ref": "t", "files": {"x": {"size": 2048}, "y": {"size": 2048}}}]
|
||||||
|
with mock.patch.object(install, "_ask", return_value=""), \
|
||||||
|
contextlib.redirect_stdout(io.StringIO()) as out:
|
||||||
|
install._prompt_custom_selection("retroarch", files, [], data_trees=trees)
|
||||||
|
self.assertIn("RetroArch: 1 file, 1.0 KB, and 2 data files (4.0 KB).", out.getvalue())
|
||||||
|
|
||||||
|
def test_one_save_file_goes(self):
|
||||||
|
files = [{"dest": "keys.txt", "root": "save", "size": 1}]
|
||||||
|
place = install.SavePlace(Path("/saves"), False)
|
||||||
|
with mock.patch.object(install, "save_directory_for", return_value=place), \
|
||||||
|
contextlib.redirect_stdout(io.StringIO()) as out:
|
||||||
|
install.place_save_entries(files, Path("/bios"), {}, None, "RetroArch")
|
||||||
|
self.assertIn("1 file read from RetroArch's save directory goes to /saves",
|
||||||
|
out.getvalue())
|
||||||
|
|
||||||
|
|
||||||
class NarrowSystemNames(unittest.TestCase):
|
class NarrowSystemNames(unittest.TestCase):
|
||||||
"""At 40 columns the size column took half the line: Game Boy, Game Boy
|
"""At 40 columns the size column took half the line: Game Boy, Game Boy
|
||||||
Advance and Game Boy Color all read "Nintendo Gam"."""
|
Advance and Game Boy Color all read "Nintendo Gam"."""
|
||||||
@@ -3554,7 +3711,7 @@ class TheSaveFolderLineFollowsTheChoice(unittest.TestCase):
|
|||||||
|
|
||||||
SAVES = {"event": "saves", "platform": "retroarch", "files": 1,
|
SAVES = {"event": "saves", "platform": "retroarch", "files": 1,
|
||||||
"folder": "/home/user/MySaves/<core>"}
|
"folder": "/home/user/MySaves/<core>"}
|
||||||
LINE = "1 file read from RetroArch's save directory goes to ~/MySaves/<core>"
|
LINE = "1 file read from RetroArch's save directory goes to: ~/MySaves/<core>"
|
||||||
|
|
||||||
def said(self, lines) -> str:
|
def said(self, lines) -> str:
|
||||||
return " ".join(" ".join(lines).split())
|
return " ".join(" ".join(lines).split())
|
||||||
@@ -4356,11 +4513,11 @@ class EndToEnd(unittest.TestCase):
|
|||||||
self.assertIn("6 files to download", screen)
|
self.assertIn("6 files to download", screen)
|
||||||
# The second folder the run writes to, named before
|
# The second folder the run writes to, named before
|
||||||
# anything is written, and again on the result.
|
# anything is written, and again on the result.
|
||||||
self.assertIn("1 file read from RetroArch's save directory goes to "
|
self.assertIn("1 file read from RetroArch's save directory goes to:\n"
|
||||||
"~/MySaves", screen)
|
" ~/MySaves", screen)
|
||||||
term.send("\r")
|
term.send("\r")
|
||||||
screen = term.wait("6 files installed.")
|
screen = term.wait("6 files installed.")
|
||||||
self.assertIn("save directory goes to ~/MySaves", screen)
|
self.assertIn("save directory goes to:\n ~/MySaves", screen)
|
||||||
term.send("\r")
|
term.send("\r")
|
||||||
self.assertEqual(term.close(), 0)
|
self.assertEqual(term.close(), 0)
|
||||||
results.append(written(repo, saves))
|
results.append(written(repo, saves))
|
||||||
@@ -4741,7 +4898,7 @@ class ThePlanNumbersAddUp(unittest.TestCase):
|
|||||||
state.found = [found("scph5500.bin", str(SYSTEM_DIR), "one folder too deep")]
|
state.found = [found("scph5500.bin", str(SYSTEM_DIR), "one folder too deep")]
|
||||||
screen = " ".join(" ".join(install.tui_view(state, 120, 40)).split())
|
screen = " ".join(" ".join(install.tui_view(state, 120, 40)).split())
|
||||||
self.assertIn("3 files to download", screen)
|
self.assertIn("3 files to download", screen)
|
||||||
self.assertIn("1 in place, 3 missing, 1 wrong copy: 1 of them is copied from this "
|
self.assertIn("1 in place, 3 missing, 1 wrong copy 4 to install: 1 copied from this "
|
||||||
"machine, 3 downloaded", screen)
|
"machine, 3 downloaded", screen)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -736,7 +736,7 @@ class TheInstallerWritesThem(unittest.TestCase):
|
|||||||
b"keys below the save directory",
|
b"keys below the save directory",
|
||||||
)
|
)
|
||||||
self.assertFalse((app / "system" / "Azahar").exists())
|
self.assertFalse((app / "system" / "Azahar").exists())
|
||||||
self.assertIn("read from RetroArch's save directory go to", proc.stdout)
|
self.assertIn("read from RetroArch's save directory goes to", proc.stdout)
|
||||||
|
|
||||||
def test_where_save_dir_points(self):
|
def test_where_save_dir_points(self):
|
||||||
repo = _Repository(self, self.FILES)
|
repo = _Repository(self, self.FILES)
|
||||||
|
|||||||
Reference in new issue
Block a user