refactor: run line mode findings for parity

The line-mode findings of code audit 18 and tui audit 11, the escape
sequence checks among them, run in LineModeAndScreenDecideAlike.
This commit is contained in:
Abdessamad Derraz committed 2026-10-27 16:29:58 +01:00
1 parent 39c2809a4b
commit dbc9da283d
2 files changed
+368 -356

No files matched your search

+366 -355
View File
@@ -5289,8 +5289,6 @@ class TheTerminalComesBack(unittest.TestCase):
"""code_audit_17 P2-1: Ctrl-Z stopped the installer alone. install.sh,
which runs it as a child, kept waiting, the shell never got its prompt
back, and the install run behind the screen kept writing."""
import signal
repo = self.repo(GameDataRepository, slow=True)
env = {**self.env, "PS1": "PROMPT$ ",
"PATH": f"{Path(sys.executable).parent}{os.pathsep}{self.env['PATH']}"}
@@ -6622,7 +6620,371 @@ def screen_plan(args: argparse.Namespace, manifest: dict, events: list[dict],
return state, " ".join(" ".join(install.tui_view(state, 120, 40)).split())
class LineModeAndScreenDecideAlike(unittest.TestCase):
class _CardFixtures:
"""Drives, lists and packs of code_audit_18 and tui_audit_11, for the
classes that test them on the screen and in line mode."""
ABOUT = "system/configs/emulationstation/about.info"
def drive(self, name: str, files: dict) -> Path:
root = self.root / "media" / name
root.mkdir(parents=True)
for path, text in files.items():
(root / path).parent.mkdir(parents=True, exist_ok=True)
(root / path).write_text(text, encoding="utf-8")
return root
def renamed(self) -> dict:
"""The drives code_audit_18 P1-1 names: KNULLI and Batocera each
renamed in its own menu, with and without the file KNULLI alone
writes, and Recalbox's share."""
conf = "system/batocera.conf"
return {
"KNULLI renamed": self.drive("K1", {self.ABOUT: "KNULLI\n",
conf: "system.hostname=RG40XXH\n"}),
"KNULLI as shipped": self.drive("K2", {self.ABOUT: "KNULLI\n",
conf: "system.hostname=KNULLI\n"}),
"renamed, no about.info": self.drive("K3", {conf: "system.hostname=RG40XXH\n"}),
"Batocera as shipped": self.drive("B1", {conf: "system.hostname=BATOCERA\n"}),
"Batocera, no hostname": self.drive("B2", {conf: "# system.hostname=X\n"}),
"Recalbox": self.drive("R1", {"system/recalbox.conf": "a=1\n"}),
"two shares": self.drive("T1", {conf: "system.hostname=BATOCERA\n",
"system/recalbox.conf": ""}),
}
def minui(self, plat: str = "minui", systems: bool = False, cores: bool = True,
regions: bool = True) -> tuple[Path, Path, dict]:
"""A list of plat's shape (the MinUI family records cores and regions,
no system), the pack built from it and the files' payloads."""
from test_install_offline import GBA, JP, US # noqa: PLC0415
payloads = {"Bios/MGBA/gba_bios.bin": (GBA, "nintendo-gba", "mgba", None),
"Bios/PS/scph5501.bin": (US, "sony-playstation", "pcsx_rearmed",
"north-america"),
"Bios/PS/scph5500.bin": (JP, "sony-playstation", "pcsx_rearmed", "japan")}
files = []
for dest, (data, system, core, region) in payloads.items():
entry = {"dest": dest, "sha1": hashlib.sha1(data).hexdigest(), "size": len(data),
"repo_path": f"bios/{dest}", "cores": [core] if cores else None}
if systems:
entry["systems"] = [system]
if regions and region:
entry.update(regions=[region], region_groups=["ps"])
files.append(entry)
listing = self.root / f"{plat}.json"
install_list = {"manifest_version": 2, "platform": plat, "files": files}
listing.write_text(json.dumps(install_list))
sys.path.insert(0, str(REPO_ROOT / "scripts"))
self.addCleanup(sys.path.remove, str(REPO_ROOT / "scripts"))
import generate_pack # noqa: PLC0415
pack_list = {"files": [{"path": dest, "sha1": entry["sha1"], "size": entry["size"]}
for dest, entry in zip(payloads, files)]}
generate_pack.annotate_pack_systems(pack_list, generate_pack.pack_systems(install_list))
name = {"minui": "MinUI", "misterfpga": "MiSTer_FPGA"}[plat]
pack = self.root / f"{name}_v1_BIOS_Pack.zip"
with zipfile.ZipFile(pack, "w") as archive:
for dest, (data, *_rest) in payloads.items():
archive.writestr(dest, data)
archive.writestr("manifest.json", json.dumps(pack_list))
return listing, pack, pack_list
def narrowed(self, plat: str, **given) -> tuple[int, str, list]:
"""(exit code, what was said, the files kept) of a --dry-run narrowed
as given."""
dest = self.root / "dest"
if not dest.exists():
make_card(dest, "MinUI")
stream = io.StringIO()
code = 0
with mock.patch.object(install.EVENTS, "stream", stream), \
mock.patch.object(install, "detect_os", return_value="linux"), \
mock.patch.dict(os.environ, {install.OFFLINE_VARIABLE: "1"}), \
contextlib.redirect_stdout(io.StringIO()) as out, \
contextlib.redirect_stderr(io.StringIO()) as err:
try:
install._install(_args(platform=plat, dest=dest, dry_run=True, events=True,
no_input=True, declared_only=False, **given),
io.StringIO())
except SystemExit as stop:
code = stop.code
finally:
install.LOCAL.reset()
kept = [json.loads(line)["dest"] for line in stream.getvalue().splitlines()
if '"event": "file"' in line]
return code, out.getvalue() + err.getvalue(), kept
class _LineModeFindings(_CardFixtures):
"""The line-mode findings of code_audit_18 and tui_audit_11, run by
LineModeAndScreenDecideAlike."""
def test_a_drive_two_systems_fit_is_taken_as_named_and_said(self):
"""code_audit_18 P1-1: --platform knulli is never refused on a
drive whose only evidence is a batocera.conf, --platform batocera
takes its bios folder, and the doubt is said. Recalbox refuses it
with words for a SHARE drive (P3-4)."""
drives = self.renamed()
for name in ("renamed, no about.info", "Batocera as shipped"):
drive = drives[name]
with self.subTest(drive=name):
choice = install.card_choice("knulli", "knulli", drive)
self.assertEqual((choice.plat, choice.folder, choice.refused),
("knulli", drive / "bios", ""))
self.assertIn("Taken as KNULLI's, as named", choice.note)
drive = drives["renamed, no about.info"]
choice = install.card_choice("batocera", "batocera", drive)
self.assertEqual((choice.plat, choice.folder), ("batocera", drive / "bios"))
self.assertIn("names the host RG40XXH", choice.note)
refused = install.card_choice("recalbox", "recalbox", drive)
self.assertIn("could be a KNULLI card or a Batocera SHARE drive", refused.refused)
words = install._dest_refusal_words(refused, None)
self.assertIn("Connect the Recalbox SHARE drive", words)
self.assertNotIn("Insert", words)
other = install.card_choice("recalbox", "recalbox", drives["Batocera as shipped"])
words = install._dest_refusal_words(other, None)
self.assertIn("Connect the Recalbox SHARE drive, or name that drive's own system: "
"--platform batocera", words)
def test_the_first_screen_and_line_mode_say_the_doubt(self):
"""code_audit_18 P1-1: the screen's first row was "Batocera SHARE
drive (KNULLI2)". A renamed card with about.info is KNULLI's, one
without is listed by neither and said."""
drives = self.renamed()
knulli, doubt = drives["KNULLI renamed"], drives["renamed, no about.info"]
with mock.patch.object(install, "_mounted_cards", return_value=[knulli, doubt]), \
mock.patch.object(install, "detect_platforms", return_value=[]), \
contextlib.redirect_stdout(io.StringIO()) as out:
setups, _mounted = install.found_setups("linux")
self.assertEqual([(plat, path) for plat, path, _label in setups],
[("knulli", knulli / "bios")])
self.assertIn("KNULLI SD card", setups[0][2])
text = out.getvalue()
self.assertIn(f"{doubt} could be a KNULLI card or a Batocera SHARE drive", text)
self.assertIn(f"--platform knulli --dest {doubt} or --platform batocera --dest {doubt}",
text)
with mock.patch.object(install, "_mounted_cards", return_value=[knulli, doubt]), \
mock.patch.object(install, "detect_platforms", return_value=[]), \
mock.patch.object(install, "detect_frontends", return_value=[]), \
contextlib.redirect_stdout(io.StringIO()):
state, _effects = install.tui_start(_args(), "linux")
self.assertTrue(any("could be a KNULLI card" in note for note in state.notes))
self.assertEqual([setup[0] for setup in state.setups], ["knulli"])
def test_a_list_without_systems_narrows_by_what_it_records(self):
"""tui_audit_11 P2-1: install/minui.json records cores and regions,
no system, and all three options were refused with a remedy that
fetched the same list."""
listing, _pack, _listed = self.minui()
code, text, kept = self.narrowed("minui", manifest=listing, core=["mgba"])
self.assertEqual(code, 0, text)
self.assertEqual(kept, ["Bios/MGBA/gba_bios.bin"])
code, text, kept = self.narrowed("minui", manifest=listing, region=["us"])
self.assertEqual(code, 0, text)
self.assertNotIn("Bios/PS/scph5500.bin", kept)
code, text, _kept = self.narrowed("minui", manifest=listing,
system=["nintendo-gba"])
self.assertEqual(code, 1)
self.assertIn("records no systems, so --system cannot narrow it", text)
self.assertIn("Run it without --system, or narrow with --core and --region", text)
self.assertNotIn("one-line installer", text)
# The remedy followed: what it names works.
code, text, _kept = self.narrowed("minui", manifest=listing, core=["mgba"],
region=["us"])
self.assertEqual(code, 0, text)
def test_a_pack_names_its_list_only_when_that_list_records_it(self):
"""tui_audit_11 P2-1: a MinUI pack sent --core to install/minui.json,
refused the same way, and a MiSTer pack to install/misterfpga.json,
which records no core. The pack now records what its list narrows
by, and each remedy changes the outcome."""
listing, pack, listed = self.minui()
self.assertEqual(listed["list_records"], ["cores", "regions"])
code, text, _kept = self.narrowed("minui", from_pack=[pack], core=["mgba"])
self.assertEqual(code, 1)
self.assertIn("Add --manifest FILE, a copy of install/minui.json, which records them",
text)
code, text, kept = self.narrowed("minui", from_pack=[pack], manifest=listing,
core=["mgba"])
self.assertEqual((code, kept), (0, ["Bios/MGBA/gba_bios.bin"]), text)
mister_list, mister, listed = self.minui("misterfpga", systems=True, cores=False,
regions=False)
self.assertEqual(listed["list_records"], ["regions", "systems"])
code, text, _kept = self.narrowed("misterfpga", from_pack=[mister], core=["x"])
self.assertEqual(code, 1)
self.assertIn("MiSTer FPGA's own file list records no cores either. Run it without "
"--core, or narrow with --system.", text)
self.assertNotIn("--manifest", text)
code, text, kept = self.narrowed("misterfpga", from_pack=[mister],
system=["nintendo-gba"])
self.assertEqual((code, kept), (0, ["Bios/MGBA/gba_bios.bin"]), text)
self.assertTrue(mister_list.exists())
def test_line_mode_offers_what_can_be_chosen(self):
"""tui_audit_11 P3-10: "c) choose systems, cores and regions" with a
pack that records systems only, and "Cores: 1) azahar" asked with
one answer."""
_listing, _pack, listed = self.minui("misterfpga", systems=True, cores=False,
regions=False)
files = [{"dest": f["path"], "sha1": f["sha1"], "size": f["size"],
"repo_path": "x", "systems": f["systems"]} for f in listed["files"]]
self.assertEqual(install.narrowing_words(files, []), "systems")
listing, _pack, _listed = self.minui()
minui = json.loads(listing.read_text())["files"]
self.assertEqual(install.narrowing_words(minui, []), "cores and regions")
one_core = [{**entry, "systems": ["nintendo-3ds"], "cores": ["azahar"]}
for entry in minui[:1]]
with mock.patch.object(install, "_ask", side_effect=["", ""]) as asked, \
contextlib.redirect_stdout(io.StringIO()) as out:
install._choose_narrowing(one_core, [], {}, None)
self.assertNotIn("Cores:", out.getvalue())
self.assertEqual(asked.call_count, 0)
# code_audit_18 P3-2, P3-3, tui_audit_11 P3-1: the remedies of a card.
def test_a_full_card_is_freed_never_left_for_another_drive(self):
with mock.patch.object(install.shutil, "disk_usage",
return_value=mock.Mock(free=10)), \
contextlib.redirect_stderr(io.StringIO()) as err, \
self.assertRaises(SystemExit):
install._check_free_space(self.root, 1000, "retroarch", card=True)
said = err.getvalue()
self.assertIn("on the card, then run it again", said)
self.assertNotIn("--platform", said)
self.assertNotIn("another drive", said)
def test_line_mode_names_the_way_to_another_folder(self):
hint = install.Invocation()
with mock.patch.object(install, "_folder_writable", return_value=False), \
mock.patch.object(install, "_on_read_only_mount", return_value=False):
lines = install.folder_warnings("retroarch", self.root, [], hint, True, False)
self.assertIn("You cannot write to this folder: run it again with another --dest.",
lines)
self.assertNotIn("choose another one", " ".join(lines))
with mock.patch.object(install, "_folder_writable", return_value=False), \
mock.patch.object(install, "_on_read_only_mount", return_value=True):
lines = install.folder_warnings("retroarch", self.root, [], hint, True, True)
self.assertEqual(lines, [install.read_only_words(self.root)])
class _OutsideTextFindings:
"""code_audit_18 P2-1: a pack's member path, a --manifest dest, the
names a list records, a card's label and a retroarch.cfg folder reached
line mode, the events and the disk with their control characters. Each
source is fed escape sequences, and none comes out raw."""
OSC = "\x1b]0;PWNED\x07"
CSI = "\x1b[41m"
@property
def dest(self) -> Path:
dest = self.root / "dest"
dest.mkdir(exist_ok=True)
return dest
def assertClean(self, text: str, where: str) -> None:
self.assertNotIn("\x1b", text, where)
self.assertNotIn("\x07", text, where)
def assertNothingRawOnDisk(self) -> None:
for path in self.root.rglob("*"):
self.assertIsNone(install._CONTROLS.search(path.name), path)
def run_install(self, args: argparse.Namespace, mounts: list = (),
env: dict | None = None) -> str:
events = io.StringIO()
install.LOCAL.reset()
self.addCleanup(install.LOCAL.reset)
with mock.patch.object(install, "_UNNAMEABLE", []), \
mock.patch.object(install, "_mounted_cards", return_value=list(mounts)), \
mock.patch.object(install, "_mount_points", return_value=frozenset()), \
mock.patch.object(install, "detect_frontends", return_value=[]), \
mock.patch.object(install, "detect_os", return_value="linux"), \
mock.patch.object(install.EVENTS, "stream", events), \
mock.patch.dict(os.environ, {install.OFFLINE_VARIABLE: "1", **(env or {})}), \
contextlib.redirect_stdout(io.StringIO()) as out, \
contextlib.redirect_stderr(io.StringIO()) as err, \
contextlib.suppress(SystemExit):
install._install(args, io.StringIO())
return out.getvalue() + err.getvalue() + events.getvalue()
def test_a_pack_member_path_is_refused(self):
from test_install_offline import _pack, GBA # noqa: PLC0415
pack = _pack(self.root, "RetroArch_v1_BIOS_Pack.zip", {f"x{self.OSC}.bin": GBA})
text = self.run_install(_args(platform="retroarch", dest=self.dest,
from_pack=[pack], no_input=True, declared_only=False))
self.assertClean(text, "pack")
self.assertIn("is not valid: unsafe", text)
self.assertEqual(list(self.dest.iterdir()), [])
self.assertNothingRawOnDisk()
def test_a_list_dest_and_its_names_are_refused_or_shown(self):
files = [{"dest": f"x{self.CSI}BAD.bin", "sha1": "0" * 40, "size": 1,
"repo_path": "bios/x", "cores": None, "systems": ["a"]}]
listing = self.root / "list.json"
listing.write_text(json.dumps({"manifest_version": 2, "platform": "retroarch",
"files": files}))
text = self.run_install(_args(platform="retroarch", dest=self.dest, manifest=listing,
no_input=True, dry_run=True, verbose=True,
declared_only=False))
self.assertClean(text, "--manifest dest")
self.assertIn("unsafe", text)
named = {"manifest_version": 2, "platform": "retroarch", "files": [
{**files[0], "dest": "a.bin", "systems": [f"a{self.CSI}"],
"cores": [f"core{self.OSC}"]}],
"system_titles": {f"a{self.CSI}": f"Title{self.OSC}"}}
cleaned = install._validate_manifest(named, "retroarch")
self.assertClean(json.dumps(cleaned, ensure_ascii=False), "names")
self.assertNothingRawOnDisk()
def test_a_card_label_is_shown_and_never_obeyed(self):
card = self.root / "media" / f"S{self.CSI}RED"
(card / "system").mkdir(parents=True)
(card / "system" / "batocera.conf").write_text("system.hostname=BATOCERA\n")
listing = self.root / "batocera.json"
listing.write_text(json.dumps({"manifest_version": 2, "platform": "batocera",
"files": []}))
with mock.patch.object(install, "detect_platforms", return_value=[]):
text = self.run_install(_args(platform="batocera", manifest=listing,
no_input=True, dry_run=True, events=True,
declared_only=False), [card])
self.assertIn("S?[41mRED", text)
self.assertClean(text, "card label")
self.assertClean(install.card_line(card), "card row")
def test_a_folder_a_config_names_is_refused(self):
home = self.root / "home"
weird = home / f"sys{self.OSC}x"
weird.mkdir(parents=True)
cfg = home / ".config" / "retroarch" / "retroarch.cfg"
cfg.parent.mkdir(parents=True)
cfg.write_text(f'system_directory = "{weird}"\n')
listing = self.root / "retroarch.json"
listing.write_text(json.dumps({"manifest_version": 2, "platform": "retroarch",
"files": []}))
text = self.run_install(
_args(manifest=listing, no_input=True, dry_run=True, events=True,
declared_only=False),
env={"HOME": str(home), "XDG_CONFIG_HOME": str(home / ".config"),
"RETROBIOS_OS": "linux"})
self.assertClean(text, "retroarch.cfg")
self.assertIn("holds a control character", text)
self.assertNotIn("using default path", text)
self.assertNotIn(str(home / ".config" / "retroarch" / "system"), text)
def test_a_path_with_a_control_character_is_no_relative_path(self):
for value in ("a\x1bb.bin", "a\x07.bin", "a\x9bb.bin", "a\nb.bin"):
with self.subTest(value=value), self.assertRaises(ValueError):
install._safe_relative_path(value, "dest")
self.assertEqual(install.shown_path(Path("/a/b\x1b[2Jc")), "/a/b?[2Jc")
class LineModeAndScreenDecideAlike(_LineModeFindings, _OutsideTextFindings,
unittest.TestCase):
"""tui_audit_8 P1-1, P2-1 and code_audit_15 P2-2: line mode looked at
this machine alone, took this computer's default folder with a card in
the reader or nothing found, and the screen put RetroArch under A for a
@@ -8746,7 +9108,7 @@ class EdgesTheMutantsFound(unittest.TestCase):
class AuditEighteenAndEleven(unittest.TestCase):
class AuditEighteenAndEleven(_CardFixtures, unittest.TestCase):
"""code_audit_18 and tui_audit_11: what the cards, the screens and the
docs answer, line mode's parity being in LineModeAndScreenDecideAlike."""
@@ -8761,34 +9123,6 @@ class AuditEighteenAndEleven(unittest.TestCase):
patcher.start()
self.addCleanup(patcher.stop)
def drive(self, name: str, files: dict) -> Path:
root = self.root / "media" / name
root.mkdir(parents=True)
for path, text in files.items():
(root / path).parent.mkdir(parents=True, exist_ok=True)
(root / path).write_text(text, encoding="utf-8")
return root
ABOUT = "system/configs/emulationstation/about.info"
def renamed(self) -> dict:
"""The drives code_audit_18 P1-1 names: KNULLI and Batocera each
renamed in its own menu, with and without the file KNULLI alone
writes, and Recalbox's share."""
conf = "system/batocera.conf"
return {
"KNULLI renamed": self.drive("K1", {self.ABOUT: "KNULLI\n",
conf: "system.hostname=RG40XXH\n"}),
"KNULLI as shipped": self.drive("K2", {self.ABOUT: "KNULLI\n",
conf: "system.hostname=KNULLI\n"}),
"renamed, no about.info": self.drive("K3", {conf: "system.hostname=RG40XXH\n"}),
"Batocera as shipped": self.drive("B1", {conf: "system.hostname=BATOCERA\n"}),
"Batocera, no hostname": self.drive("B2", {conf: "# system.hostname=X\n"}),
"Recalbox": self.drive("R1", {"system/recalbox.conf": "a=1\n"}),
"two shares": self.drive("T1", {conf: "system.hostname=BATOCERA\n",
"system/recalbox.conf": ""}),
}
def test_a_renamed_card_is_named_by_the_file_no_menu_writes(self):
"""code_audit_18 P1-1: a KNULLI card whose hostname was changed in
its menu was a Batocera SHARE drive, Enter filled it with Batocera's
@@ -8815,58 +9149,6 @@ class AuditEighteenAndEleven(unittest.TestCase):
self.assertEqual(install.share_doubt(drives["two shares"])[0],
("batocera", "recalbox"))
def test_a_drive_two_systems_fit_is_taken_as_named_and_said(self):
"""code_audit_18 P1-1: --platform knulli is never refused on a
drive whose only evidence is a batocera.conf, --platform batocera
takes its bios folder, and the doubt is said. Recalbox refuses it
with words for a SHARE drive (P3-4)."""
drives = self.renamed()
for name in ("renamed, no about.info", "Batocera as shipped"):
drive = drives[name]
with self.subTest(drive=name):
choice = install.card_choice("knulli", "knulli", drive)
self.assertEqual((choice.plat, choice.folder, choice.refused),
("knulli", drive / "bios", ""))
self.assertIn("Taken as KNULLI's, as named", choice.note)
drive = drives["renamed, no about.info"]
choice = install.card_choice("batocera", "batocera", drive)
self.assertEqual((choice.plat, choice.folder), ("batocera", drive / "bios"))
self.assertIn("names the host RG40XXH", choice.note)
refused = install.card_choice("recalbox", "recalbox", drive)
self.assertIn("could be a KNULLI card or a Batocera SHARE drive", refused.refused)
words = install._dest_refusal_words(refused, None)
self.assertIn("Connect the Recalbox SHARE drive", words)
self.assertNotIn("Insert", words)
other = install.card_choice("recalbox", "recalbox", drives["Batocera as shipped"])
words = install._dest_refusal_words(other, None)
self.assertIn("Connect the Recalbox SHARE drive, or name that drive's own system: "
"--platform batocera", words)
def test_the_first_screen_and_line_mode_say_the_doubt(self):
"""code_audit_18 P1-1: the screen's first row was "Batocera SHARE
drive (KNULLI2)". A renamed card with about.info is KNULLI's, one
without is listed by neither and said."""
drives = self.renamed()
knulli, doubt = drives["KNULLI renamed"], drives["renamed, no about.info"]
with mock.patch.object(install, "_mounted_cards", return_value=[knulli, doubt]), \
mock.patch.object(install, "detect_platforms", return_value=[]), \
contextlib.redirect_stdout(io.StringIO()) as out:
setups, _mounted = install.found_setups("linux")
self.assertEqual([(plat, path) for plat, path, _label in setups],
[("knulli", knulli / "bios")])
self.assertIn("KNULLI SD card", setups[0][2])
text = out.getvalue()
self.assertIn(f"{doubt} could be a KNULLI card or a Batocera SHARE drive", text)
self.assertIn(f"--platform knulli --dest {doubt} or --platform batocera --dest {doubt}",
text)
with mock.patch.object(install, "_mounted_cards", return_value=[knulli, doubt]), \
mock.patch.object(install, "detect_platforms", return_value=[]), \
mock.patch.object(install, "detect_frontends", return_value=[]), \
contextlib.redirect_stdout(io.StringIO()):
state, _effects = install.tui_start(_args(), "linux")
self.assertTrue(any("could be a KNULLI card" in note for note in state.notes))
self.assertEqual([setup[0] for setup in state.setups], ["knulli"])
def test_a_share_drive_is_never_called_a_card(self):
"""code_audit_18 P3-4, tui_audit_11 P3-14: "is a Batocera card"
and "is a Recalbox card" for their SHARE drives."""
@@ -8897,163 +9179,6 @@ class AuditEighteenAndEleven(unittest.TestCase):
# tui_audit_11 P2-1: a list or a pack that records some axes only.
def minui(self, plat: str = "minui", systems: bool = False, cores: bool = True,
regions: bool = True) -> tuple[Path, Path, dict]:
"""A list of plat's shape (the MinUI family records cores and regions,
no system), the pack built from it and the files' payloads."""
from test_install_offline import GBA, JP, US # noqa: PLC0415
payloads = {"Bios/MGBA/gba_bios.bin": (GBA, "nintendo-gba", "mgba", None),
"Bios/PS/scph5501.bin": (US, "sony-playstation", "pcsx_rearmed",
"north-america"),
"Bios/PS/scph5500.bin": (JP, "sony-playstation", "pcsx_rearmed", "japan")}
files = []
for dest, (data, system, core, region) in payloads.items():
entry = {"dest": dest, "sha1": hashlib.sha1(data).hexdigest(), "size": len(data),
"repo_path": f"bios/{dest}", "cores": [core] if cores else None}
if systems:
entry["systems"] = [system]
if regions and region:
entry.update(regions=[region], region_groups=["ps"])
files.append(entry)
listing = self.root / f"{plat}.json"
install_list = {"manifest_version": 2, "platform": plat, "files": files}
listing.write_text(json.dumps(install_list))
sys.path.insert(0, str(REPO_ROOT / "scripts"))
self.addCleanup(sys.path.remove, str(REPO_ROOT / "scripts"))
import generate_pack # noqa: PLC0415
pack_list = {"files": [{"path": dest, "sha1": entry["sha1"], "size": entry["size"]}
for dest, entry in zip(payloads, files)]}
generate_pack.annotate_pack_systems(pack_list, generate_pack.pack_systems(install_list))
name = {"minui": "MinUI", "misterfpga": "MiSTer_FPGA"}[plat]
pack = self.root / f"{name}_v1_BIOS_Pack.zip"
with zipfile.ZipFile(pack, "w") as archive:
for dest, (data, *_rest) in payloads.items():
archive.writestr(dest, data)
archive.writestr("manifest.json", json.dumps(pack_list))
return listing, pack, pack_list
def narrowed(self, plat: str, **given) -> tuple[int, str, list]:
"""(exit code, what was said, the files kept) of a --dry-run narrowed
as given."""
dest = self.root / "dest"
if not dest.exists():
make_card(dest, "MinUI")
stream = io.StringIO()
code = 0
with mock.patch.object(install.EVENTS, "stream", stream), \
mock.patch.object(install, "detect_os", return_value="linux"), \
mock.patch.dict(os.environ, {install.OFFLINE_VARIABLE: "1"}), \
contextlib.redirect_stdout(io.StringIO()) as out, \
contextlib.redirect_stderr(io.StringIO()) as err:
try:
install._install(_args(platform=plat, dest=dest, dry_run=True, events=True,
no_input=True, declared_only=False, **given),
io.StringIO())
except SystemExit as stop:
code = stop.code
finally:
install.LOCAL.reset()
kept = [json.loads(line)["dest"] for line in stream.getvalue().splitlines()
if '"event": "file"' in line]
return code, out.getvalue() + err.getvalue(), kept
def test_a_list_without_systems_narrows_by_what_it_records(self):
"""tui_audit_11 P2-1: install/minui.json records cores and regions,
no system, and all three options were refused with a remedy that
fetched the same list."""
listing, _pack, _listed = self.minui()
code, text, kept = self.narrowed("minui", manifest=listing, core=["mgba"])
self.assertEqual(code, 0, text)
self.assertEqual(kept, ["Bios/MGBA/gba_bios.bin"])
code, text, kept = self.narrowed("minui", manifest=listing, region=["us"])
self.assertEqual(code, 0, text)
self.assertNotIn("Bios/PS/scph5500.bin", kept)
code, text, _kept = self.narrowed("minui", manifest=listing,
system=["nintendo-gba"])
self.assertEqual(code, 1)
self.assertIn("records no systems, so --system cannot narrow it", text)
self.assertIn("Run it without --system, or narrow with --core and --region", text)
self.assertNotIn("one-line installer", text)
# The remedy followed: what it names works.
code, text, _kept = self.narrowed("minui", manifest=listing, core=["mgba"],
region=["us"])
self.assertEqual(code, 0, text)
def test_a_pack_names_its_list_only_when_that_list_records_it(self):
"""tui_audit_11 P2-1: a MinUI pack sent --core to install/minui.json,
refused the same way, and a MiSTer pack to install/misterfpga.json,
which records no core. The pack now records what its list narrows
by, and each remedy changes the outcome."""
listing, pack, listed = self.minui()
self.assertEqual(listed["list_records"], ["cores", "regions"])
code, text, _kept = self.narrowed("minui", from_pack=[pack], core=["mgba"])
self.assertEqual(code, 1)
self.assertIn("Add --manifest FILE, a copy of install/minui.json, which records them",
text)
code, text, kept = self.narrowed("minui", from_pack=[pack], manifest=listing,
core=["mgba"])
self.assertEqual((code, kept), (0, ["Bios/MGBA/gba_bios.bin"]), text)
mister_list, mister, listed = self.minui("misterfpga", systems=True, cores=False,
regions=False)
self.assertEqual(listed["list_records"], ["regions", "systems"])
code, text, _kept = self.narrowed("misterfpga", from_pack=[mister], core=["x"])
self.assertEqual(code, 1)
self.assertIn("MiSTer FPGA's own file list records no cores either. Run it without "
"--core, or narrow with --system.", text)
self.assertNotIn("--manifest", text)
code, text, kept = self.narrowed("misterfpga", from_pack=[mister],
system=["nintendo-gba"])
self.assertEqual((code, kept), (0, ["Bios/MGBA/gba_bios.bin"]), text)
self.assertTrue(mister_list.exists())
def test_line_mode_offers_what_can_be_chosen(self):
"""tui_audit_11 P3-10: "c) choose systems, cores and regions" with a
pack that records systems only, and "Cores: 1) azahar" asked with
one answer."""
_listing, _pack, listed = self.minui("misterfpga", systems=True, cores=False,
regions=False)
files = [{"dest": f["path"], "sha1": f["sha1"], "size": f["size"],
"repo_path": "x", "systems": f["systems"]} for f in listed["files"]]
self.assertEqual(install.narrowing_words(files, []), "systems")
listing, _pack, _listed = self.minui()
minui = json.loads(listing.read_text())["files"]
self.assertEqual(install.narrowing_words(minui, []), "cores and regions")
one_core = [{**entry, "systems": ["nintendo-3ds"], "cores": ["azahar"]}
for entry in minui[:1]]
with mock.patch.object(install, "_ask", side_effect=["", ""]) as asked, \
contextlib.redirect_stdout(io.StringIO()) as out:
install._choose_narrowing(one_core, [], {}, None)
self.assertNotIn("Cores:", out.getvalue())
self.assertEqual(asked.call_count, 0)
# code_audit_18 P3-2, P3-3, tui_audit_11 P3-1: the remedies of a card.
def test_a_full_card_is_freed_never_left_for_another_drive(self):
with mock.patch.object(install.shutil, "disk_usage",
return_value=mock.Mock(free=10)), \
contextlib.redirect_stderr(io.StringIO()) as err, \
self.assertRaises(SystemExit):
install._check_free_space(self.root, 1000, "retroarch", card=True)
said = err.getvalue()
self.assertIn("on the card, then run it again", said)
self.assertNotIn("--platform", said)
self.assertNotIn("another drive", said)
def test_line_mode_names_the_way_to_another_folder(self):
hint = install.Invocation()
with mock.patch.object(install, "_folder_writable", return_value=False), \
mock.patch.object(install, "_on_read_only_mount", return_value=False):
lines = install.folder_warnings("retroarch", self.root, [], hint, True, False)
self.assertIn("You cannot write to this folder: run it again with another --dest.",
lines)
self.assertNotIn("choose another one", " ".join(lines))
with mock.patch.object(install, "_folder_writable", return_value=False), \
mock.patch.object(install, "_on_read_only_mount", return_value=True):
lines = install.folder_warnings("retroarch", self.root, [], hint, True, True)
self.assertEqual(lines, [install.read_only_words(self.root)])
def test_a_card_plan_offers_one_remedy_first(self):
for facts, locked in (({"free": 50 * 1024 ** 3, "writable": False, "cores": []}, True),
({"free": 10, "writable": True, "cores": []}, False)):
@@ -9311,119 +9436,5 @@ class TheAndroidResultReadsWhole(unittest.TestCase):
self.assertNotIn("Needed to start an emulator", said)
class NothingFromOutsideReachesTheTerminalRaw(unittest.TestCase):
"""code_audit_18 P2-1: a pack's member path, a --manifest dest, the
names a list records, a card's label and a retroarch.cfg folder reached
line mode, the events and the disk with their control characters. Each
source is fed escape sequences, and none comes out raw."""
OSC = "\x1b]0;PWNED\x07"
CSI = "\x1b[41m"
def setUp(self):
(REPO_ROOT / "tmp").mkdir(exist_ok=True)
self.root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
self.addCleanup(shutil.rmtree, self.root, True)
install.LOCAL.reset()
self.addCleanup(install.LOCAL.reset)
install._UNNAMEABLE.clear()
self.addCleanup(install._UNNAMEABLE.clear)
self.dest = self.root / "dest"
self.dest.mkdir()
def assertClean(self, text: str, where: str) -> None:
self.assertNotIn("\x1b", text, where)
self.assertNotIn("\x07", text, where)
def assertNothingRawOnDisk(self) -> None:
for path in self.root.rglob("*"):
self.assertIsNone(install._CONTROLS.search(path.name), path)
def run_install(self, args: argparse.Namespace, mounts: list = (),
env: dict | None = None) -> str:
events = io.StringIO()
with mock.patch.object(install, "_mounted_cards", return_value=list(mounts)), \
mock.patch.object(install, "_mount_points", return_value=frozenset()), \
mock.patch.object(install, "detect_frontends", return_value=[]), \
mock.patch.object(install, "detect_os", return_value="linux"), \
mock.patch.object(install.EVENTS, "stream", events), \
mock.patch.dict(os.environ, {install.OFFLINE_VARIABLE: "1", **(env or {})}), \
contextlib.redirect_stdout(io.StringIO()) as out, \
contextlib.redirect_stderr(io.StringIO()) as err, \
contextlib.suppress(SystemExit):
install._install(args, io.StringIO())
return out.getvalue() + err.getvalue() + events.getvalue()
def test_a_pack_member_path_is_refused(self):
from test_install_offline import _pack, GBA # noqa: PLC0415
pack = _pack(self.root, "RetroArch_v1_BIOS_Pack.zip", {f"x{self.OSC}.bin": GBA})
text = self.run_install(_args(platform="retroarch", dest=self.dest,
from_pack=[pack], no_input=True, declared_only=False))
self.assertClean(text, "pack")
self.assertIn("is not valid: unsafe", text)
self.assertEqual(list(self.dest.iterdir()), [])
self.assertNothingRawOnDisk()
def test_a_list_dest_and_its_names_are_refused_or_shown(self):
files = [{"dest": f"x{self.CSI}BAD.bin", "sha1": "0" * 40, "size": 1,
"repo_path": "bios/x", "cores": None, "systems": ["a"]}]
listing = self.root / "list.json"
listing.write_text(json.dumps({"manifest_version": 2, "platform": "retroarch",
"files": files}))
text = self.run_install(_args(platform="retroarch", dest=self.dest, manifest=listing,
no_input=True, dry_run=True, verbose=True,
declared_only=False))
self.assertClean(text, "--manifest dest")
self.assertIn("unsafe", text)
named = {"manifest_version": 2, "platform": "retroarch", "files": [
{**files[0], "dest": "a.bin", "systems": [f"a{self.CSI}"],
"cores": [f"core{self.OSC}"]}],
"system_titles": {f"a{self.CSI}": f"Title{self.OSC}"}}
cleaned = install._validate_manifest(named, "retroarch")
self.assertClean(json.dumps(cleaned, ensure_ascii=False), "names")
self.assertNothingRawOnDisk()
def test_a_card_label_is_shown_and_never_obeyed(self):
card = self.root / "media" / f"S{self.CSI}RED"
(card / "system").mkdir(parents=True)
(card / "system" / "batocera.conf").write_text("system.hostname=BATOCERA\n")
listing = self.root / "batocera.json"
listing.write_text(json.dumps({"manifest_version": 2, "platform": "batocera",
"files": []}))
with mock.patch.object(install, "detect_platforms", return_value=[]):
text = self.run_install(_args(platform="batocera", manifest=listing,
no_input=True, dry_run=True, events=True,
declared_only=False), [card])
self.assertIn("S?[41mRED", text)
self.assertClean(text, "card label")
self.assertClean(install.card_line(card), "card row")
def test_a_folder_a_config_names_is_refused(self):
home = self.root / "home"
weird = home / f"sys{self.OSC}x"
weird.mkdir(parents=True)
cfg = home / ".config" / "retroarch" / "retroarch.cfg"
cfg.parent.mkdir(parents=True)
cfg.write_text(f'system_directory = "{weird}"\n')
listing = self.root / "retroarch.json"
listing.write_text(json.dumps({"manifest_version": 2, "platform": "retroarch",
"files": []}))
text = self.run_install(
_args(manifest=listing, no_input=True, dry_run=True, events=True,
declared_only=False),
env={"HOME": str(home), "XDG_CONFIG_HOME": str(home / ".config"),
"RETROBIOS_OS": "linux"})
self.assertClean(text, "retroarch.cfg")
self.assertIn("holds a control character", text)
self.assertNotIn("using default path", text)
self.assertNotIn(str(home / ".config" / "retroarch" / "system"), text)
def test_a_path_with_a_control_character_is_no_relative_path(self):
for value in ("a\x1bb.bin", "a\x07.bin", "a\x9bb.bin", "a\nb.bin"):
with self.subTest(value=value), self.assertRaises(ValueError):
install._safe_relative_path(value, "dest")
self.assertEqual(install.shown_path(Path("/a/b\x1b[2Jc")), "/a/b?[2Jc")
if __name__ == "__main__":
unittest.main()