Files
hyprvoice/internal/recording/recording_test.go
T

379 lines
8.4 KiB
Go

package recording
import (
"context"
"os"
"testing"
"time"
)
func TestNewRecorder(t *testing.T) {
config := Config{
SampleRate: 16000,
Channels: 1,
Format: "s16",
BufferSize: 8192,
Device: "",
ChannelBufferSize: 30,
Timeout: 5 * time.Minute,
}
recorder := NewRecorder(config)
if recorder == nil {
t.Errorf("NewRecorder() returned nil")
return
}
if recorder.config.SampleRate != config.SampleRate {
t.Errorf("SampleRate not set correctly: got %d, want %d", recorder.config.SampleRate, config.SampleRate)
}
}
func TestRecorder_IsRecording(t *testing.T) {
config := Config{
SampleRate: 16000,
Channels: 1,
Format: "s16",
BufferSize: 8192,
Device: "",
ChannelBufferSize: 30,
Timeout: 5 * time.Minute,
}
recorder := NewRecorder(config)
// Initially should not be recording
if recorder.IsRecording() {
t.Errorf("IsRecording() = true, want false initially")
}
}
func TestRecorder_ValidateConfig(t *testing.T) {
tests := []struct {
name string
config Config
wantErr bool
}{
{
name: "valid config",
config: Config{
SampleRate: 16000,
Channels: 1,
Format: "s16",
BufferSize: 8192,
Device: "",
ChannelBufferSize: 30,
Timeout: 5 * time.Minute,
},
wantErr: false,
},
{
name: "invalid sample rate",
config: Config{
SampleRate: 0,
Channels: 1,
Format: "s16",
BufferSize: 8192,
ChannelBufferSize: 30,
Timeout: 5 * time.Minute,
},
wantErr: true,
},
{
name: "invalid channels",
config: Config{
SampleRate: 16000,
Channels: 0,
Format: "s16",
BufferSize: 8192,
ChannelBufferSize: 30,
Timeout: 5 * time.Minute,
},
wantErr: true,
},
{
name: "invalid buffer size",
config: Config{
SampleRate: 16000,
Channels: 1,
Format: "s16",
BufferSize: 0,
ChannelBufferSize: 30,
Timeout: 5 * time.Minute,
},
wantErr: true,
},
{
name: "invalid channel buffer size",
config: Config{
SampleRate: 16000,
Channels: 1,
Format: "s16",
BufferSize: 8192,
ChannelBufferSize: 0,
Timeout: 5 * time.Minute,
},
wantErr: true,
},
{
name: "invalid format",
config: Config{
SampleRate: 16000,
Channels: 1,
Format: "",
BufferSize: 8192,
ChannelBufferSize: 30,
Timeout: 5 * time.Minute,
},
wantErr: true,
},
{
name: "invalid timeout",
config: Config{
SampleRate: 16000,
Channels: 1,
Format: "s16",
BufferSize: 8192,
ChannelBufferSize: 30,
Timeout: 0,
},
wantErr: false, // Timeout validation is not implemented in validateConfig
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
recorder := NewRecorder(tt.config)
err := recorder.validateConfig()
if (err != nil) != tt.wantErr {
t.Errorf("validateConfig() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestRecorder_BuildPwRecordArgs(t *testing.T) {
tests := []struct {
name string
config Config
expected []string
}{
{
name: "default config",
config: Config{
SampleRate: 16000,
Channels: 1,
Format: "s16",
Device: "",
},
expected: []string{
"--format", "s16",
"--rate", "16000",
"--channels", "1",
"-",
},
},
{
name: "with device",
config: Config{
SampleRate: 44100,
Channels: 2,
Format: "s32",
Device: "hw:0",
},
expected: []string{
"--format", "s32",
"--rate", "44100",
"--channels", "2",
"-",
"--target", "hw:0",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
recorder := NewRecorder(tt.config)
args := recorder.buildPwRecordArgs()
if len(args) != len(tt.expected) {
t.Errorf("buildPwRecordArgs() returned %d args, want %d", len(args), len(tt.expected))
return
}
for i, arg := range args {
if arg != tt.expected[i] {
t.Errorf("buildPwRecordArgs()[%d] = %q, want %q", i, arg, tt.expected[i])
}
}
})
}
}
func TestCheckPipeWireAvailable(t *testing.T) {
// Test with context timeout
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
// This test will fail if pw-record is not available in the system
// In a real CI environment, we would mock this
err := CheckPipeWireAvailable(ctx)
if err != nil {
t.Logf("CheckPipeWireAvailable() failed (expected if pw-record not installed): %v", err)
// Don't fail the test if pw-record is not available
return
}
// If pw-record is available, the function should succeed
t.Logf("CheckPipeWireAvailable() succeeded - pw-record is available")
}
func TestAudioFrame(t *testing.T) {
data := []byte{1, 2, 3, 4}
timestamp := time.Now()
frame := AudioFrame{
Data: data,
Timestamp: timestamp,
}
if len(frame.Data) != len(data) {
t.Errorf("Data length mismatch: got %d, want %d", len(frame.Data), len(data))
}
if frame.Timestamp != timestamp {
t.Errorf("Timestamp mismatch: got %v, want %v", frame.Timestamp, timestamp)
}
// Test with nil data
emptyFrame := AudioFrame{}
if emptyFrame.Data != nil {
t.Errorf("Empty frame should have nil data")
}
}
// TestRecorder_Start tests the Start method with mocked external dependencies
// This is a simplified test that focuses on the logic rather than actual audio capture
func TestRecorder_Start(t *testing.T) {
// Skip integration tests in CI environments
if os.Getenv("CI") == "true" {
t.Skip("Skipping integration test in CI environment")
}
config := Config{
SampleRate: 16000,
Channels: 1,
Format: "s16",
BufferSize: 8192,
Device: "",
ChannelBufferSize: 30,
Timeout: 5 * time.Minute,
}
recorder := NewRecorder(config)
// Test that we can't start recording if already recording
ctx := context.Background()
frameCh1, errCh1, err := recorder.Start(ctx)
if err != nil {
t.Errorf("Start() error = %v", err)
return
}
if frameCh1 == nil {
t.Errorf("Start() returned nil frame channel")
}
if errCh1 == nil {
t.Errorf("Start() returned nil error channel")
}
// Should not be able to start again
_, _, err = recorder.Start(ctx)
if err == nil {
t.Errorf("Start() should fail when already recording")
}
// Stop the recorder
recorder.Stop()
// Give it a moment to stop
time.Sleep(10 * time.Millisecond)
// Should be able to start again after stopping
frameCh2, errCh2, err := recorder.Start(ctx)
if err != nil {
t.Errorf("Start() error after stop = %v", err)
return
}
if frameCh2 == nil {
t.Errorf("Start() returned nil frame channel after restart")
}
if errCh2 == nil {
t.Errorf("Start() returned nil error channel after restart")
}
recorder.Stop()
}
// TestRecorder_Stop tests the Stop method
func TestRecorder_Stop(t *testing.T) {
// Skip integration tests in CI environments
if os.Getenv("CI") == "true" {
t.Skip("Skipping integration test in CI environment")
}
config := Config{
SampleRate: 16000,
Channels: 1,
Format: "s16",
BufferSize: 8192,
Device: "",
ChannelBufferSize: 30,
Timeout: 5 * time.Minute,
}
recorder := NewRecorder(config)
// Stop should be safe to call even when not recording
recorder.Stop()
// Start recording
ctx := context.Background()
_, _, err := recorder.Start(ctx)
if err != nil {
t.Errorf("Start() error = %v", err)
return
}
// Stop should work when recording
recorder.Stop()
// Give it a moment to stop
time.Sleep(10 * time.Millisecond)
// Should not be recording anymore
if recorder.IsRecording() {
t.Errorf("IsRecording() = true after stop, want false")
}
}
// TestRecorder_Start_InvalidConfig tests starting with invalid config
func TestRecorder_Start_InvalidConfig(t *testing.T) {
invalidConfig := Config{
SampleRate: 0, // Invalid
Channels: 1,
Format: "s16",
BufferSize: 8192,
}
recorder := NewRecorder(invalidConfig)
ctx := context.Background()
_, _, err := recorder.Start(ctx)
if err == nil {
t.Errorf("Start() should fail with invalid config")
}
}