test: add firmware capture fixtures and stdlib-level unit tests for parsers, intents, running apps
This commit is contained in:
1 parent
f829ef7a40
commit
a2f1948a88
7 files changed
+198
No files matched your search
@@ -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"))
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
Reference in new issue
Block a user