Files
unshackle/tests/core/test_manifest_corpus.py
T
imSp4rky ffeb58f808 feat(manifests): add dts-hd, mpeg-h and lcevc codecs and a manifest corpus
Fix DASH SegmentTimeline negative @r and Pattern, TTML sidecar sets with no contentType, HLS CHANNELS identifiers and Dolby channel masks; test against 408 public DASH/HLS/ISM manifests.
2026-10-26 10:30:43 -06:00

107 lines
3.9 KiB
Python

"""Parse every public test manifest in tests/fixtures/manifests (refresh: scripts/fetch_manifest_corpus.py)."""
from __future__ import annotations
import json
from collections import Counter
from pathlib import Path
from typing import Any, Union
from unittest.mock import MagicMock
import pytest
from unshackle.core.manifests import DASH, HLS, ISM
from unshackle.core.tracks import Audio, Tracks, Video
from unshackle.core.utils.xml import load_xml
CORPUS = Path(__file__).resolve().parent.parent / "fixtures" / "manifests"
INDEX: list[dict[str, Any]] = json.loads((CORPUS / "index.json").read_text(encoding="utf-8"))
# file -> reason; strict, so a fix that makes one parse fails here until its entry is removed
KNOWN_FAILURES: dict[str, str] = {}
def cases(entries: list[dict[str, Any]]) -> list[Any]:
return [
pytest.param(e, id=e["file"], marks=pytest.mark.xfail(reason=KNOWN_FAILURES[e["file"]], strict=True))
if e["file"] in KNOWN_FAILURES
else pytest.param(e, id=e["file"])
for e in entries
]
def parse(entry: dict[str, Any]) -> Tracks:
"""Parse a fixture the way from_url does: XML from bytes, M3U8 decoded as UTF-8."""
data = (CORPUS / entry["file"]).read_bytes()
parser: Union[DASH, HLS, ISM]
if entry["kind"] == "hls":
parser = HLS.from_text(data.decode("utf-8"), entry["url"])
else:
parser = (DASH if entry["kind"] == "dash" else ISM)(load_xml(data), entry["url"])
return parser.to_tracks("en")
def most_per_set(tracks: list[Any]) -> int:
return max(Counter(id(t.data["dash"]["adaptation_set"]) for t in tracks).values(), default=0)
class NeedsNetwork(Exception):
pass
def test_index_lists_every_known_failure() -> None:
assert INDEX
assert set(KNOWN_FAILURES) <= {e["file"] for e in INDEX}
@pytest.mark.parametrize("entry", cases(INDEX))
def test_manifest_parses_to_tracks(entry: dict[str, Any]) -> None:
tracks = parse(entry)
assert tracks.videos or tracks.audio or tracks.subtitles
for track in tracks:
# HLS CODECS is only a SHOULD (RFC 8216 4.3.4.2), so an HLS track can honestly have none
assert track.codec is not None or entry["kind"] == "hls", track
assert track.language, track
expect = entry.get("expect")
if expect:
assert most_per_set(tracks.videos) == expect["max_num_video"]
assert most_per_set(tracks.audio) == expect["max_num_audio"]
for codec_type, codecs, found in (
(Video.Codec, expect["video_codec"], tracks.videos),
(Audio.Codec, expect["audio_codec"], tracks.audio),
):
if codecs:
assert {codec_type.from_codecs(c) for c in codecs.split(",")} <= {t.codec for t in found}
@pytest.mark.parametrize(
"entry",
[
e
for e in INDEX
if e["kind"] == "dash"
and e["file"] not in KNOWN_FAILURES
and b'type="dynamic"' not in (CORPUS / e["file"]).read_bytes()
],
ids=lambda e: e["file"],
)
def test_dash_segments_and_drm_resolve_offline(entry: dict[str, Any]) -> None:
"""Build the DRM and the segment list of every track; a track whose segment index must be fetched is skipped."""
session = MagicMock()
session.get.side_effect = NeedsNetwork
for track in parse(entry):
dash = track.data["dash"]
manifest, adaptation_set, representation = dash["manifest"], dash["adaptation_set"], dash["representation"]
DASH.get_drm(representation.findall("ContentProtection") + adaptation_set.findall("ContentProtection"))
period = dash["period"] if dash.get("period") is not None else manifest.find("Period")
try:
_, segments, _, _, _ = DASH.get_period_segments(
period, adaptation_set, representation, manifest, track, track.url, session, probe_kid=False
)
except NeedsNetwork:
continue
assert segments, track
assert all(url.startswith(("http://", "https://")) for url, _ in segments), track