package player import ( "errors" "testing" "time" ) func TestWaitCause(t *testing.T) { ytdlErr := errors.New("yt-dlp: Sign in to confirm you're not a bot") ffmpegErr := errors.New("ffmpeg: Invalid data found when processing input") tests := []struct { name string d time.Duration ytdl error // value sent on ytdlErr, gated by send send bool // whether to send ytdl at all ff error ffSnd bool want error }{ // Blocking (grace) path. {name: "ytdl error preferred over ffmpeg", d: 50 * time.Millisecond, ytdl: ytdlErr, send: true, ff: ffmpegErr, ffSnd: true, want: ytdlErr}, {name: "ffmpeg error when ytdl exits clean", d: 50 * time.Millisecond, ytdl: nil, send: true, ff: ffmpegErr, ffSnd: true, want: ffmpegErr}, {name: "both clean exit", d: 50 * time.Millisecond, ytdl: nil, send: true, ff: nil, ffSnd: true, want: nil}, {name: "ytdl error without ffmpeg report", d: 50 * time.Millisecond, ytdl: ytdlErr, send: true, ff: nil, ffSnd: false, want: ytdlErr}, {name: "neither reports before deadline", d: 50 * time.Millisecond, send: false, ffSnd: false, want: nil}, // Non-blocking poll (d <= 0). {name: "poll ytdl error", d: 0, ytdl: ytdlErr, send: true, want: ytdlErr}, {name: "poll ffmpeg fallback after clean ytdl", d: 0, ytdl: nil, send: true, ff: ffmpegErr, ffSnd: true, want: ffmpegErr}, {name: "poll nothing pending", d: 0, send: false, ffSnd: false, want: nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ytdlCh := make(chan error, 1) ffmpegCh := make(chan error, 1) if tt.send { ytdlCh <- tt.ytdl } if tt.ffSnd { ffmpegCh <- tt.ff } y := &ytdlPipeStreamer{ytdlErr: ytdlCh, ffmpegErr: ffmpegCh} got := y.waitCause(tt.d) if !errors.Is(got, tt.want) { t.Fatalf("waitCause = %v, want %v", got, tt.want) } }) } } // TestWaitCauseReturnsBeforeDeadline verifies that a present yt-dlp error is // returned promptly rather than blocking for the full grace period waiting on // a silent ffmpeg. func TestWaitCauseReturnsBeforeDeadline(t *testing.T) { ytdlCh := make(chan error, 1) ytdlCh <- errors.New("boom") y := &ytdlPipeStreamer{ytdlErr: ytdlCh, ffmpegErr: make(chan error, 1)} start := time.Now() if err := y.waitCause(2 * time.Second); err == nil { t.Fatal("expected error, got nil") } if elapsed := time.Since(start); elapsed > 500*time.Millisecond { t.Fatalf("waitCause blocked %v waiting for ffmpeg; should return on yt-dlp error", elapsed) } }