package supervisor import ( "testing" "drmdecryption/adb" "drmdecryption/app" "drmdecryption/apps/streamd/internal/db" "drmdecryption/capture" "drmdecryption/mpd" ) // fakeApp stands in for a compiled-in app module: the supervisor must take its // declared settings without knowing anything about the provider. type fakeApp struct { name string rewriter string liveWait int tsBytes int64 } func (f *fakeApp) Name() string { return f.name } func (f *fakeApp) Package() string { return "com.example." + f.name } func (f *fakeApp) LicenseURL() string { return "" } func (f *fakeApp) Launch(*adb.Client) error { return nil } func (f *fakeApp) AutoPlay(*adb.Client, string) error { return nil } func (f *fakeApp) CaptureHints() capture.Hints { return capture.DefaultHints() } func (f *fakeApp) MPDRewriter() mpd.Rewriter { return mpd.Passthrough{} } func (f *fakeApp) Channels() []app.Channel { return nil } func (f *fakeApp) HasChannel(string) bool { return false } func (f *fakeApp) ProxyBin() string { return "proxy" } func (f *fakeApp) CAHash() string { return "deadbeef" } func (f *fakeApp) KeyMode() string { return "raw" } func (f *fakeApp) VideoSelect() string { return "res=1280x720:for=best" } func (f *fakeApp) AudioSelect() string { return "lang=en:for=best" } func (f *fakeApp) RewriterName() string { return f.rewriter } func (f *fakeApp) LiveWaitSeconds() int { return f.liveWait } func (f *fakeApp) TSReadyBytes() int64 { return f.tsBytes } func register(t *testing.T, f *fakeApp) { t.Helper() app.Register(f.name, func() (app.App, error) { return f, nil }) } func TestSettingsComeFromTheAppModule(t *testing.T) { register(t, &fakeApp{name: "fakedash", rewriter: "fakedash", liveWait: 3, tsBytes: 111}) got := settingsFor(db.Stream{App: "fakedash", MPD: "https://origin.test/manifest.mpd"}) if got.Rewriter != "fakedash" { t.Errorf("rewriter = %q, want fakedash", got.Rewriter) } if got.VideoSelect != "res=1280x720:for=best" || got.AudioSelect != "lang=en:for=best" { t.Errorf("selectors = %q / %q", got.VideoSelect, got.AudioSelect) } if got.LiveWait != 3 || got.TSReadyBytes != 111 { t.Errorf("liveWait=%d tsReadyBytes=%d", got.LiveWait, got.TSReadyBytes) } if got.IsHLS { t.Error("an .mpd manifest is not HLS") } } // The stored row wins over the module, so an operator override sticks. func TestStoredSettingsOverrideTheModule(t *testing.T) { register(t, &fakeApp{name: "fakeoverride", rewriter: "fakeoverride"}) got := settingsFor(db.Stream{ App: "fakeoverride", MPD: "https://origin.test/manifest.mpd", VideoSelect: "res=1920x1080:for=best", Rewriter: "none", }) if got.VideoSelect != "res=1920x1080:for=best" { t.Errorf("video select = %q, want the stored value", got.VideoSelect) } if got.Rewriter != "none" { t.Errorf("rewriter = %q, want the stored none", got.Rewriter) } } // An unknown app must not inherit any provider's preferences. func TestUnknownAppGetsNeutralDefaults(t *testing.T) { got := settingsFor(db.Stream{App: "nosuchapp", MPD: "https://origin.test/manifest.mpd"}) if got.VideoSelect != "for=best" || got.AudioSelect != "for=best" { t.Errorf("selectors = %q / %q, want for=best", got.VideoSelect, got.AudioSelect) } if got.Rewriter != "none" { t.Errorf("rewriter = %q, want none", got.Rewriter) } if got.LiveWait != defaultLiveWait || got.TSReadyBytes != defaultTSReadyBytes { t.Errorf("liveWait=%d tsReadyBytes=%d, want neutral defaults", got.LiveWait, got.TSReadyBytes) } } // HLS gets more buffering and a longer live wait, by manifest shape and not by // matching a stream name. func TestHLSDetectionByManifestShape(t *testing.T) { got := settingsFor(db.Stream{App: "", MPD: "https://cdn.test/x/playlist-hls-dvr.m3u8"}) if !got.IsHLS { t.Fatal("an .m3u8 manifest should be detected as HLS") } if got.LiveWait != hlsLiveWait { t.Errorf("liveWait = %d, want %d", got.LiveWait, hlsLiveWait) } if got.TSReadyBytes != hlsTSReadyBytes { t.Errorf("tsReadyBytes = %d, want %d", got.TSReadyBytes, hlsTSReadyBytes) } // The old code keyed this off stream names like "tg4"/"cula"/"plus". named := settingsFor(db.Stream{Name: "tg4-ioi", MPD: "https://origin.test/manifest.mpd"}) if named.TSReadyBytes != defaultTSReadyBytes { t.Error("buffering must not be chosen by stream name any more") } } // A module's rewriter is only used if it registered one under that name. func TestRewriterLookupGatesTheRewrite(t *testing.T) { register(t, &fakeApp{name: "fakeunregistered", rewriter: "fakeunregistered"}) cfg := settingsFor(db.Stream{App: "fakeunregistered", MPD: "https://origin.test/manifest.mpd"}) if _, needed := mpd.Lookup(cfg.Rewriter); needed { t.Error("an unregistered rewriter name must not trigger a rewrite") } mpd.Register("fakeunregistered", func() mpd.Rewriter { return stubRewriter{} }) if _, needed := mpd.Lookup(cfg.Rewriter); !needed { t.Error("a registered rewriter must trigger a rewrite") } } type stubRewriter struct{} func (stubRewriter) Name() string { return "fakeunregistered" } func (stubRewriter) Rewrite(in []byte, _ string) ([]byte, error) { return in, nil }