package config import ( "os" "path/filepath" "testing" ) func TestDefaultConfigStartsKioskDisplay(t *testing.T) { cfg := Default() if !cfg.Display.Enabled { t.Fatal("kiosk display must be enabled by default") } if cfg.Display.Command != "/usr/local/bin/groundstation-display" { t.Fatalf("command=%q", cfg.Display.Command) } if cfg.Display.User != "orangepi" { t.Fatalf("user=%q", cfg.Display.User) } if cfg.Display.Display != ":0" { t.Fatalf("display=%q want :0", cfg.Display.Display) } if cfg.Display.Xauthority != "/home/orangepi/.Xauthority" { t.Fatalf("xauthority=%q", cfg.Display.Xauthority) } } func TestLoadOverridesDisplaySettings(t *testing.T) { path := filepath.Join(t.TempDir(), "config.yml") body := "display:\n enabled: false\n command: /bin/kiosk\n" if err := os.WriteFile(path, []byte(body), 0644); err != nil { t.Fatal(err) } cfg, err := Load(path) if err != nil { t.Fatal(err) } if cfg.Display.Enabled { t.Fatal("display override must disable the kiosk") } if cfg.Display.Command != "/bin/kiosk" { t.Fatalf("command=%q want /bin/kiosk", cfg.Display.Command) } if cfg.Display.Display != ":0" { t.Fatalf("display=%q want default :0", cfg.Display.Display) } } func TestDefaultConfigPublishesOSDSnapshotsFromAUX1(t *testing.T) { cfg := Default() if cfg.OSD.StatePath != "/run/groundstation/osd-state.bin" { t.Fatalf("state_path=%q want the default tmpfs path", cfg.OSD.StatePath) } // CH5/AUX1 is the arming channel; the fail-closed convention reads it // armed above the midpoint. if cfg.OSD.ArmIndex != 4 { t.Fatalf("arm_index=%d want 4 (CH5/AUX1, zero-based)", cfg.OSD.ArmIndex) } if cfg.OSD.ArmedThreshold != 0 { t.Fatalf("armed_threshold=%d want 0", cfg.OSD.ArmedThreshold) } } func TestLoadOverridesOSDSettings(t *testing.T) { path := filepath.Join(t.TempDir(), "config.yml") body := "osd:\n state_path: /tmp/osd-state.bin\n arm_index: 6\n armed_threshold: 256\n vtx_table_gen: 3\n vtx_table_hash: 9\n" if err := os.WriteFile(path, []byte(body), 0644); err != nil { t.Fatal(err) } cfg, err := Load(path) if err != nil { t.Fatal(err) } if cfg.OSD.StatePath != "/tmp/osd-state.bin" { t.Fatalf("state_path=%q want the override", cfg.OSD.StatePath) } if cfg.OSD.ArmIndex != 6 || cfg.OSD.ArmedThreshold != 256 { t.Fatalf("arm config=%+v want the overrides", cfg.OSD) } if cfg.OSD.VtxTableGen != 3 || cfg.OSD.VtxTableHash != 9 { t.Fatalf("table config=%+v want the selected table", cfg.OSD) } } func TestDefaultConfigSelectsNoVtxTable(t *testing.T) { // No table exists yet: the operator selection defaults to none, so the // OSD shows an explicit pending table instead of static configuration // dressed up as applied VTX state. cfg := Default() if cfg.OSD.VtxTableGen != 0 || cfg.OSD.VtxTableHash != 0 { t.Fatalf("table config=%+v must default to no selection", cfg.OSD) } } func TestLoadRejectsArmAndVideoIndexesOutsideTheChannelRange(t *testing.T) { for name, body := range map[string]string{ "arm index below range": "osd:\n arm_index: -1\n", "arm index above range": "osd:\n arm_index: 16\n", "video index below range": "video:\n aux_index: -1\n", "video index above range": "video:\n aux_index: 16\n", } { path := filepath.Join(t.TempDir(), "config.yml") if err := os.WriteFile(path, []byte(body), 0644); err != nil { t.Fatal(err) } if _, err := Load(path); err == nil { t.Fatalf("%s must be rejected: an out-of-range index would panic on the first frame", name) } } }