|
|
|
@@ -3,11 +3,14 @@ package config |
|
|
|
import ( |
|
|
|
"os" |
|
|
|
"path/filepath" |
|
|
|
"strings" |
|
|
|
"testing" |
|
|
|
) |
|
|
|
|
|
|
|
func TestDefaultValidate(t *testing.T) { |
|
|
|
if err := Default().Validate(); err != nil { t.Fatalf("default invalid: %v", err) } |
|
|
|
if err := Default().Validate(); err != nil { |
|
|
|
t.Fatalf("default invalid: %v", err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestLoadAndValidate(t *testing.T) { |
|
|
|
@@ -15,56 +18,108 @@ func TestLoadAndValidate(t *testing.T) { |
|
|
|
path := filepath.Join(dir, "config.json") |
|
|
|
os.WriteFile(path, []byte(`{"audio":{"toneLeftHz":900,"toneRightHz":1700,"toneAmplitude":0.3},"fm":{"frequencyMHz":99.9},"backend":{"kind":"file","outputPath":"out.f32"},"control":{"listenAddress":"127.0.0.1:8088"}}`), 0o644) |
|
|
|
cfg, err := Load(path) |
|
|
|
if err != nil { t.Fatalf("load: %v", err) } |
|
|
|
if cfg.Audio.ToneLeftHz != 900 { t.Fatalf("unexpected left tone: %v", cfg.Audio.ToneLeftHz) } |
|
|
|
if err != nil { |
|
|
|
t.Fatalf("load: %v", err) |
|
|
|
} |
|
|
|
if cfg.Audio.ToneLeftHz != 900 { |
|
|
|
t.Fatalf("unexpected left tone: %v", cfg.Audio.ToneLeftHz) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestValidateRejectsBadFrequency(t *testing.T) { |
|
|
|
cfg := Default(); cfg.FM.FrequencyMHz = 200 |
|
|
|
if err := cfg.Validate(); err == nil { t.Fatal("expected error") } |
|
|
|
cfg := Default() |
|
|
|
cfg.FM.FrequencyMHz = 200 |
|
|
|
if err := cfg.Validate(); err == nil { |
|
|
|
t.Fatal("expected error") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestValidateRejectsBadPreEmphasis(t *testing.T) { |
|
|
|
cfg := Default(); cfg.FM.PreEmphasisTauUS = 150 |
|
|
|
if err := cfg.Validate(); err == nil { t.Fatal("expected error") } |
|
|
|
cfg := Default() |
|
|
|
cfg.FM.PreEmphasisTauUS = 150 |
|
|
|
if err := cfg.Validate(); err == nil { |
|
|
|
t.Fatal("expected error") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestDefaultPreEmphasis(t *testing.T) { |
|
|
|
if Default().FM.PreEmphasisTauUS != 50 { t.Fatal("expected 50") } |
|
|
|
if Default().FM.PreEmphasisTauUS != 50 { |
|
|
|
t.Fatal("expected 50") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestDefaultFMModulation(t *testing.T) { |
|
|
|
cfg := Default() |
|
|
|
if !cfg.FM.FMModulationEnabled { t.Fatal("expected true") } |
|
|
|
if cfg.FM.MaxDeviationHz != 75000 { t.Fatal("expected 75000") } |
|
|
|
if !cfg.FM.FMModulationEnabled { |
|
|
|
t.Fatal("expected true") |
|
|
|
} |
|
|
|
if cfg.FM.MaxDeviationHz != 75000 { |
|
|
|
t.Fatal("expected 75000") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestParsePI(t *testing.T) { |
|
|
|
tests := []struct{ in string; want uint16; ok bool }{ |
|
|
|
tests := []struct { |
|
|
|
in string |
|
|
|
want uint16 |
|
|
|
ok bool |
|
|
|
}{ |
|
|
|
{"1234", 0x1234, true}, {"0xBEEF", 0xBEEF, true}, {"0XCAFE", 0xCAFE, true}, |
|
|
|
{" 0x2345 ", 0x2345, true}, {"", 0, false}, {"nope", 0, false}, |
|
|
|
} |
|
|
|
for _, tt := range tests { |
|
|
|
got, err := ParsePI(tt.in) |
|
|
|
if tt.ok && err != nil { t.Fatalf("ParsePI(%q): %v", tt.in, err) } |
|
|
|
if !tt.ok && err == nil { t.Fatalf("ParsePI(%q): expected error", tt.in) } |
|
|
|
if tt.ok && got != tt.want { t.Fatalf("ParsePI(%q): got %x want %x", tt.in, got, tt.want) } |
|
|
|
if tt.ok && err != nil { |
|
|
|
t.Fatalf("ParsePI(%q): %v", tt.in, err) |
|
|
|
} |
|
|
|
if !tt.ok && err == nil { |
|
|
|
t.Fatalf("ParsePI(%q): expected error", tt.in) |
|
|
|
} |
|
|
|
if tt.ok && got != tt.want { |
|
|
|
t.Fatalf("ParsePI(%q): got %x want %x", tt.in, got, tt.want) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestValidateRejectsInvalidPI(t *testing.T) { |
|
|
|
cfg := Default(); cfg.RDS.PI = "nope" |
|
|
|
if err := cfg.Validate(); err == nil { t.Fatal("expected error") } |
|
|
|
cfg := Default() |
|
|
|
cfg.RDS.PI = "nope" |
|
|
|
if err := cfg.Validate(); err == nil { |
|
|
|
t.Fatal("expected error") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestValidateRejectsEmptyPI(t *testing.T) { |
|
|
|
cfg := Default(); cfg.RDS.PI = "" |
|
|
|
if err := cfg.Validate(); err == nil { t.Fatal("expected error") } |
|
|
|
cfg := Default() |
|
|
|
cfg.RDS.PI = "" |
|
|
|
if err := cfg.Validate(); err == nil { |
|
|
|
t.Fatal("expected error") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestValidateRejectsLongPS(t *testing.T) { |
|
|
|
cfg := Default() |
|
|
|
cfg.RDS.PS = "TOO_LONG_PS" |
|
|
|
if err := cfg.Validate(); err == nil { |
|
|
|
t.Fatal("expected error for PS longer than 8 characters") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestValidateRejectsLongRadioText(t *testing.T) { |
|
|
|
cfg := Default() |
|
|
|
cfg.RDS.RadioText = strings.Repeat("x", 65) |
|
|
|
if err := cfg.Validate(); err == nil { |
|
|
|
t.Fatal("expected error for RadioText longer than 64 characters") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestEffectiveDeviceRate(t *testing.T) { |
|
|
|
cfg := Default() |
|
|
|
if cfg.EffectiveDeviceRate() != float64(cfg.FM.CompositeRateHz) { t.Fatal("expected composite rate") } |
|
|
|
if cfg.EffectiveDeviceRate() != float64(cfg.FM.CompositeRateHz) { |
|
|
|
t.Fatal("expected composite rate") |
|
|
|
} |
|
|
|
cfg.Backend.DeviceSampleRateHz = 912000 |
|
|
|
if cfg.EffectiveDeviceRate() != 912000 { t.Fatal("expected 912000") } |
|
|
|
if cfg.EffectiveDeviceRate() != 912000 { |
|
|
|
t.Fatal("expected 912000") |
|
|
|
} |
|
|
|
} |