package factory import ( "bytes" "context" "errors" "testing" "time" "aoiprxkit" "github.com/jan/fm-rds-tx/internal/config" ) func TestBuildSourceNone(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = "none" src, ingress, err := BuildSource(cfg, Deps{}) if err != nil { t.Fatalf("build source: %v", err) } if src != nil || ingress != nil { t.Fatalf("expected nil source and ingress for kind=none") } } func TestBuildSourceHTTPRawProvidesIngress(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = "http-raw" src, ingress, err := BuildSource(cfg, Deps{}) if err != nil { t.Fatalf("build source: %v", err) } if src == nil { t.Fatalf("expected source") } if ingress == nil { t.Fatalf("expected ingress for http-raw") } } func TestBuildSourceKindIsNormalized(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = " HTTP-RAW " 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 normalized http-raw kind") } if got := src.Descriptor().Kind; got != "http-raw" { t.Fatalf("source kind=%q want http-raw", got) } } func TestBuildSourceStdin(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = "stdin" src, ingress, err := BuildSource(cfg, Deps{Stdin: bytes.NewReader(nil)}) if err != nil { t.Fatalf("build source: %v", err) } if src == nil { t.Fatalf("expected source") } if ingress != nil { t.Fatalf("expected no ingress for stdin") } if got := src.Descriptor().Kind; got != "stdin-pcm" { t.Fatalf("source kind=%s", got) } } func TestBuildSourceIcecastUsesDecoderPreference(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = "icecast" cfg.Ingest.Icecast.URL = "http://localhost:8000/stream" cfg.Ingest.Icecast.Decoder = "ffmpeg" src, ingress, err := BuildSource(cfg, Deps{}) if err != nil { t.Fatalf("build source: %v", err) } if src == nil { t.Fatalf("expected source") } if ingress != nil { t.Fatalf("expected no ingress for icecast") } if got := src.Descriptor().Codec; got != "ffmpeg" { t.Fatalf("codec=%s want ffmpeg", got) } if got := src.Descriptor().Origin; got == nil || got.Kind != "url" { t.Fatalf("expected icecast origin kind url, got %+v", got) } } func TestBuildSourceSRT(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = "srt" cfg.Ingest.SRT.URL = "srt://127.0.0.1:9000?mode=listener" cfg.Ingest.SRT.Mode = "listener" cfg.Ingest.SRT.SampleRateHz = 48000 cfg.Ingest.SRT.Channels = 2 src, ingress, err := BuildSource(cfg, Deps{}) if err != nil { t.Fatalf("build source: %v", err) } if src == nil { t.Fatalf("expected source") } if ingress != nil { t.Fatalf("expected no ingress for srt") } if got := src.Descriptor().Kind; got != "srt" { t.Fatalf("source kind=%s", got) } if got := src.Descriptor().Origin; got == nil || got.Kind != "url" || got.Mode != "listener" { t.Fatalf("expected srt origin url/listener, got %+v", got) } } func TestBuildSourceAES67(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = "aes67" cfg.Ingest.AES67.MulticastGroup = "239.69.10.20" cfg.Ingest.AES67.Port = 5008 cfg.Ingest.AES67.PayloadType = 98 cfg.Ingest.AES67.SampleRateHz = 48000 cfg.Ingest.AES67.Channels = 2 cfg.Ingest.AES67.Encoding = "L24" cfg.Ingest.AES67.PacketTimeMs = 1 cfg.Ingest.AES67.JitterDepthPackets = 6 src, ingress, err := BuildSource(cfg, Deps{}) if err != nil { t.Fatalf("build source: %v", err) } if src == nil { t.Fatalf("expected source") } if ingress != nil { t.Fatalf("expected no ingress for aes67") } if got := src.Descriptor().Kind; got != "aes67" { t.Fatalf("source kind=%s", got) } } func TestBuildSourceAES67FromInlineSDP(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = "aes67" cfg.Ingest.AES67.MulticastGroup = "" cfg.Ingest.AES67.SDP = "v=0\r\ns=demo\r\nc=IN IP4 239.10.20.30\r\nm=audio 5004 RTP/AVP 97\r\na=rtpmap:97 L24/48000/2\r\na=ptime:1\r\n" src, _, err := BuildSource(cfg, Deps{}) if err != nil { t.Fatalf("build source: %v", err) } desc := src.Descriptor() if desc.Transport != "rtp" { t.Fatalf("transport=%q want rtp", desc.Transport) } if desc.SampleRateHz != 48000 || desc.Channels != 2 { t.Fatalf("shape=%d/%d", desc.SampleRateHz, desc.Channels) } if desc.Origin == nil || desc.Origin.Kind != "sdp-inline" { t.Fatalf("origin=%+v want sdp-inline", desc.Origin) } if desc.Origin.Endpoint != "rtp://239.10.20.30:5004" { t.Fatalf("origin endpoint=%q", desc.Origin.Endpoint) } } func TestBuildSourceAES67WithDiscovery(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = "aes67" cfg.Ingest.AES67.MulticastGroup = "" cfg.Ingest.AES67.Port = 0 cfg.Ingest.AES67.Discovery.StreamName = "AES67-MAIN" cfg.Ingest.AES67.Discovery.TimeoutMs = 1500 var gotReq AES67DiscoverRequest src, _, err := BuildSource(cfg, Deps{ AES67Discover: func(_ context.Context, req AES67DiscoverRequest) (aoiprxkit.SAPAnnouncement, error) { gotReq = req return aoiprxkit.SAPAnnouncement{ SDP: "v=0\r\ns=AES67-MAIN\r\nc=IN IP4 239.10.20.30\r\nm=audio 5004 RTP/AVP 97\r\na=rtpmap:97 L24/48000/2\r\na=ptime:1\r\n", }, nil }, }) if err != nil { t.Fatalf("build source: %v", err) } if gotReq.StreamName != "AES67-MAIN" { t.Fatalf("discovery streamName=%q want AES67-MAIN", gotReq.StreamName) } if gotReq.Timeout != 1500*time.Millisecond { t.Fatalf("discovery timeout=%s want 1500ms", gotReq.Timeout) } desc := src.Descriptor() if desc.Detail != "rtp://239.10.20.30:5004 (SAP s=AES67-MAIN)" { t.Fatalf("descriptor detail=%q", desc.Detail) } if desc.Origin == nil || desc.Origin.Kind != "sap-discovery" { t.Fatalf("origin=%+v want sap-discovery", desc.Origin) } if desc.Origin.StreamName != "AES67-MAIN" { t.Fatalf("origin streamName=%q", desc.Origin.StreamName) } } func TestBuildSourceAES67DiscoveryError(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = "aes67" cfg.Ingest.AES67.MulticastGroup = "" cfg.Ingest.AES67.Port = 0 cfg.Ingest.AES67.Discovery.StreamName = "AES67-MAIN" _, _, err := BuildSource(cfg, Deps{ AES67Discover: func(_ context.Context, req AES67DiscoverRequest) (aoiprxkit.SAPAnnouncement, error) { _ = req return aoiprxkit.SAPAnnouncement{}, errors.New("timeout") }, }) if err == nil { t.Fatalf("expected discovery error") } } func TestBuildSourceUnsupportedKind(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = "nope" _, _, err := BuildSource(cfg, Deps{}) if err == nil { t.Fatalf("expected error") } } func TestSampleRateForKind(t *testing.T) { cfg := config.Default() cfg.Ingest.Kind = "stdin" cfg.Ingest.Stdin.SampleRateHz = 48000 if got := SampleRateForKind(cfg); got != 48000 { t.Fatalf("stdin sample rate=%d", got) } cfg.Ingest.Kind = "http-raw" cfg.Ingest.HTTPRaw.SampleRateHz = 32000 if got := SampleRateForKind(cfg); got != 32000 { t.Fatalf("http-raw sample rate=%d", got) } cfg.Ingest.Kind = "icecast" if got := SampleRateForKind(cfg); got != 44100 { t.Fatalf("icecast sample rate=%d", got) } cfg.Ingest.Kind = "srt" cfg.Ingest.SRT.SampleRateHz = 48000 if got := SampleRateForKind(cfg); got != 48000 { t.Fatalf("srt sample rate=%d", got) } cfg.Ingest.Kind = "aes67" cfg.Ingest.AES67.SampleRateHz = 32000 if got := SampleRateForKind(cfg); got != 32000 { t.Fatalf("aes67 sample rate=%d", got) } }