geometry-tower-defense/Assets/Tests/EditMode/ProcedureMainServicesTests.cs

467 lines
20 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 GeometryTD.Definition;
using GeometryTD.Factory;
using GeometryTD.Procedure;
using GeometryTD.UI;
using NUnit.Framework;
namespace GeometryTD.Tests.EditMode
{
public sealed class ProcedureMainServicesTests
{
[Test]
public void TryAdvanceRun_Returns_AdvancedToNextNode_For_Normal_Completion()
{
RunState runState = CreateTwoNodeRun();
ProcedureMainRunAdvanceResult result = ProcedureMainRunFlowService.TryAdvanceRun(
runState,
RunNodeCompletionStatus.Completed,
new BackpackInventoryData { Gold = 88 });
Assert.That(result, Is.EqualTo(ProcedureMainRunAdvanceResult.AdvancedToNextNode));
Assert.That(runState.IsCompleted, Is.False);
Assert.That(runState.Nodes[0].Status, Is.EqualTo(RunNodeStatus.Completed));
Assert.That(runState.CurrentNodeIndex, Is.EqualTo(1));
Assert.That(runState.CurrentNode.Status, Is.EqualTo(RunNodeStatus.Available));
Assert.That(runState.RunInventorySnapshot.Gold, Is.EqualTo(88));
}
[Test]
public void TryAdvanceRun_Returns_NodeException_For_Exception_Fallback()
{
RunState runState = CreateTwoNodeRun();
ProcedureMainRunAdvanceResult result = ProcedureMainRunFlowService.TryAdvanceRun(
runState,
RunNodeCompletionStatus.Exception,
new BackpackInventoryData { Gold = 5 });
Assert.That(result, Is.EqualTo(ProcedureMainRunAdvanceResult.NodeException));
Assert.That(runState.IsCompleted, Is.False);
Assert.That(runState.CurrentNodeIndex, Is.EqualTo(0));
Assert.That(runState.CurrentNode.Status, Is.EqualTo(RunNodeStatus.Exception));
Assert.That(runState.Nodes[1].Status, Is.EqualTo(RunNodeStatus.Locked));
Assert.That(runState.RunInventorySnapshot.Gold, Is.EqualTo(5));
}
[Test]
public void TryAdvanceRun_Returns_RunCompleted_When_Last_Node_Finishes()
{
RunState runState = RunStateFactory.Create(
LevelThemeType.Plain,
new BackpackInventoryData { Gold = 30 },
new[]
{
new RunNodeSeed { NodeId = 901, NodeType = RunNodeType.BossCombat, LinkedLevelId = 4 }
});
ProcedureMainRunAdvanceResult result = ProcedureMainRunFlowService.TryAdvanceRun(
runState,
RunNodeCompletionStatus.Completed,
new BackpackInventoryData { Gold = 123 });
Assert.That(result, Is.EqualTo(ProcedureMainRunAdvanceResult.RunCompleted));
Assert.That(runState.IsCompleted, Is.True);
Assert.That(runState.CurrentNodeIndex, Is.EqualTo(1));
Assert.That(runState.CompletedNodeCount, Is.EqualTo(1));
Assert.That(runState.RunInventorySnapshot.Gold, Is.EqualTo(123));
}
[Test]
public void TryAdvanceRun_Returns_NoChange_When_Run_Cannot_Advance()
{
ProcedureMainRunAdvanceResult nullRunResult = ProcedureMainRunFlowService.TryAdvanceRun(
null,
RunNodeCompletionStatus.Completed,
new BackpackInventoryData { Gold = 1 });
Assert.That(nullRunResult, Is.EqualTo(ProcedureMainRunAdvanceResult.NoChange));
RunState completedRun = RunStateFactory.Create(
LevelThemeType.Plain,
new BackpackInventoryData { Gold = 10 },
new RunNodeSeed[0]);
ProcedureMainRunAdvanceResult completedRunResult = ProcedureMainRunFlowService.TryAdvanceRun(
completedRun,
RunNodeCompletionStatus.Completed,
new BackpackInventoryData { Gold = 20 });
Assert.That(completedRunResult, Is.EqualTo(ProcedureMainRunAdvanceResult.NoChange));
Assert.That(completedRun.RunInventorySnapshot.Gold, Is.EqualTo(10));
}
[Test]
public void TryAdvanceRun_Returns_NoChange_But_Still_Replaces_Snapshot_For_NonTerminal_Status()
{
RunState runState = CreateTwoNodeRun();
ProcedureMainRunAdvanceResult result = ProcedureMainRunFlowService.TryAdvanceRun(
runState,
RunNodeCompletionStatus.None,
new BackpackInventoryData { Gold = 999 });
Assert.That(result, Is.EqualTo(ProcedureMainRunAdvanceResult.NoChange));
Assert.That(runState.CurrentNodeIndex, Is.EqualTo(0));
Assert.That(runState.CurrentNode.Status, Is.EqualTo(RunNodeStatus.Available));
Assert.That(runState.RunInventorySnapshot.Gold, Is.EqualTo(999));
}
[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 MatchesCurrentNode_Returns_False_When_Run_Has_No_Current_Node()
{
RunState completedRun = RunStateFactory.Create(
LevelThemeType.Plain,
new BackpackInventoryData { Gold = 10 },
new RunNodeSeed[0]);
bool match = ProcedureMainNodeEventGuardService.MatchesCurrentNode(
completedRun,
1,
RunNodeType.Combat,
0);
Assert.That(match, Is.False);
}
[Test]
public void TryEnterCompletedPendingFinish_Shows_Dialog_Only_Once()
{
ProcedureMainRunCompletionResult firstResult =
ProcedureMainRunCompletionService.TryEnterCompletedPendingFinish(false);
ProcedureMainRunCompletionResult secondResult =
ProcedureMainRunCompletionService.TryEnterCompletedPendingFinish(true);
Assert.That(firstResult, Is.EqualTo(ProcedureMainRunCompletionResult.ShowCompletionDialog));
Assert.That(secondResult, Is.EqualTo(ProcedureMainRunCompletionResult.NoChange));
}
[Test]
public void TryConfirmReturnToMenu_Returns_Menu_Only_In_Completed_Pending_Finish()
{
ProcedureMainRunCompletionResult validResult =
ProcedureMainRunCompletionService.TryConfirmReturnToMenu(
ProcedureMainFlowPhase.RunCompletedPendingFinish,
false);
ProcedureMainRunCompletionResult hubResult =
ProcedureMainRunCompletionService.TryConfirmReturnToMenu(
ProcedureMainFlowPhase.Hub,
false);
ProcedureMainRunCompletionResult pendingResult =
ProcedureMainRunCompletionService.TryConfirmReturnToMenu(
ProcedureMainFlowPhase.RunCompletedPendingFinish,
true);
Assert.That(validResult, Is.EqualTo(ProcedureMainRunCompletionResult.ReturnToMenu));
Assert.That(hubResult, Is.EqualTo(ProcedureMainRunCompletionResult.NoChange));
Assert.That(pendingResult, Is.EqualTo(ProcedureMainRunCompletionResult.NoChange));
}
[Test]
public void ValidateCombatEntry_Returns_InventoryUnavailable_When_Inventory_Is_Null()
{
ProcedureMainCombatEntryValidationResult result =
ProcedureMainCombatEntryValidationService.Validate(null);
Assert.That(result.CanEnterCombat, Is.False);
Assert.That(result.BlockReason, Is.EqualTo(ProcedureMainCombatEntryBlockReason.InventoryUnavailable));
Assert.That(result.ValidationSummary, Is.Not.Null);
Assert.That(result.ValidationSummary.HasAnyValidParticipantTower, Is.False);
}
[Test]
public void ValidateCombatEntry_Returns_NoValidParticipantTower_When_ParticipantArea_Is_Empty()
{
BackpackInventoryData inventory = CreateCombatInventory();
inventory.ParticipantTowerInstanceIds.Clear();
ProcedureMainCombatEntryValidationResult result =
ProcedureMainCombatEntryValidationService.Validate(inventory);
Assert.That(result.CanEnterCombat, Is.False);
Assert.That(result.BlockReason, Is.EqualTo(ProcedureMainCombatEntryBlockReason.NoValidParticipantTower));
Assert.That(result.ValidationSummary.HasAnyValidParticipantTower, Is.False);
Assert.That(result.ValidationSummary.ValidTowers.Count, Is.EqualTo(0));
Assert.That(result.ValidationSummary.InvalidResults.Count, Is.EqualTo(0));
}
[Test]
public void ValidateCombatEntry_Returns_NoValidParticipantTower_When_AllParticipantTowers_Are_Invalid()
{
BackpackInventoryData inventory = CreateCombatInventory();
inventory.Towers[0].BaseComponentInstanceId = 99903;
ProcedureMainCombatEntryValidationResult result =
ProcedureMainCombatEntryValidationService.Validate(inventory);
Assert.That(result.CanEnterCombat, Is.False);
Assert.That(result.BlockReason, Is.EqualTo(ProcedureMainCombatEntryBlockReason.NoValidParticipantTower));
Assert.That(result.ValidationSummary.ValidTowers.Count, Is.EqualTo(0));
Assert.That(result.ValidationSummary.InvalidResults.Count, Is.EqualTo(1));
Assert.That(
result.ValidationSummary.InvalidResults[0].FailureReason,
Is.EqualTo(CombatParticipantTowerValidationFailureReason.MissingBaseComponent));
}
[Test]
public void ValidateCombatEntry_Returns_NoValidParticipantTower_When_AllParticipantTowers_Are_Broken()
{
BackpackInventoryData inventory = CreateCombatInventory();
inventory.MuzzleComponents[0].Endurance = 0f;
ProcedureMainCombatEntryValidationResult result =
ProcedureMainCombatEntryValidationService.Validate(inventory);
Assert.That(result.CanEnterCombat, Is.False);
Assert.That(result.BlockReason, Is.EqualTo(ProcedureMainCombatEntryBlockReason.NoValidParticipantTower));
Assert.That(result.ValidationSummary.ValidTowers.Count, Is.EqualTo(0));
Assert.That(result.ValidationSummary.InvalidResults.Count, Is.EqualTo(1));
Assert.That(
result.ValidationSummary.InvalidResults[0].FailureReason,
Is.EqualTo(CombatParticipantTowerValidationFailureReason.BrokenMuzzleComponent));
}
[Test]
public void ValidateCombatEntry_Returns_CanEnter_When_At_Least_One_ParticipantTower_Is_Valid()
{
BackpackInventoryData inventory = CreateCombatInventory();
inventory.Towers.Add(new TowerItemData
{
InstanceId = 90002,
Name = "非法塔",
MuzzleComponentInstanceId = 10001,
BearingComponentInstanceId = 20001,
BaseComponentInstanceId = 99903
});
inventory.ParticipantTowerInstanceIds.Add(90002);
ProcedureMainCombatEntryValidationResult result =
ProcedureMainCombatEntryValidationService.Validate(inventory);
Assert.That(result.CanEnterCombat, Is.True);
Assert.That(result.BlockReason, Is.EqualTo(ProcedureMainCombatEntryBlockReason.None));
Assert.That(result.ValidationSummary.ValidTowers.Count, Is.EqualTo(1));
Assert.That(result.ValidationSummary.ValidTowers[0].InstanceId, Is.EqualTo(90001));
Assert.That(result.ValidationSummary.InvalidResults.Count, Is.EqualTo(1));
}
[Test]
public void BuildInvalidParticipantTowerLog_Returns_None_When_InvalidResults_Are_Empty()
{
string log = ProcedureMainCombatEntryValidationService.BuildInvalidParticipantTowerLog(
new CombatParticipantTowerValidationSummary());
Assert.That(log, Is.EqualTo("none"));
}
[Test]
public void BuildInvalidParticipantTowerLog_Formats_TowerIds_And_Reasons()
{
CombatParticipantTowerValidationSummary summary = new CombatParticipantTowerValidationSummary
{
InvalidResults = new[]
{
new CombatParticipantTowerValidationResult
{
TowerInstanceId = 90002,
FailureReason = CombatParticipantTowerValidationFailureReason.MissingBaseComponent
},
new CombatParticipantTowerValidationResult
{
TowerInstanceId = 90003,
FailureReason = CombatParticipantTowerValidationFailureReason.MissingBearingComponent
}
}
};
string log = ProcedureMainCombatEntryValidationService.BuildInvalidParticipantTowerLog(summary);
Assert.That(log, Is.EqualTo("#90002:MissingBaseComponent, #90003:MissingBearingComponent"));
}
[Test]
public void BuildBlockedCombatDialogRawData_Returns_InventoryUnavailable_Message()
{
DialogFormRawData rawData = ProcedureMainCombatEntryValidationService.BuildBlockedCombatDialogRawData(
new ProcedureMainCombatEntryValidationResult
{
BlockReason = ProcedureMainCombatEntryBlockReason.InventoryUnavailable,
ValidationSummary = new CombatParticipantTowerValidationSummary()
});
Assert.That(rawData.Title, Is.EqualTo("无法进入战斗"));
Assert.That(rawData.Message, Is.EqualTo("当前无法读取库存快照,暂时不能进入战斗。请重新进入本轮流程后再试。"));
Assert.That(rawData.ConfirmText, Is.EqualTo("知道了"));
Assert.That(rawData.Mode, Is.EqualTo(1));
}
[Test]
public void BuildBlockedCombatDialogRawData_Returns_Generic_Message_When_NoParticipantTower_Is_Assigned()
{
DialogFormRawData rawData = ProcedureMainCombatEntryValidationService.BuildBlockedCombatDialogRawData(
new ProcedureMainCombatEntryValidationResult
{
BlockReason = ProcedureMainCombatEntryBlockReason.NoValidParticipantTower,
ValidationSummary = new CombatParticipantTowerValidationSummary()
});
Assert.That(
rawData.Message,
Is.EqualTo("参战区至少需要 1 座完整装配了枪口、轴承、底座的塔,才能进入战斗。"));
}
[Test]
public void BuildBlockedCombatDialogRawData_Lists_Invalid_Tower_Reasons()
{
DialogFormRawData rawData = ProcedureMainCombatEntryValidationService.BuildBlockedCombatDialogRawData(
new ProcedureMainCombatEntryValidationResult
{
BlockReason = ProcedureMainCombatEntryBlockReason.NoValidParticipantTower,
ValidationSummary = new CombatParticipantTowerValidationSummary
{
InvalidResults = new[]
{
new CombatParticipantTowerValidationResult
{
TowerInstanceId = 90002,
FailureReason = CombatParticipantTowerValidationFailureReason.MissingBaseComponent
},
new CombatParticipantTowerValidationResult
{
TowerInstanceId = 90003,
FailureReason = CombatParticipantTowerValidationFailureReason.MissingMuzzleComponent
}
}
}
});
Assert.That(
rawData.Message,
Is.EqualTo("参战区没有可出战的完整塔。\n塔 #90002 缺少底座组件。\n塔 #90003 缺少枪口组件。"));
}
[Test]
public void BuildBlockedCombatDialogRawData_Lists_Broken_Component_Reasons()
{
DialogFormRawData rawData = ProcedureMainCombatEntryValidationService.BuildBlockedCombatDialogRawData(
new ProcedureMainCombatEntryValidationResult
{
BlockReason = ProcedureMainCombatEntryBlockReason.NoValidParticipantTower,
ValidationSummary = new CombatParticipantTowerValidationSummary
{
InvalidResults = new[]
{
new CombatParticipantTowerValidationResult
{
TowerInstanceId = 90002,
FailureReason = CombatParticipantTowerValidationFailureReason.BrokenBaseComponent
},
new CombatParticipantTowerValidationResult
{
TowerInstanceId = 90003,
FailureReason = CombatParticipantTowerValidationFailureReason.BrokenMuzzleComponent
}
}
}
});
Assert.That(
rawData.Message,
Is.EqualTo("参战区没有可出战的完整塔。\n塔 #90002 底座组件耐久为 0无法参战。\n塔 #90003 枪口组件耐久为 0无法参战。"));
}
private static RunState CreateTwoNodeRun()
{
return RunStateFactory.Create(
LevelThemeType.Plain,
new BackpackInventoryData { Gold = 50 },
new[]
{
new RunNodeSeed { NodeId = 101, NodeType = RunNodeType.Combat, LinkedLevelId = 1 },
new RunNodeSeed { NodeId = 102, NodeType = RunNodeType.Event }
});
}
private static BackpackInventoryData CreateCombatInventory()
{
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
});
inventory.ParticipantTowerInstanceIds.Add(90001);
return inventory;
}
}
}