Fix CI: title removal order, Windows fixtures and POSIX-only tests

- Title removal ran the Steam shortcut tidy-up before steamos-delete, which
  finds the Proton prefix through that shortcut, so compatdata was left behind
  (e2e caught it). steamos-delete runs first again; art/collection tidy-up after.
- Test fixtures are byte-exact: never convert line endings (a text-looking
  fixture APK got CRLF on Windows and failed its SHA-256).
- Read index.html/artwork-settings.js as UTF-8 in tests; app-data backup and
  OBB shell tests run only on POSIX (they exercise the Frame-side scripts).
- e2e expects the icon under artwork/ now.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
saphidandClaude Opus 5.5 committed 2026-09-28 23:59:44 +10:00
1 parent cb05395280
commit f81c98a87b
6 files changed
+33 -9

No files matched your search

+5
View File
@@ -6,3 +6,8 @@
*.json text eol=lf
*.md text eol=lf
*.bat text eol=crlf
# Test fixtures are byte-exact (hashes, signatures): never convert line endings.
tests/fixtures/** -text
*.apk binary
*.jar binary
*.obb binary
+1 -1
View File
@@ -33,7 +33,7 @@ class AndroidApps(harness.FrameTestCase):
self.assertEqual(shortcut['name'], 'App label')
self.assertEqual(shortcut['exe'], f'{APP_DIR}/launch.sh')
self.assertEqual(shortcut['start_dir'], APP_DIR)
self.assertEqual(shortcut['icon'], f'{APP_DIR}/icon.png')
self.assertEqual(shortcut['icon'], f'{APP_DIR}/artwork/icon.png')
for f in ('app.apk', 'launch.sh', 'instance.id', 'meta.json', 'icon.png', 'lepton-show-flatscreen'):
self.assertTrue(exists(f'{APP_DIR}/{f}'), f)
self.assertEqual(meta['game_id'], (meta['shortcut'] << 32) | 0x02000000)
+3 -1
View File
@@ -68,7 +68,8 @@ class ObbTests(unittest.TestCase):
data._stream('command')
@unittest.skipUnless(shutil.which("sh") and shutil.which("shasum"), "shell checksum tools unavailable")
@unittest.skipUnless(os.name == 'posix' and shutil.which("sh") and shutil.which("shasum"),
"the OBB script runs on the Frame (Linux shell)")
def test_android_shell_publish_and_hash_failure(self):
with tempfile.TemporaryDirectory() as tmp:
source = Path(tmp) / ('main.7.' + PKG + '.obb')
@@ -100,6 +101,7 @@ class ObbTests(unittest.TestCase):
self.assertEqual(list(output.parent.glob('*.part')), [])
@unittest.skipUnless(os.name == 'posix', 'app-data backups run on the Frame (Linux ownership and modes)')
class BackupTests(unittest.TestCase):
def test_roundtrip_and_retains_previous_data(self):
with tempfile.TemporaryDirectory() as tmp:
+13
View File
@@ -216,6 +216,19 @@ class EntryPoints(unittest.TestCase):
titles.remove('Game')
self.assertTrue(any('steamos-delete --delete-title Game' in c for c in cmds))
def test_native_remove_deletes_before_tidying_the_shortcut(self):
# steamos-delete finds the Proton prefix through the shortcut, so the shortcut must still exist.
order = []
def ssh(cmd, **kwargs):
if 'steamos-delete' in cmd:
order.append('delete')
return 'yes' if 'test -d' in cmd else '/home/steamos' if 'HOME' in cmd else ''
with patch.object(titles, 'ssh', side_effect=ssh), patch.object(titles, 'ensure_utils'), \
patch.object(titles, '_library_shortcut', return_value=42), \
patch.object(titles.frame_android, 'shortcut_tool', side_effect=lambda *a: order.append(a)):
titles.remove('Game')
self.assertEqual(order, ['delete', ('remove', '42')])
def test_native_refresh_art_backfills_registered_title(self):
meta = {'id':'Game','name':'My Game','source':'game.zip'}
writes = []
+2 -2
View File
@@ -277,10 +277,10 @@ class ArtworkSettings(unittest.TestCase):
proc.wait(timeout=10)
def test_panel_script_uses_the_keyed_api_helper(self):
script = (ROOT / "ui" / "artwork-settings.js").read_text()
script = (ROOT / "ui" / "artwork-settings.js").read_text(encoding="utf-8")
self.assertNotIn("fetch(", script)
self.assertIn("api('/api/settings/artwork'", script)
page = (ROOT / "ui" / "index.html").read_text()
page = (ROOT / "ui" / "index.html").read_text(encoding="utf-8")
self.assertLess(page.index("async function api("), page.index('<script src="/artwork-settings.js">'))
+9 -5
View File
@@ -890,16 +890,20 @@ def remove(gid):
try:
gid = _check_id(gid)
ensure_utils()
# Tidying Steam's side is best effort; steamos-delete removes the title regardless.
try:
shortcut = _library_shortcut(gid, f'{_home()}/{GAMES}/{gid}')
if shortcut:
frame_android.shortcut_tool('remove', str(shortcut))
except (FrameError, ValueError):
pass
# steamos-delete removes the folder and syncs Steam's shortcuts; its json files stay, so clear them too.
shortcut = None
# steamos-delete removes the folder, the shortcut and its Proton prefix (found through the
# shortcut), so it runs first; its json files stay, so clear them too.
ssh(f'{PY}steamos-delete --delete-title {gid}', timeout=120)
ssh(f'rm -f {_json_files(gid)}', timeout=30)
# Then tidy what it leaves (artwork, collections); best effort.
if shortcut:
try:
frame_android.shortcut_tool('remove', str(shortcut))
except (FrameError, ValueError):
pass
return {'id': gid}
finally:
_install_lock.release()