- 3D球冠探测: IsInCoverage, DetectionEntity, Tick 第5步扫描 - 探测融合: EarliestDetection 采样法, break 修复 - planner 诊断: HasInterceptWindow 拦截窗口检查 - TryGenerateFireEvents 返回拒绝原因 - Summary 含失败原因+建议值(探测范围/弧长) - PlanningFailed 事件: 引擎在规划失败时发出事件 - 硬编码消除: Phase2Duration→AmmunitionSpec ExpansionFactor/TimingSafetyMargin→PlannerConfig 速度从 waypoint.Speed 读取(不用 TypicalSpeed) - TestData 改为从 seeded 数据库读取(不再内嵌 JSON) - 默认数据加 3D球冠参数,巡航导弹速度300→200 空基航路10km→20km, 位置调整 - 222 测试全通过
165 lines
6.4 KiB
C#
165 lines
6.4 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using CounterDrone.Core;
|
||
using CounterDrone.Core.Algorithms;
|
||
using CounterDrone.Core.Models;
|
||
using CounterDrone.Core.Repository;
|
||
using CounterDrone.Core.Services;
|
||
using CounterDrone.Core.Simulation;
|
||
using Xunit;
|
||
|
||
namespace CounterDrone.Core.Tests
|
||
{
|
||
/// <summary>边界场景测试</summary>
|
||
public class EdgeCaseTests : IDisposable
|
||
{
|
||
private readonly string _testDir;
|
||
private readonly IScenarioService _scenario;
|
||
private readonly SQLite.SQLiteConnection _db;
|
||
private string _taskId = string.Empty;
|
||
|
||
public EdgeCaseTests()
|
||
{
|
||
_testDir = Path.Combine(Path.GetTempPath(), $"cd_edge_{Guid.NewGuid():N}");
|
||
var paths = new TestPathProvider(_testDir);
|
||
var dbm = new DatabaseManager(paths);
|
||
_db = dbm.OpenMainDb();
|
||
|
||
_scenario = new ScenarioService(
|
||
new SimTaskRepository(_db), new CombatSceneRepository(_db),
|
||
new ControlZoneRepository(_db), new TargetConfigRepository(_db),
|
||
new EquipmentDeploymentRepository(_db), new CloudDispersalRepository(_db),
|
||
new RoutePlanRepository(_db), new WaypointRepository(_db));
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
_db?.Close();
|
||
if (Directory.Exists(_testDir)) Directory.Delete(_testDir, true);
|
||
}
|
||
|
||
[Fact]
|
||
public void EmptyDeployment_EngineRunsWithoutCrash()
|
||
{
|
||
var task = _scenario.CreateTask("空部署", "");
|
||
_taskId = task.Id;
|
||
|
||
_scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
|
||
_scenario.SaveTarget(_taskId, new TargetConfig
|
||
{
|
||
WaveId = "default",
|
||
TargetType = (int)TargetType.Piston,
|
||
Quantity = 1,
|
||
TypicalSpeed = 100,
|
||
TypicalAltitude = 300,
|
||
});
|
||
_scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>());
|
||
_scenario.SaveCloudDispersal(_taskId, new CloudDispersal());
|
||
_scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||
new List<Waypoint>
|
||
{
|
||
new Waypoint { PosX = 0, PosY = 300, PosZ = 0, Speed = 100 },
|
||
new Waypoint { PosX = 1000, PosY = 300, PosZ = 0, Speed = 100 },
|
||
});
|
||
|
||
var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)),
|
||
new DamageModelRouter(), new TestPathProvider(_testDir), new DefensePlanner(TestData.Ammo, TestPlannerConfig.Instance));
|
||
engine.Initialize(_taskId);
|
||
engine.TimeScale = 4f;
|
||
|
||
for (int i = 0; i < 500; i++)
|
||
{
|
||
var r = engine.Tick(1f / 20f);
|
||
if (r.State == SimulationState.Completed) break;
|
||
}
|
||
engine.Stop();
|
||
|
||
Assert.Equal(DroneStatus.ReachedTarget, engine.Drones[0].Status);
|
||
}
|
||
|
||
[Fact]
|
||
public void ExtremeWind_DroneStillFlies()
|
||
{
|
||
var task = _scenario.CreateTask("飓风", "");
|
||
_taskId = task.Id;
|
||
|
||
_scenario.SaveScene(_taskId, new CombatScene
|
||
{
|
||
WindSpeed = 30, // 最大风速
|
||
WindDirection = (int)WindDirection.E,
|
||
});
|
||
_scenario.SaveTarget(_taskId, new TargetConfig
|
||
{
|
||
WaveId = "default",
|
||
TargetType = (int)TargetType.Piston,
|
||
Quantity = 1,
|
||
TypicalSpeed = 600, // 高速对抗强风
|
||
});
|
||
_scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>());
|
||
_scenario.SaveCloudDispersal(_taskId, new CloudDispersal());
|
||
_scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||
new List<Waypoint>
|
||
{
|
||
new Waypoint { PosX = 0, PosY = 300, PosZ = 0, Speed = 600 },
|
||
new Waypoint { PosX = 5000, PosY = 300, PosZ = 0, Speed = 600 },
|
||
});
|
||
|
||
var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)),
|
||
new DamageModelRouter(), new TestPathProvider(_testDir), new DefensePlanner(TestData.Ammo, TestPlannerConfig.Instance));
|
||
engine.Initialize(_taskId);
|
||
engine.TimeScale = 4f;
|
||
|
||
for (int i = 0; i < 500; i++)
|
||
{
|
||
var r = engine.Tick(1f / 20f);
|
||
if (r.State == SimulationState.Completed) break;
|
||
}
|
||
engine.Stop();
|
||
|
||
// 强风下应偏移到东方(+X)
|
||
Assert.True(engine.Drones[0].PosX > 0);
|
||
}
|
||
|
||
[Fact]
|
||
public void LongRoute_CompletesWithoutError()
|
||
{
|
||
var task = _scenario.CreateTask("超长航路", "");
|
||
_taskId = task.Id;
|
||
|
||
_scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
|
||
_scenario.SaveTarget(_taskId, new TargetConfig
|
||
{
|
||
WaveId = "default",
|
||
TargetType = (int)TargetType.HighSpeed,
|
||
Quantity = 1,
|
||
TypicalSpeed = 500,
|
||
});
|
||
_scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>());
|
||
_scenario.SaveCloudDispersal(_taskId, new CloudDispersal());
|
||
|
||
// 500 km/h, 100km 航程
|
||
_scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||
new List<Waypoint>
|
||
{
|
||
new Waypoint { PosX = 0, PosY = 500, PosZ = 0, Speed = 500 },
|
||
new Waypoint { PosX = 100000, PosY = 500, PosZ = 0, Speed = 500 },
|
||
});
|
||
|
||
var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)),
|
||
new DamageModelRouter(), new TestPathProvider(_testDir), new DefensePlanner(TestData.Ammo, TestPlannerConfig.Instance));
|
||
engine.Initialize(_taskId);
|
||
engine.TimeScale = 4f;
|
||
|
||
for (int i = 0; i < 5000; i++)
|
||
{
|
||
var r = engine.Tick(1f / 20f);
|
||
if (r.State == SimulationState.Completed) break;
|
||
}
|
||
engine.Stop();
|
||
|
||
Assert.Equal(DroneStatus.ReachedTarget, engine.Drones[0].Status);
|
||
}
|
||
}
|
||
}
|