| @@ -53,7 +53,11 @@ func TestSurveillanceLevelsRespectStrategy(t *testing.T) { | |||||
| if len(plan.Levels) != 1 { | if len(plan.Levels) != 1 { | ||||
| t.Fatalf("expected single level for single-resolution, got %d", len(plan.Levels)) | t.Fatalf("expected single level for single-resolution, got %d", len(plan.Levels)) | ||||
| } | } | ||||
| if plan.Levels[0].Role != pipeline.RoleSurveillancePrimary { | |||||
| t.Fatalf("expected primary role, got %q", plan.Levels[0].Role) | |||||
| } | |||||
| policy.SurveillanceStrategy = "multi-res" | policy.SurveillanceStrategy = "multi-res" | ||||
| policy.Intent = "wideband-surveillance" | |||||
| plan = rt.buildSurveillancePlan(policy) | plan = rt.buildSurveillancePlan(policy) | ||||
| if len(plan.Levels) != 2 { | if len(plan.Levels) != 2 { | ||||
| t.Fatalf("expected secondary level for multi-res, got %d", len(plan.Levels)) | t.Fatalf("expected secondary level for multi-res, got %d", len(plan.Levels)) | ||||
| @@ -61,6 +65,9 @@ func TestSurveillanceLevelsRespectStrategy(t *testing.T) { | |||||
| if plan.Levels[1].Decimation != 2 { | if plan.Levels[1].Decimation != 2 { | ||||
| t.Fatalf("expected decimation factor 2, got %d", plan.Levels[1].Decimation) | t.Fatalf("expected decimation factor 2, got %d", plan.Levels[1].Decimation) | ||||
| } | } | ||||
| if plan.Levels[1].Role != pipeline.RoleSurveillanceDerived { | |||||
| t.Fatalf("expected derived role, got %q", plan.Levels[1].Role) | |||||
| } | |||||
| } | } | ||||
| func TestWindowSpanBounds(t *testing.T) { | func TestWindowSpanBounds(t *testing.T) { | ||||
| @@ -0,0 +1,45 @@ | |||||
| package pipeline | |||||
| import "testing" | |||||
| func TestLevelRoleClassification(t *testing.T) { | |||||
| primary := AnalysisLevel{Name: "surveillance", Role: RoleSurveillancePrimary, Truth: "surveillance"} | |||||
| derived := AnalysisLevel{Name: "surveillance-lowres", Role: RoleSurveillanceDerived, Truth: "surveillance"} | |||||
| support := AnalysisLevel{Name: "surveillance-lowres", Role: RoleSurveillanceSupport, Truth: "surveillance"} | |||||
| presentation := AnalysisLevel{Name: "presentation", Role: RolePresentation, Truth: "presentation"} | |||||
| if !IsDetectionLevel(primary) || IsPresentationLevel(primary) || IsSupportLevel(primary) { | |||||
| t.Fatalf("primary role classification failed: %+v", primary) | |||||
| } | |||||
| if !IsDetectionLevel(derived) || IsPresentationLevel(derived) || IsSupportLevel(derived) { | |||||
| t.Fatalf("derived role classification failed: %+v", derived) | |||||
| } | |||||
| if IsDetectionLevel(support) || IsPresentationLevel(support) || !IsSupportLevel(support) { | |||||
| t.Fatalf("support role classification failed: %+v", support) | |||||
| } | |||||
| if IsDetectionLevel(presentation) || !IsPresentationLevel(presentation) || IsSupportLevel(presentation) { | |||||
| t.Fatalf("presentation role classification failed: %+v", presentation) | |||||
| } | |||||
| } | |||||
| func TestCandidateEvidenceStateTracksSupportLevels(t *testing.T) { | |||||
| candidate := Candidate{ | |||||
| ID: 1, | |||||
| Evidence: []LevelEvidence{ | |||||
| {Level: AnalysisLevel{Name: "surveillance", Role: RoleSurveillancePrimary, Truth: "surveillance"}, Provenance: "primary"}, | |||||
| {Level: AnalysisLevel{Name: "surveillance-lowres", Role: RoleSurveillanceDerived, Truth: "surveillance"}, Provenance: "derived"}, | |||||
| {Level: AnalysisLevel{Name: "surveillance-support", Role: RoleSurveillanceSupport, Truth: "surveillance"}, Provenance: "support"}, | |||||
| {Level: AnalysisLevel{Name: "presentation", Role: RolePresentation, Truth: "presentation"}, Provenance: "display"}, | |||||
| }, | |||||
| } | |||||
| state := CandidateEvidenceStateFor(candidate) | |||||
| if state.DetectionLevelCount != 2 || state.PrimaryLevelCount != 1 || state.DerivedLevelCount != 1 { | |||||
| t.Fatalf("unexpected detection counts: %+v", state) | |||||
| } | |||||
| if state.SupportLevelCount != 1 || state.PresentationLevelCount != 1 { | |||||
| t.Fatalf("unexpected support/presentation counts: %+v", state) | |||||
| } | |||||
| if !state.MultiLevelConfirmed || state.DerivedOnly { | |||||
| t.Fatalf("unexpected confirmation flags: %+v", state) | |||||
| } | |||||
| } | |||||
| @@ -185,7 +185,7 @@ func TestScheduleCandidatesDerivedOnlyPenalty(t *testing.T) { | |||||
| SNRDb: 10, | SNRDb: 10, | ||||
| BandwidthHz: 12000, | BandwidthHz: 12000, | ||||
| Evidence: []LevelEvidence{ | Evidence: []LevelEvidence{ | ||||
| {Level: AnalysisLevel{Name: "surveillance", Role: "surveillance", Truth: "surveillance"}}, | |||||
| {Level: AnalysisLevel{Name: "surveillance", Role: RoleSurveillancePrimary, Truth: "surveillance"}}, | |||||
| }, | }, | ||||
| } | } | ||||
| derived := Candidate{ | derived := Candidate{ | ||||
| @@ -193,7 +193,7 @@ func TestScheduleCandidatesDerivedOnlyPenalty(t *testing.T) { | |||||
| SNRDb: 10, | SNRDb: 10, | ||||
| BandwidthHz: 12000, | BandwidthHz: 12000, | ||||
| Evidence: []LevelEvidence{ | Evidence: []LevelEvidence{ | ||||
| {Level: AnalysisLevel{Name: "surveillance-lowres", Role: "surveillance-lowres", Truth: "surveillance"}}, | |||||
| {Level: AnalysisLevel{Name: "surveillance-lowres", Role: RoleSurveillanceDerived, Truth: "surveillance"}}, | |||||
| }, | }, | ||||
| } | } | ||||
| plan := BuildRefinementPlan([]Candidate{derived, primary}, policy) | plan := BuildRefinementPlan([]Candidate{derived, primary}, policy) | ||||
| @@ -211,7 +211,7 @@ func TestScheduleCandidatesDerivedOnlyStrategyBias(t *testing.T) { | |||||
| SNRDb: 9, | SNRDb: 9, | ||||
| BandwidthHz: 12000, | BandwidthHz: 12000, | ||||
| Evidence: []LevelEvidence{ | Evidence: []LevelEvidence{ | ||||
| {Level: AnalysisLevel{Name: "surveillance-lowres", Role: "surveillance-lowres", Truth: "surveillance"}}, | |||||
| {Level: AnalysisLevel{Name: "surveillance-lowres", Role: RoleSurveillanceDerived, Truth: "surveillance"}}, | |||||
| }, | }, | ||||
| } | } | ||||
| singlePlan := BuildRefinementPlan([]Candidate{cand}, Policy{MinCandidateSNRDb: 0}) | singlePlan := BuildRefinementPlan([]Candidate{cand}, Policy{MinCandidateSNRDb: 0}) | ||||
| @@ -0,0 +1,30 @@ | |||||
| package pipeline | |||||
| import "testing" | |||||
| func TestSurveillanceDetectionPolicyAuto(t *testing.T) { | |||||
| policy := Policy{ | |||||
| Mode: "wideband-balanced", | |||||
| Profile: "wideband-balanced", | |||||
| Intent: "wideband-surveillance", | |||||
| SurveillanceStrategy: "multi-resolution", | |||||
| } | |||||
| policy.SurveillanceDerivedDetection = "auto" | |||||
| got := SurveillanceDetectionPolicyFromPolicy(policy) | |||||
| if !got.DerivedDetectionEnabled { | |||||
| t.Fatalf("expected auto policy to enable derived detection, got %+v", got) | |||||
| } | |||||
| policy.Profile = "archive" | |||||
| policy.Intent = "archive-and-triage" | |||||
| got = SurveillanceDetectionPolicyFromPolicy(policy) | |||||
| if got.DerivedDetectionEnabled { | |||||
| t.Fatalf("expected archive policy to disable derived detection, got %+v", got) | |||||
| } | |||||
| policy = Policy{SurveillanceStrategy: "single-resolution", SurveillanceDerivedDetection: "auto"} | |||||
| got = SurveillanceDetectionPolicyFromPolicy(policy) | |||||
| if got.DerivedDetectionEnabled { | |||||
| t.Fatalf("expected single-resolution to disable derived detection, got %+v", got) | |||||
| } | |||||
| } | |||||