package playback import ( "testing" "time" ) func TestNewPlaylistTimingResetsState(t *testing.T) { got := NewPlaylistTiming(7, 10*time.Second) want := PlaylistTimingState{Revision: 7, Duration: 10 * time.Second} if got != want { t.Fatalf("NewPlaylistTiming() = %#v, want %#v", got, want) } } func TestStartPlaylistTiming(t *testing.T) { now := time.Date(2026, time.September, 1, 12, 0, 0, 0, time.UTC) current := NewPlaylistTiming(4, 10*time.Second) got, started := StartPlaylistTiming(current, 4, now) if !started { t.Fatal("StartPlaylistTiming() started = false, want true") } want := PlaylistTimingState{ Revision: 4, Duration: 10 * time.Second, Started: true, Deadline: now.Add(10 * time.Second), } if got != want { t.Fatalf("StartPlaylistTiming() = %#v, want %#v", got, want) } } func TestStartPlaylistTimingIgnoresInapplicableReadiness(t *testing.T) { now := time.Date(2026, time.September, 1, 12, 0, 0, 0, time.UTC) started, ok := StartPlaylistTiming(NewPlaylistTiming(4, time.Second), 4, now) if !ok { t.Fatal("initial StartPlaylistTiming() did not start") } tests := []struct { name string current PlaylistTimingState revision uint64 }{ {name: "zero duration", current: NewPlaylistTiming(4, 0), revision: 4}, {name: "stale revision", current: NewPlaylistTiming(4, time.Second), revision: 3}, {name: "future revision", current: NewPlaylistTiming(4, time.Second), revision: 5}, {name: "already started", current: started, revision: 4}, {name: "expired", current: PlaylistTimingState{Revision: 4, Duration: time.Second, Expired: true}, revision: 4}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got, changed := StartPlaylistTiming(test.current, test.revision, now.Add(time.Second)) if changed { t.Fatal("StartPlaylistTiming() changed = true, want false") } if got != test.current { t.Fatalf("StartPlaylistTiming() = %#v, want unchanged %#v", got, test.current) } }) } } func TestExpirePlaylistTiming(t *testing.T) { now := time.Date(2026, time.September, 1, 12, 0, 0, 0, time.UTC) current, _ := StartPlaylistTiming(NewPlaylistTiming(9, 5*time.Second), 9, now) got, expired := ExpirePlaylistTiming(current, 9, now.Add(5*time.Second)) if !expired { t.Fatal("ExpirePlaylistTiming() expired = false, want true") } want := PlaylistTimingState{Revision: 9, Duration: 5 * time.Second, Expired: true} if got != want { t.Fatalf("ExpirePlaylistTiming() = %#v, want %#v", got, want) } again, expired := ExpirePlaylistTiming(got, 9, now.Add(6*time.Second)) if expired || again != got { t.Fatalf("duplicate ExpirePlaylistTiming() = %#v, %v; want unchanged, false", again, expired) } } func TestExpirePlaylistTimingIgnoresInapplicableEvents(t *testing.T) { now := time.Date(2026, time.September, 1, 12, 0, 0, 0, time.UTC) current, _ := StartPlaylistTiming(NewPlaylistTiming(4, 10*time.Second), 4, now) tests := []struct { name string revision uint64 at time.Time }{ {name: "early", revision: 4, at: now.Add(9 * time.Second)}, {name: "stale revision", revision: 3, at: now.Add(10 * time.Second)}, {name: "future revision", revision: 5, at: now.Add(10 * time.Second)}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got, expired := ExpirePlaylistTiming(current, test.revision, test.at) if expired { t.Fatal("ExpirePlaylistTiming() expired = true, want false") } if got != current { t.Fatalf("ExpirePlaylistTiming() = %#v, want unchanged %#v", got, current) } }) } } func TestNewPlaylistTimingInvalidatesPreviousDeadline(t *testing.T) { now := time.Date(2026, time.September, 1, 12, 0, 0, 0, time.UTC) old, _ := StartPlaylistTiming(NewPlaylistTiming(1, time.Second), 1, now) current := NewPlaylistTiming(2, 2*time.Second) got, expired := ExpirePlaylistTiming(current, old.Revision, old.Deadline) if expired || got != current { t.Fatalf("old expiry changed new timing: %#v, %v", got, expired) } }