mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 13:33:24 -05:00
fix: count a refused standalone copy as an error
This commit is contained in:
1 parent
1a862bc318
commit
4a30cbf882
4 files changed
+92
-19
No files matched your search
+1
-1
@@ -8,7 +8,7 @@ param(
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "60e18da18b5a9a43b18d37020b676ad47babd6d12c5314e8fcc1370e4864c4e5"
|
||||
$defaultInstallSha256 = "df0adf1fd5088ef9509429fb1d5720e5ca5e7bf46aff9ad0021851dce7349107"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
$temporary = $null
|
||||
|
||||
+19
-10
@@ -120,7 +120,8 @@ def _os_default_dests(os_type: str) -> dict[str, Path]:
|
||||
appdata = Path(os.environ.get("APPDATA", str(home / "AppData" / "Roaming")))
|
||||
return {
|
||||
"retroarch": appdata / "RetroArch" / "system",
|
||||
"retrobat": profile / "RetroBat" / "bios",
|
||||
# RetroBat's installer defaults to C:\RetroBat (installer.iss).
|
||||
"retrobat": Path("C:/RetroBat/bios"),
|
||||
"bizhawk": profile / "BizHawk" / "Firmware",
|
||||
}
|
||||
if os_type == "darwin":
|
||||
@@ -1294,7 +1295,7 @@ def download_files(
|
||||
def do_standalone_copies(
|
||||
manifest: dict, bios_path: Path, os_type: str,
|
||||
extra_dirs: dict[str, Path] | None = None,
|
||||
) -> tuple[int, int]:
|
||||
) -> tuple[int, int, int]:
|
||||
"""Copy BIOS files to standalone emulator directories.
|
||||
|
||||
Supports:
|
||||
@@ -1307,16 +1308,19 @@ def do_standalone_copies(
|
||||
default per-OS locations, for setups such as LaunchBox that keep their
|
||||
emulators outside them. The layout below the root is the same.
|
||||
|
||||
Returns (copied_count, skipped_count).
|
||||
Returns (copied, skipped, failed): skipped is a target directory that
|
||||
does not exist, failed a copy the system refused (permission, disk
|
||||
full, read-only mount), which used to be counted as "dir not found".
|
||||
"""
|
||||
from fnmatch import fnmatch
|
||||
|
||||
copies = manifest.get("standalone_copies", [])
|
||||
if not copies:
|
||||
return 0, 0
|
||||
return 0, 0, 0
|
||||
|
||||
copied = 0
|
||||
skipped = 0
|
||||
failed = 0
|
||||
|
||||
for entry in copies:
|
||||
# Note entries: print message if emulator detected
|
||||
@@ -1375,10 +1379,11 @@ def do_standalone_copies(
|
||||
try:
|
||||
shutil.copy2(src, dest)
|
||||
copied += 1
|
||||
except OSError:
|
||||
skipped += 1
|
||||
except OSError as exc:
|
||||
print(f" ERROR: {dest}: {exc.strerror or exc}")
|
||||
failed += 1
|
||||
|
||||
return copied, skipped
|
||||
return copied, skipped, failed
|
||||
|
||||
|
||||
def format_size(n: int) -> str:
|
||||
@@ -1738,11 +1743,15 @@ def main() -> None:
|
||||
print("\nStandalone emulators:")
|
||||
lb_root = launchbox_root(os_type)
|
||||
extra_dirs = launchbox_bios_dirs(lb_root) if lb_root else None
|
||||
copied, skipped = do_standalone_copies(
|
||||
copied, skipped, copy_failed = do_standalone_copies(
|
||||
manifest, bios_path, os_type, extra_dirs
|
||||
)
|
||||
if copied or skipped:
|
||||
print(f" {copied} copied, {skipped} skipped (dir not found)")
|
||||
if copied or skipped or copy_failed:
|
||||
print(
|
||||
f" {copied} copied, {skipped} skipped (dir not found), "
|
||||
f"{copy_failed} failed"
|
||||
)
|
||||
total_errors += copy_failed
|
||||
elif manifest.get("standalone_copies") and not args.check:
|
||||
print(
|
||||
"\nStandalone copies skipped "
|
||||
|
||||
+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="60e18da18b5a9a43b18d37020b676ad47babd6d12c5314e8fcc1370e4864c4e5"
|
||||
DEFAULT_INSTALL_SHA256="df0adf1fd5088ef9509429fb1d5720e5ca5e7bf46aff9ad0021851dce7349107"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
+71
-7
@@ -218,6 +218,15 @@ class TestDefaultsFollowTheRegistry(unittest.TestCase):
|
||||
with self.subTest(platform=name, os=os_type):
|
||||
self.assertIn(install._default_dest(os_type, name), candidates)
|
||||
checked += 1
|
||||
# The installer's own default comes first in the registry and
|
||||
# is the one the pack guide and the installer name.
|
||||
first = next(
|
||||
(self._expand(rule["path"]) for rule in (entry.get("install") or {}).get("detect", [])
|
||||
if rule.get("os") == "windows" and rule.get("method") == "path_exists"),
|
||||
None,
|
||||
)
|
||||
if name == "retrobat":
|
||||
self.assertEqual(install._default_dest("windows", name), first)
|
||||
self.assertGreater(checked, 10)
|
||||
|
||||
|
||||
@@ -789,7 +798,7 @@ class TestStandaloneCopiesExtraDirs(unittest.TestCase):
|
||||
(bios / "ps2-0230a.bin").write_text("rom")
|
||||
extra = Path(tempfile.mkdtemp()) / "bios"
|
||||
extra.mkdir()
|
||||
copied, _ = install.do_standalone_copies(
|
||||
copied, _, _ = install.do_standalone_copies(
|
||||
self._manifest(), bios, "windows", {"pcsx2": extra}
|
||||
)
|
||||
self.assertEqual(copied, 1)
|
||||
@@ -801,7 +810,7 @@ class TestStandaloneCopiesExtraDirs(unittest.TestCase):
|
||||
(bios / "GC" / "USA" / "IPL.bin").write_text("ipl")
|
||||
user = Path(tempfile.mkdtemp()) / "User"
|
||||
(user / "GC" / "USA").mkdir(parents=True)
|
||||
copied, _ = install.do_standalone_copies(
|
||||
copied, _, _ = install.do_standalone_copies(
|
||||
self._manifest(), bios, "windows", {"dolphin": user}
|
||||
)
|
||||
self.assertEqual(copied, 1)
|
||||
@@ -811,10 +820,64 @@ class TestStandaloneCopiesExtraDirs(unittest.TestCase):
|
||||
bios = Path(tempfile.mkdtemp())
|
||||
(bios / "ps2-0230a.bin").write_text("rom")
|
||||
self.assertEqual(
|
||||
install.do_standalone_copies(self._manifest(), bios, "windows"), (0, 0)
|
||||
install.do_standalone_copies(self._manifest(), bios, "windows"), (0, 0, 0)
|
||||
)
|
||||
|
||||
|
||||
class InstallerStaysOnPython38AndTheStdlib(unittest.TestCase):
|
||||
"""install.py runs where the bootstraps let it run: Python 3.8, stdlib only.
|
||||
|
||||
The suite runs on 3.12 with pyyaml installed, so a removeprefix() or an
|
||||
import of yaml kept every test green and died at the user's first run,
|
||||
after the bootstrap had told them 3.8 was enough.
|
||||
"""
|
||||
|
||||
SOURCE = (REPO_ROOT / "install.py").read_text(encoding="utf-8")
|
||||
|
||||
def test_the_grammar_is_python_38(self):
|
||||
import ast # noqa: PLC0415
|
||||
|
||||
ast.parse(self.SOURCE, filename="install.py", feature_version=(3, 8))
|
||||
|
||||
def test_every_import_is_standard_library(self):
|
||||
import ast # noqa: PLC0415
|
||||
import sys # noqa: PLC0415
|
||||
|
||||
tree = ast.parse(self.SOURCE)
|
||||
imported = set()
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
imported.update(alias.name.split(".")[0] for alias in node.names)
|
||||
elif isinstance(node, ast.ImportFrom) and node.level == 0 and node.module:
|
||||
imported.add(node.module.split(".")[0])
|
||||
self.assertEqual(sorted(imported - set(sys.stdlib_module_names)), [])
|
||||
|
||||
def test_no_api_newer_than_38(self):
|
||||
newer = (".removeprefix(", ".removesuffix(", "zoneinfo", "graphlib",
|
||||
"functools.cache(", ".bit_count(", "math.lcm(", "math.nextafter(")
|
||||
used = [api for api in newer if api in self.SOURCE]
|
||||
self.assertEqual(used, [])
|
||||
|
||||
|
||||
class ARefusedCopyIsAnError(unittest.TestCase):
|
||||
"""A PermissionError on a standalone copy was counted as "dir not found"
|
||||
and the run ended on Done with exit code 0."""
|
||||
|
||||
def test_a_refused_copy_is_counted_failed_not_skipped(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
bios = Path(tmp, "bios")
|
||||
bios.mkdir()
|
||||
(bios / "prod.keys").write_bytes(b"k")
|
||||
target = Path(tmp, "keys")
|
||||
target.mkdir()
|
||||
manifest = {"standalone_copies": [
|
||||
{"file": "prod.keys", "targets": {"linux": [str(target)]}}
|
||||
]}
|
||||
with unittest.mock.patch("shutil.copy2", side_effect=PermissionError(13, "denied")):
|
||||
result = install.do_standalone_copies(manifest, bios, "linux")
|
||||
self.assertEqual(result, (0, 0, 1))
|
||||
|
||||
|
||||
class TargetFlagIsAppliedOrRefused(unittest.TestCase):
|
||||
"""A filter the installer cannot apply must stop it, not be ignored.
|
||||
|
||||
@@ -844,11 +907,12 @@ class TargetFlagIsAppliedOrRefused(unittest.TestCase):
|
||||
for name in ("install/targets/retroarch.json",):
|
||||
path = REPO_ROOT / name
|
||||
if not path.is_file():
|
||||
continue
|
||||
self.skipTest(f"{name} not generated yet")
|
||||
targets = json.loads(path.read_text())
|
||||
for alias in ("switch", "ps2", "psp"):
|
||||
if alias not in targets:
|
||||
continue
|
||||
# An alias the builder accepts and the manifest lacks is a
|
||||
# documented word that fails on the installer's side.
|
||||
self.assertIn(alias, targets, f"builder accepts --target {alias}")
|
||||
with self.subTest(alias=alias):
|
||||
resolved = load_target_config(
|
||||
"retroarch", alias, str(REPO_ROOT / "platforms")
|
||||
@@ -1007,7 +1071,7 @@ class TestStandaloneCopyTargetsAreUntrusted(unittest.TestCase):
|
||||
outside.write_text("untouched")
|
||||
(target / "boot.bin").symlink_to(outside)
|
||||
|
||||
copied, skipped = install.do_standalone_copies(
|
||||
copied, skipped, _ = install.do_standalone_copies(
|
||||
self._manifest(str(target)), bios, "linux"
|
||||
)
|
||||
self.assertEqual(copied, 0)
|
||||
|
||||
Reference in new issue
Block a user