fix(e2e): run emulator suites through Python

This commit is contained in:
edde746
2026-07-15 08:25:55 +02:00
parent 74cae73844
commit fa901be328
4 changed files with 142 additions and 40 deletions
+104
View File
@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""Run the Maestro groups assigned to each CI emulator."""
from __future__ import annotations
import argparse
from collections.abc import Sequence
import run_maestro
GROUPS: dict[str, tuple[tuple[str, ...], ...]] = {
"android-15": (
("basic",),
("catalog",),
("media",),
(
"basic",
"--flow",
".maestro/regression_flows/03_tv_library_focus.yaml",
"--jellyfin-log",
"build/maestro-tv/library-focus.log",
"--diagnostics-dir",
"build/maestro-tv/library-focus-diagnostics",
),
(
"basic",
"--flow",
".maestro/regression_flows/04_tv_player_keys.yaml",
"--jellyfin-log",
"build/maestro-tv/player-keys.log",
"--diagnostics-dir",
"build/maestro-tv/player-keys-diagnostics",
),
(
"basic",
"--flow",
".maestro/regression_flows/05_tv_next_episode_back.yaml",
"--jellyfin-log",
"build/maestro-tv/next-episode.log",
"--diagnostics-dir",
"build/maestro-tv/next-episode-diagnostics",
),
(
"basic",
"--fault",
"music-failure",
"--flow",
".maestro/real_flows/02_music_browse.yaml",
"--jellyfin-log",
"build/maestro-recovery/music-jellyfin.log",
"--proxy-journal",
"build/maestro-recovery/music-proxy-journal.jsonl",
"--diagnostics-dir",
"build/maestro-recovery/music-diagnostics",
),
(
"basic",
"--fault",
"recovery",
"--flow",
".maestro/regression_flows/06_playback_recovery.yaml",
"--jellyfin-log",
"build/maestro-recovery/jellyfin.log",
"--proxy-journal",
"build/maestro-recovery/proxy-journal.jsonl",
"--diagnostics-dir",
"build/maestro-recovery/diagnostics",
),
),
"android-9": (
(
"basic",
"--flow",
".maestro/flows/05_playback.yaml",
"--jellyfin-log",
"build/maestro-legacy/jellyfin.log",
"--diagnostics-dir",
"build/maestro-legacy/diagnostics",
),
),
}
def run_group(name: str) -> int:
failed = False
for arguments in GROUPS[name]:
print(f"==> Maestro {' '.join(arguments)}", flush=True)
exit_status = run_maestro.main(arguments)
if exit_status >= 128:
return exit_status
failed = failed or exit_status != 0
return 1 if failed else 0
def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("group", choices=GROUPS)
args = parser.parse_args(argv)
return run_group(args.group)
if __name__ == "__main__":
raise SystemExit(main())
+33 -1
View File
@@ -2,7 +2,7 @@
from __future__ import annotations
from contextlib import redirect_stderr
from contextlib import redirect_stderr, redirect_stdout
from dataclasses import replace
import io
from pathlib import Path
@@ -14,6 +14,7 @@ from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parent))
import run_maestro # noqa: E402
import run_maestro_ci # noqa: E402
class ParseConfigTests(unittest.TestCase):
@@ -134,5 +135,36 @@ class LifecycleTests(unittest.TestCase):
sleep.assert_called_once_with(5)
class CiGroupTests(unittest.TestCase):
def test_android_15_group_runs_every_suite_after_failure(self) -> None:
expected_runs = len(run_maestro_ci.GROUPS["android-15"])
statuses = [0, 1, *([0] * (expected_runs - 2))]
with (
patch.object(run_maestro_ci.run_maestro, "main", side_effect=statuses) as run,
redirect_stdout(io.StringIO()),
):
exit_status = run_maestro_ci.run_group("android-15")
self.assertEqual(exit_status, 1)
self.assertEqual(run.call_count, expected_runs)
def test_group_recipes_are_valid_runner_invocations(self) -> None:
for recipes in run_maestro_ci.GROUPS.values():
for arguments in recipes:
with self.subTest(arguments=arguments):
run_maestro.parse_config(arguments, {})
def test_group_stops_after_interruption(self) -> None:
with (
patch.object(run_maestro_ci.run_maestro, "main", return_value=143) as run,
redirect_stdout(io.StringIO()),
):
exit_status = run_maestro_ci.run_group("android-15")
self.assertEqual(exit_status, 143)
run.assert_called_once_with(("basic",))
if __name__ == "__main__":
unittest.main()