mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-01 00:27:20 -05:00
chore: merge line mode on the screen's decisions
# Conflicts: # tests/test_install_tui.py
This commit is contained in:
commit
a9301fbdc0
23 files changed
+1007
-246
No files matched your search
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "ad35e44d7d16467636197f7e40b34c53670eadf75d24681c32488c9bc21637f7"
|
||||
$defaultInstallSha256 = "b3363820bb97bec559287f72d9712281e22307387fa752b663f6f7af023a0e33"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installerExit = 0
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
|
||||
+427
-149
File diff suppressed because it is too large.
Load diff
+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="ad35e44d7d16467636197f7e40b34c53670eadf75d24681c32488c9bc21637f7"
|
||||
DEFAULT_INSTALL_SHA256="b3363820bb97bec559287f72d9712281e22307387fa752b663f6f7af023a0e33"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
{
|
||||
"dest": "roms/ports/retrobios/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 719855,
|
||||
"sha256": "ad35e44d7d16467636197f7e40b34c53670eadf75d24681c32488c9bc21637f7",
|
||||
"size": 734772,
|
||||
"sha256": "b3363820bb97bec559287f72d9712281e22307387fa752b663f6f7af023a0e33",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
@@ -71,8 +71,8 @@
|
||||
"muos": {
|
||||
"archive": {
|
||||
"dest": "ARCHIVE/RetroBIOS.muxapp",
|
||||
"size": 772404,
|
||||
"sha256": "e3aa2c3b790916d103731c47dc3fbf7dd30cd4416ed5120936796b545251e9cd"
|
||||
"size": 787321,
|
||||
"sha256": "45366e0a225d8143f7b359fd04cd6b341a21f43057b7d5a41ad8f734f765f142"
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
@@ -92,8 +92,8 @@
|
||||
{
|
||||
"name": "RetroBIOS/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 719855,
|
||||
"sha256": "ad35e44d7d16467636197f7e40b34c53670eadf75d24681c32488c9bc21637f7",
|
||||
"size": 734772,
|
||||
"sha256": "b3363820bb97bec559287f72d9712281e22307387fa752b663f6f7af023a0e33",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
@@ -166,8 +166,8 @@
|
||||
{
|
||||
"dest": ".config/modules/retrobios/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 719855,
|
||||
"sha256": "ad35e44d7d16467636197f7e40b34c53670eadf75d24681c32488c9bc21637f7",
|
||||
"size": 734772,
|
||||
"sha256": "b3363820bb97bec559287f72d9712281e22307387fa752b663f6f7af023a0e33",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -37,4 +37,4 @@
|
||||
|
||||
|
||||
|
||||
Ctrl-C Quit B/Esc Back
|
||||
Ctrl-C Quit B/Esc Quit
|
||||
+1
-1
@@ -9,4 +9,4 @@
|
||||
|
||||
|
||||
|
||||
Ctrl-C Quit B/Esc Back
|
||||
Ctrl-C Quit B/Esc Quit
|
||||
+1
-1
@@ -12,4 +12,4 @@
|
||||
|
||||
|
||||
|
||||
Ctrl-C Quit B/Esc Back
|
||||
Ctrl-C Quit B/Esc Quit
|
||||
+1
-1
@@ -21,4 +21,4 @@
|
||||
|
||||
|
||||
|
||||
Ctrl-C Quit B/Esc Back
|
||||
Ctrl-C Quit B/Esc Quit
|
||||
Vendored
+1
-1
@@ -37,4 +37,4 @@
|
||||
|
||||
|
||||
|
||||
A/Enter Create B/Esc Back
|
||||
A/Enter Create B/Esc Quit
|
||||
Vendored
+1
-1
@@ -9,4 +9,4 @@
|
||||
|
||||
|
||||
|
||||
A/Enter Create B/Esc Back
|
||||
A/Enter Create B/Esc Quit
|
||||
Vendored
+1
-1
@@ -12,4 +12,4 @@
|
||||
|
||||
|
||||
|
||||
A/Enter Create B/Esc Back
|
||||
A/Enter Create B/Esc Quit
|
||||
Vendored
+1
-1
@@ -21,4 +21,4 @@
|
||||
|
||||
|
||||
|
||||
A/Enter Create B/Esc Back
|
||||
A/Enter Create B/Esc Quit
|
||||
Vendored
+6
-6
@@ -334,7 +334,7 @@
|
||||
|
||||
|
||||
|
||||
A Create B Back
|
||||
A Create B Quit
|
||||
=== create 40x15
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -350,7 +350,7 @@
|
||||
|
||||
|
||||
|
||||
A Create B Back
|
||||
A Create B Quit
|
||||
=== create 80x24
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
|
||||
|
||||
|
||||
A Create B Back
|
||||
A Create B Quit
|
||||
=== checking 40x12
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -388,7 +388,7 @@
|
||||
|
||||
|
||||
|
||||
B Back
|
||||
B Quit
|
||||
=== checking 40x15
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -404,7 +404,7 @@
|
||||
|
||||
|
||||
|
||||
B Back
|
||||
B Quit
|
||||
=== checking 80x24
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -429,7 +429,7 @@
|
||||
|
||||
|
||||
|
||||
B Back
|
||||
B Quit
|
||||
=== plan 40x12
|
||||
RetroBIOS: RetroArch
|
||||
|
||||
|
||||
+6
-6
@@ -334,7 +334,7 @@
|
||||
|
||||
|
||||
|
||||
A Create B Back
|
||||
A Create B Quit
|
||||
=== create 40x15
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -350,7 +350,7 @@
|
||||
|
||||
|
||||
|
||||
A Create B Back
|
||||
A Create B Quit
|
||||
=== create 80x24
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
|
||||
|
||||
|
||||
A Create B Back
|
||||
A Create B Quit
|
||||
=== checking 40x12
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -388,7 +388,7 @@
|
||||
|
||||
|
||||
|
||||
Start+Select Quit B Back
|
||||
Start+Select Quit B Quit
|
||||
=== checking 40x15
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -404,7 +404,7 @@
|
||||
|
||||
|
||||
|
||||
Start+Select Quit B Back
|
||||
Start+Select Quit B Quit
|
||||
=== checking 80x24
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -429,7 +429,7 @@
|
||||
|
||||
|
||||
|
||||
Start+Select Quit B Back
|
||||
Start+Select Quit B Quit
|
||||
=== plan 40x12
|
||||
RetroBIOS: RetroArch
|
||||
|
||||
|
||||
+6
-6
@@ -334,7 +334,7 @@
|
||||
|
||||
|
||||
|
||||
A Create B Back
|
||||
A Create B Quit
|
||||
=== create 40x15
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -350,7 +350,7 @@
|
||||
|
||||
|
||||
|
||||
A Create B Back
|
||||
A Create B Quit
|
||||
=== create 80x24
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
|
||||
|
||||
|
||||
A Create B Back
|
||||
A Create B Quit
|
||||
=== checking 40x12
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -388,7 +388,7 @@
|
||||
|
||||
|
||||
|
||||
Start+Select Quit B Back
|
||||
Start+Select Quit B Quit
|
||||
=== checking 40x15
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -404,7 +404,7 @@
|
||||
|
||||
|
||||
|
||||
Start+Select Quit B Back
|
||||
Start+Select Quit B Quit
|
||||
=== checking 80x24
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -429,7 +429,7 @@
|
||||
|
||||
|
||||
|
||||
Start+Select Quit B Back
|
||||
Start+Select Quit B Quit
|
||||
=== plan 40x12
|
||||
RetroBIOS: RetroArch
|
||||
|
||||
|
||||
Vendored
+6
-6
@@ -280,7 +280,7 @@
|
||||
|
||||
|
||||
|
||||
Enter Create Esc Back
|
||||
Enter Create Esc Quit
|
||||
=== create 40x15
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -296,7 +296,7 @@
|
||||
|
||||
|
||||
|
||||
Enter Create Esc Back
|
||||
Enter Create Esc Quit
|
||||
=== create 80x24
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -321,7 +321,7 @@
|
||||
|
||||
|
||||
|
||||
Enter Create Esc Back
|
||||
Enter Create Esc Quit
|
||||
=== checking 40x12
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -334,7 +334,7 @@
|
||||
|
||||
|
||||
|
||||
Ctrl-C Quit Esc Back
|
||||
Ctrl-C Quit Esc Quit
|
||||
=== checking 40x15
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -350,7 +350,7 @@
|
||||
|
||||
|
||||
|
||||
Ctrl-C Quit Esc Back
|
||||
Ctrl-C Quit Esc Quit
|
||||
=== checking 80x24
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
|
||||
|
||||
|
||||
Ctrl-C Quit Esc Back
|
||||
Ctrl-C Quit Esc Quit
|
||||
=== plan 40x12
|
||||
RetroBIOS: RetroArch
|
||||
|
||||
|
||||
Vendored
+9
-9
@@ -334,7 +334,7 @@
|
||||
|
||||
|
||||
|
||||
A Create R2 Back
|
||||
A Create R2 Quit
|
||||
=== create 40x15
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -350,7 +350,7 @@
|
||||
|
||||
|
||||
|
||||
A Create R2 Back
|
||||
A Create R2 Quit
|
||||
=== create 80x24
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
|
||||
|
||||
|
||||
A Create R2 Back
|
||||
A Create R2 Quit
|
||||
=== checking 40x12
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -388,7 +388,7 @@
|
||||
|
||||
|
||||
|
||||
R2 Back
|
||||
R2 Quit
|
||||
=== checking 40x15
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -404,7 +404,7 @@
|
||||
|
||||
|
||||
|
||||
R2 Back
|
||||
R2 Quit
|
||||
=== checking 80x24
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -429,7 +429,7 @@
|
||||
|
||||
|
||||
|
||||
R2 Back
|
||||
R2 Quit
|
||||
=== plan 40x12
|
||||
RetroBIOS: RetroArch
|
||||
|
||||
@@ -712,7 +712,7 @@
|
||||
|
||||
|
||||
|
||||
Start Filter A Tick R2 Back
|
||||
A Filter R2 Back
|
||||
=== cores 40x15
|
||||
RetroBIOS: Cores
|
||||
|
||||
@@ -728,7 +728,7 @@
|
||||
|
||||
|
||||
|
||||
Start Filter A Tick R2 Back
|
||||
A Filter R2 Back
|
||||
=== cores 80x24
|
||||
RetroBIOS: Cores
|
||||
|
||||
@@ -753,7 +753,7 @@
|
||||
|
||||
|
||||
|
||||
Start Filter A Tick R2 Back
|
||||
A Filter R2 Back
|
||||
=== preview 40x12
|
||||
RetroBIOS: Preview
|
||||
|
||||
|
||||
Vendored
+6
-6
@@ -334,7 +334,7 @@
|
||||
|
||||
|
||||
|
||||
Start Create B Back
|
||||
Start Create B Quit
|
||||
=== create 40x15
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -350,7 +350,7 @@
|
||||
|
||||
|
||||
|
||||
Start Create B Back
|
||||
Start Create B Quit
|
||||
=== create 80x24
|
||||
RetroBIOS: Create the folder?
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
|
||||
|
||||
|
||||
Start Create B Back
|
||||
Start Create B Quit
|
||||
=== checking 40x12
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -388,7 +388,7 @@
|
||||
|
||||
|
||||
|
||||
Menu Quit B Back
|
||||
Menu Quit B Quit
|
||||
=== checking 40x15
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -404,7 +404,7 @@
|
||||
|
||||
|
||||
|
||||
Menu Quit B Back
|
||||
Menu Quit B Quit
|
||||
=== checking 80x24
|
||||
RetroBIOS: Checking
|
||||
|
||||
@@ -429,7 +429,7 @@
|
||||
|
||||
|
||||
|
||||
Menu Quit B Back
|
||||
Menu Quit B Quit
|
||||
=== plan 40x12
|
||||
RetroBIOS: RetroArch
|
||||
|
||||
|
||||
@@ -1794,11 +1794,13 @@ class TestDetectedPlatformPrompt(unittest.TestCase):
|
||||
def test_a_wrong_answer_names_the_choices(self):
|
||||
result, out = self._answer("x", "2")
|
||||
self.assertEqual(result, [self.PLATFORMS[1]])
|
||||
self.assertIn("Type a number from 1 to 3, or q to quit.", out)
|
||||
self.assertIn("Type a number from 1 to 2, or q to quit.", out)
|
||||
|
||||
def test_all_installs_every_platform(self):
|
||||
result, _ = self._answer("3")
|
||||
self.assertEqual(result, self.PLATFORMS)
|
||||
def test_enter_takes_the_first_and_there_is_no_all(self):
|
||||
"""tui_audit_9 P3-11: the screen focuses the first and has no All."""
|
||||
result, out = self._answer("")
|
||||
self.assertEqual(result, [self.PLATFORMS[0]])
|
||||
self.assertNotIn("All", out)
|
||||
|
||||
|
||||
class TestManualPlatformPrompt(unittest.TestCase):
|
||||
|
||||
@@ -390,7 +390,8 @@ class DecliningTheSizePointsAtTheChoice(unittest.TestCase):
|
||||
self.assertNotIn("see --help", out)
|
||||
|
||||
def test_options_given_keep_the_option_advice(self):
|
||||
out = self._run(["n"], "--system", "sony-playstation")
|
||||
# The menu is shown with --system too (tui_audit_9 P3-8): Enter keeps it.
|
||||
out = self._run(["", "n"], "--system", "sony-playstation")
|
||||
self.assertIn("--system, --core and --region install less", out)
|
||||
|
||||
|
||||
@@ -593,7 +594,7 @@ class TheOneLinerNarrows(unittest.TestCase):
|
||||
with FakeRepository() as repo:
|
||||
proc = repo.run("--system", "sony-playstation", "--region", "us")
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
|
||||
self.assertIn("Narrowed 5 -> 2 files", proc.stdout)
|
||||
self.assertIn("Narrowed 5 to 2 files", proc.stdout)
|
||||
self.assertEqual(repo.installed(), ["pcsx/extra.bin", "scph5501.bin"])
|
||||
|
||||
def test_a_core_choice_keeps_the_platform_list(self):
|
||||
@@ -1448,13 +1449,13 @@ class ThePlatformsOwnListAlone(unittest.TestCase):
|
||||
with FakeRepository() as repo:
|
||||
proc = repo.run("--declared-only")
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
|
||||
self.assertIn("5 -> 3 files", proc.stdout)
|
||||
self.assertIn("5 to 3 files", proc.stdout)
|
||||
self.assertEqual(repo.installed(),
|
||||
["gba_bios.bin", "scph5500.bin", "scph5501.bin"])
|
||||
for mode in ("--check", "--dry-run"):
|
||||
with self.subTest(mode=mode), FakeRepository() as repo:
|
||||
proc = repo.run("--declared-only", mode)
|
||||
self.assertIn("5 -> 3 files", proc.stdout, proc.stderr)
|
||||
self.assertIn("5 to 3 files", proc.stdout, proc.stderr)
|
||||
self.assertIn("0/3 present", proc.stdout)
|
||||
self.assertEqual(repo.installed(), [])
|
||||
|
||||
@@ -1476,7 +1477,7 @@ class ThePlatformsOwnListAlone(unittest.TestCase):
|
||||
proc = repo.run("--declared-only", "--dry-run", "--manifest",
|
||||
str(repo.root / "install" / "retroarch.json"))
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
|
||||
self.assertIn("5 -> 3 files", proc.stdout)
|
||||
self.assertIn("5 to 3 files", proc.stdout)
|
||||
|
||||
def test_every_mode_that_cannot_apply_it_refuses_it(self):
|
||||
"""Applied or refused, never ignored: each of these would have run
|
||||
|
||||
+483
-17
@@ -2908,8 +2908,10 @@ class ACardSystemNamedOnAComputer(unittest.TestCase):
|
||||
dest = install.card_install_dest("onion", "retroarch", interactive=True)
|
||||
self.assertEqual(dest, onion / "BIOS")
|
||||
self.assertEqual(len(asked), 3)
|
||||
self.assertIn("8 does not exist, or is not a folder. Type the top folder of the Onion "
|
||||
"card.", out.getvalue())
|
||||
# No card is listed: 8 is no number of the list, and no folder.
|
||||
self.assertIn("Type the card's top folder, or q to quit.", out.getvalue())
|
||||
self.assertIn("NEXT does not exist, or is not a folder. Type the top folder of the "
|
||||
"Onion card.", out.getvalue())
|
||||
self.assertFalse((REPO_ROOT / "tmp" / "no-such").exists())
|
||||
|
||||
def test_a_typed_root_that_is_no_card_of_that_system_is_asked_again(self):
|
||||
@@ -2953,7 +2955,7 @@ class ACardSystemNamedOnAComputer(unittest.TestCase):
|
||||
self.assertIn(f"2) {second.name} (KNULLI card, {second})", out.getvalue())
|
||||
self.assertNotIn(str(muos), out.getvalue())
|
||||
self.assertNotIn(str(blank), out.getvalue())
|
||||
self.assertIn("Type 1 to 2, or a folder.", out.getvalue())
|
||||
self.assertIn("Type 1 to 2, a folder, or q to quit.", out.getvalue())
|
||||
|
||||
def test_creating_several_folders_on_the_card_is_asked(self):
|
||||
"""tui_audit_6 P3-8: N to the question ended the whole run where it
|
||||
@@ -2969,7 +2971,7 @@ class ACardSystemNamedOnAComputer(unittest.TestCase):
|
||||
dest = install.card_install_dest("rocknix", "rocknix", interactive=True)
|
||||
self.assertEqual(dest, card / "roms" / "bios")
|
||||
self.assertIn("[y/N]", asked[1])
|
||||
self.assertEqual(asked[2], "Card root (Ctrl+C to stop): ")
|
||||
self.assertEqual(asked[2], "Card root, q to quit: ")
|
||||
self.assertIn("Not created.", out.getvalue())
|
||||
self.assertFalse((rocknix / "roms").exists())
|
||||
|
||||
@@ -2984,7 +2986,7 @@ class ACardSystemNamedOnAComputer(unittest.TestCase):
|
||||
contextlib.redirect_stdout(io.StringIO()):
|
||||
chosen = install._prompt_manual_platform("linux")
|
||||
self.assertEqual(chosen, [("rocknix", card / "roms" / "bios")])
|
||||
self.assertEqual(asked, ["Card root (Ctrl+C to stop): "])
|
||||
self.assertEqual(asked, ["Card root, q to quit: "])
|
||||
|
||||
def test_a_card_of_another_system_is_not_offered(self):
|
||||
"""tui_audit_5 P1-1: A on the first mount wrote Onion's BIOS/ on a
|
||||
@@ -3299,7 +3301,8 @@ class OneSystemInFewPresses(unittest.TestCase):
|
||||
index = next(i for i, row in enumerate(rows) if row.value == target)
|
||||
if index == focus:
|
||||
return count
|
||||
page = max(1, state.rows - 6)
|
||||
# A page is the rows drawn (tui_audit_9 P2-2).
|
||||
page = install._page_step(state)
|
||||
button = "R1" if paging and index - focus >= page else "down"
|
||||
self.press_button(state, pad, button)
|
||||
count += 1
|
||||
@@ -3339,10 +3342,12 @@ class OneSystemInFewPresses(unittest.TestCase):
|
||||
# The audit's path, A Down A Up Up A R1 x5 X Start A A. Before the
|
||||
# fast path it took 17 presses here, 57 on muxterm (None kept the
|
||||
# focus, and muxterm had no page key).
|
||||
self.assertEqual(self.presses("gptokeyb", fast=False), 15)
|
||||
self.assertEqual(self.presses("gptokeyb", fast=True), 12)
|
||||
# A page is the rows drawn since tui_audit_9 P2-2, ten here where it
|
||||
# was nine: sys-45 is four pages and five Downs away, not five pages.
|
||||
self.assertEqual(self.presses("gptokeyb", fast=False), 19)
|
||||
self.assertEqual(self.presses("gptokeyb", fast=True), 16)
|
||||
self.assertEqual(self.presses("muxterm", fast=False), 55)
|
||||
self.assertEqual(self.presses("muxterm", fast=True), 12)
|
||||
self.assertEqual(self.presses("muxterm", fast=True), 16)
|
||||
|
||||
def test_a_on_a_system_with_none_ticked_takes_it_alone(self):
|
||||
state = self.long_list("keys")
|
||||
@@ -4878,6 +4883,9 @@ class EndToEnd(unittest.TestCase):
|
||||
with self.subTest(env=env):
|
||||
self.env.update(env)
|
||||
term = self.start()
|
||||
# The setup found is asked as the screen asks it.
|
||||
term.wait("Enter for RetroArch")
|
||||
term.send("\r")
|
||||
term.wait("Enter) install everything")
|
||||
term.send("q\r")
|
||||
term.close()
|
||||
@@ -4896,6 +4904,8 @@ class EndToEnd(unittest.TestCase):
|
||||
else:
|
||||
self.env["TERM"] = value
|
||||
term = self.start()
|
||||
term.wait("Enter for RetroArch")
|
||||
term.send("\r")
|
||||
term.wait("Enter) install everything")
|
||||
term.send("\r")
|
||||
term.wait("installed")
|
||||
@@ -4911,6 +4921,8 @@ class EndToEnd(unittest.TestCase):
|
||||
with self.subTest(term=name):
|
||||
self.env["TERM"] = name
|
||||
term = self.start()
|
||||
term.wait("Enter for RetroArch")
|
||||
term.send("\r")
|
||||
term.wait("Enter) install everything")
|
||||
term.send("q\r")
|
||||
term.close()
|
||||
@@ -5759,7 +5771,7 @@ class AuditSix(unittest.TestCase):
|
||||
contextlib.redirect_stdout(io.StringIO()) as out:
|
||||
dest = install.card_install_dest("onion", "retroarch", True)
|
||||
self.assertEqual(dest, other / "BIOS")
|
||||
self.assertEqual(out.getvalue().count("Type 1 to 2, or a folder."), 2)
|
||||
self.assertEqual(out.getvalue().count("Type 1 to 2, a folder, or q to quit."), 2)
|
||||
|
||||
def test_the_articles_read(self):
|
||||
self.assertEqual([install._a(name) for name in ("Onion", "Allium", "ArkOS", "muOS",
|
||||
@@ -6362,9 +6374,6 @@ def screen_decision(args: argparse.Namespace) -> tuple[str, Path]:
|
||||
for _press in range(6):
|
||||
if any(effect[0] == "fetch" for effect in effects):
|
||||
return state.platform, state.dest
|
||||
if state.screen == "create":
|
||||
# Line mode creates a default folder where the screen asks.
|
||||
state.focus["create"] = 0
|
||||
effects = install.tui_update(state, Key("enter"))
|
||||
finally:
|
||||
install.LOCAL.reset()
|
||||
@@ -6400,8 +6409,10 @@ class LineModeAndScreenDecideAlike(unittest.TestCase):
|
||||
|
||||
def both(self, mounts: list, found: list, **given) -> tuple:
|
||||
def answer(prompt: str) -> str:
|
||||
# The first entry of a numbered list, the default of a folder.
|
||||
return "1" if prompt.startswith(">") else ""
|
||||
# The first entry of a numbered list, the default of a folder,
|
||||
# the first drive of a folder question that has no default.
|
||||
first = prompt.startswith(">") or ("Enter for" not in prompt and " or 1" in prompt)
|
||||
return "1" if first else ""
|
||||
|
||||
extra = {"declared_only": False, "offline": False}
|
||||
with mock.patch.object(install, "_mounted_cards", return_value=mounts), \
|
||||
@@ -6421,7 +6432,28 @@ class LineModeAndScreenDecideAlike(unittest.TestCase):
|
||||
shutil.rmtree(bare / "BIOS")
|
||||
default = install._default_dest("linux", "retroarch")
|
||||
pc = [("retroarch", self.here)]
|
||||
share = self.root / "media" / "SHARE"
|
||||
(share / "roms").mkdir(parents=True)
|
||||
batocera = self.root / "batocera.json"
|
||||
batocera.write_text(json.dumps({"manifest_version": 2, "platform": "batocera",
|
||||
"files": []}))
|
||||
blank = self.root / "blank"
|
||||
blank.mkdir()
|
||||
scenarios = {
|
||||
# code_audit_16 P1-1, P2-2, tui_audit_9 P1-1: the drive's root in
|
||||
# line mode, another system's card first on the screen.
|
||||
"a Batocera list, its SHARE drive": (
|
||||
[share], [], {"manifest": batocera}, ("batocera", share / "bios")),
|
||||
"a Batocera list, a muOS card and its SHARE drive": (
|
||||
[muos, share], [], {"manifest": batocera}, ("batocera", share / "bios")),
|
||||
# tui_audit_9 P2-3, P3-11: the setups found asked as the screen
|
||||
# asks, Enter on the first.
|
||||
"nothing named, RetroArch here": ([], pc, {}, ("retroarch", self.here)),
|
||||
"a list, RetroArch here and an Onion card": (
|
||||
[onion], pc, {"manifest": self.list}, ("retroarch", self.here)),
|
||||
# code_audit_16 P3-1: --dest with a setup found.
|
||||
"--dest a blank folder, RetroArch here": (
|
||||
[onion], pc, {"dest": blank}, ("retroarch", blank)),
|
||||
"a list, an Onion card in the reader": (
|
||||
[onion], [], {"manifest": self.list}, ("retroarch", onion / "BIOS")),
|
||||
"a list, an Onion card without its BIOS folder": (
|
||||
@@ -6568,8 +6600,9 @@ class OneDeviceRegistry(unittest.TestCase):
|
||||
line = str(install._row_line(rows[label], False, 38))
|
||||
self.assertIn(label, line)
|
||||
self.assertTrue(line.rstrip() != (" " + label).rstrip(), line)
|
||||
# tui_audit_9 P3-3: its one device, cut, where "1 device" named none.
|
||||
self.assertTrue(str(install._row_line(rows["CrossMix-OS, SD card"], False, 38))
|
||||
.rstrip().endswith("1 device"))
|
||||
.rstrip().endswith("TrimUI Smar..."))
|
||||
|
||||
|
||||
class AuditFifteenAndEightScreens(unittest.TestCase):
|
||||
@@ -6813,6 +6846,437 @@ class CardBoundaries(unittest.TestCase):
|
||||
self.assertIn("/", points)
|
||||
|
||||
|
||||
|
||||
def _screen_text(state: install.TuiState, cols: int = 80, rows: int = 24) -> str:
|
||||
return "\n".join(line.rstrip() for line in install.tui_view(state, cols, rows))
|
||||
|
||||
|
||||
class AuditSixteenAndNineLineMode(unittest.TestCase):
|
||||
"""code_audit_16 and tui_audit_9: line mode read other decisions than the
|
||||
screen (a drive's root for Batocera, no other setup once one was found,
|
||||
no Enter default, a folder question with no way out)."""
|
||||
|
||||
def setUp(self):
|
||||
self.root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
|
||||
self.addCleanup(shutil.rmtree, self.root, True)
|
||||
for patcher in (mock.patch.dict(install._DETECTED_OS, {}),
|
||||
mock.patch.dict(os.environ, {"HOME": str(self.root / "home"),
|
||||
"XDG_CONFIG_HOME": str(self.root / "cfg"),
|
||||
"RETROBIOS_OS": "linux"})):
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def ask(self, *answers: str):
|
||||
asked = []
|
||||
given = iter(answers)
|
||||
|
||||
def answer(prompt: str) -> str:
|
||||
asked.append(prompt)
|
||||
return next(given)
|
||||
|
||||
return mock.patch.object(install, "_ask", side_effect=answer), asked
|
||||
|
||||
def share_drive(self, name: str = "SHARE") -> Path:
|
||||
drive = self.root / "media" / name
|
||||
(drive / "roms").mkdir(parents=True)
|
||||
return drive
|
||||
|
||||
def test_a_numbered_share_drive_is_its_bios_folder(self):
|
||||
"""code_audit_16 P1-1, tui_audit_9 P1-1: 2) SHARE gave the drive's
|
||||
root, the screen its bios folder."""
|
||||
muos = self.root / "media" / "MUOS"
|
||||
make_card(muos, "muOS")
|
||||
share = self.share_drive()
|
||||
patcher, _asked = self.ask("1")
|
||||
with mock.patch.object(install, "_mounted_cards", return_value=[muos, share]), \
|
||||
mock.patch.object(install, "_mount_points", return_value=frozenset()), \
|
||||
patcher, contextlib.redirect_stdout(io.StringIO()) as out:
|
||||
folder = install.ask_platform_folder("linux", "batocera")
|
||||
self.assertEqual(folder, share / "bios")
|
||||
text = out.getvalue()
|
||||
self.assertIn(f"1) SHARE: bios ({share / 'bios'}, created)", text)
|
||||
self.assertNotIn("MUOS", text)
|
||||
|
||||
def test_the_folder_question_quits_on_q_and_says_what_to_type(self):
|
||||
"""code_audit_16 P2-3: q became the folder ./q, filled, and Enter
|
||||
with no default repeated the prompt with no word."""
|
||||
patcher, asked = self.ask("", "q")
|
||||
with mock.patch.object(install, "_mounted_cards", return_value=[]), patcher, \
|
||||
contextlib.redirect_stdout(io.StringIO()) as out, \
|
||||
self.assertRaises(SystemExit) as stop:
|
||||
install.ask_platform_folder("linux", "batocera")
|
||||
self.assertEqual(stop.exception.code, 130)
|
||||
self.assertIn("q to quit", asked[0])
|
||||
self.assertIn("Type a folder, or q to quit.", out.getvalue())
|
||||
self.assertFalse((Path.cwd() / "q").exists())
|
||||
|
||||
def test_a_relative_folder_is_named_in_full(self):
|
||||
"""code_audit_16 P2-3: BIOS folder: q, Created q, Location: q."""
|
||||
here = self.root / "work"
|
||||
here.mkdir()
|
||||
patcher, _asked = self.ask("./q")
|
||||
with mock.patch.object(install, "_mounted_cards", return_value=[]), patcher, \
|
||||
mock.patch.object(install.Path, "cwd", return_value=here), \
|
||||
mock.patch.object(install.os, "getcwd", return_value=str(here)), \
|
||||
contextlib.redirect_stdout(io.StringIO()):
|
||||
folder = install.ask_platform_folder("linux", "batocera", ask=False)
|
||||
self.assertTrue(folder.is_absolute())
|
||||
self.assertEqual(folder, here / "q")
|
||||
|
||||
def test_a_card_root_question_quits_on_q(self):
|
||||
patcher, asked = self.ask("q")
|
||||
with mock.patch.object(install, "_mounted_cards", return_value=[]), patcher, \
|
||||
contextlib.redirect_stdout(io.StringIO()), \
|
||||
contextlib.redirect_stderr(io.StringIO()), \
|
||||
self.assertRaises(SystemExit) as stop:
|
||||
install.card_install_dest("rocknix", "rocknix", interactive=True)
|
||||
self.assertEqual(stop.exception.code, 130)
|
||||
self.assertEqual(asked, ["Card root, q to quit: "])
|
||||
|
||||
def test_install_for_takes_enter_and_offers_another_setup(self):
|
||||
"""tui_audit_9 P2-3, P3-11: no Enter default, an All the screen does
|
||||
not have, and no other setup once one was found."""
|
||||
here = self.root / "system"
|
||||
found = [("retroarch", here)]
|
||||
patcher, asked = self.ask("")
|
||||
with patcher, contextlib.redirect_stdout(io.StringIO()) as out:
|
||||
chosen = install._prompt_platform_choice(
|
||||
found, labels={("retroarch", here): "RetroArch"}, other=lambda: [])
|
||||
self.assertEqual(chosen, found)
|
||||
self.assertEqual(asked, ["> [Enter for RetroArch] "])
|
||||
self.assertIn("o) another setup or folder", out.getvalue())
|
||||
self.assertNotIn("All", out.getvalue())
|
||||
other = [("batocera", self.root / "bios")]
|
||||
patcher, _asked = self.ask("o")
|
||||
with patcher, contextlib.redirect_stdout(io.StringIO()):
|
||||
self.assertEqual(install._prompt_platform_choice(found, other=lambda: other), other)
|
||||
|
||||
def test_the_setups_for_a_dest_come_in_the_screens_order(self):
|
||||
"""code_audit_16 P3-1: with RetroArch found, --plain --dest put Onion
|
||||
at 19, the screen at 29."""
|
||||
dest = self.root / "blank"
|
||||
dest.mkdir()
|
||||
here = self.root / "system"
|
||||
listed = {}
|
||||
|
||||
def choose(title, default="", setups=None, mounted=None):
|
||||
listed["rows"] = [name for name, _label in install._line_setups(
|
||||
setups, mounted, "linux")]
|
||||
listed["default"] = default
|
||||
raise _Decided(None)
|
||||
|
||||
with mock.patch.object(install, "detect_platforms",
|
||||
return_value=[("retroarch", here)]), \
|
||||
mock.patch.object(install, "_mounted_cards", return_value=[]), \
|
||||
mock.patch.object(install, "_choose_platform", side_effect=choose), \
|
||||
contextlib.redirect_stdout(io.StringIO()), self.assertRaises(_Decided):
|
||||
install._platform_for_dest("linux", dest, True)
|
||||
with mock.patch.object(install, "detect_platforms",
|
||||
return_value=[("retroarch", here)]), \
|
||||
mock.patch.object(install, "_mounted_cards", return_value=[]), \
|
||||
mock.patch.object(install, "detect_frontends", return_value=[]), \
|
||||
contextlib.redirect_stdout(io.StringIO()):
|
||||
state, _effects = install.tui_start(_args(dest=dest), "linux")
|
||||
screen = [install._row_name(row) for row in install.tui_rows(state)
|
||||
if row.action != "filter"]
|
||||
self.assertEqual(listed["rows"], screen)
|
||||
rows = install.tui_rows(state)
|
||||
self.assertEqual(install._row_name(rows[state.focus["platforms"]]), listed["default"])
|
||||
|
||||
def test_system_named_on_the_command_line_keeps_the_menu(self):
|
||||
"""tui_audit_9 P3-8: --system skipped the menu and its folder line."""
|
||||
files = [{"dest": "a.bin", "size": 1, "systems": ["nintendo-gba"], "cores": None},
|
||||
{"dest": "b.bin", "size": 1, "systems": ["sega-saturn"], "cores": None}]
|
||||
given = (["nintendo-gba"], [], [])
|
||||
patcher, _asked = self.ask("")
|
||||
with patcher, contextlib.redirect_stdout(io.StringIO()) as out:
|
||||
chosen = install._prompt_custom_selection(
|
||||
"retroarch", files, [], bios_path=self.root, given=given)
|
||||
self.assertEqual(chosen, given)
|
||||
text = out.getvalue()
|
||||
self.assertIn("RetroArch: 1 file, 1 B, for 1 of 2 systems (--system).", text)
|
||||
self.assertIn("Enter) install these", text)
|
||||
self.assertIn(f"BIOS folder: {self.root}", text)
|
||||
|
||||
def test_narrowing_is_said_without_an_arrow(self):
|
||||
source = Path(install.__file__).read_text(encoding="utf-8")
|
||||
self.assertNotIn("} -> {len(files)}", source)
|
||||
|
||||
def test_android_typed_names_retroarch(self):
|
||||
"""tui_audit_9 P3-5: android matched nothing, and nothing more."""
|
||||
patcher, _asked = self.ask("android", "q")
|
||||
with patcher, mock.patch.object(install, "detect_os", return_value="linux"), \
|
||||
contextlib.redirect_stdout(io.StringIO()) as out, \
|
||||
self.assertRaises(SystemExit):
|
||||
install._choose_platform("Which setup?")
|
||||
self.assertIn("On Android, choose RetroArch", out.getvalue())
|
||||
|
||||
def test_batocera_named_on_a_computer_without_questions_is_refused(self):
|
||||
"""code_audit_16 P3-3: /userdata/bios taken, Cannot create, end 2
|
||||
with no error event."""
|
||||
listing = self.root / "batocera.json"
|
||||
listing.write_text(json.dumps({"manifest_version": 2, "platform": "batocera",
|
||||
"files": []}))
|
||||
events = io.StringIO()
|
||||
with mock.patch.object(install, "detect_platforms", return_value=[]), \
|
||||
mock.patch.object(install, "detect_os", return_value="linux"), \
|
||||
mock.patch.object(install.EVENTS, "stream", events), \
|
||||
contextlib.redirect_stdout(io.StringIO()), \
|
||||
contextlib.redirect_stderr(io.StringIO()) as err, \
|
||||
self.assertRaises(SystemExit) as stop:
|
||||
install._install(_args(platform="batocera", no_input=True, manifest=listing),
|
||||
io.StringIO())
|
||||
self.assertEqual(stop.exception.code, 1)
|
||||
self.assertNotIn("/userdata/bios", err.getvalue())
|
||||
self.assertIn("--dest /media/$USER/SHARE/bios", err.getvalue())
|
||||
kinds = [json.loads(line)["event"] for line in events.getvalue().splitlines()]
|
||||
self.assertEqual(kinds[-1], "error")
|
||||
|
||||
|
||||
class AuditSixteenAndNineScreens(unittest.TestCase):
|
||||
"""The screen points of code_audit_16 and tui_audit_9."""
|
||||
|
||||
def setUp(self):
|
||||
self.root = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
|
||||
self.addCleanup(shutil.rmtree, self.root, True)
|
||||
patcher = mock.patch.dict(install._DETECTED_OS, {})
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def start(self, mounts=(), found=(), **given) -> tuple:
|
||||
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_platforms", return_value=list(found)), \
|
||||
mock.patch.object(install, "detect_frontends", return_value=[]):
|
||||
return install.tui_start(_args(**given), "linux")
|
||||
|
||||
def test_a_folder_holding_a_mounted_muos_card_is_no_muos_card(self):
|
||||
"""code_audit_16 P2-2: /media/me held the MUOS mount point and read as
|
||||
a muOS card, and _own_card_root walked above the mount."""
|
||||
parent = self.root / "media"
|
||||
muos = parent / "MUOS"
|
||||
make_card(muos, "muOS")
|
||||
points = frozenset({str(muos)})
|
||||
with mock.patch.object(install, "_mount_points", return_value=points), \
|
||||
mock.patch.object(install, "_mounted_cards", return_value=[muos]):
|
||||
self.assertIsNone(install.card_system(parent))
|
||||
self.assertEqual(install.card_system(muos)[1], muos / "MUOS" / "bios")
|
||||
self.assertIsNone(install._own_card_root(muos / "bios"))
|
||||
self.assertEqual(install._own_card_root(muos / "MUOS" / "bios"), muos)
|
||||
|
||||
def test_batocera_share_rows_leave_out_another_systems_card(self):
|
||||
"""code_audit_16 P2-2: the muOS card was the first share row."""
|
||||
muos = self.root / "MUOS"
|
||||
make_card(muos, "muOS")
|
||||
share = self.root / "SHARE"
|
||||
share.mkdir()
|
||||
state, _effects = self.start([muos, share], platform="batocera")
|
||||
with mock.patch.object(install, "_mounted_cards", return_value=[muos, share]), \
|
||||
mock.patch.object(install, "_mount_points", return_value=frozenset()):
|
||||
rows = [(row.action, row.label) for row in install.tui_rows(state)]
|
||||
self.assertIn(("share", "SHARE: bios"), rows)
|
||||
self.assertNotIn(("share", "MUOS: bios"), rows)
|
||||
|
||||
def test_a_proposed_folder_is_created_without_the_guard(self):
|
||||
"""tui_audit_9 P2-1: Use this folder, then Create the folder? focused
|
||||
on Choose another folder, and Enter, Enter looped."""
|
||||
state = install.TuiState(os_type="linux", home=self.root)
|
||||
usual = self.root / ".config" / "retroarch" / "system"
|
||||
state.stack, state.platform = ["platforms"], "retroarch"
|
||||
install._open_folder_screen(state, usual)
|
||||
effects = install.tui_update(state, Key("enter"))
|
||||
self.assertEqual(effects, [("fetch", "retroarch")])
|
||||
self.assertNotIn("create", state.stack)
|
||||
self.assertIn("system is created in", state.card_note)
|
||||
typed = self.root / "a" / "b" / "c"
|
||||
state = install.TuiState(os_type="linux", home=self.root)
|
||||
state.stack, state.platform = ["folder"], "retroarch"
|
||||
install._choose(state, "retroarch", typed)
|
||||
self.assertEqual(state.screen, "create")
|
||||
self.assertEqual(install.tui_rows(state)[state.focus["create"]].action, "other")
|
||||
|
||||
def test_a_page_moves_by_the_rows_drawn(self):
|
||||
"""tui_audit_9 P2-2: at 40x12 PgDn moved six rows over four drawn,
|
||||
and muOS and ROCKNIX were never shown."""
|
||||
for cols, rows in ((40, 12), (40, 15), (80, 24)):
|
||||
with self.subTest(size=f"{cols}x{rows}"):
|
||||
state, _effects = self.start()
|
||||
state.cols, state.rows = cols, rows
|
||||
seen = set()
|
||||
for _press in range(40):
|
||||
text = _screen_text(state, cols, rows)
|
||||
seen |= {line.strip("|> ").split(",")[0].strip()
|
||||
for line in text.splitlines()}
|
||||
install.tui_update(state, Key("pgdn"))
|
||||
for name in ("muOS, SD card", "ROCKNIX, SD card", "JELOS, SD card",
|
||||
"EmuELEC, SD card", "RetroOZ, SD card"):
|
||||
self.assertTrue(any(line.startswith(name.split(",")[0]) for line in seen),
|
||||
name)
|
||||
|
||||
def test_the_first_screen_says_quit_and_a_filter_says_clear(self):
|
||||
"""tui_audit_9 P3-1, P3-2."""
|
||||
state, _effects = self.start(platform="onion")
|
||||
self.assertEqual(state.stack, ["folder"])
|
||||
bar = install.tui_view(state, 80, 24)[-1]
|
||||
self.assertIn("Esc Quit", bar)
|
||||
self.assertNotIn("Esc Back", bar)
|
||||
state, _effects = self.start()
|
||||
press(state, "/", "o", "n")
|
||||
self.assertIn("Esc Clear", install.tui_view(state, 80, 24)[-1])
|
||||
press(state, "enter")
|
||||
self.assertIn("Esc Clear", install.tui_view(state, 80, 24)[-1])
|
||||
|
||||
def test_rows_at_40_columns_name_a_device(self):
|
||||
"""tui_audit_9 P3-3: "1 device" for CrossMix-OS."""
|
||||
state, _effects = self.start()
|
||||
text = _screen_text(state, 40, 60)
|
||||
self.assertNotIn("1 device", text)
|
||||
crossmix = next(line for line in text.splitlines() if "CrossMix-OS" in line)
|
||||
self.assertIn("TrimUI", crossmix)
|
||||
|
||||
def test_a_model_finds_the_systems_that_list_its_family(self):
|
||||
"""code_audit_16 P2-1, tui_audit_9 P3-4: RG353V missed ROCKNIX, RG351P
|
||||
missed AmberELEC and ROCKNIX."""
|
||||
for system, devices in install._SETUP_DEVICES.items():
|
||||
for device in devices:
|
||||
for model in (device, *install._DEVICE_FAMILIES.get(device, ())):
|
||||
rows = install.setup_list([], set(), model, "android")
|
||||
names = {install._row_name(row) for row in rows}
|
||||
with self.subTest(system=system, model=model):
|
||||
self.assertIn(system, names)
|
||||
|
||||
def test_the_filter_reads_system_names_as_line_mode_does(self):
|
||||
"""code_audit_16 P3-2."""
|
||||
for typed, system in (("spruce os", "spruceOS"), ("unofficial os", "UnofficialOS"),
|
||||
("cross mix", "CrossMix-OS")):
|
||||
with self.subTest(typed=typed):
|
||||
rows = install.setup_list([], set(), typed, "linux")
|
||||
self.assertIn(system, {install._row_name(row) for row in rows})
|
||||
|
||||
def test_android_on_the_filter_names_retroarch(self):
|
||||
state, _effects = self.start()
|
||||
state.filters["platforms"] = "android"
|
||||
self.assertIn("On Android, choose RetroArch", " ".join(_screen_text(state).split()))
|
||||
|
||||
def test_a_wrapped_path_has_no_space_inserted(self):
|
||||
"""tui_audit_9 P3-6: retroarch/ system."""
|
||||
path = "/home/moi/retroarch_system/tmp/tui_audit9/sb/nd/home/.config/retroarch/system"
|
||||
for width in range(30, 100):
|
||||
lines = install._wrap(f"{path} does not exist.", width)
|
||||
# Each line after the first is indented by two spaces.
|
||||
joined = lines[0] + "".join(line[2:] for line in lines[1:])
|
||||
with self.subTest(width=width):
|
||||
self.assertIn(path, joined)
|
||||
self.assertTrue(all("/ " not in line for line in lines), lines)
|
||||
|
||||
def test_f1_names_what_the_card_lacks(self):
|
||||
"""tui_audit_9 P3-7: F1 said the refusal again."""
|
||||
muos = self.root / "MUOS"
|
||||
make_card(muos, "muOS")
|
||||
setup = install.card_setup("Onion", "retroarch")
|
||||
choice = install._not_the_card(setup, muos, muos, "muOS")
|
||||
self.assertIn(".tmp_update", choice.detail)
|
||||
|
||||
def test_help_at_40x12_keeps_quit_and_this_help(self):
|
||||
"""tui_audit_9 P3-15, and Space for ticking on a keyboard."""
|
||||
state = checked_state()
|
||||
press(state, "down", "down", "down", "down", "enter")
|
||||
state.stack.append("systems")
|
||||
state.help = True
|
||||
text = _screen_text(state, 40, 12)
|
||||
self.assertIn("Quit", text)
|
||||
self.assertIn("This help", text)
|
||||
self.assertIn("X, Space, Right/Left", " ".join(text.split()))
|
||||
|
||||
def test_the_copy_hint_fits_at_40_columns(self):
|
||||
"""tui_audit_9 P3-16: cut after "copied,"."""
|
||||
state = checked_state()
|
||||
with mock.patch.object(install, "_mounted_cards", return_value=[]), \
|
||||
mock.patch.object(install, "_listed", return_value=[]):
|
||||
install._open_from_browser(state)
|
||||
text = " ".join(_screen_text(state, 40, 12).split())
|
||||
self.assertIn("Choose the old card, a backup folder or a ZIP.", text)
|
||||
|
||||
def test_the_miyoo_bar_says_what_a_does(self):
|
||||
"""tui_audit_9 P3-17: A Tick on All and None, where A unticks."""
|
||||
state = checked_state()
|
||||
state.pad = "miyoo"
|
||||
state.stack.append("systems")
|
||||
state.focus["systems"] = 1
|
||||
bar = install.tui_view(state, 40, 12)[-1]
|
||||
self.assertNotIn("A Tick", bar)
|
||||
state.focus["systems"] = 3
|
||||
self.assertIn("A Tick", install.tui_view(state, 40, 12)[-1])
|
||||
|
||||
def test_system_given_is_said_and_widened(self):
|
||||
"""tui_audit_9 P3-8: one system named, no word on the plan and no
|
||||
way to widen it."""
|
||||
state = checked_state()
|
||||
state.cli_systems = ["gba"]
|
||||
state.systems = [row for row in state.systems if row["system"] == "nintendo-gba"]
|
||||
state.ticked = {"nintendo-gba"}
|
||||
self.assertIn("For 1 of 3 systems (--system).", " ".join(_screen_text(state).split()))
|
||||
row = next(row for row in install.tui_rows(state) if row.action == "systems")
|
||||
effects = install._activate(state, row)
|
||||
self.assertEqual(effects, [("run", "check")])
|
||||
self.assertNotIn(("--system", "gba"), install.tui_flags(state))
|
||||
self.assertTrue(state.widen)
|
||||
|
||||
def test_a_local_copy_draws_no_speed(self):
|
||||
"""tui_audit_9 P3-9: 0 B/s 0:00 left."""
|
||||
out = io.StringIO()
|
||||
term = install.Terminal(out)
|
||||
term.live = True
|
||||
progress = install.Progress(2, 100, term)
|
||||
progress.add(50, resumed=True, network=False)
|
||||
progress._draw()
|
||||
self.assertNotIn("B/s", out.getvalue())
|
||||
self.assertNotIn("left", out.getvalue())
|
||||
|
||||
def test_the_offline_remedy_follows_the_files_it_is_about(self):
|
||||
"""tui_audit_9 P3-10."""
|
||||
unplaced = [{"reason": "no_save_directory", "frontend": "RetroArch"}]
|
||||
with mock.patch.object(install, "_offline_only", return_value=True), \
|
||||
contextlib.redirect_stdout(io.StringIO()) as out, \
|
||||
self.assertRaises(SystemExit):
|
||||
install._report_outcome([self.root], 6, 0, 0, 1, False,
|
||||
omitted_entries=unplaced, offline=16,
|
||||
sources="6 files copied from OLD, 16 still missing.")
|
||||
lines = out.getvalue().splitlines()
|
||||
remedy = next(i for i, line in enumerate(lines) if "Copy them" in line
|
||||
or "They are not in" in line)
|
||||
missing = next(i for i, line in enumerate(lines) if "still missing" in line)
|
||||
save = next(i for i, line in enumerate(lines) if "save directory" in line)
|
||||
self.assertEqual(remedy, missing + 1)
|
||||
self.assertLess(remedy, save)
|
||||
|
||||
def test_first_install_says_where_to_type(self):
|
||||
"""code_audit_16 P3-4, tui_audit_9 P3-19."""
|
||||
page = (REPO_ROOT / "wiki" / "first-install.md").read_text(encoding="utf-8")
|
||||
self.assertNotIn("There, type its name", page)
|
||||
self.assertIn("Press / and type the device", page)
|
||||
|
||||
def test_a_folder_that_cannot_be_created_ends_on_an_error_and_a_summary(self):
|
||||
"""code_audit_16 P3-3: Cannot create, then end 2 and nothing else."""
|
||||
import subprocess # noqa: PLC0415
|
||||
|
||||
from test_install_offline import PAYLOAD, _offline_env, _pack
|
||||
|
||||
blocker = self.root / "file"
|
||||
blocker.write_text("not a folder")
|
||||
pack = _pack(self.root, "RetroArch_v1_BIOS_Pack.zip", PAYLOAD)
|
||||
proc = subprocess.run(
|
||||
[sys.executable, str(REPO_ROOT / "install.py"), "--platform", "retroarch",
|
||||
"--dest", str(blocker / "system"), "--from-pack", str(pack), "--events"],
|
||||
env=_offline_env(), capture_output=True, text=True, timeout=120, check=False,
|
||||
stdin=subprocess.DEVNULL)
|
||||
events = [json.loads(line) for line in proc.stdout.splitlines()]
|
||||
kinds = [event["event"] for event in events]
|
||||
self.assertEqual(kinds[-3:], ["error", "summary", "end"], proc.stderr)
|
||||
self.assertEqual((events[-3]["stage"], events[-3]["reason"]), ("dest", "not_writable"))
|
||||
self.assertEqual(events[-1]["code"], 2)
|
||||
|
||||
|
||||
class EdgesTheMutantsFound(unittest.TestCase):
|
||||
"""Changes to the code that every test let through: the survivors of a
|
||||
mutmut run over the device filter, the card bounds and the WSL paths."""
|
||||
@@ -6844,7 +7308,9 @@ class EdgesTheMutantsFound(unittest.TestCase):
|
||||
make_card(root / "card", system)
|
||||
return root / "card"
|
||||
|
||||
def test_a_card_is_looked_for_up_to_the_mount_point_and_no_further(self):
|
||||
def test_a_card_is_looked_for_up_to_its_mount_point_and_no_further(self):
|
||||
"""code_audit_15 P3-2 reads every folder up to the mount point:
|
||||
three at most took MUOS/a/b/c/d for a KNULLI card."""
|
||||
card = self.card("KNULLI")
|
||||
setup = install.card_setup("KNULLI", "knulli")
|
||||
deep = card / "a" / "b" / "c" / "d"
|
||||
|
||||
@@ -80,8 +80,9 @@ It opens a menu:
|
||||
```
|
||||
|
||||
- **Right folder?** Press Enter.
|
||||
- **Wrong folder?** Choose **Another setup or folder**, then browse to yours.
|
||||
- **A handheld's SD card?** There, type its name (Miyoo Mini).
|
||||
- **Wrong folder, or a handheld's SD card?** Choose
|
||||
**Another setup or folder**. Press / and type the device (Miyoo Mini),
|
||||
or browse.
|
||||
|
||||
Arrows move, Enter chooses, Esc goes back, as A and B on a handheld.
|
||||
|
||||
|
||||
+25
-12
@@ -208,6 +208,10 @@ line instead, by themselves. A terminal whose description is not found on the
|
||||
machine gets the screen. A listing, `--check`, `--dry-run`, `--events`,
|
||||
`--quiet`, `--no-input`, `--yes` and `--restore` keep the plain output. Options
|
||||
given on the command line fill the screens in advance and are kept as they are.
|
||||
`--system` is said on the plan (`For 1 of 12 systems (--system)`), and
|
||||
**Choose systems** reads every system again with those ticked. The `--plain`
|
||||
menu shows it the same way, Enter keeping what `--system` named and `c`
|
||||
choosing anew.
|
||||
|
||||
However the screen ends (Enter on the result, `q`, Ctrl+C, Ctrl+D, SIGTERM, a
|
||||
hangup), the terminal is given back as the screen found it: the cursor shown,
|
||||
@@ -959,7 +963,7 @@ own in the setup list (**Onion, SD card**), without the filter, and in the
|
||||
`--plain` menu, beside the devices that run it (Miyoo Mini, Miyoo Mini Plus).
|
||||
The filter takes a device name, and so does the `--plain` menu, which lists
|
||||
the setups it matches by number. With nothing found here, the SD card rows
|
||||
come right after RetroArch: Onion is four presses away with L1 and R1 at
|
||||
come right after RetroArch: Onion comes into view after five presses of L1 and R1 at
|
||||
40x12, where it was row 30 of 34. That row, `--platform onion` and the menu
|
||||
all ask for the card: one card of that system mounted when the run starts is
|
||||
taken, otherwise
|
||||
@@ -994,14 +998,22 @@ home folder or above the default, no default is taken: the frontend's own
|
||||
configuration there may name another folder, so the run asks for `--dest`.
|
||||
|
||||
With nothing detected, the setup screen lists the platforms to choose from,
|
||||
and `--plain` gives the same list numbered, in the same order. Both find the
|
||||
SD cards mounted here, the screen under **Found on this machine**. With
|
||||
several found, the setup screen lists each one, and `--plain` asks which. A
|
||||
platform named, or read from a list or a pack, and found nowhere gets its
|
||||
folder asked, its usual one under Enter, the mounted drives numbered. On a
|
||||
computer Batocera and Recalbox have none: the `bios` folder of their SHARE
|
||||
drive or network share is theirs. A device several systems run lists them
|
||||
all. Both need a terminal: piped into a script
|
||||
and `--plain` gives the same list numbered, in the same order, with `--dest`
|
||||
too. Both find the SD cards mounted here, the screen under **Found on this
|
||||
machine**. With a setup found, the setup screen lists it with **Another setup
|
||||
or folder**, and `--plain` asks the same: Enter takes the first, `o` opens the
|
||||
numbered list and then the folder, `q` quits. A platform named, or read from
|
||||
a list or a pack, and found nowhere gets its folder asked, its usual one under
|
||||
Enter, the mounted drives numbered. A folder typed is read from the current
|
||||
one when it is not a full path, and named in full after. `q` quits there too
|
||||
(a folder named `q` is typed `./q`), and Enter with no default says what to
|
||||
type. On a computer Batocera and Recalbox have no default: the drives are
|
||||
numbered by their `bios` folder (`SHARE: bios`), as the screen lists them,
|
||||
and a drive whose files name another system is left out. Without questions
|
||||
the run stops there with an `error` event and a `--dest` example. A device
|
||||
several systems run lists them all, and a model finds the systems that list
|
||||
its family (`RG353V` finds ROCKNIX, which lists `RG353`). Both need a
|
||||
terminal: piped into a script
|
||||
with no platform to install for, it prints the manual invocation, in the form
|
||||
the run was started with (the one-line command for a one-liner user), and
|
||||
exits 1.
|
||||
@@ -1196,7 +1208,7 @@ gets the column that names both:
|
||||
| Move | Up/Down, k/j | Up/Down, k/j | D-pad, Up/Down | D-pad, Up/Down | D-pad, Up/Down | D-pad, Up/Down | D-pad, Up/Down |
|
||||
| Page | PgUp/PgDn | PgUp/PgDn, L1/R1 | L1/R1, PgUp/PgDn | D-pad Left/Right | D-pad Left/Right | L1/R1, PgUp/PgDn | L1/R1, PgUp/PgDn |
|
||||
| Continue, open, choose | Enter | A/Enter, Start | A, Start, Enter | A, Start, Enter | Start, Enter | A, Start, Enter | A, Start, Enter |
|
||||
| Tick, untick | Space, Right/Left | X, Right/Left | X, Space, Right/Left | X, Space | Y, Space | X, Space, Right/Left | A, Space, Right/Left |
|
||||
| Tick, untick | Space, Right/Left | X, Space, Right/Left | X, Space, Right/Left | X, Space | Y, Space | X, Space, Right/Left | A, Space, Right/Left |
|
||||
| Options | o, Tab | Y, o, Tab | Y, Tab, o | Y, Tab, o | o, Tab | Y, Tab, o | L2, Tab, o |
|
||||
| Back, and quit on the first screen | Esc, Backspace | B/Esc, Backspace | B, Select, Esc | B, Select, Esc | B, Esc, Backspace | B, Select, Esc | R2, Esc, Backspace |
|
||||
| Stop an install | Esc, Ctrl-C | B/Esc, Ctrl-C | B, Esc, Ctrl-C | B, Esc, Ctrl-C | B, Esc, Ctrl-C | B, Esc, Ctrl-C | R2, Esc, Ctrl-C |
|
||||
@@ -1376,7 +1388,8 @@ The tables of options, events, exit codes and keys on this page sit between
|
||||
from `EVENT_KEYS`, the codes from its `sys.exit` calls, the keys from
|
||||
`TUI_ROUTES`. The prose cells stay as written. A row the code gains stops the
|
||||
script until its text is added inside the markers, and `--check`, which a
|
||||
test runs, fails on a table behind the code.
|
||||
test runs, fails on a table behind the code. `--page FILE` renders another
|
||||
copy of the page, which the tests use.
|
||||
|
||||
The [handheld](#on-a-handheld-the-retrobios-port) archives each carry
|
||||
`install.py`, the one the one-line command of the same commit downloads:
|
||||
@@ -1444,7 +1457,7 @@ other schemas under `api/v1/schemas/`.
|
||||
| `start` | `schema`, `installer`, `mode` | First line. `schema` is 1, `installer` the identity `--version` prints, `mode` one of `install`, `check`, `dry-run`, `list` |
|
||||
| `unsearched` | `folder`, `reason`, `message` | Before `detect`, once for each folder the search for setups could not look in, this user being refused it. What it would hold counts as absent, and the other places are still searched: `detect` then lists what they hold, and an empty `platforms` after an `unsearched` line means nothing was found where the installer could look. `reason` says why in words (`Permission denied`), `message` says it in a sentence |
|
||||
| `detect` | `os`, `os_name`, `platforms` | Once the platforms are known. `os_name` comes from `/etc/os-release`. `platforms` lists `{platform, name, bios_dir}`, and is empty when nothing supported was found |
|
||||
| `error` | `stage`, `reason`, `platform`, `message` | The file list could not be read (`stage` is `manifest`): `reason` is `network`, `not_found` or `invalid`, `clock` when the server's certificate was refused and the system clock reads a date before the installer was written, `no_ca` when the machine holds no list of trusted certificates (`SSL_CERT_FILE` names one), or `offline` when `--offline` forbids the request. A folder named with `--from` cannot be read (`stage` is `from`, `reason` `not_found`, `not_a_folder` for a file that is no ZIP, `unreadable`, or `system` for the root of the system, `/proc`, `/sys` or `/dev`). The BIOS folder sits under a folder this user cannot open (`stage` is `dest`, `reason` `unreadable`), and the run ends with code 1 before anything is written. Two systems are detected on one machine and no `--dest` names the folder (`stage` is `detect`, `reason` `conflict`). The platform named with `--platform` is found nowhere and no folder can be assumed for it: a card system with no card named, a portable emulator, or no usual folder on this system or one set aside behind a folder this user cannot open (`stage` is `detect`, `reason` `not_found`). A `--dest` refused as the card of the system named carries the same `stage` and `reason`: another system's card, a folder inside a card, a drive with none of the files its card always has, or a folder that does not exist. `message` says it in words, and is the only thing that tells these cases apart |
|
||||
| `error` | `stage`, `reason`, `platform`, `message` | The file list could not be read (`stage` is `manifest`): `reason` is `network`, `not_found` or `invalid`, `clock` when the server's certificate was refused and the system clock reads a date before the installer was written, `no_ca` when the machine holds no list of trusted certificates (`SSL_CERT_FILE` names one), or `offline` when `--offline` forbids the request. A folder named with `--from` cannot be read (`stage` is `from`, `reason` `not_found`, `not_a_folder` for a file that is no ZIP, `unreadable`, or `system` for the root of the system, `/proc`, `/sys` or `/dev`). The BIOS folder sits under a folder this user cannot open (`stage` is `dest`, `reason` `unreadable`), and the run ends with code 1 before anything is written. The BIOS folder cannot be created (`stage` is `dest`, `reason` `not_writable`): a `summary` follows with what the run installed before it. Two systems are detected on one machine and no `--dest` names the folder (`stage` is `detect`, `reason` `conflict`). The platform named with `--platform` is found nowhere and no folder can be assumed for it: a card system with no card named, a portable emulator, Batocera or Recalbox named on a computer (their folder is the `bios` folder of their SHARE drive), or no usual folder on this system or one set aside behind a folder this user cannot open (`stage` is `detect`, `reason` `not_found`). A `--dest` refused as the card of the system named carries the same `stage` and `reason`: another system's card, a folder inside a card, a drive with none of the files its card always has, or a folder that does not exist. `message` says it in words, and is the only thing that tells these cases apart |
|
||||
| `scanned` | `folder`, `files`, `truncated`, `other_drives` | Each folder named with `--from`, once read and before `detect`. `files` counts the files found, `truncated` is true when the search stopped at 200,000 of them, and `other_drives` lists the folders below it on another drive, which were not entered |
|
||||
| `file` | `platform`, `dest`, `systems`, `size`, `state` | Each file of the list after the check. `state` is `present` (hash verified), `wrong_hash` or `missing` |
|
||||
| `system` | `platform`, `system`, `name`, `files`, `present`, `missing`, `wrong_hash`, `bytes_needed` | Each system, sorted by id, and with `--system` only the systems named. Files with no system count under `other`. `name` is the platform's own name for it |
|
||||
|
||||
Reference in new issue
Block a user