add validated audio interleaving

This commit is contained in:
Dmitry Sergeev
2026-08-28 21:53:27 +03:00
parent 1083f3b83c
commit d57e403c2e
2 changed files with 181 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
package output
import (
"bytes"
"errors"
"testing"
"mxl-player/internal/playback"
)
func validAudioFrame() playback.AudioFrame {
return playback.AudioFrame{
Index: 42,
SampleCount: 2,
Channels: 2,
SampleRateNumerator: 48000,
SampleRateDenominator: 1,
Samples: [][]byte{
{1, 2, 3, 4, 5, 6, 7, 8},
{9, 10, 11, 12, 13, 14, 15, 16},
},
}
}
func TestInterleaveF32OrdersSamplesByFrameThenChannel(t *testing.T) {
frame := validAudioFrame()
want := []byte{
1, 2, 3, 4,
9, 10, 11, 12,
5, 6, 7, 8,
13, 14, 15, 16,
}
got, err := InterleaveF32(frame)
if err != nil {
t.Fatalf("InterleaveF32() error = %v", err)
}
if !bytes.Equal(got, want) {
t.Fatalf("InterleaveF32() = %v, want %v", got, want)
}
}
func TestInterleaveF32IgnoresBytesAfterSampleCount(t *testing.T) {
frame := validAudioFrame()
frame.SampleCount = 1
got, err := InterleaveF32(frame)
if err != nil {
t.Fatalf("InterleaveF32() error = %v", err)
}
want := []byte{1, 2, 3, 4, 9, 10, 11, 12}
if !bytes.Equal(got, want) {
t.Fatalf("InterleaveF32() = %v, want %v", got, want)
}
}
func TestInterleaveF32RejectsInvalidMetadata(t *testing.T) {
tests := []struct {
name string
mutate func(*playback.AudioFrame)
}{
{name: "zero samples", mutate: func(f *playback.AudioFrame) { f.SampleCount = 0 }},
{name: "zero channels", mutate: func(f *playback.AudioFrame) { f.Channels = 0 }},
{name: "zero rate numerator", mutate: func(f *playback.AudioFrame) { f.SampleRateNumerator = 0 }},
{name: "negative rate numerator", mutate: func(f *playback.AudioFrame) { f.SampleRateNumerator = -1 }},
{name: "zero rate denominator", mutate: func(f *playback.AudioFrame) { f.SampleRateDenominator = 0 }},
{name: "wrong channel count", mutate: func(f *playback.AudioFrame) { f.Channels = 3 }},
{name: "sample size overflow", mutate: func(f *playback.AudioFrame) { f.SampleCount = ^uint64(0) }},
{
name: "allocation size overflow",
mutate: func(f *playback.AudioFrame) {
f.SampleCount = uint64(^uint(0)>>1) / 4
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
frame := validAudioFrame()
tt.mutate(&frame)
result, err := InterleaveF32(frame)
if result != nil {
t.Fatalf("InterleaveF32() result = %v, want nil", result)
}
if !errors.Is(err, ErrInvalidAudioFrame) {
t.Fatalf("InterleaveF32() error = %v, want %v", err, ErrInvalidAudioFrame)
}
})
}
}
func TestInterleaveF32RejectsShortChannel(t *testing.T) {
frame := validAudioFrame()
frame.Samples[1] = frame.Samples[1][:7]
result, err := InterleaveF32(frame)
if result != nil {
t.Fatalf("InterleaveF32() result = %v, want nil", result)
}
if !errors.Is(err, ErrAudioPayloadTooSmall) {
t.Fatalf("InterleaveF32() error = %v, want %v", err, ErrAudioPayloadTooSmall)
}
}
func TestInterleaveF32ReturnsIndependentOutput(t *testing.T) {
frame := validAudioFrame()
result, err := InterleaveF32(frame)
if err != nil {
t.Fatalf("InterleaveF32() error = %v", err)
}
frame.Samples[0][0] = 99
if result[0] != 1 {
t.Fatalf("output changed with borrowed input: first byte = %d, want 1", result[0])
}
}