test: add firmware capture fixtures and stdlib-level unit tests for parsers, intents, running apps

This commit is contained in:
James Mackay committed 2026-10-08 07:37:34 +01:00
1 parent f829ef7a40
commit a2f1948a88
7 files changed
+198

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
"""Test bootstrap.
Puts the vendored ha_adb_common package on sys.path so helpers can be imported
directly (their code is stdlib-only) without installing Home Assistant or
executing the integration's __init__ module.
"""
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO_ROOT / "custom_components" / "nvidia_shield"))
+5
View File
@@ -0,0 +1,5 @@
mResumedActivity: ActivityRecord{4f0d030 u0 ar.tvplayer.tv/.ui.MainActivity t8152}
state=RESUMED stopped=false delayedResume=false finishing=false
Resumed activities in task display areas (from top to bottom):
Resumed: ActivityRecord{4f0d030 u0 ar.tvplayer.tv/.ui.MainActivity t8152}
ResumedActivity: ActivityRecord{4f0d030 u0 ar.tvplayer.tv/.ui.MainActivity t8152}
+33
View File
@@ -0,0 +1,33 @@
MEDIA SESSION SERVICE (dumpsys media_session)
9 sessions listeners.
Global priority session is null
User Records:
Record for full_user=0
Volume key long-press listener: null
Volume key long-press listener package:
Media key listener: null
Media key listener package:
OnMediaKeyEventDispatchedListener: added 0 listener(s)
OnMediaKeyEventSessionChangedListener: added 1 listener(s)
from com.android.bluetooth
Last MediaButtonReceiver: null
Media button session is null
Sessions Stack - have 1 sessions:
Netflix media session com.netflix.ninja/Netflix media session (userId=0)
ownerPid=5833, ownerUid=10080, userId=0
package=com.netflix.ninja
launchIntent=null
mediaButtonReceiver=null
active=false
flags=3
rating type=0
controllers: 0
state=null
audioAttrs=AudioAttributes: usage=USAGE_MEDIA content=CONTENT_TYPE_UNKNOWN flags=0x800 tags= bundle=null
volumeType=1, controlType=2, max=0, current=0
metadata: null
queueTitle=null, size=0
Audio playback (lastly played comes first)
uid=10117 packages=ar.tvplayer.tv
uid=10006 packages=com.google.android.apps.mediashell
+23
View File
@@ -0,0 +1,23 @@
MEDIA SESSION SERVICE (dumpsys media_session)
9 sessions listeners.
User Records:
Record for full_user=0
Media button session is null
Sessions Stack - have 1 sessions:
Wholphin media session com.github.damontecres.wholphin/Wholphin media session (userId=0)
ownerPid=6117, ownerUid=10117, userId=0
package=com.github.damontecres.wholphin
launchIntent=null
mediaButtonReceiver=null
active=true
flags=3
rating type=0
controllers: 1
state=PlaybackState {state=3, position=17237, buffered position=70070, speed=1.0, updated=1408392, actions=7339995, custom actions=[], active item id=0, error=null}
audioAttrs=AudioAttributes: usage=USAGE_MEDIA content=CONTENT_TYPE_MOVIE flags=0x800 tags= bundle=null
volumeType=1, controlType=2, max=0, current=0
metadata: size=11, description=Twirlywoos, null, null
queueTitle=null, size=1
Audio playback (lastly played comes first)
uid=10117 packages=com.github.damontecres.wholphin
+32
View File
@@ -0,0 +1,32 @@
"""Tests for the PlaybackIntent dataclass (optimistic state tracking)."""
from ha_adb_common.intent import PlaybackIntent
def test_create_captures_fields():
intent = PlaybackIntent.create(
target_state="paused", command="media_pause", prior_state="playing"
)
assert intent.target_state == "paused"
assert intent.command == "media_pause"
assert intent.prior_state == "playing"
assert intent.retry_count == 0
assert intent.created_at > 0
def test_debug_dict_shape():
intent = PlaybackIntent.create(target_state="playing", command="media_play")
debug = intent.to_debug_dict()
assert debug["target"] == "playing"
assert debug["command"] == "media_play"
assert debug["retries"] == 0
assert "age_s" in debug
def test_age_increases():
import time
intent = PlaybackIntent.create(target_state="playing", command="media_play")
before = intent.age_seconds
time.sleep(0.01)
assert intent.age_seconds >= before
+53
View File
@@ -0,0 +1,53 @@
"""Parser tests driven by real Shield firmware captures (Android 11, RQ1A.210105.003).
Fixtures live in tests/fixtures — see docs/RESEARCH_SHIELD.md for how each
capture was produced and which expectations it pins.
"""
from pathlib import Path
from ha_adb_common.media_session import parse_all_media_sessions, parse_media_session
FIXTURES = Path(__file__).parent / "fixtures"
def _fixture(name: str) -> str:
return (FIXTURES / name).read_text(encoding="utf-8")
class TestTiviMateHasNoSession:
"""TiviMate (ar.tvplayer.tv) registers no MediaSession on this firmware."""
def test_no_sessions_parsed(self):
assert parse_all_media_sessions(_fixture("media_session_tivimate.txt")) == []
def test_session_query_returns_none(self):
dump = _fixture("media_session_tivimate.txt")
assert parse_media_session(dump, "ar.tvplayer.tv") is None
def test_inactive_stale_session_is_not_surfaced(self):
"""Netflix registers an inactive, state-less session; the parser must
ignore it — an active=false block never appears in the results."""
dump = _fixture("media_session_tivimate.txt")
assert parse_media_session(dump, "com.netflix.ninja") is None
class TestWholphinSession:
"""Wholphin (com.github.damontecres.wholphin) publishes a proper session."""
def test_playing_session_parsed(self):
sessions = parse_all_media_sessions(_fixture("media_session_wholphin.txt"))
assert len(sessions) == 1
session = sessions[0]
assert session.package == "com.github.damontecres.wholphin"
assert session.playing is True
assert session.paused is False
assert session.title == "Twirlywoos"
assert session.artist is None
def test_foreground_package_is_preferred(self):
dump = _fixture("media_session_wholphin.txt")
best = parse_media_session(dump, "com.github.damontecres.wholphin")
assert best is not None
assert best.package == "com.github.damontecres.wholphin"
assert best.playing is True
+40
View File
@@ -0,0 +1,40 @@
"""Tests for the running-apps list builder (recent tasks + media sessions)."""
from ha_adb_common.running_apps import running_apps_from_tasks
def _session(package: str, playing: bool = False, title: str | None = None):
from ha_adb_common.media_session import MediaSessionInfo
s = MediaSessionInfo(package=package)
s.playing = playing
s.paused = not playing
s.title = title
return s
def test_returns_none_for_fewer_than_two_tasks():
assert running_apps_from_tasks([]) is None
assert running_apps_from_tasks([{"package": "app.a"}]) is None
def test_marks_foreground_and_defaults_to_running():
tasks = [{"package": "app.a"}, {"package": "app.b"}]
apps = running_apps_from_tasks(tasks, [], foreground_package="app.b")
assert apps is not None
assert len(apps) == 2
by_pkg = {a["package"]: a for a in apps}
assert "foreground" not in by_pkg["app.a"]
assert by_pkg["app.b"]["foreground"] is True
assert by_pkg["app.a"]["state"] == "running"
def test_enriches_with_media_session_state():
tasks = [{"package": "app.a"}, {"package": "app.b"}]
sessions = [_session("app.a", playing=True, title="Song")]
apps = running_apps_from_tasks(tasks, sessions)
assert apps is not None
by_pkg = {a["package"]: a for a in apps}
assert by_pkg["app.a"]["state"] == "playing"
assert by_pkg["app.a"]["title"] == "Song"
assert by_pkg["app.b"]["state"] == "running"