CounterDroneBackend/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs
tian 317f47280b refactor: DefensePlanner 替代 DefenseAdvisor
- 新增 IDefensePlanner 接口 + DefaultDefensePlanner 实现
- FireUnit 重构为统一平台模型(Type/Position/CruiseSpeed/ReleaseAltitude/MuzzleVelocity)
- DroneGroup 加入威胁指数(类型系数×速度系数)和优先级排序
- 五步流水线:威胁排序→弹药匹配→候选生成→贪心分配→时序生成
- 弹药精确匹配,不降级;临界方案基于50%概率阈值
- 删除 IDefenseAdvisor/DefaultDefenseAdvisor/RecommendMultiGroup
- AlgorithmFactory 注册 IDefensePlanner
- 测试更新:DefensePlannerTests 替代旧测试,FullPipelineTests 适配新 API
- Unity Managers 更新调用
- 125 测试全通过
2026-06-12 18:20:25 +08:00

144 lines
4.9 KiB
C#

using System.Collections.Generic;
using CounterDrone.Core.Algorithms;
using CounterDrone.Core.Models;
using Xunit;
namespace CounterDrone.Core.Tests
{
public class DefensePlannerTests
{
private static readonly List<AmmunitionSpec> TestAmmo = DefaultAmmunition.GetAll();
private static List<FireUnit> MakeGroundUnits(int count = 5)
{
var units = new List<FireUnit>();
for (int i = 0; i < count; i++)
units.Add(new FireUnit
{
Id = $"u{i}",
Type = PlatformType.GroundBased,
Position = new Vector3(5000 + i * 50, 0, 50),
MuzzleVelocity = 800f,
TotalMunitions = 3,
AmmoTypes = new List<AerosolType> { AerosolType.InertGas, AerosolType.ActiveMaterial, AerosolType.ActiveFuel },
});
return units;
}
private DroneGroup MakeThreat(PowerType powerType = PowerType.Piston, float speed = 120f)
{
return new DroneGroup
{
GroupId = "default",
Target = new TargetConfig
{
TargetType = (int)TargetType.Piston,
PowerType = (int)powerType,
Quantity = 1,
TypicalSpeed = speed,
TypicalAltitude = 500,
},
Waypoints = new List<Waypoint>
{
new Waypoint { PosX = 0, PosY = 500, PosZ = 100, Altitude = 500, Speed = speed },
new Waypoint { PosX = 10000, PosY = 500, PosZ = 100, Altitude = 500, Speed = speed },
},
};
}
[Fact]
public void Piston_Engaged()
{
var result = new DefaultDefensePlanner(TestAmmo).Plan(
MakeGroundUnits(),
new List<DroneGroup> { MakeThreat(PowerType.Piston) },
new CombatScene { WindSpeed = 5 });
Assert.True(result.Best.ThreatsEngaged > 0);
Assert.Contains("分配", result.Best.Summary);
}
[Fact]
public void Jet_Engaged()
{
var result = new DefaultDefensePlanner(TestAmmo).Plan(
MakeGroundUnits(),
new List<DroneGroup> { MakeThreat(PowerType.Jet, 500) },
new CombatScene());
Assert.True(result.Best.ThreatsEngaged > 0);
}
[Fact]
public void Electric_Engaged()
{
var result = new DefaultDefensePlanner(TestAmmo).Plan(
MakeGroundUnits(),
new List<DroneGroup> { MakeThreat(PowerType.Electric) },
new CombatScene());
Assert.True(result.Best.ThreatsEngaged > 0);
}
[Fact]
public void Best_HigherThanCritical()
{
var result = new DefaultDefensePlanner(TestAmmo).Plan(
MakeGroundUnits(10),
new List<DroneGroup> { MakeThreat() },
new CombatScene());
Assert.True(result.Best.ThreatsEngaged > 0);
Assert.True(result.Critical.ThreatsEngaged > 0);
}
[Fact]
public void Has_FireSchedule()
{
var result = new DefaultDefensePlanner(TestAmmo).Plan(
MakeGroundUnits(),
new List<DroneGroup> { MakeThreat() },
new CombatScene());
Assert.NotEmpty(result.Best.MergedSchedule);
}
[Fact]
public void Has_Assignments()
{
var result = new DefaultDefensePlanner(TestAmmo).Plan(
MakeGroundUnits(),
new List<DroneGroup> { MakeThreat() },
new CombatScene());
Assert.NotEmpty(result.Best.Assignments);
}
[Fact]
public void Critical_Within50Percent()
{
var result = new DefaultDefensePlanner(TestAmmo).Plan(
MakeGroundUnits(10),
new List<DroneGroup> { MakeThreat() },
new CombatScene());
// Critical 方案概率标记为 0.5(阈值下限)
Assert.True(result.Critical.OverallProbability >= 0.45f);
Assert.True(result.Critical.Assignments.Count > 0);
}
[Fact]
public void NoUnits_ThreatsUnengaged()
{
var result = new DefaultDefensePlanner(TestAmmo).Plan(
new List<FireUnit>(),
new List<DroneGroup> { MakeThreat() },
new CombatScene());
Assert.Equal(1, result.Best.ThreatsUnengaged);
}
[Fact]
public void NoThreats_EmptyPlan()
{
var result = new DefaultDefensePlanner(TestAmmo).Plan(
MakeGroundUnits(),
new List<DroneGroup>(),
new CombatScene());
Assert.Equal(0, result.Best.ThreatsEngaged);
}
}
}