CounterDroneBackend/test/unit/CounterDrone.Core.Tests/PlannerConfigTests.cs
tian 11f8cb2c79 feat: detection-driven planning (v0.6.0)
Planner no longer has god-view. It assumes threats enter from detection boundary (unified info network earliest detection point), not route start.

Core: DetectionCalculator (EO attenuated by Visibility, radar/IR unaffected; earliest detection via segment-circle intersection). EquipmentDeployment: drop DetectionRadius, add RadarRange/EORange/IRange/DetectionAccuracy. FireUnit detection fields activated in BuildFireUnits. IDefensePlanner.Plan adds 4th param detectionSources.

Engine: BuildDetectionSources merges standalone detectors + fire-unit self-detection into unified list, passed to planner.

Fix: Solve robustness when GenerateFireEventsAt returns empty (detection boundary too late to intercept).

Tests 193 to 208 (+15). 0 warnings.
2026-06-15 12:52:57 +08:00

97 lines
3.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.IO;
using CounterDrone.Core.Algorithms;
using CounterDrone.Core.Models;
using Xunit;
namespace CounterDrone.Core.Tests
{
public class PlannerConfigTests : IDisposable
{
private readonly string _testDir;
public PlannerConfigTests()
{
_testDir = Path.Combine(Path.GetTempPath(), $"cd_cfg_{Guid.NewGuid():N}");
Directory.CreateDirectory(_testDir);
}
public void Dispose()
{
if (Directory.Exists(_testDir)) Directory.Delete(_testDir, true);
}
private void WriteConfig(string json)
=> File.WriteAllText(Path.Combine(_testDir, "planner_config.json"), json);
private static readonly string ValidJson = @"
{
""CloudOverlapRatio"": 0.2,
""CriticalProbabilityThreshold"": 0.5,
""MaxInterceptProbability"": 0.95,
""TypeCoefficient"": {
""HighSpeed"": 4.0, ""FixedWing"": 2.0, ""Piston"": 2.0,
""Rotor"": 1.0, ""Electric"": 1.0
},
""AmmoMatch"": {
""Electric"": ""InertGas"", ""Piston"": ""InertGas"", ""Jet"": ""ActiveMaterial""
},
""DefaultDetectionAccuracy"": 50.0
}";
[Fact]
public void Load_ValidJson_ReturnsAllFields()
{
WriteConfig(ValidJson);
var cfg = PlannerConfig.Load(_testDir);
Assert.Equal(0.2f, cfg.CloudOverlapRatio);
Assert.Equal(0.5f, cfg.CriticalProbabilityThreshold);
Assert.Equal(0.95f, cfg.MaxInterceptProbability);
Assert.Equal(4.0f, cfg.TypeCoefficient[TargetType.HighSpeed]);
Assert.Equal(AerosolType.InertGas, cfg.AmmoMatch[PowerType.Piston]);
Assert.Equal(AerosolType.ActiveMaterial, cfg.AmmoMatch[PowerType.Jet]);
}
[Fact]
public void Load_MissingFile_Throws()
{
// 不写文件
Assert.Throws<FileNotFoundException>(() => PlannerConfig.Load(_testDir));
}
[Fact]
public void Load_MissingField_Throws()
{
// 缺 CriticalProbabilityThreshold
WriteConfig(@"{ ""CloudOverlapRatio"": 0.2, ""MaxInterceptProbability"": 0.95,
""TypeCoefficient"": {""Piston"": 2.0}, ""AmmoMatch"": {""Piston"": ""InertGas""} }");
Assert.Throws<InvalidDataException>(() => PlannerConfig.Load(_testDir));
}
[Fact]
public void Load_OverlapOutOfRange_Throws()
{
// CloudOverlapRatio = 1.0 非法(必须 < 1
var bad = ValidJson.Replace("0.2", "1.0");
WriteConfig(bad);
Assert.Throws<InvalidDataException>(() => PlannerConfig.Load(_testDir));
}
[Fact]
public void Load_EmptyDataRoot_Throws()
{
Assert.Throws<ArgumentException>(() => PlannerConfig.Load(""));
}
[Fact]
public void Load_EnumParsedFromString()
{
// 验证枚举用字符串(非数字)也能正确解析
WriteConfig(ValidJson);
var cfg = PlannerConfig.Load(_testDir);
Assert.Equal(1.0f, cfg.TypeCoefficient[TargetType.Rotor]);
Assert.Equal(AerosolType.InertGas, cfg.AmmoMatch[PowerType.Electric]);
}
}
}