129 lines
3.2 KiB
Go
129 lines
3.2 KiB
Go
// ABOUTME: Tests for the FLAC streaming decoder
|
|
// ABOUTME: Lifecycle tests — create with header, error without, close cleanly
|
|
package decode
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/Sendspin/sendspin-go/pkg/audio"
|
|
)
|
|
|
|
// buildMinimalFLACHeader creates a syntactically valid FLAC codec_header
|
|
// (fLaC marker + STREAMINFO metadata block) for testing decoder lifecycle.
|
|
// The header is valid enough for mewkiz/flac to parse STREAMINFO, though
|
|
// no frames follow it.
|
|
func buildMinimalFLACHeader(sampleRate, channels, bitDepth, blockSize int) []byte {
|
|
header := make([]byte, 0, 42)
|
|
|
|
// fLaC marker
|
|
header = append(header, 'f', 'L', 'a', 'C')
|
|
|
|
// Metadata block header: last=1 (0x80), type=0 (STREAMINFO), length=34
|
|
header = append(header, 0x80, 0x00, 0x00, 34)
|
|
|
|
// STREAMINFO (34 bytes)
|
|
streamInfo := make([]byte, 34)
|
|
// min block size (bytes 0-1)
|
|
streamInfo[0] = byte(blockSize >> 8)
|
|
streamInfo[1] = byte(blockSize)
|
|
// max block size (bytes 2-3)
|
|
streamInfo[2] = byte(blockSize >> 8)
|
|
streamInfo[3] = byte(blockSize)
|
|
// min/max frame size (bytes 4-9): 0 = unknown
|
|
// sample rate (20 bits) | channels-1 (3 bits) | bps-1 (5 bits) | total samples (36 bits)
|
|
// packed into bytes 10-17
|
|
packed := uint64(sampleRate)<<44 | uint64(channels-1)<<41 | uint64(bitDepth-1)<<36
|
|
for i := 0; i < 8; i++ {
|
|
streamInfo[10+i] = byte(packed >> (56 - 8*i))
|
|
}
|
|
// MD5 (bytes 18-33): zeros
|
|
|
|
header = append(header, streamInfo...)
|
|
return header
|
|
}
|
|
|
|
func TestNewFLAC_RequiresCodecHeader(t *testing.T) {
|
|
format := audio.Format{
|
|
Codec: "flac",
|
|
SampleRate: 48000,
|
|
Channels: 2,
|
|
BitDepth: 24,
|
|
}
|
|
_, err := NewFLAC(format)
|
|
if err == nil {
|
|
t.Error("expected error when CodecHeader is nil")
|
|
}
|
|
}
|
|
|
|
func TestNewFLAC_InvalidCodec(t *testing.T) {
|
|
format := audio.Format{
|
|
Codec: "opus",
|
|
SampleRate: 48000,
|
|
Channels: 2,
|
|
BitDepth: 24,
|
|
}
|
|
_, err := NewFLAC(format)
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid codec")
|
|
}
|
|
}
|
|
|
|
func TestNewFLAC_ValidHeader(t *testing.T) {
|
|
codecHeader := buildMinimalFLACHeader(48000, 2, 24, 4096)
|
|
format := audio.Format{
|
|
Codec: "flac",
|
|
SampleRate: 48000,
|
|
Channels: 2,
|
|
BitDepth: 24,
|
|
CodecHeader: codecHeader,
|
|
}
|
|
dec, err := NewFLAC(format)
|
|
if err != nil {
|
|
t.Fatalf("NewFLAC: %v", err)
|
|
}
|
|
defer dec.Close()
|
|
}
|
|
|
|
func TestFLACDecoder_CloseWithoutDecode(t *testing.T) {
|
|
codecHeader := buildMinimalFLACHeader(48000, 2, 24, 4096)
|
|
format := audio.Format{
|
|
Codec: "flac",
|
|
SampleRate: 48000,
|
|
Channels: 2,
|
|
BitDepth: 24,
|
|
CodecHeader: codecHeader,
|
|
}
|
|
dec, err := NewFLAC(format)
|
|
if err != nil {
|
|
t.Fatalf("NewFLAC: %v", err)
|
|
}
|
|
if err := dec.Close(); err != nil {
|
|
t.Errorf("Close: %v", err)
|
|
}
|
|
// Double close should not panic
|
|
if err := dec.Close(); err != nil {
|
|
t.Errorf("double Close: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestFLACDecoder_DecodeAfterClose(t *testing.T) {
|
|
codecHeader := buildMinimalFLACHeader(48000, 2, 24, 4096)
|
|
format := audio.Format{
|
|
Codec: "flac",
|
|
SampleRate: 48000,
|
|
Channels: 2,
|
|
BitDepth: 24,
|
|
CodecHeader: codecHeader,
|
|
}
|
|
dec, err := NewFLAC(format)
|
|
if err != nil {
|
|
t.Fatalf("NewFLAC: %v", err)
|
|
}
|
|
dec.Close()
|
|
|
|
_, err = dec.Decode([]byte{0x00})
|
|
if err == nil {
|
|
t.Error("expected error after Close")
|
|
}
|
|
}
|