|
- package factory
-
- import (
- "bytes"
- "context"
- "io"
- "testing"
- "time"
-
- "aoiprxkit"
- "github.com/jan/fm-rds-tx/internal/audio"
- "github.com/jan/fm-rds-tx/internal/config"
- "github.com/jan/fm-rds-tx/internal/ingest"
- )
-
- type streamReadCloser struct{ io.Reader }
-
- func (r streamReadCloser) Close() error { return nil }
-
- func TestHTTPRawFactoryToRuntimeSmoke(t *testing.T) {
- cfg := config.Default()
- cfg.Ingest.Kind = "http-raw"
- cfg.Ingest.HTTPRaw.SampleRateHz = 44100
- cfg.Ingest.HTTPRaw.Channels = 2
-
- src, ingress, err := BuildSource(cfg, Deps{})
- if err != nil {
- t.Fatalf("build source: %v", err)
- }
- if src == nil || ingress == nil {
- t.Fatalf("expected source and ingress for kind=http-raw")
- }
-
- sink := audio.NewStreamSource(128, cfg.Ingest.HTTPRaw.SampleRateHz)
- rt := ingest.NewRuntime(sink, src)
- if err := rt.Start(context.Background()); err != nil {
- t.Fatalf("runtime start: %v", err)
- }
- defer rt.Stop()
-
- // Two stereo frames: L1,R1,L2,R2 (S16LE).
- frames, err := ingress.WritePCM16([]byte{
- 0xE8, 0x03, 0x18, 0xFC,
- 0xD0, 0x07, 0x30, 0xF8,
- })
- if err != nil {
- t.Fatalf("write pcm16: %v", err)
- }
- if frames != 2 {
- t.Fatalf("frames=%d want 2", frames)
- }
-
- waitForSinkFrames(t, sink, 2)
-
- stats := rt.Stats()
- if stats.Active.Kind != "http-raw" {
- t.Fatalf("active kind=%q want http-raw", stats.Active.Kind)
- }
- if stats.Source.ChunksIn != 1 {
- t.Fatalf("source chunksIn=%d want 1", stats.Source.ChunksIn)
- }
- if stats.Source.SamplesIn != 4 {
- t.Fatalf("source samplesIn=%d want 4", stats.Source.SamplesIn)
- }
- if stats.Runtime.State != "running" {
- t.Fatalf("runtime state=%q want running", stats.Runtime.State)
- }
- if stats.Runtime.LastChunkAt.IsZero() {
- t.Fatalf("runtime lastChunkAt should be set")
- }
- }
-
- func TestSRTFactoryToRuntimeSmoke(t *testing.T) {
- var stream bytes.Buffer
- if err := aoiprxkit.WritePCM32Packet(&stream, 2, 48000, 2, 1, 480, []int32{11, -11, 22, -22}); err != nil {
- t.Fatalf("write packet: %v", err)
- }
-
- cfg := config.Default()
- cfg.Ingest.Kind = "srt"
- cfg.Ingest.SRT.URL = "srt://127.0.0.1:9000?mode=listener"
- cfg.Ingest.SRT.SampleRateHz = 48000
- cfg.Ingest.SRT.Channels = 2
-
- src, ingress, err := BuildSource(cfg, Deps{
- SRTOpener: func(ctx context.Context, srtCfg aoiprxkit.SRTConfig) (io.ReadCloser, error) {
- _ = ctx
- _ = srtCfg
- return streamReadCloser{Reader: bytes.NewReader(stream.Bytes())}, nil
- },
- })
- if err != nil {
- t.Fatalf("build source: %v", err)
- }
- if src == nil {
- t.Fatalf("expected source for kind=srt")
- }
- if ingress != nil {
- t.Fatalf("expected no ingress for kind=srt")
- }
-
- sink := audio.NewStreamSource(128, cfg.Ingest.SRT.SampleRateHz)
- rt := ingest.NewRuntime(sink, src)
- if err := rt.Start(context.Background()); err != nil {
- t.Fatalf("runtime start: %v", err)
- }
- defer rt.Stop()
-
- waitForSinkFrames(t, sink, 2)
-
- stats := rt.Stats()
- if stats.Active.Kind != "srt" {
- t.Fatalf("active kind=%q want srt", stats.Active.Kind)
- }
- if stats.Source.ChunksIn != 1 {
- t.Fatalf("source chunksIn=%d want 1", stats.Source.ChunksIn)
- }
- if stats.Source.SamplesIn != 4 {
- t.Fatalf("source samplesIn=%d want 4", stats.Source.SamplesIn)
- }
- }
-
- func waitForSinkFrames(t *testing.T, sink *audio.StreamSource, minFrames int) {
- t.Helper()
- deadline := time.Now().Add(1 * time.Second)
- for time.Now().Before(deadline) {
- if sink.Available() >= minFrames {
- return
- }
- time.Sleep(10 * time.Millisecond)
- }
- t.Fatalf("timeout waiting for sink frames: have=%d want>=%d", sink.Available(), minFrames)
- }
|