Files
2026-08-15 09:23:26 +12:00

139 lines
5.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package credits
import (
"testing"
"time"
)
// The scan window is where the performance claim is kept or given away, so these tests are
// about size: how much of a file each source of evidence lets us avoid reading.
const bbRuntime = 44*60*1000 + 12*1000 // 44:12
func TestGenericTailIsBoundedBothWays(t *testing.T) {
// A short episode still gets a usable window.
short := GenericTailWindow(18 * 60 * 1000)
if short.DurationMs() < genericTailMinimum.Milliseconds() {
t.Fatalf("short episode window was %v, below the floor", short.DurationMs())
}
// A three-hour film does not turn into a thirty-six minute scan.
long := GenericTailWindow(3 * 60 * 60 * 1000)
if long.DurationMs() > genericTailMaximum.Milliseconds() {
t.Fatalf("film window was %v, past the ceiling", long.DurationMs())
}
if long.EndMs != 3*60*60*1000 {
t.Fatal("the window must run to the end of the file")
}
if long.Source != WindowGeneric {
t.Fatalf("source = %q, want %q", long.Source, WindowGeneric)
}
}
func TestNoRuntimeMeansNoWindow(t *testing.T) {
if GenericTailWindow(0).Valid() {
t.Fatal("a file with no runtime produced a scannable window")
}
}
// The optimisation the brief calls one of the most important: knowing where this show puts
// its credits should turn a ten-minute tail into a couple of minutes.
func TestSeasonHistoryNarrowsTheScan(t *testing.T) {
// Blue Bloods S06E0406, credits around 40:50 of a ~44 minute episode.
history := []Marker{
{CreditsStartMs: 40*60*1000 + 51*1000, Confidence: 0.92},
{CreditsStartMs: 40*60*1000 + 47*1000, Confidence: 0.90},
{CreditsStartMs: 40*60*1000 + 50*1000, Confidence: 0.94},
}
runtimeOf := func(Marker) int64 { return bbRuntime }
generic := GenericTailWindow(bbRuntime)
narrowed := NarrowWindow(bbRuntime, history, runtimeOf, BehaviourEvidence{})
if narrowed.Source != WindowSeason {
t.Fatalf("source = %q, want %q", narrowed.Source, WindowSeason)
}
if narrowed.DurationMs() >= generic.DurationMs() {
t.Fatalf("season history did not narrow anything: %v vs generic %v",
narrowed.DurationMs(), generic.DurationMs())
}
// The brief's example expects roughly 39:3042:00 for an expected 40:45.
expected := int64(40*60+50) * 1000
if narrowed.StartMs > expected || narrowed.EndMs < expected {
t.Fatalf("window %v%v does not contain the expected position %v",
narrowed.StartMs, narrowed.EndMs, expected)
}
}
// The rule that stops one wrong marker propagating through a whole season.
func TestASingleMarkerIsNotEnoughToNarrow(t *testing.T) {
history := []Marker{{CreditsStartMs: 40*60*1000 + 51*1000, Confidence: 0.95}}
window := NarrowWindow(bbRuntime, history, func(Marker) int64 { return bbRuntime },
BehaviourEvidence{})
if window.Source != WindowGeneric {
t.Fatalf("source = %q; one marker is an anecdote, not a pattern", window.Source)
}
}
func TestInconsistentSeasonFallsBackToTheGenericTail(t *testing.T) {
// A show whose known markers are minutes apart is telling us its episodes are not
// structurally alike, so their average points nowhere useful.
history := []Marker{
{CreditsStartMs: 36 * 60 * 1000, Confidence: 0.9},
{CreditsStartMs: 41 * 60 * 1000, Confidence: 0.9},
{CreditsStartMs: 39 * 60 * 1000, Confidence: 0.9},
}
window := NarrowWindow(bbRuntime, history, func(Marker) int64 { return bbRuntime },
BehaviourEvidence{})
if window.Source != WindowGeneric {
t.Fatalf("source = %q, want the generic fallback", window.Source)
}
}
func TestLowConfidenceHistoryDoesNotSteerAScan(t *testing.T) {
history := []Marker{
{CreditsStartMs: 40*60*1000 + 51*1000, Confidence: 0.4},
{CreditsStartMs: 40*60*1000 + 47*1000, Confidence: 0.3},
}
window := NarrowWindow(bbRuntime, history, func(Marker) int64 { return bbRuntime },
BehaviourEvidence{})
if window.Source != WindowGeneric {
t.Fatalf("source = %q; a doubtful marker must not decide where everything else looks",
window.Source)
}
}
// Behaviour is a direct observation of this episode, where season history is an inference
// from its neighbours, so it wins.
func TestBehaviourOutranksSeasonHistory(t *testing.T) {
history := []Marker{
{CreditsStartMs: 40*60*1000 + 51*1000, Confidence: 0.92},
{CreditsStartMs: 40*60*1000 + 47*1000, Confidence: 0.90},
}
evidence := BehaviourEvidence{
Found: true, StartMs: 41*60*1000 + 30*1000, UserCount: 3, SpreadMs: 3000,
}
window := NarrowWindow(bbRuntime, history, func(Marker) int64 { return bbRuntime }, evidence)
if window.Source != WindowBehaviour {
t.Fatalf("source = %q, want %q", window.Source, WindowBehaviour)
}
if window.StartMs > evidence.StartMs || window.EndMs < evidence.StartMs {
t.Fatal("the window does not contain the observed cluster")
}
}
func TestNarrowedWindowsCarryAMargin(t *testing.T) {
evidence := BehaviourEvidence{
Found: true, StartMs: 40 * 60 * 1000, UserCount: 3, SpreadMs: 2000,
}
window := NarrowWindow(bbRuntime, nil, nil, evidence)
// Nothing here is precise enough to scan a single instant, and being slightly early is
// the harmless direction — but the margin has to exist on both sides.
if window.StartMs >= evidence.StartMs || window.EndMs <= evidence.StartMs {
t.Fatalf("window %v%v has no margin around %v",
window.StartMs, window.EndMs, evidence.StartMs)
}
if window.DurationMs() > 2*narrowMargin.Milliseconds()+time.Second.Milliseconds() {
t.Fatalf("narrowed window is %v; wider than the margin allows", window.DurationMs())
}
}