test: refactor FFmpeg context cancellation tests for improved clarity and reliability

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan 2025-07-31 11:43:40 -04:00
parent b12e3ff367
commit f085446cc6

View File

@ -2,7 +2,6 @@ package ffmpeg
import ( import (
"context" "context"
"io"
"os/exec" "os/exec"
"testing" "testing"
"time" "time"
@ -70,7 +69,7 @@ var _ = Describe("ffmpeg", func() {
}) })
}) })
Describe("Context Cancellation", func() { Describe("FFmpeg", func() {
Context("when FFmpeg is available", func() { Context("when FFmpeg is available", func() {
var ff FFmpeg var ff FFmpeg
@ -83,51 +82,34 @@ var _ = Describe("ffmpeg", func() {
}) })
It("should interrupt transcoding when context is cancelled", func() { It("should interrupt transcoding when context is cancelled", func() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithTimeout(GinkgoT().Context(), 5*time.Second)
defer cancel() defer cancel()
// Start a long-running FFmpeg process (slow transcode to give time for cancellation) // Use a command that generates audio indefinitely
// Use a command that will take some time to complete // -f lavfi uses FFmpeg's built-in audio source
command := "ffmpeg -f lavfi -i sine=frequency=1000:duration=30 -acodec pcm_s16le -" // -t 0 means no time limit (runs forever)
command := "ffmpeg -f lavfi -i sine=frequency=1000:duration=0 -f mp3 -"
errChan := make(chan error, 1) // The input file is not used here, but we need to provide a valid path to the Transcode function
var stream io.ReadCloser stream, err := ff.Transcode(ctx, command, "tests/fixtures/test.mp3", 128, 0)
Expect(err).ToNot(HaveOccurred())
defer stream.Close()
go func() { // Read some data first to ensure FFmpeg is running
var err error buf := make([]byte, 1024)
// The input file is not used here, but we need to provide a valid path to the Transcode function _, err = stream.Read(buf)
stream, err = ff.Transcode(ctx, command, "tests/fixtures/test.mp3", 128, 0) Expect(err).ToNot(HaveOccurred())
errChan <- err
}()
// Give FFmpeg a moment to start
time.Sleep(100 * time.Millisecond)
// Cancel the context // Cancel the context
cancel() cancel()
// The operation should fail due to cancellation // Next read should fail due to cancelled context
select { _, err = stream.Read(buf)
case err := <-errChan: Expect(err).To(HaveOccurred())
if err == nil {
// If no error during start, the stream should be cancelled when we try to read
if stream != nil {
defer stream.Close()
buf := make([]byte, 1024)
_, readErr := stream.Read(buf)
Expect(readErr).To(HaveOccurred(), "Expected read to fail due to cancelled context")
}
} else {
// Starting should fail due to cancelled context
Expect(err).To(HaveOccurred())
}
case <-time.After(5 * time.Second):
Fail("Expected FFmpeg to be cancelled within 5 seconds")
}
}) })
It("should handle immediate context cancellation", func() { It("should handle immediate context cancellation", func() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(GinkgoT().Context())
cancel() // Cancel immediately cancel() // Cancel immediately
// This should fail immediately // This should fail immediately
@ -151,7 +133,7 @@ var _ = Describe("ffmpeg", func() {
It("should terminate the underlying process when context is cancelled", func() { It("should terminate the underlying process when context is cancelled", func() {
ff := New() ff := New()
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithTimeout(GinkgoT().Context(), 5*time.Second)
// Start a process that will run for a while // Start a process that will run for a while
stream, err := ff.Transcode(ctx, "sleep 10", "tests/fixtures/test.mp3", 0, 0) stream, err := ff.Transcode(ctx, "sleep 10", "tests/fixtures/test.mp3", 0, 0)