mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 12:49:03 -05:00
fix: tell offline from a lost connection on screen
With --offline and no list, the full screen said No connection and offered a Try again that could never work. It now names offline mode and the two ways a run gets its list without the network. With a list, the plan counted downloads the run forbids: it says what this machine can supply and what needs the internet, and the printed command keeps --offline.
This commit is contained in:
1 parent
ba4db2ab57
commit
812d5d157e
4 files changed
+93
-5
No files matched your search
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "eaa912af47dc6f29e4436ec200a1b431b8ddd8006ca08e57c8c73d3a05717818"
|
||||
$defaultInstallSha256 = "1f6b90760303d04cb6beef7fd294371b2e1d4ba733578c59a95e1b0395c2f376"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installerExit = 0
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
|
||||
+43
-3
@@ -10140,6 +10140,8 @@ def tui_flags(state: TuiState) -> list[tuple[str, str | None]]:
|
||||
if state.jobs != 8:
|
||||
flags.append(("--jobs", str(state.jobs)))
|
||||
flags += [("--from", str(folder)) for folder in state.from_folders]
|
||||
if offline_requested() and not any(flag == "--offline" for flag, _v in flags):
|
||||
flags.append(("--offline", None))
|
||||
return flags
|
||||
|
||||
|
||||
@@ -10312,7 +10314,8 @@ def tui_rows(state: TuiState) -> list[TuiRow]:
|
||||
return [TuiRow("install", "Install"), TuiRow("back", "Back")]
|
||||
if screen == "error":
|
||||
rows = ([TuiRow("retry", "Try again")]
|
||||
if state.run == "check" and state.error_title else [])
|
||||
if state.run == "check" and state.error_title
|
||||
and state.error_title != TUI_OFFLINE_TITLE else [])
|
||||
return rows + [TuiRow("back", "Choose another setup or folder" if state.run == "check"
|
||||
else "Back"),
|
||||
TuiRow("quit", "Quit")]
|
||||
@@ -10328,6 +10331,10 @@ def _plan_rows(state: TuiState) -> list[TuiRow]:
|
||||
size = check.get("bytes_needed", 0)
|
||||
label = f"Install {format_size(size)}" if size >= CONFIRM_FROM_BYTES else "Install"
|
||||
rows = [TuiRow("install", label)]
|
||||
if _offline_only() and not _plan_copies(state, check):
|
||||
# Nothing here to copy, and the network refused: Install would
|
||||
# only fail every file.
|
||||
rows = []
|
||||
if state.facts.get("writable") is False or _short_of_space(state, check):
|
||||
# Every file would fail alike, or the run would stop before the
|
||||
# first one: the way out is another folder.
|
||||
@@ -10343,6 +10350,18 @@ def _plan_rows(state: TuiState) -> list[TuiRow]:
|
||||
return rows + [TuiRow("options", "Options"), TuiRow("quit", "Quit")]
|
||||
|
||||
|
||||
def _offline_only() -> bool:
|
||||
"""--offline without a pack: what is not on this machine cannot come."""
|
||||
return offline_requested() and LOCAL.pack is None
|
||||
|
||||
|
||||
def _plan_copies(state: TuiState, check: dict) -> list[str]:
|
||||
"""The files to install that this machine holds, by destination."""
|
||||
local = {event["dest"] for event in state.found} if check is state.check else set()
|
||||
sizes = {event["dest"] for event in state.files}
|
||||
return [dest for dest in local if dest in sizes]
|
||||
|
||||
|
||||
def _short_of_space(state: TuiState, check: dict) -> bool:
|
||||
free = state.facts.get("free")
|
||||
return free is not None and check.get("bytes_needed", 0) > free
|
||||
@@ -11443,13 +11462,18 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list:
|
||||
fetch = max(0, need - len(copied))
|
||||
fetch_bytes = max(0, check["bytes_needed"] - sum(sizes[dest_] for dest_ in copied))
|
||||
elsewhere = _tui_found_elsewhere(state) if check is state.check else {}
|
||||
offline = _offline_only()
|
||||
if elsewhere:
|
||||
# The folder by its last name: the whole path pushed the BIOS
|
||||
# folder off a 40x15 screen. The Options row spells it out.
|
||||
where = ", ".join(f"{len(events)} found in {_last_name(folder)}"
|
||||
for folder, events in elsewhere.items())
|
||||
headline = (f"{where}, {fetch} to download ({format_size(fetch_bytes)})"
|
||||
headline = (f"{where}, {fetch} need the internet (--offline)" if fetch and offline
|
||||
else f"{where}, {fetch} to download ({format_size(fetch_bytes)})"
|
||||
if fetch else f"{where}, nothing to download")
|
||||
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:
|
||||
headline = f"{_plural(fetch, 'file')} to download, {format_size(fetch_bytes)}"
|
||||
elif need:
|
||||
@@ -11461,6 +11485,9 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list:
|
||||
headline = (f"{_plural(data, 'data file')} to fetch" if data
|
||||
else "Everything is in place")
|
||||
wrong = check["wrong_hash"]
|
||||
offline_lines = (["Copy them from your old card or a folder, or run the installer "
|
||||
"again with a pack (--from-pack) or without --offline."]
|
||||
if offline and fetch else [])
|
||||
# The folder comes second: on a 40x12 screen it is what is left once
|
||||
# the rows are drawn, and nothing is written before it is seen.
|
||||
lines: list = [
|
||||
@@ -11505,7 +11532,7 @@ def _plan_lines(check: dict, dest: Path | None, state: TuiState) -> list:
|
||||
lines += _unavailable_lines(state)
|
||||
if check is state.check:
|
||||
lines += _unplaced_lines(state.check_summary, state)
|
||||
return lines
|
||||
return lines[:2] + offline_lines + lines[2:]
|
||||
|
||||
|
||||
def _unplaced_lines(summary: dict | None, state: TuiState) -> list[str]:
|
||||
@@ -12379,6 +12406,9 @@ class _EngineRun:
|
||||
TUI_FETCH_TIMEOUT = 15
|
||||
|
||||
|
||||
TUI_OFFLINE_TITLE = "No internet: offline mode"
|
||||
|
||||
|
||||
def _tui_fetch(plat: str, inbox: queue.Queue, log: io.StringIO) -> None:
|
||||
"""The file list, fetched once in this process, or what went wrong."""
|
||||
if LOCAL.manifest is not None or LOCAL.pack is not None:
|
||||
@@ -12407,6 +12437,16 @@ def _tui_fetch(plat: str, inbox: queue.Queue, log: io.StringIO) -> None:
|
||||
if exc.code == 404 else f"The server answered HTTP {exc.code}."]
|
||||
except (urllib.error.URLError, OSError) as exc:
|
||||
code, words = network_words(exc)
|
||||
if code == "offline":
|
||||
# Asked for, not a failure: no Try again, and the two ways a
|
||||
# run without the network gets its list.
|
||||
inbox.put(("manifest_failed", plat, TUI_OFFLINE_TITLE, [
|
||||
"This run makes no network request (--offline), and the list of "
|
||||
"BIOS files comes from the network.",
|
||||
"Run the installer again with the list saved as a file "
|
||||
"(--manifest FILE, a copy of install/<platform>.json) or with a pack "
|
||||
"(--from-pack PACK), or without --offline."], 1))
|
||||
return
|
||||
title = {"clock": "The clock is wrong",
|
||||
"no_ca": "No trusted certificates"}.get(code, "No connection")
|
||||
# The screen's first row is Try again: "then run again" was a second way.
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
||||
TEMP_INSTALLER=""
|
||||
TEMP_DIRECTORY=""
|
||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
DEFAULT_INSTALL_SHA256="eaa912af47dc6f29e4436ec200a1b431b8ddd8006ca08e57c8c73d3a05717818"
|
||||
DEFAULT_INSTALL_SHA256="1f6b90760303d04cb6beef7fd294371b2e1d4ba733578c59a95e1b0395c2f376"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -19,6 +19,7 @@ import http.server
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import queue
|
||||
import re
|
||||
import select
|
||||
import shutil
|
||||
@@ -2578,6 +2579,53 @@ class AuditRoundFour(unittest.TestCase):
|
||||
self.assertIn(line, install.tui_outcome(state)[1])
|
||||
|
||||
|
||||
class OfflineInTheInterface(unittest.TestCase):
|
||||
"""--offline in the full screen: a "No connection" error offering a Try
|
||||
again that can never work, and a plan counting downloads the run
|
||||
forbids."""
|
||||
|
||||
def setUp(self):
|
||||
patcher = mock.patch.dict(os.environ, {install.OFFLINE_VARIABLE: "1"})
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_no_list_names_the_ways_that_work(self):
|
||||
inbox = queue.Queue()
|
||||
install._tui_fetch("retroarch", inbox, io.StringIO())
|
||||
kind, plat, title, lines, code = inbox.get_nowait()
|
||||
self.assertEqual((kind, title, code), ("manifest_failed",
|
||||
"No internet: offline mode", 1))
|
||||
text = " ".join(lines)
|
||||
for words in ("--manifest", "--from-pack", "without --offline"):
|
||||
self.assertIn(words, text)
|
||||
state = fresh_state()
|
||||
state.stack = ["setup"]
|
||||
with mock.patch.object(install.Path, "exists", return_value=True):
|
||||
install.tui_update(state, Key("enter"))
|
||||
install.tui_receive(state, (kind, plat, title, lines, code))
|
||||
self.assertEqual(state.screen, "error")
|
||||
self.assertNotIn("retry", [row.action for row in install.tui_rows(state)])
|
||||
view = "\n".join(install.tui_view(state, 80, 24))
|
||||
self.assertIn("No internet: offline mode", view)
|
||||
self.assertNotIn("Try again", view)
|
||||
|
||||
def test_the_plan_offers_no_download(self):
|
||||
state = checked_state()
|
||||
text = " ".join(" ".join(install.tui_view(state, 120, 40)).split())
|
||||
self.assertNotIn("to download", text)
|
||||
self.assertIn("need the internet, which --offline does not use", text)
|
||||
self.assertNotIn("install", [row.action for row in install.tui_rows(state)])
|
||||
# A file found on this machine can still be copied.
|
||||
state.found = [{"event": "found", "platform": "retroarch", "dest": FILES[0]["dest"],
|
||||
"folder": "/media/old", "path": "/media/old/scph5500.bin",
|
||||
"member": "", "reason": ""}]
|
||||
text = " ".join(" ".join(install.tui_view(state, 120, 40)).split())
|
||||
self.assertIn("1 found in old,", text)
|
||||
self.assertIn("need the internet (--offline)", text)
|
||||
self.assertEqual(install.tui_rows(state)[0].action, "install")
|
||||
self.assertIn("--offline", install.tui_command(state))
|
||||
|
||||
|
||||
class NarrowSystemNames(unittest.TestCase):
|
||||
"""At 40 columns the size column took half the line: Game Boy, Game Boy
|
||||
Advance and Game Boy Color all read "Nintendo Gam"."""
|
||||
|
||||
Reference in new issue
Block a user