From 49d421a76719bcade195859ea84f4ce033c65bc9 Mon Sep 17 00:00:00 2001 From: Johnny D Date: Fri, 18 Sep 2026 12:57:11 -0400 Subject: [PATCH 1/6] test: check README navigation instead of an arbitrary line count --- tests/repository/test_layout.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/repository/test_layout.py b/tests/repository/test_layout.py index 0d7d04b..505455e 100644 --- a/tests/repository/test_layout.py +++ b/tests/repository/test_layout.py @@ -49,11 +49,31 @@ def test_documented_rail_bits_match_public_api(self): self.assertEqual(documented, {"SL (L)": bits["slL"], "SR (L)": bits["srL"], "SL (R)": bits["slR"], "SR (R)": bits["srR"]}) + def test_readme_navigation(self): + readme = (ROOT / "README.md").read_text() + # Protect the landing-page route to usable apps, not an arbitrary line + # count that makes documentation-only PRs fail native build jobs. + introduction = readme.split("```", 1)[0] + for repository in ("dolphin", "Cemu"): + with self.subTest(repository=repository): + self.assertIn(f"https://github.com/jmonster/{repository}", introduction) + headings = set() + for heading in re.findall(r"^#{1,6} (.+)$", readme, re.MULTILINE): + slug = re.sub(r"[^\w -]", "", heading.lower()).replace(" ", "-") + suffix = 0 + unique = slug + while unique in headings: + suffix += 1 + unique = f"{slug}-{suffix}" + headings.add(unique) + for fragment in re.findall(r"\]\(#([^)]+)\)", readme): + with self.subTest(fragment=fragment): + self.assertIn(unquote(fragment), headings, f"Missing README heading: {fragment}") + def test_source_only_tree(self): tracked = subprocess.check_output(["git", "ls-files", "-z"], cwd=ROOT).decode().split("\0") for path in filter(None, tracked): self.assertFalse(path.endswith((".dylib", ".xcframework.zip")), path) - self.assertLess(len((ROOT / "README.md").read_text().splitlines()), 100) if __name__ == "__main__": unittest.main() From 048222738938d0ac2fccfebfd5d0ede4cedb5f9a Mon Sep 17 00:00:00 2001 From: Johnny D Date: Fri, 18 Sep 2026 14:11:57 -0400 Subject: [PATCH 2/6] test: remove arbitrary README layout and prose assertions Remove the line-count replacement that enforced links before code, along with exact license-index wording checks. Retain actual broken-link, public protocol value, binary exclusion, and distribution-notice integrity checks. --- tests/distribution-notices/test_notices.py | 10 ---------- tests/repository/test_layout.py | 23 +--------------------- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/tests/distribution-notices/test_notices.py b/tests/distribution-notices/test_notices.py index ec61187..eaa93f8 100644 --- a/tests/distribution-notices/test_notices.py +++ b/tests/distribution-notices/test_notices.py @@ -79,16 +79,6 @@ def test_packagers_copy_notices_before_signing_or_archiving(self): self.assertNotIn('COMMAND codesign', bundle) self.assertIn('verify-distribution-notices.py', (ROOT / 'scripts/build-switch2kit-emulator.sh').read_text()) - def test_notice_index_links_sources_and_retained_texts(self): - text = (ROOT / 'LICENSES/README.md').read_text() - self.assertIn('https://github.com/Peterksharma/switch2mac/tree/', text) - self.assertIn('[CREDITS.md](../CREDITS.md)', text) - for name in ('MIT-trevlars.txt', 'SDL-zlib.txt'): - self.assertIn('(' + name + ')', text) - self.assertIn('modified, unofficial SDL sources', text) - self.assertIn('Permission is hereby granted', (ROOT / 'LICENSES/MIT-trevlars.txt').read_text()) - self.assertIn('This notice may not be removed', (ROOT / 'LICENSES/SDL-zlib.txt').read_text()) - if __name__ == '__main__': unittest.main() diff --git a/tests/repository/test_layout.py b/tests/repository/test_layout.py index 505455e..7d4c86e 100644 --- a/tests/repository/test_layout.py +++ b/tests/repository/test_layout.py @@ -27,7 +27,7 @@ def test_browser_identity(self): self.assertIn("Switch2Kit", manifest["description"]) def test_documentation_links(self): - roots = [ROOT / "docs", ROOT / "Examples", ROOT / "sdl", ROOT / "browser"] + roots = [ROOT / "docs", ROOT / "Examples", ROOT / "sdl", ROOT / "browser", ROOT / "LICENSES"] files = [ROOT / "README.md", ROOT / "CREDITS.md"] for root in roots: files.extend(root.rglob("*.md")) @@ -49,27 +49,6 @@ def test_documented_rail_bits_match_public_api(self): self.assertEqual(documented, {"SL (L)": bits["slL"], "SR (L)": bits["srL"], "SL (R)": bits["slR"], "SR (R)": bits["srR"]}) - def test_readme_navigation(self): - readme = (ROOT / "README.md").read_text() - # Protect the landing-page route to usable apps, not an arbitrary line - # count that makes documentation-only PRs fail native build jobs. - introduction = readme.split("```", 1)[0] - for repository in ("dolphin", "Cemu"): - with self.subTest(repository=repository): - self.assertIn(f"https://github.com/jmonster/{repository}", introduction) - headings = set() - for heading in re.findall(r"^#{1,6} (.+)$", readme, re.MULTILINE): - slug = re.sub(r"[^\w -]", "", heading.lower()).replace(" ", "-") - suffix = 0 - unique = slug - while unique in headings: - suffix += 1 - unique = f"{slug}-{suffix}" - headings.add(unique) - for fragment in re.findall(r"\]\(#([^)]+)\)", readme): - with self.subTest(fragment=fragment): - self.assertIn(unquote(fragment), headings, f"Missing README heading: {fragment}") - def test_source_only_tree(self): tracked = subprocess.check_output(["git", "ls-files", "-z"], cwd=ROOT).decode().split("\0") for path in filter(None, tracked): From a0cef35e77ff018ea87336d40ec61b4e2ab73c35 Mon Sep 17 00:00:00 2001 From: Johnny D Date: Fri, 18 Sep 2026 15:32:46 -0400 Subject: [PATCH 3/6] test: stop treating presentation names and descriptions as regressions --- tests/repository/test_layout.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/tests/repository/test_layout.py b/tests/repository/test_layout.py index 7d4c86e..53b90bd 100644 --- a/tests/repository/test_layout.py +++ b/tests/repository/test_layout.py @@ -1,6 +1,5 @@ -"""Repository layout, documentation links, and application identity.""" +"""Repository integrity, documentation links, and executable identity.""" from pathlib import Path -import json import plistlib import re import subprocess @@ -12,19 +11,8 @@ class RepositoryTests(unittest.TestCase): def test_application_identity(self): info = plistlib.loads((ROOT / "Resources/Info.plist").read_bytes()) - self.assertEqual(info["CFBundleName"], "Switch2Kit") - self.assertEqual(info["CFBundleDisplayName"], "Switch2Kit") self.assertEqual(info["CFBundleExecutable"], "Switch2KitApp") self.assertEqual(info["CFBundleIdentifier"], "wabisabi.ware.gamecubed") - self.assertTrue((ROOT / "Sources/Switch2KitApp/Switch2KitApp.swift").is_file()) - self.assertIn('name: "Switch2KitApp"', (ROOT / "Package.swift").read_text()) - self.assertIn('APP_NAME="Switch2Kit"', (ROOT / "scripts/build-app.sh").read_text()) - self.assertIn('EXE=Switch2KitApp', (ROOT / "scripts/build-app.sh").read_text()) - - def test_browser_identity(self): - manifest = json.loads((ROOT / "browser/extension/manifest.json").read_text()) - self.assertTrue(manifest["name"].startswith("Switch2Kit")) - self.assertIn("Switch2Kit", manifest["description"]) def test_documentation_links(self): roots = [ROOT / "docs", ROOT / "Examples", ROOT / "sdl", ROOT / "browser", ROOT / "LICENSES"] From a42fd69408de963d084a8b01adeb60ec049fc57c Mon Sep 17 00:00:00 2001 From: Johnny D Date: Fri, 18 Sep 2026 15:33:05 -0400 Subject: [PATCH 4/6] test: keep executable and signing checks independent of display names --- tests/app-identity/check.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/app-identity/check.py b/tests/app-identity/check.py index d99a766..dc12477 100644 --- a/tests/app-identity/check.py +++ b/tests/app-identity/check.py @@ -7,8 +7,6 @@ root = Path(__file__).resolve().parents[2] info = plistlib.loads((root / 'Resources/Info.plist').read_bytes()) assert info['CFBundleIdentifier'] == 'wabisabi.ware.gamecubed' -assert info['CFBundleDisplayName'] == 'Switch2Kit' -assert info['CFBundleName'] == 'Switch2Kit' assert info['CFBundleExecutable'] == 'Switch2KitApp' assert 'Peter Sharma' in info['NSHumanReadableCopyright'] # Automatic installation is removed, not merely disabled by a preference. From bddcde51c58f8e80ecc2d1503302aff52584e778 Mon Sep 17 00:00:00 2001 From: Johnny D Date: Fri, 18 Sep 2026 15:33:24 -0400 Subject: [PATCH 5/6] test: remove English UI copy assertions from output regressions --- tests/output-health/run.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/output-health/run.sh b/tests/output-health/run.sh index 1c75eae..4ab5d40 100644 --- a/tests/output-health/run.sh +++ b/tests/output-health/run.sh @@ -18,14 +18,11 @@ assert '.disabled(!status.model.capabilities.contains(.rumble))' in dashboard assert 'engine.testRumble(serial: serial)' in dashboard assert 'engine.testRumble(serial: controller.serial)' in view assert 'engine.testRumble(player:' not in dashboard + view -assert 'No matching connected controller' in view -assert 'Test preset' in dashboard and 'Rumble is muted.' in dashboard assert 'id: "output-status"' in app -assert 'Output Status and Capabilities' in app and 'Output Status and Capabilities' in view assert '.disabled(controller == nil || !OutputCapabilities(model: model, backend: backend).directRumble)' in view for sink in ('UDPHub','WebSocketHub','NetworkGamepadSink','VirtualHIDSink'): assert f'OutputStatusStore.shared.register({sink}())' in app -print('PASS output UI wiring, serial-addressed rumble tests, mute and preset guidance') +print('PASS output UI wiring, serial-addressed rumble tests and capability guards') PY # Exercise real production sinks, sharing only existing harness declarations. From aaa3747cced550a2bd1b558dbcad7d24de1f60ae Mon Sep 17 00:00:00 2001 From: Johnny D Date: Fri, 18 Sep 2026 18:02:02 -0400 Subject: [PATCH 6/6] Stop hashing the license directory's documentation index Keep package verification of CREDITS.md and both required upstream license texts, upstream application identity/copyright, missing or altered notices, and signing order. LICENSES/README.md is navigation prose, not a required license text; it is still copied with the directory but is no longer an integrity assertion. Retained repository (4), distribution-notices (5), and application identity/signing checks pass. --- scripts/verify-distribution-notices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/verify-distribution-notices.py b/scripts/verify-distribution-notices.py index d909593..9da89b7 100644 --- a/scripts/verify-distribution-notices.py +++ b/scripts/verify-distribution-notices.py @@ -7,7 +7,7 @@ import plistlib ROOT = Path(__file__).resolve().parents[1] -NOTICES = ('CREDITS.md', 'LICENSES/README.md', 'LICENSES/MIT-trevlars.txt', 'LICENSES/SDL-zlib.txt') +NOTICES = ('CREDITS.md', 'LICENSES/MIT-trevlars.txt', 'LICENSES/SDL-zlib.txt') def verify(app, emulator, root=ROOT):