package flowdef import ( "encoding/json" "strings" "testing" ) const testVideoID = "5fbec3b1-1b0f-417d-9059-8b94a47197ed" const testAudioID = "b3bb5be7-9fe9-4324-a5bb-4c70e1084449" func TestNewV210Video(t *testing.T) { definition, err := NewV210Video(testVideoID, 1920, 1080, Rational{Numerator: 30000, Denominator: 1001}) if err != nil { t.Fatalf("NewV210Video: %v", err) } if definition.ID != testVideoID { t.Fatalf("ID = %q, want %q", definition.ID, testVideoID) } if definition.Format != FormatVideo || definition.MediaType != MediaTypeV210 { t.Fatalf("format/media type = %q/%q", definition.Format, definition.MediaType) } if len(definition.Components) != 3 { t.Fatalf("component count = %d, want 3", len(definition.Components)) } if definition.Parents == nil { t.Fatal("Parents is nil; want an empty JSON array") } } func TestNewV210AVideo(t *testing.T) { definition, err := NewV210AVideo(testVideoID, 1280, 720, Rational{Numerator: 50, Denominator: 1}) if err != nil { t.Fatalf("NewV210AVideo: %v", err) } if definition.MediaType != MediaTypeV210A { t.Fatalf("media type = %q, want %q", definition.MediaType, MediaTypeV210A) } wantComponents := []VideoComponent{ {Name: "Y", Width: 1280, Height: 720, BitDepth: 10}, {Name: "Cb", Width: 640, Height: 720, BitDepth: 10}, {Name: "Cr", Width: 640, Height: 720, BitDepth: 10}, } if len(definition.Components) != len(wantComponents) { t.Fatalf("component count = %d, want %d", len(definition.Components), len(wantComponents)) } for i, want := range wantComponents { if definition.Components[i] != want { t.Fatalf("component %d = %+v, want %+v", i, definition.Components[i], want) } } } func TestNewV210VideoRejectsOddWidth(t *testing.T) { _, err := NewV210Video(testVideoID, 1919, 1080, Rational{Numerator: 25, Denominator: 1}) if err == nil || !strings.Contains(err.Error(), "even") { t.Fatalf("error = %v, want even-width error", err) } } func TestParseVideo(t *testing.T) { want, err := NewV210Video(testVideoID, 1920, 1080, Rational{Numerator: 25, Denominator: 1}) if err != nil { t.Fatalf("NewV210Video: %v", err) } data, err := json.Marshal(want) if err != nil { t.Fatalf("json.Marshal: %v", err) } got, err := ParseVideo(data) if err != nil { t.Fatalf("ParseVideo: %v", err) } if got.ID != want.ID || got.FrameWidth != want.FrameWidth || got.GrainRate != want.GrainRate { t.Fatalf("parsed definition = %+v, want %+v", got, want) } } func TestParseVideoAcceptsV210A(t *testing.T) { want, err := NewV210AVideo(testVideoID, 1920, 1080, Rational{Numerator: 25, Denominator: 1}) if err != nil { t.Fatalf("NewV210AVideo: %v", err) } data, err := json.Marshal(want) if err != nil { t.Fatalf("json.Marshal: %v", err) } got, err := ParseVideo(data) if err != nil { t.Fatalf("ParseVideo: %v", err) } if got.MediaType != MediaTypeV210A { t.Fatalf("media type = %q, want %q", got.MediaType, MediaTypeV210A) } } func TestParseVideoRejectsAudio(t *testing.T) { data := []byte(`{ "id":"5fbec3b1-1b0f-417d-9059-8b94a47197ed", "format":"urn:x-nmos:format:audio", "media_type":"audio/float32" }`) _, err := ParseVideo(data) if err == nil || !strings.Contains(err.Error(), "format must be") { t.Fatalf("error = %v, want video format error", err) } } func TestVideoRejectsUnknownMediaType(t *testing.T) { definition, err := NewV210Video(testVideoID, 1920, 1080, Rational{Numerator: 25, Denominator: 1}) if err != nil { t.Fatalf("NewV210Video: %v", err) } definition.MediaType = "video/unknown" if err := definition.Validate(); err == nil || !strings.Contains(err.Error(), "media_type") { t.Fatalf("error = %v, want media_type error", err) } } func TestNewFloat32Audio(t *testing.T) { definition, err := NewFloat32Audio(testAudioID, 2, Rational{Numerator: 48000, Denominator: 1}) if err != nil { t.Fatalf("NewFloat32Audio: %v", err) } if definition.ID != testAudioID { t.Fatalf("ID = %q, want %q", definition.ID, testAudioID) } if definition.Format != FormatAudio || definition.MediaType != MediaTypeFloat32 { t.Fatalf("format/media type = %q/%q", definition.Format, definition.MediaType) } if definition.ChannelCount != 2 || definition.BitDepth != 32 { t.Fatalf("channels/bit depth = %d/%d, want 2/32", definition.ChannelCount, definition.BitDepth) } if definition.SampleRate != (Rational{Numerator: 48000, Denominator: 1}) { t.Fatalf("sample rate = %+v, want 48000/1", definition.SampleRate) } if definition.Parents == nil { t.Fatal("Parents is nil; want an empty JSON array") } } func TestNewFloat32AudioRejectsZeroChannels(t *testing.T) { _, err := NewFloat32Audio(testAudioID, 0, Rational{Numerator: 48000, Denominator: 1}) if err == nil || !strings.Contains(err.Error(), "channel_count") { t.Fatalf("error = %v, want channel_count error", err) } } func TestNewFloat32AudioRejectsInvalidRate(t *testing.T) { _, err := NewFloat32Audio(testAudioID, 2, Rational{Numerator: 48000, Denominator: 0}) if err == nil || !strings.Contains(err.Error(), "sample_rate") { t.Fatalf("error = %v, want sample_rate error", err) } } func TestParseFloat32Audio(t *testing.T) { want, err := NewFloat32Audio(testAudioID, 2, Rational{Numerator: 48000, Denominator: 1}) if err != nil { t.Fatalf("NewFloat32Audio: %v", err) } data, err := json.Marshal(want) if err != nil { t.Fatalf("json.Marshal: %v", err) } got, err := ParseFloat32Audio(data) if err != nil { t.Fatalf("ParseFloat32Audio: %v", err) } if got.ID != want.ID || got.ChannelCount != want.ChannelCount || got.SampleRate != want.SampleRate { t.Fatalf("parsed definition = %+v, want %+v", got, want) } } func TestParseFloat32AudioDefaultsDenominator(t *testing.T) { data := []byte(`{ "description":"test audio", "id":"b3bb5be7-9fe9-4324-a5bb-4c70e1084449", "tags":{}, "format":"urn:x-nmos:format:audio", "label":"test audio", "parents":[], "media_type":"audio/float32", "sample_rate":{"numerator":48000}, "channel_count":2, "bit_depth":32 }`) definition, err := ParseFloat32Audio(data) if err != nil { t.Fatalf("ParseFloat32Audio: %v", err) } if definition.SampleRate.Denominator != 1 { t.Fatalf("denominator = %d, want implicit 1", definition.SampleRate.Denominator) } } func TestParseFloat32AudioRejectsExplicitZeroDenominator(t *testing.T) { data := []byte(`{ "id":"b3bb5be7-9fe9-4324-a5bb-4c70e1084449", "format":"urn:x-nmos:format:audio", "media_type":"audio/float32", "sample_rate":{"numerator":48000,"denominator":0}, "channel_count":2, "bit_depth":32 }`) _, err := ParseFloat32Audio(data) if err == nil || !strings.Contains(err.Error(), "sample_rate") { t.Fatalf("error = %v, want sample_rate error", err) } } func TestParseFloat32AudioRejectsVideo(t *testing.T) { definition, err := NewV210Video(testVideoID, 1920, 1080, Rational{Numerator: 25, Denominator: 1}) if err != nil { t.Fatalf("NewV210Video: %v", err) } data, err := json.Marshal(definition) if err != nil { t.Fatalf("json.Marshal: %v", err) } _, err = ParseFloat32Audio(data) if err == nil || !strings.Contains(err.Error(), "format must be") { t.Fatalf("error = %v, want audio format error", err) } }