"""Tests for ``alfred.domain.shared.media`` — pure ffprobe dataclasses. Exercises: - ``AudioTrack`` / ``SubtitleTrack`` / ``VideoTrack`` — simple dataclass construction. - ``VideoTrack.resolution`` — width-priority resolution detection (handles widescreen/scope crops where width > height bucket), with height fallback when width is missing. - ``MediaInfo.resolution`` — delegates to the primary video track. - ``MediaInfo.audio_languages`` — order-preserving deduplication. - ``MediaInfo.is_multi_audio`` — multi-language detection. """ from __future__ import annotations import pytest from alfred.domain.shared.media import AudioTrack, MediaInfo, SubtitleTrack, VideoTrack class TestTracks: def test_audio_track_defaults(self): t = AudioTrack( index=0, codec="aac", channels=2, channel_layout="stereo", language="eng" ) assert t.is_default is False def test_subtitle_track_defaults(self): t = SubtitleTrack(index=2, codec="subrip", language="fre") assert t.is_default is False assert t.is_forced is False def test_video_track_defaults(self): v = VideoTrack(index=0, codec="hevc", width=1920, height=1080) assert v.is_default is False class TestVideoTrackResolution: def test_no_dimensions(self): assert ( VideoTrack(index=0, codec=None, width=None, height=None).resolution is None ) @pytest.mark.parametrize( "w,expected", [ (3840, "2160p"), # UHD lower bound (3996, "2160p"), # cinema 4K (1920, "1080p"), (1280, "720p"), (720, "576p"), (640, "480p"), ], ) def test_width_priority(self, w, expected): assert ( VideoTrack(index=0, codec=None, width=w, height=1080).resolution == expected ) def test_widescreen_scope_crop(self): # 1920x960 (scope crop) → still 1080p because width-priority assert ( VideoTrack(index=0, codec=None, width=1920, height=960).resolution == "1080p" ) @pytest.mark.parametrize( "h,expected", [ (2160, "2160p"), (1080, "1080p"), (720, "720p"), (576, "576p"), (480, "480p"), ], ) def test_height_fallback_when_width_missing(self, h, expected): assert ( VideoTrack(index=0, codec=None, width=None, height=h).resolution == expected ) def test_width_below_buckets_falls_to_height(self): # width=320 falls below every bucket; falls back to f"{h}p" assert ( VideoTrack(index=0, codec=None, width=320, height=240).resolution == "240p" ) def test_width_only_below_buckets(self): # width=200, no height → f"{w}w" sentinel result = VideoTrack(index=0, codec=None, width=200, height=None).resolution assert result == "200w" class TestMediaInfoResolutionDelegation: def test_no_video_track(self): assert MediaInfo().resolution is None def test_delegates_to_primary_video(self): m = MediaInfo( video_tracks=[VideoTrack(index=0, codec="hevc", width=1920, height=1080)] ) assert m.resolution == "1080p" assert m.width == 1920 assert m.height == 1080 assert m.video_codec == "hevc" def test_multiple_video_tracks_uses_first(self): m = MediaInfo( video_tracks=[ VideoTrack(index=0, codec="hevc", width=3840, height=2160), VideoTrack(index=1, codec="mjpeg", width=320, height=240), # cover art ] ) assert m.resolution == "2160p" class TestAudioLanguages: def test_empty(self): assert MediaInfo().audio_languages == [] def test_dedup_preserves_order(self): m = MediaInfo( audio_tracks=[ AudioTrack(0, "eac3", 6, "5.1", "eng"), AudioTrack(1, "ac3", 6, "5.1", "fre"), AudioTrack(2, "ac3", 2, "stereo", "eng"), # duplicate eng AudioTrack(3, "aac", 2, "stereo", None), # ignored ] ) assert m.audio_languages == ["eng", "fre"] def test_all_none_languages(self): m = MediaInfo( audio_tracks=[ AudioTrack(0, "aac", 2, "stereo", None), AudioTrack(1, "aac", 2, "stereo", None), ] ) assert m.audio_languages == [] def test_is_multi_audio_false_single_lang(self): m = MediaInfo(audio_tracks=[AudioTrack(0, "aac", 2, "stereo", "eng")]) assert m.is_multi_audio is False def test_is_multi_audio_true(self): m = MediaInfo( audio_tracks=[ AudioTrack(0, "aac", 2, "stereo", "eng"), AudioTrack(1, "aac", 2, "stereo", "fre"), ] ) assert m.is_multi_audio is True