chore: checkpoint app work before firmware sync
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate MP3 Player close-and-resume evidence captured from serial."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def evaluate_resume_trace(trace: str, fixture: str, minimum_position: int = 6) -> dict[str, int]:
|
||||
"""Require real playback, persisted position, and same-file relaunch evidence."""
|
||||
escaped = re.escape(fixture)
|
||||
initial = re.search(rf"Starting MP3 playback: {escaped} .* resume=0\b", trace)
|
||||
stream_opened = re.search(r"Audio stream opened: \d+ Hz, \d+ channels", trace)
|
||||
persisted = re.search(rf"History saved on hide: {escaped} pos (\d+) total \d+", trace)
|
||||
resumed = re.search(rf"Resuming last play {escaped} at (\d+) sec from history", trace)
|
||||
playback_matches = list(re.finditer(rf"Starting MP3 playback: {escaped} .* resume=(\d+)\b", trace))
|
||||
resumed_playback = playback_matches[-1] if playback_matches else None
|
||||
|
||||
if "Playback task stuck, force deleting" in trace:
|
||||
raise RuntimeError("forced playback task termination invalidates the device test")
|
||||
|
||||
if not initial or not stream_opened:
|
||||
raise RuntimeError("missing verified playback evidence for the fixture")
|
||||
if not persisted or not resumed or not resumed_playback:
|
||||
raise RuntimeError("missing persisted or resumed playback evidence")
|
||||
|
||||
persisted_position = int(persisted.group(1))
|
||||
resumed_position = int(resumed.group(1))
|
||||
resumed_playback_position = int(resumed_playback.group(1))
|
||||
if persisted_position < minimum_position:
|
||||
raise RuntimeError(f"persisted position {persisted_position} is below {minimum_position}")
|
||||
if resumed_position != persisted_position or resumed_playback_position != persisted_position:
|
||||
raise RuntimeError("relaunch did not resume the persisted position on the same file")
|
||||
return {"persisted_position": persisted_position, "resumed_position": resumed_position}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--trace", type=Path, required=True, help="Captured serial log")
|
||||
parser.add_argument("--fixture", required=True, help="Absolute fixture path logged by MP3 Player")
|
||||
parser.add_argument("--minimum-position", type=int, default=6)
|
||||
args = parser.parse_args()
|
||||
result = evaluate_resume_trace(args.trace.read_text(encoding="utf-8", errors="replace"), args.fixture, args.minimum_position)
|
||||
print(f"PASS persisted_position={result['persisted_position']} resumed_position={result['resumed_position']}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user