package app import ( "context" "testing" "time" cfgpkg "github.com/jan/fm-rds-tx/internal/config" "github.com/jan/fm-rds-tx/internal/platform" ) func TestEngineContinuousRun(t *testing.T) { cfg := cfgpkg.Default() driver := platform.NewSimulatedDriver(nil) eng := NewEngine(cfg, driver) eng.SetChunkDuration(10 * time.Millisecond) ctx := context.Background() if err := eng.Start(ctx); err != nil { t.Fatalf("start: %v", err) } // Let it run for 200ms time.Sleep(200 * time.Millisecond) stats := eng.Stats() if stats.State != "running" { t.Fatalf("expected running, got %s", stats.State) } if stats.ChunksProduced < 5 { t.Fatalf("expected at least 5 chunks, got %d", stats.ChunksProduced) } if stats.TotalSamples == 0 { t.Fatal("expected non-zero samples") } if err := eng.Stop(ctx); err != nil { t.Fatalf("stop: %v", err) } stats = eng.Stats() if stats.State != "idle" { t.Fatalf("expected idle after stop, got %s", stats.State) } } func TestEngineDoubleStartFails(t *testing.T) { cfg := cfgpkg.Default() driver := platform.NewSimulatedDriver(nil) eng := NewEngine(cfg, driver) ctx := context.Background() if err := eng.Start(ctx); err != nil { t.Fatalf("first start: %v", err) } defer eng.Stop(ctx) if err := eng.Start(ctx); err == nil { t.Fatal("expected error on double start") } } func TestEngineDriverStats(t *testing.T) { cfg := cfgpkg.Default() driver := platform.NewSimulatedDriver(nil) eng := NewEngine(cfg, driver) eng.SetChunkDuration(10 * time.Millisecond) ctx := context.Background() _ = eng.Start(ctx) time.Sleep(100 * time.Millisecond) _ = eng.Stop(ctx) driverStats := driver.Stats() if driverStats.SamplesWritten == 0 { t.Fatal("expected driver to have written samples") } if driverStats.FramesWritten == 0 { t.Fatal("expected driver to have written frames") } } func TestEngineSplitRate(t *testing.T) { // Simulate Pluto-like config: composite at 228kHz, device at 2.28MHz cfg := cfgpkg.Default() cfg.Backend.DeviceSampleRateHz = 2280000 // 10:1 ratio driver := platform.NewSimulatedDriver(nil) eng := NewEngine(cfg, driver) eng.SetChunkDuration(10 * time.Millisecond) // Verify split-rate path was chosen if eng.upsampler == nil { t.Fatal("expected split-rate mode (upsampler != nil)") } ctx := context.Background() if err := eng.Start(ctx); err != nil { t.Fatalf("start: %v", err) } time.Sleep(200 * time.Millisecond) stats := eng.Stats() if stats.ChunksProduced < 5 { t.Fatalf("expected at least 5 chunks, got %d", stats.ChunksProduced) } // With 10:1 upsampling and 10ms chunks at 228kHz source: // 2280 src samples → 22800 output samples per chunk // 5 chunks minimum → at least 114000 samples if stats.TotalSamples < 50000 { t.Fatalf("expected at least 50000 upsampled samples, got %d", stats.TotalSamples) } if err := eng.Stop(ctx); err != nil { t.Fatalf("stop: %v", err) } if stats.Underruns > 0 { t.Fatalf("unexpected underruns: %d", stats.Underruns) } } func TestEngineSameRate(t *testing.T) { // Default config: deviceSampleRateHz=0, compositeRateHz=228000 // EffectiveDeviceRate = 228000 → same-rate mode, no upsampler cfg := cfgpkg.Default() driver := platform.NewSimulatedDriver(nil) eng := NewEngine(cfg, driver) if eng.upsampler != nil { t.Fatal("expected same-rate mode (upsampler == nil)") } }