S3-01 + S3-02

This commit is contained in:
2026-03-09 17:50:49 +08:00
parent 793a87c171
commit 88641f17b0
19 changed files with 838 additions and 46 deletions
@@ -0,0 +1,199 @@
using System.Linq;
using GeometryTD.Definition;
using NUnit.Framework;
namespace GeometryTD.Tests.EditMode
{
public sealed class CombatParticipantTowerValidationServiceTests
{
[Test]
public void ValidateTower_Returns_Valid_When_Tower_And_Three_Components_Exist()
{
BackpackInventoryData inventory = CreateInventory();
CombatParticipantTowerValidationResult result =
CombatParticipantTowerValidationService.ValidateTower(inventory, 90001);
Assert.That(result.IsValid, Is.True);
Assert.That(result.TowerInstanceId, Is.EqualTo(90001));
Assert.That(result.FailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.None));
Assert.That(result.Tower, Is.Not.Null);
}
[Test]
public void ValidateTower_Returns_TowerMissing_When_Tower_Does_Not_Exist()
{
BackpackInventoryData inventory = CreateInventory();
CombatParticipantTowerValidationResult result =
CombatParticipantTowerValidationService.ValidateTower(inventory, 12345);
Assert.That(result.IsValid, Is.False);
Assert.That(result.TowerInstanceId, Is.EqualTo(12345));
Assert.That(result.FailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.TowerMissing));
}
[Test]
public void ValidateTower_Returns_MissingMuzzle_When_Muzzle_Is_Not_Resolvable()
{
BackpackInventoryData inventory = CreateInventory();
inventory.Towers[0].MuzzleComponentInstanceId = 99901;
CombatParticipantTowerValidationResult result =
CombatParticipantTowerValidationService.ValidateTower(inventory, inventory.Towers[0]);
Assert.That(result.IsValid, Is.False);
Assert.That(result.FailureReason,
Is.EqualTo(CombatParticipantTowerValidationFailureReason.MissingMuzzleComponent));
}
[Test]
public void ValidateTower_Returns_MissingBearing_When_Bearing_Is_Not_Resolvable()
{
BackpackInventoryData inventory = CreateInventory();
inventory.Towers[0].BearingComponentInstanceId = 99902;
CombatParticipantTowerValidationResult result =
CombatParticipantTowerValidationService.ValidateTower(inventory, inventory.Towers[0]);
Assert.That(result.IsValid, Is.False);
Assert.That(result.FailureReason,
Is.EqualTo(CombatParticipantTowerValidationFailureReason.MissingBearingComponent));
}
[Test]
public void ValidateTower_Returns_MissingBase_When_Base_Is_Not_Resolvable()
{
BackpackInventoryData inventory = CreateInventory();
inventory.Towers[0].BaseComponentInstanceId = 99903;
CombatParticipantTowerValidationResult result =
CombatParticipantTowerValidationService.ValidateTower(inventory, inventory.Towers[0]);
Assert.That(result.IsValid, Is.False);
Assert.That(result.FailureReason,
Is.EqualTo(CombatParticipantTowerValidationFailureReason.MissingBaseComponent));
}
[Test]
public void ValidateParticipantTowers_Splits_Valid_And_Invalid_Towers()
{
BackpackInventoryData inventory = CreateInventory();
inventory.Towers.Add(new TowerItemData
{
InstanceId = 90002,
Name = "非法塔",
MuzzleComponentInstanceId = 10001,
BearingComponentInstanceId = 20001,
BaseComponentInstanceId = 99903,
Stats = new TowerStatsData()
});
inventory.ParticipantTowerInstanceIds.Add(90002);
CombatParticipantTowerValidationSummary summary =
CombatParticipantTowerValidationService.ValidateParticipantTowers(inventory);
Assert.That(summary.HasAnyValidParticipantTower, Is.True);
Assert.That(summary.ValidTowers.Count, Is.EqualTo(1));
Assert.That(summary.ValidTowers[0].InstanceId, Is.EqualTo(90001));
Assert.That(summary.InvalidResults.Count, Is.EqualTo(1));
Assert.That(summary.InvalidResults[0].TowerInstanceId, Is.EqualTo(90002));
Assert.That(summary.InvalidResults[0].FailureReason,
Is.EqualTo(CombatParticipantTowerValidationFailureReason.MissingBaseComponent));
}
[Test]
public void ValidateParticipantTowers_Treats_Zero_Endurance_As_Valid_For_S3_01()
{
BackpackInventoryData inventory = CreateInventory();
inventory.MuzzleComponents[0].Endurance = 0f;
inventory.BearingComponents[0].Endurance = 0f;
inventory.BaseComponents[0].Endurance = 0f;
CombatParticipantTowerValidationSummary summary =
CombatParticipantTowerValidationService.ValidateParticipantTowers(inventory);
Assert.That(summary.HasAnyValidParticipantTower, Is.True);
Assert.That(summary.ValidTowers.Count, Is.EqualTo(1));
Assert.That(summary.InvalidResults.Count, Is.EqualTo(0));
}
[Test]
public void ValidateParticipantTowers_Does_Not_Require_TowerStats()
{
BackpackInventoryData inventory = CreateInventory();
inventory.Towers[0].Stats = null;
CombatParticipantTowerValidationResult result =
CombatParticipantTowerValidationService.ValidateTower(inventory, inventory.Towers[0]);
Assert.That(result.IsValid, Is.True);
Assert.That(result.FailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.None));
}
[Test]
public void ValidateParticipantTowers_Handles_Empty_And_Duplicate_Participant_Ids()
{
BackpackInventoryData emptyInventory = CreateInventory();
emptyInventory.ParticipantTowerInstanceIds.Clear();
CombatParticipantTowerValidationSummary emptySummary =
CombatParticipantTowerValidationService.ValidateParticipantTowers(emptyInventory);
Assert.That(emptySummary.HasAnyValidParticipantTower, Is.False);
Assert.That(emptySummary.ValidTowers.Count, Is.EqualTo(0));
Assert.That(emptySummary.InvalidResults.Count, Is.EqualTo(0));
BackpackInventoryData duplicateInventory = CreateInventory();
duplicateInventory.ParticipantTowerInstanceIds.Add(90001);
duplicateInventory.ParticipantTowerInstanceIds.Add(99999);
duplicateInventory.ParticipantTowerInstanceIds.Add(99999);
CombatParticipantTowerValidationSummary duplicateSummary =
CombatParticipantTowerValidationService.ValidateParticipantTowers(duplicateInventory);
Assert.That(duplicateSummary.ValidTowers.Count, Is.EqualTo(1));
Assert.That(duplicateSummary.InvalidResults.Count, Is.EqualTo(1));
Assert.That(duplicateSummary.InvalidResults.Single().TowerInstanceId, Is.EqualTo(99999));
Assert.That(duplicateSummary.InvalidResults.Single().FailureReason,
Is.EqualTo(CombatParticipantTowerValidationFailureReason.TowerMissing));
}
private static BackpackInventoryData CreateInventory()
{
BackpackInventoryData inventory = new BackpackInventoryData();
inventory.MuzzleComponents.Add(new MuzzleCompItemData
{
InstanceId = 10001,
Name = "枪口",
Endurance = 100f
});
inventory.BearingComponents.Add(new BearingCompItemData
{
InstanceId = 20001,
Name = "轴承",
Endurance = 100f
});
inventory.BaseComponents.Add(new BaseCompItemData
{
InstanceId = 30001,
Name = "底座",
Endurance = 100f
});
inventory.Towers.Add(new TowerItemData
{
InstanceId = 90001,
Name = "合法塔",
MuzzleComponentInstanceId = 10001,
BearingComponentInstanceId = 20001,
BaseComponentInstanceId = 30001,
Stats = new TowerStatsData()
});
inventory.ParticipantTowerInstanceIds.Add(90001);
return inventory;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8abdddb33ee9436aad59df649f03bc57
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,136 @@
using GeometryTD.CustomUtility;
using GeometryTD.Definition;
using NUnit.Framework;
namespace GeometryTD.Tests.EditMode
{
public sealed class ParticipantTowerAssignResultTests
{
[Test]
public void TryAddParticipantTower_Returns_Success_For_Legal_Tower()
{
BackpackInventoryData inventory = CreateValidInventory();
ParticipantTowerAssignResult result = InventoryParticipantUtility.TryAddParticipantTower(inventory, 90001, 4);
Assert.That(result, Is.Not.Null);
Assert.That(result.IsSuccess, Is.True);
Assert.That(result.FailureReason, Is.EqualTo(ParticipantTowerAssignFailureReason.None));
Assert.That(result.ValidationFailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.None));
Assert.That(inventory.ParticipantTowerInstanceIds.Count, Is.EqualTo(1));
Assert.That(inventory.ParticipantTowerInstanceIds[0], Is.EqualTo(90001));
Assert.That(inventory.Towers[0].IsParticipatingInCombat, Is.True);
}
[Test]
public void TryAddParticipantTower_Returns_TowerMissing_When_Tower_Is_Not_Found()
{
BackpackInventoryData inventory = CreateValidInventory();
ParticipantTowerAssignResult result = InventoryParticipantUtility.TryAddParticipantTower(inventory, 99999, 4);
Assert.That(result.IsSuccess, Is.False);
Assert.That(result.FailureReason, Is.EqualTo(ParticipantTowerAssignFailureReason.TowerMissing));
Assert.That(result.ValidationFailureReason,
Is.EqualTo(CombatParticipantTowerValidationFailureReason.TowerMissing));
Assert.That(inventory.ParticipantTowerInstanceIds.Count, Is.EqualTo(0));
}
[Test]
public void TryAddParticipantTower_Returns_InvalidTower_With_Missing_Component_Reason()
{
BackpackInventoryData inventory = CreateValidInventory();
inventory.Towers[0].BaseComponentInstanceId = 33333;
ParticipantTowerAssignResult result = InventoryParticipantUtility.TryAddParticipantTower(inventory, 90001, 4);
Assert.That(result.IsSuccess, Is.False);
Assert.That(result.FailureReason, Is.EqualTo(ParticipantTowerAssignFailureReason.InvalidTower));
Assert.That(result.ValidationFailureReason,
Is.EqualTo(CombatParticipantTowerValidationFailureReason.MissingBaseComponent));
Assert.That(inventory.ParticipantTowerInstanceIds.Count, Is.EqualTo(0));
}
[Test]
public void TryAddParticipantTower_Returns_AlreadyAssigned_Without_Duplicating_List()
{
BackpackInventoryData inventory = CreateValidInventory();
inventory.ParticipantTowerInstanceIds.Add(90001);
inventory.Towers[0].IsParticipatingInCombat = true;
ParticipantTowerAssignResult result = InventoryParticipantUtility.TryAddParticipantTower(inventory, 90001, 4);
Assert.That(result.IsSuccess, Is.False);
Assert.That(result.FailureReason, Is.EqualTo(ParticipantTowerAssignFailureReason.AlreadyAssigned));
Assert.That(result.ValidationFailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.None));
Assert.That(inventory.ParticipantTowerInstanceIds.Count, Is.EqualTo(1));
}
[Test]
public void TryAddParticipantTower_Returns_ParticipantAreaFull_When_MaxCount_Reached()
{
BackpackInventoryData inventory = CreateInventoryWithFourParticipantTowers();
ParticipantTowerAssignResult result = InventoryParticipantUtility.TryAddParticipantTower(inventory, 90005, 4);
Assert.That(result.IsSuccess, Is.False);
Assert.That(result.FailureReason, Is.EqualTo(ParticipantTowerAssignFailureReason.ParticipantAreaFull));
Assert.That(result.ValidationFailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.None));
Assert.That(inventory.ParticipantTowerInstanceIds.Count, Is.EqualTo(4));
Assert.That(inventory.Towers[4].IsParticipatingInCombat, Is.False);
}
private static BackpackInventoryData CreateValidInventory()
{
BackpackInventoryData inventory = new BackpackInventoryData();
inventory.MuzzleComponents.Add(new MuzzleCompItemData { InstanceId = 10001, Name = "枪口" });
inventory.BearingComponents.Add(new BearingCompItemData { InstanceId = 20001, Name = "轴承" });
inventory.BaseComponents.Add(new BaseCompItemData { InstanceId = 30001, Name = "底座" });
inventory.Towers.Add(new TowerItemData
{
InstanceId = 90001,
Name = "合法塔",
MuzzleComponentInstanceId = 10001,
BearingComponentInstanceId = 20001,
BaseComponentInstanceId = 30001,
Stats = new TowerStatsData()
});
return inventory;
}
private static BackpackInventoryData CreateInventoryWithFourParticipantTowers()
{
BackpackInventoryData inventory = new BackpackInventoryData();
for (int i = 0; i < 5; i++)
{
long towerId = 90001 + i;
long muzzleId = 10001 + i;
long bearingId = 20001 + i;
long baseId = 30001 + i;
inventory.MuzzleComponents.Add(new MuzzleCompItemData { InstanceId = muzzleId, Name = $"枪口-{i}" });
inventory.BearingComponents.Add(new BearingCompItemData { InstanceId = bearingId, Name = $"轴承-{i}" });
inventory.BaseComponents.Add(new BaseCompItemData { InstanceId = baseId, Name = $"底座-{i}" });
inventory.Towers.Add(new TowerItemData
{
InstanceId = towerId,
Name = $"塔-{i}",
MuzzleComponentInstanceId = muzzleId,
BearingComponentInstanceId = bearingId,
BaseComponentInstanceId = baseId,
Stats = new TowerStatsData(),
IsParticipatingInCombat = i < 4
});
if (i < 4)
{
inventory.ParticipantTowerInstanceIds.Add(towerId);
}
}
return inventory;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0053c263319b48eb930ef0d7794fb9dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -89,6 +89,52 @@ namespace GeometryTD.Tests.EditMode
Assert.That(completedRun.RunInventorySnapshot.Gold, Is.EqualTo(10));
}
[Test]
public void MatchesCurrentNode_Returns_True_For_Current_Node_And_Default_Wildcards()
{
RunState runState = CreateTwoNodeRun();
bool exactMatch = ProcedureMainNodeEventGuardService.MatchesCurrentNode(
runState,
101,
RunNodeType.Combat,
0);
bool wildcardMatch = ProcedureMainNodeEventGuardService.MatchesCurrentNode(
runState,
0,
RunNodeType.None,
-1);
Assert.That(exactMatch, Is.True);
Assert.That(wildcardMatch, Is.True);
}
[Test]
public void MatchesCurrentNode_Returns_False_For_Mismatched_Node_Event()
{
RunState runState = CreateTwoNodeRun();
bool nodeIdMismatch = ProcedureMainNodeEventGuardService.MatchesCurrentNode(
runState,
999,
RunNodeType.Combat,
0);
bool nodeTypeMismatch = ProcedureMainNodeEventGuardService.MatchesCurrentNode(
runState,
101,
RunNodeType.Shop,
0);
bool sequenceMismatch = ProcedureMainNodeEventGuardService.MatchesCurrentNode(
runState,
101,
RunNodeType.Combat,
1);
Assert.That(nodeIdMismatch, Is.False);
Assert.That(nodeTypeMismatch, Is.False);
Assert.That(sequenceMismatch, Is.False);
}
[Test]
public void TryEnterCompletedPendingFinish_Shows_Dialog_Only_Once()
{
+49 -1
View File
@@ -107,7 +107,7 @@ namespace GeometryTD.Tests.EditMode
}
[Test]
public void NodeCompleteEventArgs_Clones_Inventory_And_Clears_State()
public void NodeCompleteEventArgs_Completed_Win_Clones_Inventory_And_Clears_State()
{
BackpackInventoryData inventory = new BackpackInventoryData { Gold = 66 };
NodeCompleteEventArgs eventArgs = NodeCompleteEventArgs.Create(
@@ -140,6 +140,54 @@ namespace GeometryTD.Tests.EditMode
Assert.That(eventArgs.InventorySnapshotAfterNode, Is.Null);
}
[Test]
public void NodeCompleteEventArgs_Completed_Loss_Preserves_NonWin_Semantics()
{
BackpackInventoryData inventory = new BackpackInventoryData { Gold = 12 };
NodeCompleteEventArgs eventArgs = NodeCompleteEventArgs.Create(
"run-2",
8,
RunNodeType.Combat,
4,
RunNodeCompletionStatus.Completed,
false,
inventory);
inventory.Gold = 99;
Assert.That(eventArgs.RunId, Is.EqualTo("run-2"));
Assert.That(eventArgs.NodeId, Is.EqualTo(8));
Assert.That(eventArgs.NodeType, Is.EqualTo(RunNodeType.Combat));
Assert.That(eventArgs.SequenceIndex, Is.EqualTo(4));
Assert.That(eventArgs.CompletionStatus, Is.EqualTo(RunNodeCompletionStatus.Completed));
Assert.That(eventArgs.CombatWon, Is.False);
Assert.That(eventArgs.InventorySnapshotAfterNode.Gold, Is.EqualTo(12));
}
[Test]
public void NodeCompleteEventArgs_Exception_Preserves_Exception_Semantics()
{
BackpackInventoryData inventory = new BackpackInventoryData { Gold = 7 };
NodeCompleteEventArgs eventArgs = NodeCompleteEventArgs.Create(
"run-3",
9,
RunNodeType.BossCombat,
5,
RunNodeCompletionStatus.Exception,
false,
inventory);
inventory.Gold = 100;
Assert.That(eventArgs.RunId, Is.EqualTo("run-3"));
Assert.That(eventArgs.NodeId, Is.EqualTo(9));
Assert.That(eventArgs.NodeType, Is.EqualTo(RunNodeType.BossCombat));
Assert.That(eventArgs.SequenceIndex, Is.EqualTo(5));
Assert.That(eventArgs.CompletionStatus, Is.EqualTo(RunNodeCompletionStatus.Exception));
Assert.That(eventArgs.CombatWon, Is.False);
Assert.That(eventArgs.InventorySnapshotAfterNode.Gold, Is.EqualTo(7));
}
[Test]
public void FixedRunNodeSequenceBuilder_Builds_Plain_Ten_Node_Sequence()
{