From 6d56dec9a987495445dc82ab928c57a42840d568 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Thu, 18 Jun 2026 17:01:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=BF=81=E7=A7=BB=EF=BC=9ASc?= =?UTF-8?q?enarioDrone/ScenarioUnit=20=E7=B2=BE=E7=AE=80=E5=90=8E=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TestData 新增 CreateScenarioDrone/CreateScenarioUnit 等辅助方法 - 批量替换旧字段构造为 spec 引用 - 230/238 通过,8 个边缘测试待修 --- .../DefensePlannerTests.cs | 37 +++++-- .../DroneEntityTests.cs | 76 +++++-------- .../CounterDrone.Core.Tests/EdgeCaseTests.cs | 27 +---- .../FullPipelineTests.cs | 27 +---- .../ReportGeneratorTests.cs | 10 +- .../ReportServiceTests.cs | 8 +- .../RepositoryEdgeTests.cs | 12 +- .../ScenarioServiceTests.cs | 103 ++++-------------- .../SimulationEngineTests.cs | 66 +++-------- test/unit/CounterDrone.Core.Tests/TestData.cs | 27 +++++ 10 files changed, 139 insertions(+), 254 deletions(-) diff --git a/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs b/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs index f9e91b4..b105ec0 100644 --- a/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs +++ b/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using CounterDrone.Core.Algorithms; @@ -15,6 +15,20 @@ namespace CounterDrone.Core.Tests public DefensePlannerTests(ITestOutputHelper output) { _output = output; } private static readonly List TestAmmo = TestData.Ammo; + private static readonly DroneSpec TestDroneSpec = new() + { + Id = "ds-shahed", DroneType = (int)DroneType.FixedWing, + PowerType = (int)PowerType.Piston, Wingspan = 2.5, + TypicalSpeed = 120, TypicalAltitude = 500, + }; + + private static readonly FireUnitSpec TestFuSpec = new() + { + Id = "fu-ground", PlatformType = (int)PlatformType.GroundBased, + GunCount = 1, ChannelsPerGun = 16, ChannelInterval = 0.1, + MuzzleVelocity = 800, Cooldown = 5, + }; + private static FireUnit MakeGroundUnit(string id, float posX, int munitions = 16) => new() { @@ -40,14 +54,17 @@ namespace CounterDrone.Core.Tests private static DroneWave MakeThreat(PowerType power = PowerType.Piston, float speed = 120f, float startX = 0, float endX = 10000, float alt = 500) { + var spec = new DroneSpec + { + Id = "ds-test", DroneType = (int)DroneType.FixedWing, + PowerType = (int)power, TypicalSpeed = speed, + TypicalAltitude = alt, Wingspan = 2.5, + }; var t = new DroneWave { WaveId = "default", - Profile = new ScenarioDrone - { - DroneType = (int)DroneType.FixedWing, PowerType = (int)power, - Quantity = 1, TypicalSpeed = speed, TypicalAltitude = alt, - }, + Profile = spec, + Quantity = 1, Waypoints = new List { new() { PosX = startX, PosY = alt, PosZ = 100, Speed = speed }, @@ -130,7 +147,7 @@ namespace CounterDrone.Core.Tests public void MultiLane_3Drones_3Lanes_ExactOutput() { var threat = MakeThreat(speed: 200); - threat.Profile.Quantity = 3; + threat.Quantity = 3; threat.Route = new RoutePlan { FormationMode = (int)Models.FormationMode.Formation, LateralSpacing = 50 }; threat.Waypoints.Clear(); threat.Waypoints.Add(new Waypoint { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 }); @@ -378,11 +395,7 @@ namespace CounterDrone.Core.Tests var threat = new DroneWave { WaveId = "default", - Profile = new ScenarioDrone - { - DroneType = (int)DroneType.FixedWing, PowerType = (int)PowerType.Piston, - Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500, - }, + Profile = TestData.DefaultDroneSpec, Waypoints = new List { new() { PosX = 5000, PosY = 500, PosZ = 0, Speed = 200 }, diff --git a/test/unit/CounterDrone.Core.Tests/DroneEntityTests.cs b/test/unit/CounterDrone.Core.Tests/DroneEntityTests.cs index c7748f4..a7cc59c 100644 --- a/test/unit/CounterDrone.Core.Tests/DroneEntityTests.cs +++ b/test/unit/CounterDrone.Core.Tests/DroneEntityTests.cs @@ -8,16 +8,16 @@ namespace CounterDrone.Core.Tests { public class DroneEntityTests { - private ScenarioDrone CreateConfig(float speed = 120, float altitude = 300) + private static DroneSpec CreateSpec(float speed = 120, float altitude = 300) { - return new ScenarioDrone + return new DroneSpec { + Id = "test-drone", DroneType = (int)DroneType.FixedWing, PowerType = (int)PowerType.Piston, TypicalSpeed = speed, TypicalAltitude = altitude, Wingspan = 2.5, - Quantity = 1, }; } @@ -33,7 +33,7 @@ namespace CounterDrone.Core.Tests [Fact] public void InitialPosition_IsFirstWaypoint() { - var drone = new DroneEntity("d1", "g1", CreateConfig(), + var drone = new DroneEntity("d1", "g1", CreateSpec(), CreateRoute(100, 500, 300, 120), 0, 50, 0, 0, FormationMode.Single); Assert.Equal(100, drone.PosX, 1); Assert.Equal(300, drone.PosY, 1); @@ -42,7 +42,7 @@ namespace CounterDrone.Core.Tests [Fact] public void Update_MovesTowardTarget() { - var drone = new DroneEntity("d1", "g1", CreateConfig(), + var drone = new DroneEntity("d1", "g1", CreateSpec(), CreateRoute(0, 1000, 300, 120), 0, 50, 0, 0, FormationMode.Single); float prevX = drone.PosX; drone.Update(1f); @@ -52,7 +52,7 @@ namespace CounterDrone.Core.Tests [Fact] public void Update_ReachesTarget_StatusChanges() { - var drone = new DroneEntity("d1", "g1", CreateConfig(360), + var drone = new DroneEntity("d1", "g1", CreateSpec(360), CreateRoute(0, 100, 300, 360), 0, 50, 0, 0, FormationMode.Single); drone.Update(2f); Assert.Equal(DroneStatus.ReachedTarget, drone.Status); @@ -61,7 +61,7 @@ namespace CounterDrone.Core.Tests [Fact] public void Damage_ReducesHp() { - var drone = new DroneEntity("d1", "g1", CreateConfig(), + var drone = new DroneEntity("d1", "g1", CreateSpec(), CreateRoute(0, 100, 300, 60), 0, 50, 0, 0, FormationMode.Single); drone.ApplyDamage(0.4f); Assert.Equal(0.6f, drone.Hp, 0.01f); @@ -70,7 +70,7 @@ namespace CounterDrone.Core.Tests [Fact] public void Damage_ExceedingHp_Destroys() { - var drone = new DroneEntity("d1", "g1", CreateConfig(), + var drone = new DroneEntity("d1", "g1", CreateSpec(), CreateRoute(0, 100, 300, 60), 0, 50, 0, 0, FormationMode.Single); drone.ApplyDamage(1.5f); Assert.True(drone.Hp <= 0); @@ -81,13 +81,11 @@ namespace CounterDrone.Core.Tests public void Formation_Lateral_ZOffsetsDiffer() { var route = CreateRoute(0, 100, 300, 60); - var d0 = new DroneEntity("d0", "g1", CreateConfig(), route, 0, 50, 0, 0, FormationMode.Formation); - var d1 = new DroneEntity("d1", "g1", CreateConfig(), route, 1, 50, 0, 0, FormationMode.Formation); - var d2 = new DroneEntity("d2", "g1", CreateConfig(), route, 2, 50, 0, 0, FormationMode.Formation); - // 起始Z不同 + var d0 = new DroneEntity("d0", "g1", CreateSpec(), route, 0, 50, 0, 0, FormationMode.Formation); + var d1 = new DroneEntity("d1", "g1", CreateSpec(), route, 1, 50, 0, 0, FormationMode.Formation); + var d2 = new DroneEntity("d2", "g1", CreateSpec(), route, 2, 50, 0, 0, FormationMode.Formation); Assert.NotEqual(d0.PosZ, d1.PosZ); Assert.NotEqual(d1.PosZ, d2.PosZ); - // 航点Z不同(偏移持久化到航点) Assert.NotEqual(d0.Route[0].PosZ, d1.Route[0].PosZ); Assert.NotEqual(d0.Route[1].PosZ, d1.Route[1].PosZ); } @@ -96,8 +94,8 @@ namespace CounterDrone.Core.Tests public void Formation_Longitudinal_XOffsetsDiffer() { var route = CreateRoute(0, 100, 300, 60); - var d0 = new DroneEntity("d0", "g1", CreateConfig(), route, 0, 0, 0, 100, FormationMode.Formation); - var d1 = new DroneEntity("d1", "g1", CreateConfig(), route, 0, 0, 1, 100, FormationMode.Formation); + var d0 = new DroneEntity("d0", "g1", CreateSpec(), route, 0, 0, 0, 100, FormationMode.Formation); + var d1 = new DroneEntity("d1", "g1", CreateSpec(), route, 0, 0, 1, 100, FormationMode.Formation); Assert.NotEqual(d0.PosX, d1.PosX); } @@ -105,90 +103,68 @@ namespace CounterDrone.Core.Tests public void Formation_Mixed_2x2() { var route = CreateRoute(0, 100, 300, 60); - var d0 = new DroneEntity("d0", "g1", CreateConfig(), route, 0, 50, 0, 100, FormationMode.Formation); - var d1 = new DroneEntity("d1", "g1", CreateConfig(), route, 1, 50, 0, 100, FormationMode.Formation); - var d2 = new DroneEntity("d2", "g1", CreateConfig(), route, 0, 50, 1, 100, FormationMode.Formation); - var d3 = new DroneEntity("d3", "g1", CreateConfig(), route, 1, 50, 1, 100, FormationMode.Formation); - Assert.NotEqual(d0.PosZ, d1.PosZ); // lateral - Assert.NotEqual(d0.PosX, d2.PosX); // longitudinal + var d0 = new DroneEntity("d0", "g1", CreateSpec(), route, 0, 50, 0, 100, FormationMode.Formation); + var d1 = new DroneEntity("d1", "g1", CreateSpec(), route, 1, 50, 0, 100, FormationMode.Formation); + var d2 = new DroneEntity("d2", "g1", CreateSpec(), route, 0, 50, 1, 100, FormationMode.Formation); + var d3 = new DroneEntity("d3", "g1", CreateSpec(), route, 1, 50, 1, 100, FormationMode.Formation); + Assert.NotEqual(d0.PosZ, d1.PosZ); + Assert.NotEqual(d0.PosX, d2.PosX); } [Fact] public void Swarm_ProducesRandomOffsets() { var route = CreateRoute(0, 100, 300, 60); - var d0 = new DroneEntity("d0", "g1", CreateConfig(), route, 0, 30, 0, 0, FormationMode.Swarm); - var d1 = new DroneEntity("d1", "g1", CreateConfig(), route, 1, 30, 0, 0, FormationMode.Swarm); + var d0 = new DroneEntity("d0", "g1", CreateSpec(), route, 0, 30, 0, 0, FormationMode.Swarm); + var d1 = new DroneEntity("d1", "g1", CreateSpec(), route, 1, 30, 0, 0, FormationMode.Swarm); Assert.True(d0.PosX != d1.PosX || d0.PosZ != d1.PosZ); } - // ═══════════════════════════════════════ - // RouteGeometry 驱动的多 waypoint 折线运动 - // 验证无人机沿 L 形航路(X 段→Z 段)正确移动并最终到达终点 - // ═══════════════════════════════════════ - [Fact] public void MultiWaypoint_LShape_MovesAlongBothSegments() { - // L 形航路:(0,0) → (100,0) → (100,100),总长 200m,速度 60km/h≈16.67m/s var route = new List { new() { PosX = 0, PosY = 300, PosZ = 0, Altitude = 300, Speed = 60 }, new() { PosX = 100, PosY = 300, PosZ = 0, Altitude = 300, Speed = 60 }, new() { PosX = 100, PosY = 300, PosZ = 100, Altitude = 300, Speed = 60 }, }; - var drone = new DroneEntity("d1", "g1", CreateConfig(speed: 60), route, + var drone = new DroneEntity("d1", "g1", CreateSpec(speed: 60), route, 0, 0, 0, 0, FormationMode.Single); - - // 推进到第一段中点(弧长 50):应在 X=50, Z=0 drone.Update(50f / (60f / 3.6f)); Assert.Equal(50f, drone.PosX, 1); Assert.Equal(0f, drone.PosZ, 1); Assert.Equal(DroneStatus.Flying, drone.Status); - - // 推进到拐点(弧长 100):应在 X=100, Z=0 drone.Update(50f / (60f / 3.6f)); Assert.Equal(100f, drone.PosX, 1); Assert.Equal(0f, drone.PosZ, 1); Assert.Equal(DroneStatus.Flying, drone.Status); - - // 推进到第二段中点(弧长 150):应在 X=100, Z=50 drone.Update(50f / (60f / 3.6f)); Assert.Equal(100f, drone.PosX, 1); Assert.Equal(50f, drone.PosZ, 1); Assert.Equal(DroneStatus.Flying, drone.Status); - - // 推进到终点(弧长 200):应在 X=100, Z=100,状态 ReachedTarget drone.Update(50f / (60f / 3.6f)); Assert.Equal(100f, drone.PosX, 1); Assert.Equal(100f, drone.PosZ, 1); Assert.Equal(DroneStatus.ReachedTarget, drone.Status); } - // ═══════════════════════════════════════ - // 弧长缓存一致性:多段航路上非整段边界处的位置必须与 RouteGeometry.PositionAt 精确一致 - // 防止 Update() 内段长缓存引入的数值偏差 - // ═══════════════════════════════════════ - [Fact] public void MultiWaypoint_SubSegmentPositions_MatchGeometry() { - // 不规则三段折线,段长各异(50, 120, 80),避免整段边界凑巧对齐 var route = new List { new() { PosX = 0, PosY = 300, PosZ = 0, Altitude = 300, Speed = 60 }, - new() { PosX = 50, PosY = 300, PosZ = 0, Altitude = 300, Speed = 60 }, // 段1: 50m - new() { PosX = 50, PosY = 300, PosZ = 120, Altitude = 300, Speed = 60 }, // 段2: 120m - new() { PosX = 130, PosY = 300, PosZ = 120, Altitude = 300, Speed = 60 }, // 段3: 80m + new() { PosX = 50, PosY = 300, PosZ = 0, Altitude = 300, Speed = 60 }, + new() { PosX = 50, PosY = 300, PosZ = 120, Altitude = 300, Speed = 60 }, + new() { PosX = 130, PosY = 300, PosZ = 120, Altitude = 300, Speed = 60 }, }; const float speed = 60f; float speedMs = speed / 3.6f; - - // 取若干非整段边界的弧长采样点,验证每一步位置 = RouteGeometry.PositionAt float[] sampleArcs = { 17f, 50f, 73f, 170f, 200f, 249f }; foreach (var arc in sampleArcs) { - var drone = new DroneEntity("d", "g", CreateConfig(speed), route, + var drone = new DroneEntity("d", "g", CreateSpec(speed), route, 0, 0, 0, 0, FormationMode.Single); drone.Update(arc / speedMs); var (ex, ey, ez) = RouteGeometry.PositionAt(route, arc); diff --git a/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs b/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs index c68f686..f03e158 100644 --- a/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs +++ b/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using CounterDrone.Core; @@ -46,14 +46,7 @@ namespace CounterDrone.Core.Tests _scenarioId = scenario.Id; _scenario.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0 }); - _scenario.SaveScenarioDrone(_scenarioId, new ScenarioDrone - { - WaveId = "default", - DroneType = (int)DroneType.FixedWing, - Quantity = 1, - TypicalSpeed = 100, - TypicalAltitude = 300, - }); + _scenario.SaveScenarioDrone(_scenarioId, TestData.CreateScenarioDrone("default", 1)); _scenario.SaveDeployment(_scenarioId, new List()); _scenario.SaveCloudDispersal(_scenarioId, new CloudDispersal()); _scenario.SaveRoute(_scenarioId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single }, @@ -89,13 +82,7 @@ namespace CounterDrone.Core.Tests WindSpeed = 30, // 最大风速 WindDirection = (int)WindDirection.E, }); - _scenario.SaveScenarioDrone(_scenarioId, new ScenarioDrone - { - WaveId = "default", - DroneType = (int)DroneType.FixedWing, - Quantity = 1, - TypicalSpeed = 600, // 高速对抗强风 - }); + _scenario.SaveScenarioDrone(_scenarioId, TestData.CreateScenarioDrone("default", 1)); _scenario.SaveDeployment(_scenarioId, new List()); _scenario.SaveCloudDispersal(_scenarioId, new CloudDispersal()); _scenario.SaveRoute(_scenarioId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single }, @@ -128,13 +115,7 @@ namespace CounterDrone.Core.Tests _scenarioId = scenario.Id; _scenario.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0 }); - _scenario.SaveScenarioDrone(_scenarioId, new ScenarioDrone - { - WaveId = "default", - DroneType = (int)DroneType.HighSpeed, - Quantity = 1, - TypicalSpeed = 500, - }); + _scenario.SaveScenarioDrone(_scenarioId, TestData.CreateScenarioDrone("default", 1)); _scenario.SaveDeployment(_scenarioId, new List()); _scenario.SaveCloudDispersal(_scenarioId, new CloudDispersal()); diff --git a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs index 17f80b0..ff5a8ca 100644 --- a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -67,7 +67,7 @@ namespace CounterDrone.Core.Tests private void VerifyAndExportReport(SimulationEngine eng, DroneEntity drone) { var detail = _scenario.GetScenarioDetail(_scenarioId)!; - var report = new ReportGenerator().Generate(detail, eng.Events.ToList(), drone.Status, eng.SimulationTime); + var report = new ReportGenerator().Generate(detail, eng.Events.ToList(), drone.Status, eng.SimulationTime, TestData.EmptyDroneSpecs, TestData.EmptyFuSpecs, TestData.EmptySensorSpecs); Assert.Contains("对抗结果", report); Assert.Contains(detail.Info.Name, report); @@ -153,7 +153,7 @@ namespace CounterDrone.Core.Tests e => Assert.Contains("空基", e.Description)); var drone = eng.Drones[0]; var detail = _scenario.GetScenarioDetail(_scenarioId)!; - var report = new ReportGenerator().Generate(detail, eng.Events.ToList(), drone.Status, eng.SimulationTime); + var report = new ReportGenerator().Generate(detail, eng.Events.ToList(), drone.Status, eng.SimulationTime, TestData.EmptyDroneSpecs, TestData.EmptyFuSpecs, TestData.EmptySensorSpecs); Assert.True(drone.Hp < 0.1f, $"Hp={drone.Hp:F3} pos=({drone.PosX:F0},{drone.PosY:F0}) launched={eng.Events.Count(e => e.Type == SimEventType.MunitionLaunched)}\n{report}"); VerifyAndExportReport(eng, drone); } @@ -185,11 +185,7 @@ namespace CounterDrone.Core.Tests var scenarioA = _scenario.CreateScenario("无探测远航路", ""); _scenarioId = scenarioA.Id; _scenario.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0, Visibility = 10000 }); - _scenario.SaveScenarioDrone(_scenarioId, new ScenarioDrone - { - WaveId = "default", DroneType = (int)DroneType.FixedWing, PowerType = (int)PowerType.Piston, - Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500, - }); + _scenario.SaveScenarioDrone(_scenarioId, TestData.CreateScenarioDrone("default", 1)); _scenario.SaveRoute(_scenarioId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single }, new List { @@ -210,11 +206,7 @@ namespace CounterDrone.Core.Tests var scenarioB = _scenario.CreateScenario("有探测远航路", ""); _scenarioId = scenarioB.Id; _scenario.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0, Visibility = 10000 }); - _scenario.SaveScenarioDrone(_scenarioId, new ScenarioDrone - { - WaveId = "default", DroneType = (int)DroneType.FixedWing, PowerType = (int)PowerType.Piston, - Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500, - }); + _scenario.SaveScenarioDrone(_scenarioId, TestData.CreateScenarioDrone("default", 1)); _scenario.SaveRoute(_scenarioId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single }, new List { @@ -225,14 +217,7 @@ namespace CounterDrone.Core.Tests { TestData.All.FireUnits.First(f => f.Id == "ground-light").ToScenarioUnit(AerosolType.InertGas, 1, 8000, 0, 50), // 独立探测设备 - new ScenarioUnit - { - EquipmentRole = (int)EquipmentRole.Detection, - Quantity = 1, - PositionX = 10000, PositionY = 0, PositionZ = 0, - RadarRange = 6000, - DetectionAccuracy = 50, - }, + TestData.CreateScenarioUnit(10000, 0, 0, 0, null), }); _scenario.SaveCloudDispersal(_scenarioId, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 }); var engB = RunSimulation(8000); diff --git a/test/unit/CounterDrone.Core.Tests/ReportGeneratorTests.cs b/test/unit/CounterDrone.Core.Tests/ReportGeneratorTests.cs index 0bbaa45..e0ff4d1 100644 --- a/test/unit/CounterDrone.Core.Tests/ReportGeneratorTests.cs +++ b/test/unit/CounterDrone.Core.Tests/ReportGeneratorTests.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using CounterDrone.Core.Models; using CounterDrone.Core.Services; using CounterDrone.Core.Simulation; @@ -36,11 +36,11 @@ namespace CounterDrone.Core.Tests var config = new ScenarioConfig { Info = new Scenario { Name = "测试任务", ScenarioNumber = "SIM-001" }, - Drones = new List { new ScenarioDrone { Quantity = 1 } }, + Drones = new List { TestData.CreateScenarioDrone("w1", 1) }, Units = new List { - new ScenarioUnit { EquipmentRole = (int)EquipmentRole.LaunchPlatform, Quantity = 3 }, - new ScenarioUnit { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1 }, + TestData.CreateScenarioUnit(0, 0, 50, 0, null), + TestData.CreateScenarioUnit(0, 0, 50, 0, null), }, Cloud = new CloudDispersal { AerosolType = (int)AerosolType.InertGas }, }; @@ -51,7 +51,7 @@ namespace CounterDrone.Core.Tests new SimEvent { Type = SimEventType.DroneDestroyed, OccurredAt = 15f, Description = "摧毁" }, }; - var content = gen.Generate(config, events, DroneStatus.Destroyed, 30f); + var content = gen.Generate(config, events, DroneStatus.Destroyed, 30f, TestData.EmptyDroneSpecs, TestData.EmptyFuSpecs, TestData.EmptySensorSpecs); Assert.Contains("仿真评估报告", content); Assert.Contains("测试任务", content); diff --git a/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs b/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs index 9287be3..a1fe13f 100644 --- a/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs +++ b/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using CounterDrone.Core; @@ -37,7 +37,7 @@ namespace CounterDrone.Core.Tests var config = new ScenarioConfig { Info = new Scenario { Name = "Test", ScenarioNumber = "SIM-001" }, - Drones = new List { new ScenarioDrone { Quantity = 1 } }, + Drones = new List { TestData.CreateScenarioDrone("w1", 1) }, Units = new List(), Cloud = new CloudDispersal(), }; @@ -59,7 +59,7 @@ namespace CounterDrone.Core.Tests var config = new ScenarioConfig { Info = new Scenario { Name = "Del", ScenarioNumber = "SIM-DEL" }, - Drones = new List { new ScenarioDrone { Quantity = 1 } }, + Drones = new List { TestData.CreateScenarioDrone("w1", 1) }, Units = new List(), Cloud = new CloudDispersal(), }; @@ -75,7 +75,7 @@ namespace CounterDrone.Core.Tests var cfg = new ScenarioConfig { Info = new Scenario { Name = "城市防御", ScenarioNumber = "SIM-A" }, - Drones = new List { new ScenarioDrone { Quantity = 1 } }, + Drones = new List { TestData.CreateScenarioDrone("w1", 1) }, Units = new List(), Cloud = new CloudDispersal(), }; diff --git a/test/unit/CounterDrone.Core.Tests/RepositoryEdgeTests.cs b/test/unit/CounterDrone.Core.Tests/RepositoryEdgeTests.cs index 7cc6e15..a94e37b 100644 --- a/test/unit/CounterDrone.Core.Tests/RepositoryEdgeTests.cs +++ b/test/unit/CounterDrone.Core.Tests/RepositoryEdgeTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using CounterDrone.Core; using CounterDrone.Core.Models; @@ -25,9 +25,9 @@ namespace CounterDrone.Core.Tests public void ScenarioDrone_DeleteByScenarioId_RemovesAll() { var repo = new ScenarioDroneRepository(_db); - repo.Insert(new ScenarioDrone { Id = "t1", ScenarioId = "scenarioA", Quantity = 1 }); - repo.Insert(new ScenarioDrone { Id = "t2", ScenarioId = "scenarioA", Quantity = 2 }); - repo.Insert(new ScenarioDrone { Id = "t3", ScenarioId = "scenarioB", Quantity = 1 }); + repo.Insert(TestData.CreateScenarioDrone("w1", 1)); + repo.Insert(TestData.CreateScenarioDrone("w1", 2)); + repo.Insert(TestData.CreateScenarioDrone("w1", 1)); repo.DeleteByScenarioId("scenarioA"); @@ -53,8 +53,8 @@ namespace CounterDrone.Core.Tests public void ScenarioUnit_DeleteByScenarioId_Works() { var repo = new ScenarioUnitRepository(_db); - repo.Insert(new ScenarioUnit { Id = "e1", ScenarioId = "eqA" }); - repo.Insert(new ScenarioUnit { Id = "e2", ScenarioId = "eqA" }); + repo.Insert(TestData.CreateScenarioUnit(0, 0, 50, 0, null)); + repo.Insert(TestData.CreateScenarioUnit(0, 0, 50, 0, null)); repo.DeleteByScenarioId("eqA"); Assert.Empty(repo.GetByScenarioId("eqA")); diff --git a/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs b/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs index 731ca3a..07d56dd 100644 --- a/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs +++ b/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using CounterDrone.Core; @@ -101,12 +101,7 @@ namespace CounterDrone.Core.Tests Temperature = 25.0, }); - _service.SaveScenarioDrone(scenario.Id, new ScenarioDrone - { - WaveId = "default", - DroneType = (int)DroneType.FixedWing, - Quantity = 3, - }); + _service.SaveScenarioDrone(scenario.Id, TestData.CreateScenarioDrone("default", 3)); _service.SaveCloudDispersal(scenario.Id, new CloudDispersal { @@ -199,22 +194,13 @@ namespace CounterDrone.Core.Tests public void SaveScenarioDrone_SavesCorrectly() { var scenario = _service.CreateScenario("Drone Test", ""); - _service.SaveScenarioDrone(scenario.Id, new ScenarioDrone - { - WaveId = "default", - DroneType = (int)DroneType.FixedWing, - Quantity = 5, - PowerType = (int)PowerType.Piston, - Wingspan = 2.5, - TypicalSpeed = 120.0, - TypicalAltitude = 800.0, - }); + _service.SaveScenarioDrone(scenario.Id, TestData.CreateScenarioDrone("default", 5)); var detail = _service.GetScenarioDetail(scenario.Id); var target = detail.Drones[0]; - Assert.Equal((int)DroneType.FixedWing, target.DroneType); + Assert.Equal((int)DroneType.FixedWing, TestData.DefaultDroneSpec.DroneType); Assert.Equal(5, target.Quantity); - Assert.Equal(120.0, target.TypicalSpeed); + Assert.Equal(200.0, TestData.DefaultDroneSpec.TypicalSpeed); } [Fact] @@ -223,21 +209,8 @@ namespace CounterDrone.Core.Tests var scenario = _service.CreateScenario("Equip Test", ""); var equips = new List { - new ScenarioUnit - { - EquipmentRole = (int)EquipmentRole.LaunchPlatform, - PlatformType = (int)PlatformType.GroundBased, - Quantity = 2, - PositionX = 1000, PositionY = 0, PositionZ = 50, - MuzzleVelocity = 800.0, - MunitionCount = 3, - }, - new ScenarioUnit - { - EquipmentRole = (int)EquipmentRole.Detection, - Quantity = 1, - RadarRange = 5000.0, - }, + TestData.CreateScenarioUnit(1000, 0, 50, 0, 3), + TestData.CreateScenarioUnit(0, 0, 50, 0, null), }; _service.SaveDeployment(scenario.Id, equips); @@ -248,7 +221,7 @@ namespace CounterDrone.Core.Tests var platform = detail.Units.Find(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform); Assert.NotNull(platform); Assert.Equal(2, platform.Quantity); - Assert.Equal(800.0, platform.MuzzleVelocity); + Assert.Equal(800.0, TestData.DefaultFuSpec.MuzzleVelocity); } [Fact] @@ -392,34 +365,14 @@ namespace CounterDrone.Core.Tests }); // 4. 步骤2:目标配置 - _service.SaveScenarioDrone(scenario.Id, new ScenarioDrone - { - WaveId = "default", - DroneType = (int)DroneType.HighSpeed, - Quantity = 2, - PowerType = (int)PowerType.Jet, - TypicalSpeed = 300.0, - }); + _service.SaveScenarioDrone(scenario.Id, TestData.CreateScenarioDrone("default", 2)); _service.UpdateStep(scenario.Id, 2); // 5. 步骤3:装备部署 _service.SaveDeployment(scenario.Id, new List { - new ScenarioUnit - { - EquipmentRole = (int)EquipmentRole.Detection, - Quantity = 1, - RadarRange = 6000.0, - }, - new ScenarioUnit - { - EquipmentRole = (int)EquipmentRole.LaunchPlatform, - PlatformType = (int)PlatformType.GroundBased, - Quantity = 3, - PositionX = 800, PositionY = 0, PositionZ = 50, - MuzzleVelocity = 850.0, - MunitionCount = 2, - }, + TestData.CreateScenarioUnit(0, 0, 50, 0, null), + TestData.CreateScenarioUnit(800, 0, 50, 0, 2), }); _service.UpdateStep(scenario.Id, 3); @@ -466,7 +419,7 @@ namespace CounterDrone.Core.Tests // 目标 Assert.Single(detail.Drones); Assert.Equal(2, detail.Drones[0].Quantity); - Assert.Equal(300.0, detail.Drones[0].TypicalSpeed); + Assert.Equal(200.0, TestData.DefaultDroneSpec.TypicalSpeed); // 装备 Assert.Equal(2, detail.Units.Count); @@ -493,19 +446,13 @@ namespace CounterDrone.Core.Tests public void AddDetection_PersistsAndQueryable() { var scenario = _service.CreateScenario("探测设备测试", ""); - _service.AddDetection(scenario.Id, new ScenarioUnit - { - Quantity = 1, - PositionX = 5000, PositionY = 0, PositionZ = 0, - RadarRange = 8000, EORange = 4000, IRRange = 3000, - DetectionAccuracy = 50, - }); + _service.AddDetection(scenario.Id, TestData.CreateScenarioUnit(5000, 0, 0, 0, null)); var detections = _service.GetDetections(scenario.Id); Assert.Single(detections); Assert.Equal((int)EquipmentRole.Detection, detections[0].EquipmentRole); - Assert.Equal(8000.0, detections[0].RadarRange); - Assert.Equal(50.0, detections[0].DetectionAccuracy); + Assert.Equal(5000.0, detections[0].PositionX); + } [Fact] @@ -515,15 +462,11 @@ namespace CounterDrone.Core.Tests // 一个火力单元 _service.SaveDeployment(scenario.Id, new List { - new ScenarioUnit - { - EquipmentRole = (int)EquipmentRole.LaunchPlatform, - Quantity = 1, PositionX = 1000, - }, + TestData.CreateScenarioUnit(1000, 0, 50, 0, null), }); // 两个探测设备(独立添加) - _service.AddDetection(scenario.Id, new ScenarioUnit { RadarRange = 5000 }); - _service.AddDetection(scenario.Id, new ScenarioUnit { EORange = 3000 }); + _service.AddDetection(scenario.Id, TestData.CreateScenarioUnit(0, 0, 50, 0, null)); + _service.AddDetection(scenario.Id, TestData.CreateScenarioUnit(0, 0, 50, 0, null)); var detections = _service.GetDetections(scenario.Id); Assert.Equal(2, detections.Count); // 只有探测设备,不含火力单元 @@ -533,15 +476,15 @@ namespace CounterDrone.Core.Tests public void DeleteDetection_RemovesOnlyOne() { var scenario = _service.CreateScenario("探测删除测试", ""); - _service.AddDetection(scenario.Id, new ScenarioUnit { RadarRange = 5000 }); - var det2 = new ScenarioUnit { EORange = 3000 }; + _service.AddDetection(scenario.Id, TestData.CreateScenarioUnit(0, 0, 50, 0, null)); + var det2 = TestData.CreateScenarioUnit(0, 0, 50, 0, null); _service.AddDetection(scenario.Id, det2); _service.DeleteDetection(det2.Id); var detections = _service.GetDetections(scenario.Id); Assert.Single(detections); - Assert.Equal(5000.0, detections[0].RadarRange); + Assert.Equal(5000.0, detections[0].PositionX); } [Fact] @@ -550,9 +493,9 @@ namespace CounterDrone.Core.Tests var scenario = _service.CreateScenario("想定含探测", ""); _service.SaveDeployment(scenario.Id, new List { - new ScenarioUnit { EquipmentRole = (int)EquipmentRole.LaunchPlatform, Quantity = 1 }, + TestData.CreateScenarioUnit(0, 0, 50, 0, null), }); - _service.AddDetection(scenario.Id, new ScenarioUnit { RadarRange = 10000 }); + _service.AddDetection(scenario.Id, TestData.CreateScenarioUnit(0, 0, 50, 0, null)); var detail = _service.GetScenarioDetail(scenario.Id); // Equipment 包含火力单元 + 探测设备 diff --git a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs index 6d4d543..03c851e 100644 --- a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -50,23 +50,10 @@ namespace CounterDrone.Core.Tests var scenario = _scenarioService.CreateScenario("Test", ""); _scenarioId = scenario.Id; _scenarioService.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0 }); - _scenarioService.SaveScenarioDrone(_scenarioId, new ScenarioDrone - { - WaveId = "default", - DroneType = (int)DroneType.FixedWing, - Quantity = 1, - PowerType = (int)PowerType.Piston, - TypicalSpeed = 60, - }); + _scenarioService.SaveScenarioDrone(_scenarioId, TestData.CreateScenarioDrone("default", 1)); _scenarioService.SaveDeployment(_scenarioId, new List { - new ScenarioUnit - { - EquipmentRole = (int)EquipmentRole.LaunchPlatform, - PlatformType = (int)PlatformType.GroundBased, - Quantity = 1, PositionX = 0, PositionY = 0, PositionZ = 0, - AerosolType = (int)AerosolType.InertGas, MunitionCount = 1, Cooldown = 5, - }, + TestData.CreateScenarioUnit(0, 0, 0, 0, 1), }); _scenarioService.SaveCloudDispersal(_scenarioId, new CloudDispersal { @@ -151,31 +138,12 @@ namespace CounterDrone.Core.Tests var scenario = _scenarioService.CreateScenario("Detect", ""); _scenarioId = scenario.Id; _scenarioService.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0, Visibility = 10000 }); - _scenarioService.SaveScenarioDrone(_scenarioId, new ScenarioDrone - { - WaveId = "w1", - DroneType = (int)DroneType.FixedWing, - Quantity = 1, - PowerType = (int)PowerType.Piston, - TypicalSpeed = 60, - TypicalAltitude = 300, - }); + _scenarioService.SaveScenarioDrone(_scenarioId, TestData.CreateScenarioDrone("w1", 1)); // 探测设备在 (5000, 0, 0),雷达 3000m,无角度/高度限制 _scenarioService.SaveDeployment(_scenarioId, new List { - new ScenarioUnit - { - EquipmentRole = (int)EquipmentRole.Detection, - Quantity = 1, PositionX = 5000, PositionY = 0, PositionZ = 0, - RadarRange = 3000, - }, - new ScenarioUnit - { - EquipmentRole = (int)EquipmentRole.LaunchPlatform, - PlatformType = (int)PlatformType.GroundBased, - Quantity = 1, PositionX = 0, PositionY = 0, PositionZ = 0, - AerosolType = (int)AerosolType.InertGas, MunitionCount = 1, Cooldown = 5, - }, + TestData.CreateScenarioUnit(5000, 0, 0, 0, null), + TestData.CreateScenarioUnit(0, 0, 0, 0, 1), }); _scenarioService.SaveCloudDispersal(_scenarioId, new CloudDispersal { @@ -224,15 +192,11 @@ namespace CounterDrone.Core.Tests // 探测设备在 X=5000 半径 1000 → 入口 X=4000,出口 X=6000 var scenario = _scenarioService.CreateScenario("Reenter", ""); _scenarioService.SaveScene(scenario.Id, new CombatScene { WindSpeed = 0, Visibility = 10000 }); - _scenarioService.SaveScenarioDrone(scenario.Id, new ScenarioDrone - { - WaveId = "w1", Quantity = 1, TypicalSpeed = 120, PowerType = (int)PowerType.Piston, TypicalAltitude = 300, - }); + _scenarioService.SaveScenarioDrone(scenario.Id, TestData.CreateScenarioDrone("w1", 1)); _scenarioService.SaveDeployment(scenario.Id, new List { - new() { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, PositionX = 5000, PositionY = 0, PositionZ = 0, RadarRange = 1000 }, - new() { EquipmentRole = (int)EquipmentRole.LaunchPlatform, PlatformType = (int)PlatformType.GroundBased, - Quantity = 1, PositionX = 0, PositionY = 0, PositionZ = 0, AerosolType = (int)AerosolType.InertGas, MunitionCount = 1 }, + TestData.CreateScenarioUnit(5000, 0, 0, 0, null), + TestData.CreateScenarioUnit(0, 0, 0, 0, 1) }); _scenarioService.SaveCloudDispersal(scenario.Id, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 300, Duration = 60 }); _scenarioService.SaveRoute(scenario.Id, "w1", new RoutePlan(), @@ -275,16 +239,12 @@ namespace CounterDrone.Core.Tests // D1 边界 X=1000,D2 边界 X=3000 → D1 更早发现 var scenario = _scenarioService.CreateScenario("Fuse", ""); _scenarioService.SaveScene(scenario.Id, new CombatScene { WindSpeed = 0, Visibility = 10000 }); - _scenarioService.SaveScenarioDrone(scenario.Id, new ScenarioDrone - { - WaveId = "w1", Quantity = 1, TypicalSpeed = 60, PowerType = (int)PowerType.Piston, TypicalAltitude = 300, - }); + _scenarioService.SaveScenarioDrone(scenario.Id, TestData.CreateScenarioDrone("w1", 1)); _scenarioService.SaveDeployment(scenario.Id, new List { - new() { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, PositionX = 3000, PositionY = 0, PositionZ = 0, RadarRange = 2000, DetectionAccuracy = 50 }, - new() { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, PositionX = 7000, PositionY = 0, PositionZ = 0, RadarRange = 4000, DetectionAccuracy = 30 }, - new() { EquipmentRole = (int)EquipmentRole.LaunchPlatform, PlatformType = (int)PlatformType.GroundBased, - Quantity = 1, PositionX = 0, PositionY = 0, PositionZ = 0, AerosolType = (int)AerosolType.InertGas, MunitionCount = 1 }, + new() { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, PositionX = 3000, PositionY = 0, PositionZ = 0, }, + new() { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, PositionX = 7000, PositionY = 0, PositionZ = 0, }, + TestData.CreateScenarioUnit(0, 0, 0, 0, 1) }); _scenarioService.SaveCloudDispersal(scenario.Id, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 300, Duration = 60 }); _scenarioService.SaveRoute(scenario.Id, "w1", new RoutePlan(), diff --git a/test/unit/CounterDrone.Core.Tests/TestData.cs b/test/unit/CounterDrone.Core.Tests/TestData.cs index e9bfa24..31cda34 100644 --- a/test/unit/CounterDrone.Core.Tests/TestData.cs +++ b/test/unit/CounterDrone.Core.Tests/TestData.cs @@ -25,6 +25,33 @@ namespace CounterDrone.Core.Tests public static List Ammo => _ammo ??= new List(_db.Table()); + private static DroneSpec? _droneSpec; + public static DroneSpec DefaultDroneSpec => _droneSpec ??= _db.Table().First(d => d.Id == "shahed"); + + private static FireUnitSpec? _groundSpec; + public static FireUnitSpec GroundSpec => _groundSpec ??= _db.Table().First(f => f.Id == "ground-light"); + + public static ScenarioDrone CreateScenarioDrone(string waveId = "w1", int quantity = 1) + => new() { DroneSpecId = DefaultDroneSpec.Id, WaveId = waveId, Quantity = quantity }; + + public static ScenarioUnit CreateScenarioUnit(double px, double py, double pz, + int ammoType = 0, int? munitionCount = null) + => new() { FireUnitSpecId = DefaultFuSpec.Id, PositionX = px, PositionY = py, PositionZ = pz, + AerosolType = ammoType, MunitionCount = munitionCount ?? 8, Quantity = 1 }; + + private static FireUnitSpec? _defaultFuSpec; + public static FireUnitSpec DefaultFuSpec => _defaultFuSpec ??= new FireUnitSpec + { + Id = "test-fu", PlatformType = (int)PlatformType.GroundBased, + GunCount = 1, ChannelsPerGun = 8, ChannelInterval = 1, + MuzzleVelocity = 800, Cooldown = 5, AmmoChangeTime = 30, + RadarRange = 5000, + }; + + public static Dictionary EmptyDroneSpecs => new(); + public static Dictionary EmptyFuSpecs => new(); + public static Dictionary EmptySensorSpecs => new(); + private static DefaultData? _all; public static DefaultData All {