|
@@ -12,6 +12,7 @@
|
|
|
#include "EzAblityTestTypes.h"
|
|
|
#include "GameplayTagsManager.h"
|
|
|
#include "Condition/EzAbilityCommonConditions.h"
|
|
|
+#include "EzAbilityTypes.h"
|
|
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(EzAbilityTest)
|
|
|
|
|
@@ -19,6 +20,8 @@
|
|
|
|
|
|
UE_DISABLE_OPTIMIZATION_SHIP
|
|
|
|
|
|
+std::atomic<int32> FEzAbilityTestConditionInstanceData::GlobalCounter = 0;
|
|
|
+
|
|
|
namespace UE::EzAbility::Tests
|
|
|
{
|
|
|
UEzAbility& NewAbility(UObject* Outer = GetTransientPackage())
|
|
@@ -56,8 +59,8 @@ namespace UE::EzAbility::Tests
|
|
|
virtual void AddTags() override
|
|
|
{
|
|
|
UGameplayTagsManager& Manager = UGameplayTagsManager::Get();
|
|
|
- TestTag = Manager.AddNativeGameplayTag(TEXT("Test.StateTree.Tag"));
|
|
|
- TestTag2 = Manager.AddNativeGameplayTag(TEXT("Test.StateTree.Tag2"));
|
|
|
+ TestTag = Manager.AddNativeGameplayTag(TEXT("Test.EzAbility.Tag"));
|
|
|
+ TestTag2 = Manager.AddNativeGameplayTag(TEXT("Test.EzAbility.Tag2"));
|
|
|
}
|
|
|
|
|
|
FORCEINLINE static const FNativeGameplayTags& Get()
|
|
@@ -159,5 +162,3344 @@ struct FEzAbilityTest_EmptyAbility : FAITestBase
|
|
|
|
|
|
IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_EmptyAbility, "System.EzAbility.EmptyAbility");
|
|
|
|
|
|
+struct FEzAbilityTest_Sequence : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& State1 = Root.AddChildState(FName(TEXT("State1")));
|
|
|
+ UEzAbilityState& State2 = Root.AddChildState(FName(TEXT("State2")));
|
|
|
+
|
|
|
+ auto& Task1 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+ State1.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::NextState);
|
|
|
+
|
|
|
+ auto& Task2 = State2.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task2")));
|
|
|
+ State2.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::Succeeded);
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);
|
|
|
+ AITEST_TRUE("EzAbility Task1 should enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+ AITEST_FALSE("EzAbility Task1 should not tick", Exec.Expect(Task1.GetName(), TickStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Task1 should tick, and exit state", Exec.Expect(Task1.GetName(), TickStr).Then(Task1.GetName(), ExitStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task2 should enter state", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+ AITEST_FALSE("EzAbility Task2 should not tick", Exec.Expect(Task2.GetName(), TickStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Task2 should tick, and exit state", Exec.Expect(Task2.GetName(), TickStr).Then(Task2.GetName(), ExitStateStr));
|
|
|
+ AITEST_FALSE("EzAbility Task1 should not tick", Exec.Expect(Task1.GetName(), TickStr));
|
|
|
+ AITEST_TRUE("EzAbility should be completed", Status == EAbilityRunStatus::Succeeded);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_FALSE("EzAbility Task1 should not tick", Exec.Expect(Task1.GetName(), TickStr));
|
|
|
+ AITEST_FALSE("EzAbility Task2 should not tick", Exec.Expect(Task2.GetName(), TickStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Sequence, "System.EzAbility.Sequence");
|
|
|
+
|
|
|
+struct FEzAbilityTest_Select : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& State1 = Root.AddChildState(FName(TEXT("State1")));
|
|
|
+ UEzAbilityState& State1A = State1.AddChildState(FName(TEXT("State1A")));
|
|
|
+
|
|
|
+ auto& TaskRoot = Root.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskRoot")));
|
|
|
+ TaskRoot.GetNode().TicksToCompletion = 3; // let Task1A to complete first
|
|
|
+
|
|
|
+ auto& Task1 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+ Task1.GetNode().TicksToCompletion = 3; // let Task1A to complete first
|
|
|
+
|
|
|
+ auto& Task1A = State1A.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1A")));
|
|
|
+ Task1A.GetNode().TicksToCompletion = 2;
|
|
|
+ State1A.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::GotoState, &State1);
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+
|
|
|
+ // Start and enter state
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility TaskRoot should enter state", Exec.Expect(TaskRoot.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task1 should enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task1A should enter state", Exec.Expect(Task1A.GetName(), EnterStateStr));
|
|
|
+ AITEST_FALSE("EzAbility TaskRoot should not tick", Exec.Expect(TaskRoot.GetName(), TickStr));
|
|
|
+ AITEST_FALSE("EzAbility Task1 should not tick", Exec.Expect(Task1.GetName(), TickStr));
|
|
|
+ AITEST_FALSE("EzAbility Task1A should not tick", Exec.Expect(Task1A.GetName(), TickStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Regular tick, no state selection at all.
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility tasks should update in order", Exec.Expect(TaskRoot.GetName(), TickStr).Then(Task1.GetName(), TickStr).Then(Task1A.GetName(), TickStr));
|
|
|
+ AITEST_FALSE("EzAbility TaskRoot should not EnterState", Exec.Expect(TaskRoot.GetName(), EnterStateStr));
|
|
|
+ AITEST_FALSE("EzAbility Task1 should not EnterState", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+ AITEST_FALSE("EzAbility Task1A should not EnterState", Exec.Expect(Task1A.GetName(), EnterStateStr));
|
|
|
+ AITEST_FALSE("EzAbility TaskRoot should not ExitState", Exec.Expect(TaskRoot.GetName(), ExitStateStr));
|
|
|
+ AITEST_FALSE("EzAbility Task1 should not ExitState", Exec.Expect(Task1.GetName(), ExitStateStr));
|
|
|
+ AITEST_FALSE("EzAbility Task1A should not ExitState", Exec.Expect(Task1A.GetName(), ExitStateStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Partial reselect, Root should not get EnterState
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_FALSE("EzAbility TaskRoot should not enter state", Exec.Expect(TaskRoot.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task1 should tick, exit state, and enter state", Exec.Expect(Task1.GetName(), TickStr).Then(Task1.GetName(), ExitStateStr).Then(Task1.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task1A should tick, exit state, and enter state", Exec.Expect(Task1A.GetName(), TickStr).Then(Task1A.GetName(), ExitStateStr).Then(Task1A.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Select, "System.EzAbility.Select");
|
|
|
+
|
|
|
+
|
|
|
+struct FEzAbilityTest_FailEnterState : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& State1 = Root.AddChildState(FName(TEXT("State1")));
|
|
|
+ UEzAbilityState& State1A = State1.AddChildState(FName(TEXT("State1A")));
|
|
|
+
|
|
|
+ auto& TaskRoot = Root.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskRoot")));
|
|
|
+
|
|
|
+ auto& Task1 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+ auto& Task2 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task2")));
|
|
|
+ Task2.GetNode().EnterStateResult = EAbilityRunStatus::Failed;
|
|
|
+ auto& Task3 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task3")));
|
|
|
+
|
|
|
+ auto& Task1A = State1A.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1A")));
|
|
|
+ State1A.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::GotoState, &State1);
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+ const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+
|
|
|
+ // Start and enter state
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility TaskRoot should enter state", Exec.Expect(TaskRoot.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task1 should enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task2 should enter state", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+ AITEST_FALSE("EzAbility Task3 should not enter state", Exec.Expect(Task3.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Should execute StateCompleted in reverse order", Exec.Expect(Task2.GetName(), StateCompletedStr).Then(Task1.GetName(), StateCompletedStr).Then(TaskRoot.GetName(), StateCompletedStr));
|
|
|
+ AITEST_FALSE("EzAbility Task3 should not state complete", Exec.Expect(Task3.GetName(), StateCompletedStr));
|
|
|
+ AITEST_TRUE("EzAbility exec status should be failed", Exec.GetLastTickStatus() == EAbilityRunStatus::Failed);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Stop and exit state
|
|
|
+ Status = Exec.Stop();
|
|
|
+ AITEST_TRUE("EzAbility TaskRoot should exit state", Exec.Expect(TaskRoot.GetName(), ExitStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task1 should exit state", Exec.Expect(Task1.GetName(), ExitStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task2 should exit state", Exec.Expect(Task2.GetName(), ExitStateStr));
|
|
|
+ AITEST_FALSE("EzAbility Task3 should not exit state", Exec.Expect(Task3.GetName(), ExitStateStr));
|
|
|
+ AITEST_TRUE("EzAbility status should be stopped", Status == EAbilityRunStatus::Stopped);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_FailEnterState, "System.EzAbility.FailEnterState");
|
|
|
+
|
|
|
+
|
|
|
+struct FEzAbilityTest_Restart : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& State1 = Root.AddChildState(FName(TEXT("State1")));
|
|
|
+
|
|
|
+ auto& Task1 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+ Task1.GetNode().TicksToCompletion = 2;
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+ const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+
|
|
|
+ // Start and enter state
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Task1 should enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility exec status should be running", Exec.GetLastTickStatus() == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Tick
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility exec status should be running", Exec.GetLastTickStatus() == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Call Start again, should stop and start the tree.
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Task1 should exit state", Exec.Expect(Task1.GetName(), ExitStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task1 should enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility exec status should be running", Exec.GetLastTickStatus() == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Restart, "System.EzAbility.Restart");
|
|
|
+
|
|
|
+struct FEzAbilityTest_SubTree : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& State1 = Root.AddChildState(FName(TEXT("State1")), EEzAbilityStateType::Linked);
|
|
|
+ UEzAbilityState& State2 = Root.AddChildState(FName(TEXT("State2")));
|
|
|
+ UEzAbilityState& State3 = Root.AddChildState(FName(TEXT("State3")), EEzAbilityStateType::Subtree);
|
|
|
+ UEzAbilityState& State3A = State3.AddChildState(FName(TEXT("State3A")));
|
|
|
+ UEzAbilityState& State3B = State3.AddChildState(FName(TEXT("State3B")));
|
|
|
+
|
|
|
+ State1.LinkedSubtree = State3.GetLinkToState();
|
|
|
+
|
|
|
+ State1.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::GotoState, &State2);
|
|
|
+
|
|
|
+ auto& Task2 = State2.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task2")));
|
|
|
+ State2.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::Succeeded);
|
|
|
+
|
|
|
+ auto& Task3A = State3A.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task3A")));
|
|
|
+ State3A.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::GotoState, &State3B);
|
|
|
+
|
|
|
+ auto& Task3B = State3B.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task3B")));
|
|
|
+ State3B.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::Succeeded);
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+ const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+
|
|
|
+ // Start and enter state
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility Active States should be in Root/State1/State3/State3A", Exec.ExpectInActiveStates(Root.Name, State1.Name, State3.Name, State3A.Name));
|
|
|
+ AITEST_FALSE("EzAbility Task2 should not enter state", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task3A should enter state", Exec.Expect(Task3A.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Transition within subtree
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Active States should be in Root/State1/State3/State3B", Exec.ExpectInActiveStates(Root.Name, State1.Name, State3.Name, State3B.Name));
|
|
|
+ AITEST_TRUE("EzAbility Task3B should enter state", Exec.Expect(Task3B.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Complete subtree
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Active States should be in Root/State2", Exec.ExpectInActiveStates(Root.Name, State2.Name));
|
|
|
+ AITEST_TRUE("EzAbility Task2 should enter state", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Complete the whole tree
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility should complete in succeeded", Status == EAbilityRunStatus::Succeeded);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_SubTree, "System.EzAbility.SubTree");
|
|
|
+
|
|
|
+struct FEzAbilityTest_SubTreeCondition : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ /*
|
|
|
+ - Root
|
|
|
+ - Linked : Subtree -> Root
|
|
|
+ - SubTree : Task1
|
|
|
+ - ? State1 : Task2 -> Succeeded // condition linked to Task1
|
|
|
+ - State2 : Task3
|
|
|
+ */
|
|
|
+
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& Linked = Root.AddChildState(FName(TEXT("Linked")), EEzAbilityStateType::Linked);
|
|
|
+
|
|
|
+ UEzAbilityState& SubTree = Root.AddChildState(FName(TEXT("SubTree")), EEzAbilityStateType::Subtree);
|
|
|
+ UEzAbilityState& State1 = SubTree.AddChildState(FName(TEXT("State1")));
|
|
|
+ UEzAbilityState& State2 = SubTree.AddChildState(FName(TEXT("State2")));
|
|
|
+
|
|
|
+ Linked.LinkedSubtree = SubTree.GetLinkToState();
|
|
|
+
|
|
|
+ Linked.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::GotoState, &Root);
|
|
|
+
|
|
|
+ // SubTask should not complete during the test.
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& SubTask = SubTree.AddTask<FAbilityTestTask_Stand>(FName(TEXT("SubTask")));
|
|
|
+ SubTask.GetNode().TicksToCompletion = 100;
|
|
|
+
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task1 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+ Task1.GetNode().TicksToCompletion = 1;
|
|
|
+
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task2 = State2.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task2")));
|
|
|
+ Task2.GetNode().TicksToCompletion = 1;
|
|
|
+
|
|
|
+ // Allow to enter State1 if Task1 instance data TicksToCompletion > 0.
|
|
|
+ TEzAbilityEditorNode<FAbilityCompareIntCondition>& IntCond1 = State1.AddEnterCondition<FAbilityCompareIntCondition>(EGenericCheck::Greater);
|
|
|
+ EditorData.AddPropertyBinding(SubTask, TEXT("CurrentTick"), IntCond1, TEXT("Left"));
|
|
|
+ IntCond1.GetInstanceData().Right = 0;
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+ const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+
|
|
|
+ // Start and enter state
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility Active States should be in Root/Linked/SubTree/State2", Exec.ExpectInActiveStates(Root.Name, Linked.Name, SubTree.Name, State2.Name));
|
|
|
+ AITEST_FALSE("EzAbility State1 should not be active", Exec.ExpectInActiveStates(State1.Name)); // Enter condition should prevent to enter State1
|
|
|
+ AITEST_TRUE("EzAbility SubTask should enter state", Exec.Expect(SubTask.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task2 should enter state", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Task1 completes, and we should enter State1 since the enter condition now passes.
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Active States should be in Root/Linked/SubTree/State1", Exec.ExpectInActiveStates(Root.Name, Linked.Name, SubTree.Name, State1.Name));
|
|
|
+ AITEST_FALSE("EzAbility State2 should not be active", Exec.ExpectInActiveStates(State2.Name));
|
|
|
+ AITEST_TRUE("EzAbility Task1 should enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_SubTreeCondition, "System.EzAbility.SubTreeCondition");
|
|
|
+
|
|
|
+struct FEzAbilityTest_SubTree_CascadedSucceeded : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ // - Root [TaskA]
|
|
|
+ // - LinkedState>SubTreeState -> (F)Failed
|
|
|
+ // - SubTreeState [TaskB]
|
|
|
+ // - SubLinkedState>SubSubTreeState -> (S)Failed
|
|
|
+ // - SubSubTreeState
|
|
|
+ // - SubSubLeaf [TaskC] -> (S)Succeeded
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& LinkedState = Root.AddChildState(FName(TEXT("Linked")), EEzAbilityStateType::Linked);
|
|
|
+
|
|
|
+ UEzAbilityState& SubTreeState = Root.AddChildState(FName(TEXT("SubTreeState")), EEzAbilityStateType::Subtree);
|
|
|
+ UEzAbilityState& SubLinkedState = SubTreeState.AddChildState(FName(TEXT("SubLinkedState")), EEzAbilityStateType::Linked);
|
|
|
+
|
|
|
+ UEzAbilityState& SubSubTreeState = Root.AddChildState(FName(TEXT("SubSubTreeState")), EEzAbilityStateType::Subtree);
|
|
|
+ UEzAbilityState& SubSubLeaf = SubSubTreeState.AddChildState(FName(TEXT("SubSubLeaf")));
|
|
|
+
|
|
|
+ LinkedState.LinkedSubtree = SubTreeState.GetLinkToState();
|
|
|
+ SubLinkedState.LinkedSubtree = SubSubTreeState.GetLinkToState();
|
|
|
+
|
|
|
+ LinkedState.AddTransition(EEzAbilityTransitionTrigger::OnStateFailed, EEzAbilityTransitionType::Failed);
|
|
|
+ SubLinkedState.AddTransition(EEzAbilityTransitionTrigger::OnStateSucceeded, EEzAbilityTransitionType::Failed);
|
|
|
+ SubSubLeaf.AddTransition(EEzAbilityTransitionTrigger::OnStateSucceeded, EEzAbilityTransitionType::Succeeded);
|
|
|
+
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& TaskA = Root.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskA")));
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& TaskB = SubTreeState.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskB")));
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& TaskC = SubSubLeaf.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskC")));
|
|
|
+
|
|
|
+ TaskA.GetNode().TicksToCompletion = 2;
|
|
|
+ TaskB.GetNode().TicksToCompletion = 2;
|
|
|
+ TaskC.GetNode().TicksToCompletion = 1; // The deepest task completes first.
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+ const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+
|
|
|
+ // Start and enter state
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Active States should be in Root/Linked/SubTreeState", Exec.ExpectInActiveStates(Root.Name, LinkedState.Name, SubTreeState.Name, SubLinkedState.Name, SubSubTreeState.Name, SubSubLeaf.Name));
|
|
|
+ AITEST_TRUE("TaskA,B,C should enter state", Exec.Expect(TaskA.GetName(), EnterStateStr).Then(TaskB.GetName(), EnterStateStr).Then(TaskC.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Subtrees completes, and it completes the whole tree too.
|
|
|
+ // There's no good way to observe this externally. We switch the return along the way to make sure the transition does not happen directly from the leaf to failed.
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility should be Failed", Status == EAbilityRunStatus::Failed);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_SubTree_CascadedSucceeded, "System.EzAbility.SubTree.CascadedSucceeded");
|
|
|
+
|
|
|
+
|
|
|
+struct FEzAbilityTest_SharedInstanceData : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ auto& IntCond = Root.AddEnterCondition<FEzAbilityTestCondition>();
|
|
|
+ IntCond.GetInstanceData().Count = 1;
|
|
|
+
|
|
|
+ auto& Task = Root.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task")));
|
|
|
+ Task.GetNode().TicksToCompletion = 2;
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ // Init, nothing should access the shared data.
|
|
|
+ constexpr int32 NumConcurrent = 100;
|
|
|
+ FEzAbilityTestConditionInstanceData::GlobalCounter = 0;
|
|
|
+
|
|
|
+ bool bInitSucceeded = true;
|
|
|
+ TArray<FEzAbilityInstanceData> InstanceDatas;
|
|
|
+
|
|
|
+ InstanceDatas.SetNum(NumConcurrent);
|
|
|
+ for (int32 Index = 0; Index < NumConcurrent; Index++)
|
|
|
+ {
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceDatas[Index]);
|
|
|
+ bInitSucceeded &= Exec.IsValid();
|
|
|
+ }
|
|
|
+ AITEST_TRUE("All EzAbility contexts should init", bInitSucceeded);
|
|
|
+ AITEST_EQUAL("Test condition global counter should be 0", (int32)FEzAbilityTestConditionInstanceData::GlobalCounter, 0);
|
|
|
+
|
|
|
+ // Start in parallel
|
|
|
+ // This should create shared data per thread.
|
|
|
+ // We expect that ParallelForWithTaskContext() creates a context per thread.
|
|
|
+ TArray<FEzAbilityTestRunContext> RunContexts;
|
|
|
+
|
|
|
+ ParallelForWithTaskContext(
|
|
|
+ RunContexts,
|
|
|
+ InstanceDatas.Num(),
|
|
|
+ [&InstanceDatas, &EzAbility](FEzAbilityTestRunContext& RunContext, int32 Index)
|
|
|
+ {
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceDatas[Index]);
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ const EAbilityRunStatus Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ if (Status == EAbilityRunStatus::Running)
|
|
|
+ {
|
|
|
+ RunContext.Count++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ int32 StartTotalRunning = 0;
|
|
|
+ for (FEzAbilityTestRunContext RunContext : RunContexts)
|
|
|
+ {
|
|
|
+ StartTotalRunning += RunContext.Count;
|
|
|
+ }
|
|
|
+ AITEST_EQUAL("All EzAbility contexts should be running after Start", StartTotalRunning, NumConcurrent);
|
|
|
+ AITEST_EQUAL("Test condition global counter should equal context count after Start", (int32)FEzAbilityTestConditionInstanceData::GlobalCounter, InstanceDatas.Num());
|
|
|
+
|
|
|
+
|
|
|
+ // Tick in parallel
|
|
|
+ // This should not recreate the data, so FEzAbilityTestConditionInstanceData::GlobalCounter should stay as is.
|
|
|
+ for (FEzAbilityTestRunContext RunContext : RunContexts)
|
|
|
+ {
|
|
|
+ RunContext.Count = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ ParallelForWithTaskContext(
|
|
|
+ RunContexts,
|
|
|
+ InstanceDatas.Num(),
|
|
|
+ [&InstanceDatas, &EzAbility](FEzAbilityTestRunContext& RunContext, int32 Index)
|
|
|
+ {
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceDatas[Index]);
|
|
|
+ const EAbilityRunStatus Status = Exec.Tick(0.1f);
|
|
|
+ if (Status == EAbilityRunStatus::Running)
|
|
|
+ {
|
|
|
+ RunContext.Count++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ int32 TickTotalRunning = 0;
|
|
|
+ for (FEzAbilityTestRunContext RunContext : RunContexts)
|
|
|
+ {
|
|
|
+ TickTotalRunning += RunContext.Count;
|
|
|
+ }
|
|
|
+ AITEST_EQUAL("All EzAbility contexts should be running after Tick", TickTotalRunning, NumConcurrent);
|
|
|
+ AITEST_EQUAL("Test condition global counter should equal context count after Tick", (int32)FEzAbilityTestConditionInstanceData::GlobalCounter, InstanceDatas.Num());
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_SharedInstanceData, "System.EzAbility.SharedInstanceData");
|
|
|
+
|
|
|
+struct FEzAbilityTest_TransitionPriority : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ /*
|
|
|
+ - Root
|
|
|
+ - State1 : Task1 -> Succeeded
|
|
|
+ - State1A : Task1A -> Next
|
|
|
+ - State1B : Task1B -> Next
|
|
|
+ - State1C : Task1C
|
|
|
+
|
|
|
+ Task1A completed first, transitioning to State1B.
|
|
|
+ Task1, Task1B, and Task1C complete at the same time, we should take the transition on the first completed state (State1).
|
|
|
+ */
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& State1 = Root.AddChildState(FName(TEXT("State1")));
|
|
|
+ UEzAbilityState& State1A = State1.AddChildState(FName(TEXT("State1A")));
|
|
|
+ UEzAbilityState& State1B = State1.AddChildState(FName(TEXT("State1B")));
|
|
|
+ UEzAbilityState& State1C = State1.AddChildState(FName(TEXT("State1C")));
|
|
|
+
|
|
|
+ auto& Task1 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+ Task1.GetNode().TicksToCompletion = 2;
|
|
|
+ State1.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::Succeeded);
|
|
|
+
|
|
|
+ auto& Task1A = State1A.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1A")));
|
|
|
+ Task1A.GetNode().TicksToCompletion = 1;
|
|
|
+ State1A.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::NextState);
|
|
|
+
|
|
|
+ auto& Task1B = State1B.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1B")));
|
|
|
+ Task1B.GetNode().TicksToCompletion = 2;
|
|
|
+ State1B.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::NextState);
|
|
|
+
|
|
|
+ auto& Task1C = State1C.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1C")));
|
|
|
+ Task1C.GetNode().TicksToCompletion = 2;
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+ const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+
|
|
|
+ // Start and enter state
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Task1 should enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task1A should enter state", Exec.Expect(Task1A.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Transition from Task1A to Task1B
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Task1A should complete", Exec.Expect(Task1A.GetName(), StateCompletedStr));
|
|
|
+ AITEST_TRUE("EzAbility Task1B should enter state", Exec.Expect(Task1B.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Task1 completes, and we should take State1 transition.
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Task1 should complete", Exec.Expect(Task1.GetName(), StateCompletedStr));
|
|
|
+ AITEST_EQUAL("Tree execution should stop on success", Status, EAbilityRunStatus::Succeeded);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_TransitionPriority, "System.EzAbility.Transition.Priority");
|
|
|
+
|
|
|
+struct FEzAbilityTest_TransitionPriorityEnterState : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& State0 = Root.AddChildState(FName(TEXT("State0")));
|
|
|
+ UEzAbilityState& State1 = Root.AddChildState(FName(TEXT("State1")));
|
|
|
+ UEzAbilityState& State1A = State1.AddChildState(FName(TEXT("State1A")));
|
|
|
+ UEzAbilityState& State2 = Root.AddChildState(FName(TEXT("State2")));
|
|
|
+ UEzAbilityState& State3 = Root.AddChildState(FName(TEXT("State3")));
|
|
|
+
|
|
|
+ auto& Task0 = State0.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task0")));
|
|
|
+ State0.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::GotoState, &State1);
|
|
|
+
|
|
|
+ auto& Task1 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+ Task1.GetNode().EnterStateResult = EAbilityRunStatus::Failed;
|
|
|
+ State1.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::GotoState, &State2);
|
|
|
+
|
|
|
+ auto& Task1A = State1A.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1A")));
|
|
|
+ State1A.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::GotoState, &State3);
|
|
|
+
|
|
|
+ auto& Task2 = State2.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task2")));
|
|
|
+ State2.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::Succeeded);
|
|
|
+
|
|
|
+ auto& Task3 = State3.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task3")));
|
|
|
+ State3.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::Succeeded);
|
|
|
+
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+ const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+
|
|
|
+ // Start and enter state
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Task0 should enter state", Exec.Expect(Task0.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Transition from State0 to State1, it should fail (Task1), and the transition on State1->State2 (and not State1A->State3)
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Task0 should complete", Exec.Expect(Task0.GetName(), StateCompletedStr));
|
|
|
+ AITEST_TRUE("EzAbility Task2 should enter state", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+ AITEST_FALSE("EzAbility Task3 should not enter state", Exec.Expect(Task3.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_TransitionPriorityEnterState, "System.EzAbility.Transition.PriorityEnterState");
|
|
|
+
|
|
|
+struct FEzAbilityTest_TransitionNextSelectableState : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& State0 = Root.AddChildState(FName(TEXT("State0")));
|
|
|
+ UEzAbilityState& State1 = Root.AddChildState(FName(TEXT("State1")));
|
|
|
+ UEzAbilityState& State2 = Root.AddChildState(FName(TEXT("State2")));
|
|
|
+
|
|
|
+ auto& EvalA = EditorData.AddEvaluator<FEzAbilityTestEval_A>();
|
|
|
+ EvalA.GetInstanceData().bBoolA = true;
|
|
|
+
|
|
|
+ auto& Task0 = State0.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task0")));
|
|
|
+ State0.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::NextSelectableState);
|
|
|
+
|
|
|
+ // Add Task 1 with Condition that will always fail
|
|
|
+ auto& Task1 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+ auto& BoolCond1 = State1.AddEnterCondition<FAbilityCompareBoolCondition>();
|
|
|
+
|
|
|
+ EditorData.AddPropertyBinding(EvalA, TEXT("bBoolA"), BoolCond1, TEXT("bLeft"));
|
|
|
+ BoolCond1.GetInstanceData().bRight = !EvalA.GetInstanceData().bBoolA;
|
|
|
+
|
|
|
+ // Add Task 2 with Condition that will always succeed
|
|
|
+ auto& Task2 = State2.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task2")));
|
|
|
+ auto& BoolCond2 = State2.AddEnterCondition<FAbilityCompareBoolCondition>();
|
|
|
+ State2.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::Succeeded);
|
|
|
+
|
|
|
+ EditorData.AddPropertyBinding(EvalA, TEXT("bBoolA"), BoolCond2, TEXT("bLeft"));
|
|
|
+ BoolCond2.GetInstanceData().bRight = EvalA.GetInstanceData().bBoolA;
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+ const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ // Start and enter state
|
|
|
+ Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Task0 should enter state", Exec.Expect(Task0.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Transition from State0 and tries to select State1. It should fail (Task1) and because transition is set to "Next Selectable", it should now select Task 2 and Enter State
|
|
|
+ Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Task0 should complete", Exec.Expect(Task0.GetName(), StateCompletedStr));
|
|
|
+ AITEST_FALSE("EzAbility Task1 should not enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task2 should enter state", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Complete Task2
|
|
|
+ Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Task2 should complete", Exec.Expect(Task2.GetName(), StateCompletedStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_TransitionNextSelectableState, "System.EzAbility.Transition.NextSelectableState");
|
|
|
+
|
|
|
+
|
|
|
+struct FEzAbilityTest_TransitionNextWithParentData : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& State0 = Root.AddChildState(FName(TEXT("State0")));
|
|
|
+ UEzAbilityState& State1 = Root.AddChildState(FName(TEXT("State1")));
|
|
|
+ UEzAbilityState& State1A = State1.AddChildState(FName(TEXT("State1A")));
|
|
|
+
|
|
|
+ auto& RootTask = Root.AddTask<FEzAbilityTestTask_B>(FName(TEXT("RootTask")));
|
|
|
+ RootTask.GetInstanceData().bBoolB = true;
|
|
|
+
|
|
|
+ auto& Task0 = State0.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task0")));
|
|
|
+ State0.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::NextState);
|
|
|
+
|
|
|
+ auto& Task1A = State1A.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1A")));
|
|
|
+ auto& BoolCond1 = State1A.AddEnterCondition<FAbilityCompareBoolCondition>();
|
|
|
+
|
|
|
+ EditorData.AddPropertyBinding(RootTask, TEXT("bBoolB"), BoolCond1, TEXT("bLeft"));
|
|
|
+ BoolCond1.GetInstanceData().bRight = true;
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+ const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+
|
|
|
+ // Start and enter state
|
|
|
+ Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Task0 should enter state", Exec.Expect(Task0.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Transition from State0 and tries to select State1.
|
|
|
+ // This tests that data from current shared active states (Root) is available during state selection.
|
|
|
+ Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Task0 should complete", Exec.Expect(Task0.GetName(), StateCompletedStr));
|
|
|
+ AITEST_TRUE("EzAbility Task1A should enter state", Exec.Expect(Task1A.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_TransitionNextWithParentData, "System.EzAbility.Transition.NextWithParentData");
|
|
|
+
|
|
|
+// struct FEzAbilityTest_LastConditionWithIndent : FAITestBase
|
|
|
+// {
|
|
|
+// virtual bool InstantTest() override
|
|
|
+// {
|
|
|
+// UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+//
|
|
|
+// UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+// UEzAbilityState& State1 = Root.AddChildState(FName(TEXT("State1")));
|
|
|
+//
|
|
|
+// auto& Task1 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+// State1.AddEnterCondition<FEzAbilityTestCondition>();
|
|
|
+// auto& LastCondition = State1.AddEnterCondition<FEzAbilityTestCondition>();
|
|
|
+//
|
|
|
+// // Last condition has Indent
|
|
|
+// LastCondition.ExpressionIndent = 1;
|
|
|
+//
|
|
|
+// State1.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::Succeeded);
|
|
|
+//
|
|
|
+// FEzAbilityCompilerLog Log;
|
|
|
+// FEzAbilityCompiler Compiler(Log);
|
|
|
+// const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+// AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+//
|
|
|
+// EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+// FEzAbilityInstanceData InstanceData;
|
|
|
+// FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+// const bool bInitSucceeded = Exec.IsValid();
|
|
|
+// AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+//
|
|
|
+// const FString TickStr(TEXT("Tick"));
|
|
|
+// const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+// const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+//
|
|
|
+// FEzAbilityParameter Parameter;
|
|
|
+// FText OutText;
|
|
|
+// Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+// AITEST_TRUE("EzAbility Task1 should enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+// AITEST_FALSE("EzAbility Task1 should not tick", Exec.Expect(Task1.GetName(), TickStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_TRUE("EzAbility Task1 should tick, and exit state", Exec.Expect(Task1.GetName(), TickStr).Then(Task1.GetName(), ExitStateStr));
|
|
|
+// AITEST_TRUE("EzAbility should be completed", Status == EAbilityRunStatus::Succeeded);
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_FALSE("EzAbility Task1 should not tick", Exec.Expect(Task1.GetName(), TickStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_LastConditionWithIndent, "System.EzAbility.LastConditionWithIndent");
|
|
|
+
|
|
|
+struct FEzAbilityTest_TransitionGlobalDataView : FAITestBase
|
|
|
+{
|
|
|
+ // Tests that the global eval and task dataviews are kept up to date when transitioning from
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& StateA = Root.AddChildState(FName(TEXT("A")));
|
|
|
+ UEzAbilityState& StateB = Root.AddChildState(FName(TEXT("B")));
|
|
|
+
|
|
|
+ auto& EvalA = EditorData.AddEvaluator<FEzAbilityTestEval_A>(FName(TEXT("Eval")));
|
|
|
+ EvalA.GetInstanceData().IntA = 42;
|
|
|
+ auto& GlobalTask = EditorData.AddGlobalTask<FAbilityTestTask_PrintValue>(FName(TEXT("Global")));
|
|
|
+ GlobalTask.GetInstanceData().Value = 123;
|
|
|
+
|
|
|
+ // State A
|
|
|
+ auto& Task0 = StateA.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task0")));
|
|
|
+ StateA.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::GotoState, &StateB);
|
|
|
+
|
|
|
+ // State B
|
|
|
+ auto& Task1 = StateB.AddTask<FAbilityTestTask_PrintValue>(FName(TEXT("Task1")));
|
|
|
+ EditorData.AddPropertyBinding(EvalA, TEXT("IntA"), Task1, TEXT("Value"));
|
|
|
+ auto& Task2 = StateB.AddTask<FAbilityTestTask_PrintValue>(FName(TEXT("Task2")));
|
|
|
+ EditorData.AddPropertyBinding(GlobalTask, TEXT("Value"), Task2, TEXT("Value"));
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString EnterState42Str(TEXT("EnterState42"));
|
|
|
+ const FString EnterState123Str(TEXT("EnterState123"));
|
|
|
+
|
|
|
+ // Start and enter state
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Task0 should enter state", Exec.Expect(Task0.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Transition from StateA to StateB, Task0 should enter state with evaluator value copied.
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Task0 should enter state with value 42", Exec.Expect(Task1.GetName(), EnterState42Str));
|
|
|
+ AITEST_TRUE("EzAbility Task1 should enter state with value 123", Exec.Expect(Task2.GetName(), EnterState123Str));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_TransitionGlobalDataView, "System.EzAbility.Transition.GlobalDataView");
|
|
|
+
|
|
|
+// struct FEzAbilityTest_TransitionDelay : FAITestBase
|
|
|
+// {
|
|
|
+// virtual bool InstantTest() override
|
|
|
+// {
|
|
|
+// UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+// const FGameplayTag Tag = UE::EzAbility::Tests::FNativeGameplayTags::Get().TestTag;
|
|
|
+//
|
|
|
+// UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+// UEzAbilityState& StateA = Root.AddChildState(FName(TEXT("A")));
|
|
|
+// UEzAbilityState& StateB = Root.AddChildState(FName(TEXT("B")));
|
|
|
+//
|
|
|
+// // State A
|
|
|
+// auto& Task0 = StateA.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task0")));
|
|
|
+// Task0.GetNode().TicksToCompletion = 100;
|
|
|
+//
|
|
|
+// FEzAbilityTransition& Transition = StateA.AddTransition(EEzAbilityTransitionTrigger::OnEvent, EEzAbilityTransitionType::GotoState, &StateB);
|
|
|
+// Transition.bDelayTransition = true;
|
|
|
+// Transition.DelayDuration = 0.15f;
|
|
|
+// Transition.DelayRandomVariance = 0.0f;
|
|
|
+// Transition.RequiredEvent.Tag = Tag;
|
|
|
+//
|
|
|
+// // State B
|
|
|
+// auto& Task1 = StateB.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+// Task1.GetNode().TicksToCompletion = 100;
|
|
|
+//
|
|
|
+// FEzAbilityCompilerLog Log;
|
|
|
+// FEzAbilityCompiler Compiler(Log);
|
|
|
+// const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+//
|
|
|
+// AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+//
|
|
|
+// EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+// FEzAbilityInstanceData InstanceData;
|
|
|
+// FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+// const bool bInitSucceeded = Exec.IsValid();
|
|
|
+// AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+//
|
|
|
+// const FString TickStr(TEXT("Tick"));
|
|
|
+// const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+// const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+// const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+//
|
|
|
+// // Start and enter state
|
|
|
+// FEzAbilityParameter Parameter;
|
|
|
+// FText OutText;
|
|
|
+// Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+// AITEST_TRUE("EzAbility Task0 should enter state", Exec.Expect(Task0.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// // This should cause delayed transition.
|
|
|
+// Exec.SendEvent(Tag);
|
|
|
+//
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_TRUE("EzAbility Task0 should tick", Exec.Expect(Task0.GetName(), TickStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// // Should have execution frames
|
|
|
+// AITEST_TRUE("Should have active frames", InstanceData.GetExecutionState()->ActiveFrames.Num() > 0);
|
|
|
+//
|
|
|
+// // Should have delayed transitions
|
|
|
+// const int32 NumDelayedTransitions0 = InstanceData.GetExecutionState()->DelayedTransitions.Num();
|
|
|
+// AITEST_EQUAL("Should have a delayed transition", NumDelayedTransitions0, 1);
|
|
|
+//
|
|
|
+// // Tick and expect a delayed transition.
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_TRUE("EzAbility Task0 should tick", Exec.Expect(Task0.GetName(), TickStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// const int32 NumDelayedTransitions1 = InstanceData.GetExecutionState()->DelayedTransitions.Num();
|
|
|
+// AITEST_EQUAL("Should have a delayed transition", NumDelayedTransitions1, 1);
|
|
|
+//
|
|
|
+// // Should complete delayed transition.
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_TRUE("EzAbility Task0 should exit state", Exec.Expect(Task0.GetName(), ExitStateStr));
|
|
|
+// AITEST_TRUE("EzAbility Task1 should enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_TransitionDelay, "System.EzAbility.TransitionDelay");
|
|
|
+
|
|
|
+// struct FEzAbilityTest_TransitionDelayZero : FAITestBase
|
|
|
+// {
|
|
|
+// virtual bool InstantTest() override
|
|
|
+// {
|
|
|
+// UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+// const FGameplayTag Tag = UE::EzAbility::Tests::FNativeGameplayTags::Get().TestTag;
|
|
|
+//
|
|
|
+// UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+// UEzAbilityState& StateA = Root.AddChildState(FName(TEXT("A")));
|
|
|
+// UEzAbilityState& StateB = Root.AddChildState(FName(TEXT("B")));
|
|
|
+//
|
|
|
+// // State A
|
|
|
+// auto& Task0 = StateA.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task0")));
|
|
|
+// Task0.GetNode().TicksToCompletion = 100;
|
|
|
+//
|
|
|
+// FEzAbilityTransition& Transition = StateA.AddTransition(EEzAbilityTransitionTrigger::OnEvent, EEzAbilityTransitionType::GotoState, &StateB);
|
|
|
+// Transition.bDelayTransition = true;
|
|
|
+// Transition.DelayDuration = 0.0f;
|
|
|
+// Transition.DelayRandomVariance = 0.0f;
|
|
|
+// Transition.RequiredEvent.Tag = Tag;
|
|
|
+//
|
|
|
+// // State B
|
|
|
+// auto& Task1 = StateB.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+// Task1.GetNode().TicksToCompletion = 100;
|
|
|
+//
|
|
|
+// FEzAbilityCompilerLog Log;
|
|
|
+// FEzAbilityCompiler Compiler(Log);
|
|
|
+// const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+//
|
|
|
+// AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+//
|
|
|
+// EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+// FEzAbilityInstanceData InstanceData;
|
|
|
+// FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+// const bool bInitSucceeded = Exec.IsValid();
|
|
|
+// AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+//
|
|
|
+// const FString TickStr(TEXT("Tick"));
|
|
|
+// const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+// const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+// const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+//
|
|
|
+// // Start and enter state
|
|
|
+// FEzAbilityParameter Parameter;
|
|
|
+// FText OutText;
|
|
|
+// Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+// AITEST_TRUE("EzAbility Task0 should enter state", Exec.Expect(Task0.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// // This should cause delayed transition. Because the time is 0, it should happen immediately.
|
|
|
+// Exec.SendEvent(Tag);
|
|
|
+//
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_TRUE("EzAbility Task0 should exit state", Exec.Expect(Task0.GetName(), ExitStateStr));
|
|
|
+// AITEST_TRUE("EzAbility Task1 should enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_TransitionDelayZero, "System.EzAbility.TransitionDelayZero");
|
|
|
+
|
|
|
+// struct FEzAbilityTest_StateRequiringEvent : FAITestBase
|
|
|
+// {
|
|
|
+// virtual bool InstantTest() override
|
|
|
+// {
|
|
|
+// UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+//
|
|
|
+// UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+//
|
|
|
+// FGameplayTag ValidTag = UE::EzAbility::Tests::FNativeGameplayTags::Get().TestTag;
|
|
|
+// FGameplayTag InvalidTag = UE::EzAbility::Tests::FNativeGameplayTags::Get().TestTag2;
|
|
|
+//
|
|
|
+// using FValidPayload = FEzAbilityTest_PropertyStructA;
|
|
|
+// using FInvalidPayload = FEzAbilityTest_PropertyStructB;
|
|
|
+//
|
|
|
+// // This state shouldn't be selected as it requires different tag.
|
|
|
+// UEzAbilityState& StateA = Root.AddChildState(FName(TEXT("A")));
|
|
|
+// StateA.bHasRequiredEventToEnter = true;
|
|
|
+// StateA.RequiredEventToEnter.Tag = InvalidTag;
|
|
|
+// auto& TaskA = StateA.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskA")));
|
|
|
+//
|
|
|
+// // This state shouldn't be selected as it requires different payload.
|
|
|
+// UEzAbilityState& StateB = Root.AddChildState(FName(TEXT("B")));
|
|
|
+// StateB.bHasRequiredEventToEnter = true;
|
|
|
+// StateB.RequiredEventToEnter.PayloadStruct = FInvalidPayload::StaticStruct();
|
|
|
+// auto& TaskB = StateB.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskB")));
|
|
|
+//
|
|
|
+// // This state shouldn't be selected as it requires the same tag, but different payload.
|
|
|
+// UEzAbilityState& StateC = Root.AddChildState(FName(TEXT("C")));
|
|
|
+// StateC.bHasRequiredEventToEnter = true;
|
|
|
+// StateC.RequiredEventToEnter.Tag = ValidTag;
|
|
|
+// StateC.RequiredEventToEnter.PayloadStruct = FInvalidPayload::StaticStruct();
|
|
|
+// auto& TaskC = StateC.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskC")));
|
|
|
+//
|
|
|
+// // This state shouldn't be selected as it requires the same payload, but different tag.
|
|
|
+// UEzAbilityState& StateD = Root.AddChildState(FName(TEXT("D")));
|
|
|
+// StateD.bHasRequiredEventToEnter = true;
|
|
|
+// StateD.RequiredEventToEnter.Tag = InvalidTag;
|
|
|
+// StateD.RequiredEventToEnter.PayloadStruct = FValidPayload::StaticStruct();
|
|
|
+// auto& TaskD = StateD.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskD")));
|
|
|
+//
|
|
|
+// // This state should be selected as the arrived event matches the requirement.
|
|
|
+// UEzAbilityState& StateE = Root.AddChildState(FName(TEXT("E")));
|
|
|
+// StateE.bHasRequiredEventToEnter = true;
|
|
|
+// StateE.RequiredEventToEnter.Tag = ValidTag;
|
|
|
+// StateE.RequiredEventToEnter.PayloadStruct = FValidPayload::StaticStruct();
|
|
|
+// auto& TaskE = StateE.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskE")));
|
|
|
+//
|
|
|
+// // This state should be selected only initially when there's not event in the queue.
|
|
|
+// UEzAbilityState& StateInitial = Root.AddChildState(FName(TEXT("Initial")));
|
|
|
+// auto& TaskInitial = StateInitial.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskInitial")));
|
|
|
+// StateInitial.AddTransition(EEzAbilityTransitionTrigger::OnEvent, ValidTag, EEzAbilityTransitionType::GotoState, &Root);
|
|
|
+//
|
|
|
+// FEzAbilityCompilerLog Log;
|
|
|
+// FEzAbilityCompiler Compiler(Log);
|
|
|
+// const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+//
|
|
|
+// AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+//
|
|
|
+// EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+// FEzAbilityInstanceData InstanceData;
|
|
|
+// FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+// const bool bInitSucceeded = Exec.IsValid();
|
|
|
+// AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+//
|
|
|
+// const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+//
|
|
|
+// FEzAbilityParameter Parameter;
|
|
|
+// FText OutText;
|
|
|
+// Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+// AITEST_TRUE("EzAbility TaskInitial should enter state", Exec.Expect(TaskInitial.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// Exec.SendEvent(ValidTag, FConstStructView::Make(FValidPayload()));
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+//
|
|
|
+// AITEST_FALSE("EzAbility TaskA should not enter state", Exec.Expect(TaskA.GetName(), EnterStateStr));
|
|
|
+// AITEST_FALSE("EzAbility TaskB should not enter state", Exec.Expect(TaskB.GetName(), EnterStateStr));
|
|
|
+// AITEST_FALSE("EzAbility TaskC should not enter state", Exec.Expect(TaskC.GetName(), EnterStateStr));
|
|
|
+// AITEST_FALSE("EzAbility TaskD should not enter state", Exec.Expect(TaskD.GetName(), EnterStateStr));
|
|
|
+// AITEST_TRUE("EzAbility TaskE should enter state", Exec.Expect(TaskE.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_StateRequiringEvent, "System.EzAbility.StateRequiringEvent");
|
|
|
+
|
|
|
+// struct FEzAbilityTest_PassingTransitionEventToStateSelection : FAITestBase
|
|
|
+// {
|
|
|
+// virtual bool InstantTest() override
|
|
|
+// {
|
|
|
+// UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+//
|
|
|
+// UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+//
|
|
|
+// FEzAbilityPropertyPath PathToPayloadMember;
|
|
|
+// {
|
|
|
+// const bool bParseResult = PathToPayloadMember.FromString(TEXT("Payload.A"));
|
|
|
+//
|
|
|
+// AITEST_TRUE("Parsing path should succeeed", bParseResult);
|
|
|
+//
|
|
|
+// FEzAbilityEvent EventWithPayload;
|
|
|
+// EventWithPayload.Payload = FInstancedStruct::Make<FEzAbilityTest_PropertyStructA>();
|
|
|
+// const bool bUpdateSegments = PathToPayloadMember.UpdateSegmentsFromValue(FEzAbilityDataView(FStructView::Make(EventWithPayload)));
|
|
|
+// AITEST_TRUE("Updating segments should succeeed", bUpdateSegments);
|
|
|
+// }
|
|
|
+//
|
|
|
+// // This state shouldn't be selected, because transition's condition and state's enter condition exlude each other.
|
|
|
+// UEzAbilityState& StateA = Root.AddChildState(FName(TEXT("A")));
|
|
|
+// StateA.bHasRequiredEventToEnter = true;
|
|
|
+// StateA.RequiredEventToEnter.PayloadStruct = FEzAbilityTest_PropertyStructA::StaticStruct();
|
|
|
+// auto& TaskA = StateA.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskA")));
|
|
|
+// TEzAbilityEditorNode<FAbilityCompareIntCondition>& AIntCond = StateA.AddEnterCondition<FAbilityCompareIntCondition>(EGenericCheck::Equal);
|
|
|
+// AIntCond.GetInstanceData().Right = 0;
|
|
|
+// EditorData.AddPropertyBinding(
|
|
|
+// FEzAbilityPropertyPath(StateA.GetEventID(), PathToPayloadMember.GetSegments()),
|
|
|
+// FEzAbilityPropertyPath(AIntCond.ID, TEXT("Left")));
|
|
|
+//
|
|
|
+// // This state should be selected as the sent event fullfils both transition's condition and state's enter condition.
|
|
|
+// UEzAbilityState& StateB = Root.AddChildState(FName(TEXT("B")));
|
|
|
+// StateB.bHasRequiredEventToEnter = true;
|
|
|
+// StateB.RequiredEventToEnter.PayloadStruct = FEzAbilityTest_PropertyStructA::StaticStruct();
|
|
|
+// auto& TaskB = StateB.AddTask<FAbilityTestTask_PrintValue>(FName(TEXT("TaskB")));
|
|
|
+// // Test copying data from the state event. The condition properties are copied from temp instance data during selection, this gets copied from active instance data.
|
|
|
+// TaskB.GetInstanceData().Value = -1; // Initially -1, expected to be overridden by property binding below.
|
|
|
+// EditorData.AddPropertyBinding(
|
|
|
+// FEzAbilityPropertyPath(StateB.GetEventID(), PathToPayloadMember.GetSegments()),
|
|
|
+// FEzAbilityPropertyPath(TaskB.ID, TEXT("Value")));
|
|
|
+//
|
|
|
+// TEzAbilityEditorNode<FAbilityCompareIntCondition>& BIntCond = StateB.AddEnterCondition<FAbilityCompareIntCondition>(EGenericCheck::Equal);
|
|
|
+// BIntCond.GetInstanceData().Right = 1;
|
|
|
+// EditorData.AddPropertyBinding(
|
|
|
+// FEzAbilityPropertyPath(StateB.GetEventID(), PathToPayloadMember.GetSegments()),
|
|
|
+// FEzAbilityPropertyPath(BIntCond.ID, TEXT("Left")));
|
|
|
+//
|
|
|
+// // This state should be selected only initially when there's not event in the queue.
|
|
|
+// UEzAbilityState& StateInitial = Root.AddChildState(FName(TEXT("Initial")));
|
|
|
+// auto& TaskInitial = StateInitial.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskInitial")));
|
|
|
+// // Transition from Initial -> StateA
|
|
|
+// FEzAbilityTransition& TransA = StateInitial.AddTransition(EEzAbilityTransitionTrigger::OnEvent, FGameplayTag(), EEzAbilityTransitionType::GotoState, &StateA);
|
|
|
+// TransA.RequiredEvent.PayloadStruct = FEzAbilityTest_PropertyStructA::StaticStruct();
|
|
|
+// TEzAbilityEditorNode<FAbilityCompareIntCondition>& TransAIntCond = TransA.AddCondition<FAbilityCompareIntCondition>(EGenericCheck::Equal);
|
|
|
+// TransAIntCond.GetInstanceData().Right = 1;
|
|
|
+// EditorData.AddPropertyBinding(
|
|
|
+// FEzAbilityPropertyPath(TransA.GetEventID(), PathToPayloadMember.GetSegments()),
|
|
|
+// FEzAbilityPropertyPath(TransAIntCond.ID, TEXT("Left")));
|
|
|
+// // Transition from Initial -> StateB
|
|
|
+// FEzAbilityTransition& TransB = StateInitial.AddTransition(EEzAbilityTransitionTrigger::OnEvent, FGameplayTag(), EEzAbilityTransitionType::GotoState, &StateB);
|
|
|
+// TransB.RequiredEvent.PayloadStruct = FEzAbilityTest_PropertyStructA::StaticStruct();
|
|
|
+// TEzAbilityEditorNode<FAbilityCompareIntCondition>& TransBIntCond = TransB.AddCondition<FAbilityCompareIntCondition>(EGenericCheck::Equal);
|
|
|
+// TransBIntCond.GetInstanceData().Right = 1;
|
|
|
+// EditorData.AddPropertyBinding(
|
|
|
+// FEzAbilityPropertyPath(TransB.GetEventID(), PathToPayloadMember.GetSegments()),
|
|
|
+// FEzAbilityPropertyPath(TransBIntCond.ID, TEXT("Left")));
|
|
|
+//
|
|
|
+// FEzAbilityCompilerLog Log;
|
|
|
+// FEzAbilityCompiler Compiler(Log);
|
|
|
+// const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+//
|
|
|
+// AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+//
|
|
|
+// EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+// FEzAbilityInstanceData InstanceData;
|
|
|
+// FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+// const bool bInitSucceeded = Exec.IsValid();
|
|
|
+// AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+//
|
|
|
+// const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+//
|
|
|
+// FEzAbilityParameter Parameter;
|
|
|
+// FText OutText;
|
|
|
+// Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+// AITEST_TRUE("EzAbility TaskInitial should enter state", Exec.Expect(TaskInitial.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// // The conditions test for payload Value=1, the first event should not trigger transition.
|
|
|
+// Exec.SendEvent(UE::EzAbility::Tests::FNativeGameplayTags::Get().TestTag, FConstStructView::Make(FEzAbilityTest_PropertyStructA{0}));
|
|
|
+// Exec.SendEvent(UE::EzAbility::Tests::FNativeGameplayTags::Get().TestTag, FConstStructView::Make(FEzAbilityTest_PropertyStructA{1}));
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+//
|
|
|
+// AITEST_FALSE("EzAbility TaskA should not enter state", Exec.Expect(TaskA.GetName(), EnterStateStr));
|
|
|
+// AITEST_TRUE("EzAbility TaskB should enter state", Exec.Expect(TaskB.GetName(), TEXT("EnterState1"))); // TaskB decorates "EnterState" with value from the payload.
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_PassingTransitionEventToStateSelection, "System.EzAbility.PassingTransitionEventToStateSelection");
|
|
|
+
|
|
|
+struct FEzAbilityTest_PropertyPathOffset : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ const bool bParseResult = Path.FromString(TEXT("StructB.B"));
|
|
|
+
|
|
|
+ AITEST_TRUE("Parsing path should succeeed", bParseResult);
|
|
|
+ AITEST_EQUAL("Should have 2 path segments", Path.NumSegments(), 2);
|
|
|
+
|
|
|
+ FString ResolveErrors;
|
|
|
+ TArray<FEzAbilityPropertyPathIndirection> Indirections;
|
|
|
+ const bool bResolveResult = Path.ResolveIndirections(FEzAbilityTest_PropertyStruct::StaticStruct(), Indirections, &ResolveErrors);
|
|
|
+
|
|
|
+ AITEST_TRUE("Resolve path should succeeed", bResolveResult);
|
|
|
+ AITEST_EQUAL("Should have no resolve errors", ResolveErrors.Len(), 0);
|
|
|
+
|
|
|
+ AITEST_EQUAL("Should have 2 indirections", Indirections.Num(), 2);
|
|
|
+ AITEST_EQUAL("Indirection 0 should be Offset type", Indirections[0].GetAccessType(), EEzAbilityPropertyAccessType::Offset);
|
|
|
+ AITEST_EQUAL("Indirection 1 should be Offset type", Indirections[1].GetAccessType(), EEzAbilityPropertyAccessType::Offset);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_PropertyPathOffset, "System.EzAbility.PropertyPath.Offset");
|
|
|
+
|
|
|
+struct FEzAbilityTest_PropertyPathParseFail : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ const bool bParseResult = Path.FromString(TEXT("")); // empty is valid.
|
|
|
+ AITEST_TRUE("Parsing path should succeed", bParseResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ const bool bParseResult = Path.FromString(TEXT("StructB.[0]B"));
|
|
|
+ AITEST_FALSE("Parsing path should fail", bParseResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ const bool bParseResult = Path.FromString(TEXT("StructB..NoThere"));
|
|
|
+ AITEST_FALSE("Parsing path should fail", bParseResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ const bool bParseResult = Path.FromString(TEXT("."));
|
|
|
+ AITEST_FALSE("Parsing path should fail", bParseResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ const bool bParseResult = Path.FromString(TEXT("StructB..B"));
|
|
|
+ AITEST_FALSE("Parsing path should fail", bParseResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_PropertyPathParseFail, "System.EzAbility.PropertyPath.ParseFail");
|
|
|
+
|
|
|
+struct FEzAbilityTest_PropertyPathOffsetFail : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ const bool bParseResult = Path.FromString(TEXT("StructB.Q"));
|
|
|
+
|
|
|
+ AITEST_TRUE("Parsing path should succeeed", bParseResult);
|
|
|
+ AITEST_EQUAL("Should have 2 path segments", Path.NumSegments(), 2);
|
|
|
+
|
|
|
+ FString ResolveErrors;
|
|
|
+ TArray<FEzAbilityPropertyPathIndirection> Indirections;
|
|
|
+ const bool bResolveResult = Path.ResolveIndirections(FEzAbilityTest_PropertyStruct::StaticStruct(), Indirections, &ResolveErrors);
|
|
|
+
|
|
|
+ AITEST_FALSE("Resolve path should not succeeed", bResolveResult);
|
|
|
+ AITEST_NOT_EQUAL("Should have errors", ResolveErrors.Len(), 0);
|
|
|
+
|
|
|
+ AITEST_EQUAL("Should have 0 indirections", Indirections.Num(), 0);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_PropertyPathOffsetFail, "System.EzAbility.PropertyPath.OffsetFail");
|
|
|
+
|
|
|
+struct FEzAbilityTest_PropertyPathObject : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ const bool bParseResult = Path.FromString(TEXT("InstancedObject.A"));
|
|
|
+
|
|
|
+ AITEST_TRUE("Parsing path should succeeed", bParseResult);
|
|
|
+ AITEST_EQUAL("Should have 2 path segments", Path.NumSegments(), 2);
|
|
|
+
|
|
|
+ UEzAbilityTest_PropertyObject* Object = NewObject<UEzAbilityTest_PropertyObject>();
|
|
|
+ Object->InstancedObject = NewObject<UEzAbilityTest_PropertyObjectInstanced>();
|
|
|
+
|
|
|
+ const bool bUpdateResult = Path.UpdateSegmentsFromValue(FEzAbilityDataView(Object));
|
|
|
+
|
|
|
+ AITEST_TRUE("Update instance types should succeeed", bUpdateResult);
|
|
|
+ AITEST_TRUE("Path segment 0 instance type should be UEzAbilityTest_PropertyObjectInstanced", Path.GetSegment(0).GetInstanceStruct() == UEzAbilityTest_PropertyObjectInstanced::StaticClass());
|
|
|
+ AITEST_TRUE("Path segment 1 instance type should be nullptr", Path.GetSegment(1).GetInstanceStruct() == nullptr);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_PropertyPathObject, "System.EzAbility.PropertyPath.Object");
|
|
|
+
|
|
|
+struct FEzAbilityTest_PropertyPathWrongObject : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ const bool bParseResult = Path.FromString(TEXT("InstancedObject.B"));
|
|
|
+
|
|
|
+ AITEST_TRUE("Parsing path should succeeed", bParseResult);
|
|
|
+ AITEST_EQUAL("Should have 2 path segments", Path.NumSegments(), 2);
|
|
|
+
|
|
|
+ UEzAbilityTest_PropertyObject* Object = NewObject<UEzAbilityTest_PropertyObject>();
|
|
|
+
|
|
|
+ Object->InstancedObject = NewObject<UEzAbilityTest_PropertyObjectInstancedWithB>();
|
|
|
+ {
|
|
|
+ FString ResolveErrors;
|
|
|
+ TArray<FEzAbilityPropertyPathIndirection> Indirections;
|
|
|
+ const bool bResolveResult = Path.ResolveIndirectionsWithValue(FEzAbilityDataView(Object), Indirections, &ResolveErrors);
|
|
|
+
|
|
|
+ AITEST_TRUE("Resolve path should succeeed", bResolveResult);
|
|
|
+ AITEST_EQUAL("Should have 2 indirections", Indirections.Num(), 2);
|
|
|
+ AITEST_TRUE("Object ", Indirections[0].GetAccessType() == EEzAbilityPropertyAccessType::ObjectInstance);
|
|
|
+ AITEST_TRUE("Object ", Indirections[0].GetContainerStruct() == Object->GetClass());
|
|
|
+ AITEST_TRUE("Object ", Indirections[0].GetInstanceStruct() == UEzAbilityTest_PropertyObjectInstancedWithB::StaticClass());
|
|
|
+ AITEST_EQUAL("Should not have error", ResolveErrors.Len(), 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ Object->InstancedObject = NewObject<UEzAbilityTest_PropertyObjectInstanced>();
|
|
|
+ {
|
|
|
+ FString ResolveErrors;
|
|
|
+ TArray<FEzAbilityPropertyPathIndirection> Indirections;
|
|
|
+ const bool bResolveResult = Path.ResolveIndirectionsWithValue(FEzAbilityDataView(Object), Indirections, &ResolveErrors);
|
|
|
+
|
|
|
+ AITEST_FALSE("Resolve path should fail", bResolveResult);
|
|
|
+ AITEST_EQUAL("Should have 0 indirections", Indirections.Num(), 0);
|
|
|
+ AITEST_NOT_EQUAL("Should have error", ResolveErrors.Len(), 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_PropertyPathWrongObject, "System.EzAbility.PropertyPath.WrongObject");
|
|
|
+
|
|
|
+struct FEzAbilityTest_PropertyPathArray : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ const bool bParseResult = Path.FromString(TEXT("ArrayOfInts[1]"));
|
|
|
+
|
|
|
+ AITEST_TRUE("Parsing path should succeeed", bParseResult);
|
|
|
+ AITEST_EQUAL("Should have 1 path segments", Path.NumSegments(), 1);
|
|
|
+
|
|
|
+ UEzAbilityTest_PropertyObject* Object = NewObject<UEzAbilityTest_PropertyObject>();
|
|
|
+ Object->ArrayOfInts.Add(42);
|
|
|
+ Object->ArrayOfInts.Add(123);
|
|
|
+
|
|
|
+ FString ResolveErrors;
|
|
|
+ TArray<FEzAbilityPropertyPathIndirection> Indirections;
|
|
|
+ const bool bResolveResult = Path.ResolveIndirectionsWithValue(FEzAbilityDataView(Object), Indirections, &ResolveErrors);
|
|
|
+
|
|
|
+ AITEST_TRUE("Resolve path should succeeed", bResolveResult);
|
|
|
+ AITEST_EQUAL("Should have no resolve errors", ResolveErrors.Len(), 0);
|
|
|
+ AITEST_EQUAL("Should have 2 indirections", Indirections.Num(), 2);
|
|
|
+ AITEST_EQUAL("Indirection 0 should be IndexArray type", Indirections[0].GetAccessType(), EEzAbilityPropertyAccessType::IndexArray);
|
|
|
+ AITEST_EQUAL("Indirection 1 should be Offset type", Indirections[1].GetAccessType(), EEzAbilityPropertyAccessType::Offset);
|
|
|
+
|
|
|
+ const int32 Value = *reinterpret_cast<const int32*>(Indirections[1].GetPropertyAddress());
|
|
|
+ AITEST_EQUAL("Value should be 123", Value, 123);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_PropertyPathArray, "System.EzAbility.PropertyPath.Array");
|
|
|
+
|
|
|
+struct FEzAbilityTest_PropertyPathArrayInvalidIndex : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ const bool bParseResult = Path.FromString(TEXT("ArrayOfInts[123]"));
|
|
|
+
|
|
|
+ AITEST_TRUE("Parsing path should succeeed", bParseResult);
|
|
|
+ AITEST_EQUAL("Should have 1 path segments", Path.NumSegments(), 1);
|
|
|
+
|
|
|
+ UEzAbilityTest_PropertyObject* Object = NewObject<UEzAbilityTest_PropertyObject>();
|
|
|
+ Object->ArrayOfInts.Add(42);
|
|
|
+ Object->ArrayOfInts.Add(123);
|
|
|
+
|
|
|
+ FString ResolveErrors;
|
|
|
+ TArray<FEzAbilityPropertyPathIndirection> Indirections;
|
|
|
+ const bool bResolveResult = Path.ResolveIndirectionsWithValue(FEzAbilityDataView(Object), Indirections, &ResolveErrors);
|
|
|
+
|
|
|
+ AITEST_FALSE("Resolve path should fail", bResolveResult);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_PropertyPathArrayInvalidIndex, "System.EzAbility.PropertyPath.ArrayInvalidIndex");
|
|
|
+
|
|
|
+struct FEzAbilityTest_PropertyPathArrayOfStructs : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path1;
|
|
|
+ Path1.FromString(TEXT("ArrayOfStruct[0].B"));
|
|
|
+
|
|
|
+ FEzAbilityPropertyPath Path2;
|
|
|
+ Path2.FromString(TEXT("ArrayOfStruct[2].StructB.B"));
|
|
|
+
|
|
|
+ UEzAbilityTest_PropertyObject* Object = NewObject<UEzAbilityTest_PropertyObject>();
|
|
|
+ Object->ArrayOfStruct.AddDefaulted_GetRef().B = 3;
|
|
|
+ Object->ArrayOfStruct.AddDefaulted();
|
|
|
+ Object->ArrayOfStruct.AddDefaulted_GetRef().StructB.B = 42;
|
|
|
+
|
|
|
+ {
|
|
|
+ FString ResolveErrors;
|
|
|
+ TArray<FEzAbilityPropertyPathIndirection> Indirections;
|
|
|
+ const bool bResolveResult = Path1.ResolveIndirectionsWithValue(FEzAbilityDataView(Object), Indirections, &ResolveErrors);
|
|
|
+
|
|
|
+ AITEST_TRUE("Resolve path1 should succeeed", bResolveResult);
|
|
|
+ AITEST_EQUAL("Should have no resolve errors", ResolveErrors.Len(), 0);
|
|
|
+ AITEST_EQUAL("Should have 3 indirections", Indirections.Num(), 3);
|
|
|
+ AITEST_EQUAL("Indirection 0 should be ArrayIndex type", Indirections[0].GetAccessType(), EEzAbilityPropertyAccessType::IndexArray);
|
|
|
+ AITEST_EQUAL("Indirection 1 should be Offset type", Indirections[1].GetAccessType(), EEzAbilityPropertyAccessType::Offset);
|
|
|
+ AITEST_EQUAL("Indirection 2 should be Offset type", Indirections[2].GetAccessType(), EEzAbilityPropertyAccessType::Offset);
|
|
|
+
|
|
|
+ const int32 Value = *reinterpret_cast<const int32*>(Indirections[2].GetPropertyAddress());
|
|
|
+ AITEST_EQUAL("Value should be 3", Value, 3);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ FString ResolveErrors;
|
|
|
+ TArray<FEzAbilityPropertyPathIndirection> Indirections;
|
|
|
+ const bool bResolveResult = Path2.ResolveIndirectionsWithValue(FEzAbilityDataView(Object), Indirections, &ResolveErrors);
|
|
|
+
|
|
|
+ AITEST_TRUE("Resolve path2 should succeeed", bResolveResult);
|
|
|
+ AITEST_EQUAL("Should have no resolve errors", ResolveErrors.Len(), 0);
|
|
|
+ AITEST_EQUAL("Should have 4 indirections", Indirections.Num(), 4);
|
|
|
+ AITEST_EQUAL("Indirection 0 should be ArrayIndex type", Indirections[0].GetAccessType(), EEzAbilityPropertyAccessType::IndexArray);
|
|
|
+ AITEST_EQUAL("Indirection 1 should be Offset type", Indirections[1].GetAccessType(), EEzAbilityPropertyAccessType::Offset);
|
|
|
+ AITEST_EQUAL("Indirection 2 should be Offset type", Indirections[2].GetAccessType(), EEzAbilityPropertyAccessType::Offset);
|
|
|
+ AITEST_EQUAL("Indirection 3 should be Offset type", Indirections[3].GetAccessType(), EEzAbilityPropertyAccessType::Offset);
|
|
|
+
|
|
|
+ const int32 Value = *reinterpret_cast<const int32*>(Indirections[3].GetPropertyAddress());
|
|
|
+ AITEST_EQUAL("Value should be 42", Value, 42);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_PropertyPathArrayOfStructs, "System.EzAbility.PropertyPath.ArrayOfStructs");
|
|
|
+
|
|
|
+struct FEzAbilityTest_PropertyPathArrayOfInstancedObjects : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityPropertyPath Path;
|
|
|
+ Path.FromString(TEXT("ArrayOfInstancedStructs[0].B"));
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyStruct Struct;
|
|
|
+ Struct.B = 123;
|
|
|
+
|
|
|
+ UEzAbilityTest_PropertyObject* Object = NewObject<UEzAbilityTest_PropertyObject>();
|
|
|
+ Object->ArrayOfInstancedStructs.Emplace(FConstStructView::Make(Struct));
|
|
|
+
|
|
|
+ const bool bUpdateResult = Path.UpdateSegmentsFromValue(FEzAbilityDataView(Object));
|
|
|
+ AITEST_TRUE("Update instance types should succeeed", bUpdateResult);
|
|
|
+ AITEST_EQUAL("Should have 2 path segments", Path.NumSegments(), 2);
|
|
|
+ AITEST_TRUE("Path segment 0 instance type should be FEzAbilityTest_PropertyStruct", Path.GetSegment(0).GetInstanceStruct() == FEzAbilityTest_PropertyStruct::StaticStruct());
|
|
|
+ AITEST_TRUE("Path segment 1 instance type should be nullptr", Path.GetSegment(1).GetInstanceStruct() == nullptr);
|
|
|
+
|
|
|
+ {
|
|
|
+ FString ResolveErrors;
|
|
|
+ TArray<FEzAbilityPropertyPathIndirection> Indirections;
|
|
|
+ const bool bResolveResult = Path.ResolveIndirections(UEzAbilityTest_PropertyObject::StaticClass(), Indirections, &ResolveErrors);
|
|
|
+
|
|
|
+ AITEST_TRUE("Resolve path should succeeed", bResolveResult);
|
|
|
+ AITEST_EQUAL("Should have no resolve errors", ResolveErrors.Len(), 0);
|
|
|
+ AITEST_EQUAL("Should have 3 indirections", Indirections.Num(), 3);
|
|
|
+ AITEST_EQUAL("Indirection 0 should be ArrayIndex type", Indirections[0].GetAccessType(), EEzAbilityPropertyAccessType::IndexArray);
|
|
|
+ AITEST_EQUAL("Indirection 1 should be StructInstance type", Indirections[1].GetAccessType(), EEzAbilityPropertyAccessType::StructInstance);
|
|
|
+ AITEST_EQUAL("Indirection 2 should be Offset type", Indirections[2].GetAccessType(), EEzAbilityPropertyAccessType::Offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ FString ResolveErrors;
|
|
|
+ TArray<FEzAbilityPropertyPathIndirection> Indirections;
|
|
|
+ const bool bResolveResult = Path.ResolveIndirectionsWithValue(FEzAbilityDataView(Object), Indirections, &ResolveErrors);
|
|
|
+
|
|
|
+ AITEST_TRUE("Resolve path should succeeed", bResolveResult);
|
|
|
+ AITEST_EQUAL("Should have no resolve errors", ResolveErrors.Len(), 0);
|
|
|
+ AITEST_EQUAL("Should have 3 indirections", Indirections.Num(), 3);
|
|
|
+ AITEST_EQUAL("Indirection 0 should be ArrayIndex type", Indirections[0].GetAccessType(), EEzAbilityPropertyAccessType::IndexArray);
|
|
|
+ AITEST_EQUAL("Indirection 1 should be StructInstance type", Indirections[1].GetAccessType(), EEzAbilityPropertyAccessType::StructInstance);
|
|
|
+ AITEST_EQUAL("Indirection 2 should be Offset type", Indirections[2].GetAccessType(), EEzAbilityPropertyAccessType::Offset);
|
|
|
+
|
|
|
+ const int32 Value = *reinterpret_cast<const int32*>(Indirections[2].GetPropertyAddress());
|
|
|
+ AITEST_EQUAL("Value should be 123", Value, 123);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_PropertyPathArrayOfInstancedObjects, "System.EzAbility.PropertyPath.ArrayOfInstancedObjects");
|
|
|
+
|
|
|
+struct FEzAbilityTest_BindingsCompiler : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityPropertyBindings Bindings;
|
|
|
+ FEzAbilityPropertyBindingCompiler BindingCompiler;
|
|
|
+
|
|
|
+ const bool bInitResult = BindingCompiler.Init(Bindings, Log);
|
|
|
+ AITEST_TRUE("Expect init to succeed", bInitResult);
|
|
|
+
|
|
|
+ FEzAbilityBindableStructDesc SourceADesc;
|
|
|
+ SourceADesc.Name = FName(TEXT("SourceA"));
|
|
|
+ SourceADesc.Struct = TBaseStructure<FEzAbilityTest_PropertyCopy>::Get();
|
|
|
+ SourceADesc.DataSource = EEzAbilityBindableStructSource::Parameter;
|
|
|
+ SourceADesc.DataHandle = FEzAbilityDataHandle(EEzAbilityDataSourceType::ContextData, 0); // Used as index to SourceViews below.
|
|
|
+ SourceADesc.ID = FGuid::NewGuid();
|
|
|
+
|
|
|
+ FEzAbilityBindableStructDesc SourceBDesc;
|
|
|
+ SourceBDesc.Name = FName(TEXT("SourceB"));
|
|
|
+ SourceBDesc.Struct = TBaseStructure<FEzAbilityTest_PropertyCopy>::Get();
|
|
|
+ SourceBDesc.DataSource = EEzAbilityBindableStructSource::Parameter;
|
|
|
+ SourceBDesc.DataHandle = FEzAbilityDataHandle(EEzAbilityDataSourceType::ContextData, 1); // Used as index to SourceViews below.
|
|
|
+ SourceBDesc.ID = FGuid::NewGuid();
|
|
|
+
|
|
|
+ FEzAbilityBindableStructDesc TargetDesc;
|
|
|
+ TargetDesc.Name = FName(TEXT("Target"));
|
|
|
+ TargetDesc.Struct = TBaseStructure<FEzAbilityTest_PropertyCopy>::Get();
|
|
|
+ TargetDesc.DataSource = EEzAbilityBindableStructSource::Parameter;
|
|
|
+ TargetDesc.ID = FGuid::NewGuid();
|
|
|
+
|
|
|
+ const int32 SourceAIndex = BindingCompiler.AddSourceStruct(SourceADesc);
|
|
|
+ const int32 SourceBIndex = BindingCompiler.AddSourceStruct(SourceBDesc);
|
|
|
+
|
|
|
+ TArray<FEzAbilityPropertyPathBinding> PropertyBindings;
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceBDesc.ID, TEXT("Item"), TargetDesc.ID, TEXT("Array[1]")));
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceADesc.ID, TEXT("Item.B"), TargetDesc.ID, TEXT("Array[1].B")));
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceADesc.ID, TEXT("Array"), TargetDesc.ID, TEXT("Array")));
|
|
|
+
|
|
|
+ int32 CopyBatchIndex = INDEX_NONE;
|
|
|
+ const bool bCompileBatchResult = BindingCompiler.CompileBatch(TargetDesc, PropertyBindings, CopyBatchIndex);
|
|
|
+ AITEST_TRUE("CompileBatch should succeed", bCompileBatchResult);
|
|
|
+ AITEST_NOT_EQUAL("CopyBatchIndex should not be INDEX_NONE", CopyBatchIndex, (int32)INDEX_NONE);
|
|
|
+
|
|
|
+ BindingCompiler.Finalize();
|
|
|
+
|
|
|
+ const bool bResolveResult = Bindings.ResolvePaths();
|
|
|
+ AITEST_TRUE("ResolvePaths should succeed", bResolveResult);
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyCopy SourceA;
|
|
|
+ SourceA.Item.B = 123;
|
|
|
+ SourceA.Array.AddDefaulted_GetRef().A = 1;
|
|
|
+ SourceA.Array.AddDefaulted_GetRef().B = 2;
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyCopy SourceB;
|
|
|
+ SourceB.Item.A = 41;
|
|
|
+ SourceB.Item.B = 42;
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyCopy Target;
|
|
|
+
|
|
|
+ AITEST_TRUE("SourceAIndex should be less than max number of source structs.", SourceAIndex < Bindings.GetSourceStructNum());
|
|
|
+ AITEST_TRUE("SourceBIndex should be less than max number of source structs.", SourceBIndex < Bindings.GetSourceStructNum());
|
|
|
+
|
|
|
+ TArray<FEzAbilityDataView> SourceViews;
|
|
|
+ SourceViews.SetNum(Bindings.GetSourceStructNum());
|
|
|
+ SourceViews[SourceAIndex] = FEzAbilityDataView(FStructView::Make(SourceA));
|
|
|
+ SourceViews[SourceBIndex] = FEzAbilityDataView(FStructView::Make(SourceB));
|
|
|
+ FEzAbilityDataView TargetView(FStructView::Make(Target));
|
|
|
+
|
|
|
+ bool bCopyResult = true;
|
|
|
+ for (const FEzAbilityPropertyCopy& Copy : Bindings.GetBatchCopies(FEzAbilityIndex16(CopyBatchIndex)))
|
|
|
+ {
|
|
|
+ bCopyResult &= Bindings.CopyProperty(Copy, SourceViews[Copy.SourceDataHandle.GetIndex()], TargetView);
|
|
|
+ }
|
|
|
+ AITEST_TRUE("CopyTo should succeed", bCopyResult);
|
|
|
+
|
|
|
+ // Due to binding sorting, we expect them to executed in this order (sorted based on target access, earliest to latest)
|
|
|
+ // SourceA.Array -> Target.Array
|
|
|
+ // SourceB.Item -> Target.Array[1]
|
|
|
+ // SourceA.Item.B -> Target.Array[1].B
|
|
|
+
|
|
|
+ AITEST_EQUAL("Expect TargetArray to be copied from SourceA", Target.Array.Num(), SourceA.Array.Num());
|
|
|
+ AITEST_EQUAL("Expect Target.Array[0].A copied from SourceA.Array[0].A", Target.Array[0].A, SourceA.Array[0].A);
|
|
|
+ AITEST_EQUAL("Expect Target.Array[0].B copied from SourceA.Array[0].B", Target.Array[0].B, SourceA.Array[0].B);
|
|
|
+ AITEST_EQUAL("Expect Target.Array[1].A copied from SourceB.Item.A", Target.Array[1].A, SourceB.Item.A);
|
|
|
+ AITEST_EQUAL("Expect Target.Array[1].B copied from SourceA.Item.B", Target.Array[1].B, SourceA.Item.B);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_BindingsCompiler, "System.EzAbility.BindingsCompiler");
|
|
|
+
|
|
|
+// struct FEzAbilityTest_PropertyFunctions : FAITestBase
|
|
|
+// {
|
|
|
+// virtual bool InstantTest() override
|
|
|
+// {
|
|
|
+// UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+// UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+// FEzAbilityPropertyPathSegment PathSegmentToFuncResult = FEzAbilityPropertyPathSegment(TEXT("Result"));
|
|
|
+//
|
|
|
+// // Condition with property function binding.
|
|
|
+// {
|
|
|
+// TEzAbilityEditorNode<FAbilityCompareIntCondition>& EnterCond = Root.AddEnterCondition<FAbilityCompareIntCondition>(EGenericCheck::Equal);
|
|
|
+// EnterCond.GetInstanceData().Right = 1;
|
|
|
+// EditorData.AddPropertyBinding(CastChecked<UScriptStruct>(FTestPropertyFunction::StaticStruct()), {PathSegmentToFuncResult}, FEzAbilityPropertyPath(EnterCond.ID, TEXT("Left")));
|
|
|
+// }
|
|
|
+//
|
|
|
+// // Task with multiple nested property function bindings.
|
|
|
+// auto& TaskA = Root.AddTask<FAbilityTestTask_PrintAndResetValue>(FName(TEXT("TaskA")));
|
|
|
+// constexpr int32 TaskAPropertyFunctionsAmount = 10;
|
|
|
+// {
|
|
|
+// EditorData.AddPropertyBinding(CastChecked<UScriptStruct>(FTestPropertyFunction::StaticStruct()), {PathSegmentToFuncResult}, FEzAbilityPropertyPath(TaskA.ID, TEXT("Value")));
|
|
|
+//
|
|
|
+// for (int32 i = 0; i < TaskAPropertyFunctionsAmount - 1; ++i)
|
|
|
+// {
|
|
|
+// const FEzAbilityPropertyPathBinding& LastBinding = EditorData.GetPropertyEditorBindings()->GetBindings().Last();
|
|
|
+// const FGuid LastBindingPropertyFuncID = LastBinding.GetPropertyFunctionNode().Get<const FEzAbilityEditorNode>().ID;
|
|
|
+// EditorData.AddPropertyBinding(CastChecked<UScriptStruct>(FTestPropertyFunction::StaticStruct()), {PathSegmentToFuncResult}, FEzAbilityPropertyPath(LastBindingPropertyFuncID, TEXT("Input")));
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// // Task bound to state parameter with multiple nested property function bindings.
|
|
|
+// auto& TaskB = Root.AddTask<FAbilityTestTask_PrintAndResetValue>(FName(TEXT("TaskB")));
|
|
|
+// constexpr int32 ParameterPropertyFunctionsAmount = 5;
|
|
|
+// {
|
|
|
+// Root.Parameters.Parameters.AddProperty(FName(TEXT("Int")), EPropertyBagPropertyType::Int32);
|
|
|
+// const FEzAbilityPropertyPath PathToProperty = FEzAbilityPropertyPath(Root.Parameters.ID, TEXT("Int"));
|
|
|
+// EditorData.AddPropertyBinding(PathToProperty, FEzAbilityPropertyPath(TaskB.ID, TEXT("Value")));
|
|
|
+// EditorData.AddPropertyBinding(CastChecked<UScriptStruct>(FTestPropertyFunction::StaticStruct()), {PathSegmentToFuncResult}, PathToProperty);
|
|
|
+//
|
|
|
+// for (int32 i = 0; i < ParameterPropertyFunctionsAmount - 1; ++i)
|
|
|
+// {
|
|
|
+// const FEzAbilityPropertyPathBinding& LastBinding = EditorData.GetPropertyEditorBindings()->GetBindings().Last();
|
|
|
+// const FGuid LastBindingPropertyFuncID = LastBinding.GetPropertyFunctionNode().Get<const FEzAbilityEditorNode>().ID;
|
|
|
+// EditorData.AddPropertyBinding(CastChecked<UScriptStruct>(FTestPropertyFunction::StaticStruct()), {PathSegmentToFuncResult}, FEzAbilityPropertyPath(LastBindingPropertyFuncID, TEXT("Input")));
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// FEzAbilityCompilerLog Log;
|
|
|
+// FEzAbilityCompiler Compiler(Log);
|
|
|
+// const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+// AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+//
|
|
|
+// FEzAbilityInstanceData InstanceData;
|
|
|
+// FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+// const bool bInitSucceeded = Exec.IsValid();
|
|
|
+// AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+//
|
|
|
+// FEzAbilityParameter Parameter;
|
|
|
+// FText OutText;
|
|
|
+// Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+// AITEST_TRUE(*FString::Printf(TEXT("EzAbility TaskA should enter state with value %d"), TaskAPropertyFunctionsAmount), Exec.Expect(TaskA.GetName(), *FString::Printf(TEXT("EnterState%d"), TaskAPropertyFunctionsAmount)));
|
|
|
+// AITEST_TRUE(*FString::Printf(TEXT("EzAbility TaskB should enter state with value %d"), ParameterPropertyFunctionsAmount), Exec.Expect(TaskB.GetName(), *FString::Printf(TEXT("EnterState%d"), ParameterPropertyFunctionsAmount)));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// Exec.Tick(0.1f);
|
|
|
+// AITEST_TRUE(*FString::Printf(TEXT("EzAbility TaskA should tick with value %d"), TaskAPropertyFunctionsAmount), Exec.Expect(TaskA.GetName(), *FString::Printf(TEXT("Tick%d"), TaskAPropertyFunctionsAmount)));
|
|
|
+// AITEST_TRUE(*FString::Printf(TEXT("EzAbility TaskB should tick with value %d"), ParameterPropertyFunctionsAmount), Exec.Expect(TaskB.GetName(), *FString::Printf(TEXT("Tick%d"), ParameterPropertyFunctionsAmount)));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// Exec.Stop(EAbilityRunStatus::Stopped);
|
|
|
+// AITEST_TRUE(*FString::Printf(TEXT("EzAbility TaskA should exit state with value %d"), TaskAPropertyFunctionsAmount), Exec.Expect(TaskA.GetName(), *FString::Printf(TEXT("ExitState%d"), TaskAPropertyFunctionsAmount)));
|
|
|
+// AITEST_TRUE(*FString::Printf(TEXT("EzAbility TaskB should exit state with value %d"), ParameterPropertyFunctionsAmount), Exec.Expect(TaskB.GetName(), *FString::Printf(TEXT("ExitState%d"), ParameterPropertyFunctionsAmount)));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_PropertyFunctions, "System.EzAbility.PropertyFunctions");
|
|
|
+
|
|
|
+struct FEzAbilityTest_CopyObjects : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityPropertyBindings Bindings;
|
|
|
+ FEzAbilityPropertyBindingCompiler BindingCompiler;
|
|
|
+
|
|
|
+ const bool bInitResult = BindingCompiler.Init(Bindings, Log);
|
|
|
+ AITEST_TRUE("Expect init to succeed", bInitResult);
|
|
|
+
|
|
|
+ FEzAbilityBindableStructDesc SourceDesc;
|
|
|
+ SourceDesc.Name = FName(TEXT("Source"));
|
|
|
+ SourceDesc.Struct = TBaseStructure<FEzAbilityTest_PropertyCopyObjects>::Get();
|
|
|
+ SourceDesc.DataSource = EEzAbilityBindableStructSource::Parameter;
|
|
|
+ SourceDesc.DataHandle = FEzAbilityDataHandle(EEzAbilityDataSourceType::ContextData, 0); // Used as index to SourceViews below.
|
|
|
+ SourceDesc.ID = FGuid::NewGuid();
|
|
|
+
|
|
|
+ FEzAbilityBindableStructDesc TargetADesc;
|
|
|
+ TargetADesc.Name = FName(TEXT("TargetA"));
|
|
|
+ TargetADesc.Struct = TBaseStructure<FEzAbilityTest_PropertyCopyObjects>::Get();
|
|
|
+ TargetADesc.DataSource = EEzAbilityBindableStructSource::Parameter;
|
|
|
+ TargetADesc.ID = FGuid::NewGuid();
|
|
|
+
|
|
|
+ FEzAbilityBindableStructDesc TargetBDesc;
|
|
|
+ TargetBDesc.Name = FName(TEXT("TargetB"));
|
|
|
+ TargetBDesc.Struct = TBaseStructure<FEzAbilityTest_PropertyCopyObjects>::Get();
|
|
|
+ TargetBDesc.DataSource = EEzAbilityBindableStructSource::Parameter;
|
|
|
+ TargetBDesc.ID = FGuid::NewGuid();
|
|
|
+
|
|
|
+ const int32 SourceIndex = BindingCompiler.AddSourceStruct(SourceDesc);
|
|
|
+
|
|
|
+ TArray<FEzAbilityPropertyPathBinding> PropertyBindings;
|
|
|
+ // One-to-one copy from source to target A
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceDesc.ID, TEXT("Object"), TargetADesc.ID, TEXT("Object")));
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceDesc.ID, TEXT("SoftObject"), TargetADesc.ID, TEXT("SoftObject")));
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceDesc.ID, TEXT("Class"), TargetADesc.ID, TEXT("Class")));
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceDesc.ID, TEXT("SoftClass"), TargetADesc.ID, TEXT("SoftClass")));
|
|
|
+
|
|
|
+ // Cross copy from source to target B
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceDesc.ID, TEXT("SoftObject"), TargetBDesc.ID, TEXT("Object")));
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceDesc.ID, TEXT("Object"), TargetBDesc.ID, TEXT("SoftObject")));
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceDesc.ID, TEXT("SoftClass"), TargetBDesc.ID, TEXT("Class")));
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceDesc.ID, TEXT("Class"), TargetBDesc.ID, TEXT("SoftClass")));
|
|
|
+
|
|
|
+ int32 TargetACopyBatchIndex = INDEX_NONE;
|
|
|
+ const bool bCompileBatchResultA = BindingCompiler.CompileBatch(TargetADesc, PropertyBindings, TargetACopyBatchIndex);
|
|
|
+ AITEST_TRUE("CompileBatchResultA should succeed", bCompileBatchResultA);
|
|
|
+ AITEST_NOT_EQUAL("TargetACopyBatchIndex should not be INDEX_NONE", TargetACopyBatchIndex, (int32)INDEX_NONE);
|
|
|
+
|
|
|
+ int32 TargetBCopyBatchIndex = INDEX_NONE;
|
|
|
+ const bool bCompileBatchResultB = BindingCompiler.CompileBatch(TargetBDesc, PropertyBindings, TargetBCopyBatchIndex);
|
|
|
+ AITEST_TRUE("CompileBatchResultB should succeed", bCompileBatchResultB);
|
|
|
+ AITEST_NOT_EQUAL("TargetBCopyBatchIndex should not be INDEX_NONE", TargetBCopyBatchIndex, (int32)INDEX_NONE);
|
|
|
+
|
|
|
+ BindingCompiler.Finalize();
|
|
|
+
|
|
|
+ const bool bResolveResult = Bindings.ResolvePaths();
|
|
|
+ AITEST_TRUE("ResolvePaths should succeed", bResolveResult);
|
|
|
+
|
|
|
+ UEzAbilityTest_PropertyObject* ObjectA = NewObject<UEzAbilityTest_PropertyObject>();
|
|
|
+ UEzAbilityTest_PropertyObject2* ObjectB = NewObject<UEzAbilityTest_PropertyObject2>();
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyCopyObjects Source;
|
|
|
+ Source.Object = ObjectA;
|
|
|
+ Source.SoftObject = ObjectB;
|
|
|
+ Source.Class = UEzAbilityTest_PropertyObject::StaticClass();
|
|
|
+ Source.SoftClass = UEzAbilityTest_PropertyObject::StaticClass();
|
|
|
+
|
|
|
+ AITEST_TRUE("SourceIndex should be less than max number of source structs.", SourceIndex < Bindings.GetSourceStructNum());
|
|
|
+
|
|
|
+ TArray<FEzAbilityDataView> SourceViews;
|
|
|
+ SourceViews.SetNum(Bindings.GetSourceStructNum());
|
|
|
+ SourceViews[SourceIndex] = FEzAbilityDataView(FStructView::Make(Source));
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyCopyObjects TargetA;
|
|
|
+ bool bCopyResultA = true;
|
|
|
+ for (const FEzAbilityPropertyCopy& Copy : Bindings.GetBatchCopies(FEzAbilityIndex16(TargetACopyBatchIndex)))
|
|
|
+ {
|
|
|
+ bCopyResultA &= Bindings.CopyProperty(Copy, SourceViews[Copy.SourceDataHandle.GetIndex()], FStructView::Make(TargetA));
|
|
|
+ }
|
|
|
+ AITEST_TRUE("CopyTo should succeed", bCopyResultA);
|
|
|
+
|
|
|
+ AITEST_TRUE("Expect TargetA.Object == Source.Object", TargetA.Object == Source.Object);
|
|
|
+ AITEST_TRUE("Expect TargetA.SoftObject == Source.SoftObject", TargetA.SoftObject == Source.SoftObject);
|
|
|
+ AITEST_TRUE("Expect TargetA.Class == Source.Class", TargetA.Class == Source.Class);
|
|
|
+ AITEST_TRUE("Expect TargetA.SoftClass == Source.SoftClass", TargetA.SoftClass == Source.SoftClass);
|
|
|
+
|
|
|
+ // Copying to TargetB should not affect TargetA
|
|
|
+ TargetA.Object = nullptr;
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyCopyObjects TargetB;
|
|
|
+ bool bCopyResultB = true;
|
|
|
+ for (const FEzAbilityPropertyCopy& Copy : Bindings.GetBatchCopies(FEzAbilityIndex16(TargetBCopyBatchIndex)))
|
|
|
+ {
|
|
|
+ bCopyResultB &= Bindings.CopyProperty(Copy, SourceViews[Copy.SourceDataHandle.GetIndex()], FStructView::Make(TargetB));
|
|
|
+ }
|
|
|
+ AITEST_TRUE("CopyTo should succeed", bCopyResultB);
|
|
|
+
|
|
|
+ AITEST_TRUE("Expect TargetB.Object == Source.SoftObject", TSoftObjectPtr<UObject>(TargetB.Object) == Source.SoftObject);
|
|
|
+ AITEST_TRUE("Expect TargetB.SoftObject == Source.Object", TargetB.SoftObject == TSoftObjectPtr<UObject>(Source.Object));
|
|
|
+ AITEST_TRUE("Expect TargetB.Class == Source.SoftClass", TSoftClassPtr<UObject>(TargetB.Class) == Source.SoftClass);
|
|
|
+ AITEST_TRUE("Expect TargetB.SoftClass == Source.Class", TargetB.SoftClass == TSoftClassPtr<UObject>(Source.Class));
|
|
|
+
|
|
|
+ AITEST_TRUE("Expect TargetA.Object == nullptr after copy of TargetB", TargetA.Object == nullptr);
|
|
|
+
|
|
|
+ // Collect ObjectA and ObjectB, soft object paths should still copy ok.
|
|
|
+ ObjectA = nullptr;
|
|
|
+ ObjectB = nullptr;
|
|
|
+ Source.Object = nullptr;
|
|
|
+ CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyCopyObjects TargetC;
|
|
|
+ bool bCopyResultC = true;
|
|
|
+ for (const FEzAbilityPropertyCopy& Copy : Bindings.GetBatchCopies(FEzAbilityIndex16(TargetACopyBatchIndex)))
|
|
|
+ {
|
|
|
+ bCopyResultB &= Bindings.CopyProperty(Copy, SourceViews[Copy.SourceDataHandle.GetIndex()], FStructView::Make(TargetC));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ AITEST_TRUE("CopyTo should succeed", bCopyResultC);
|
|
|
+ AITEST_TRUE("Expect TargetC.SoftObject == Source.SoftObject after GC", TargetC.SoftObject == Source.SoftObject);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_CopyObjects, "System.EzAbility.CopyObjects");
|
|
|
+
|
|
|
+struct FEzAbilityTest_References : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityPropertyBindings Bindings;
|
|
|
+ FEzAbilityPropertyBindingCompiler BindingCompiler;
|
|
|
+
|
|
|
+ const bool bInitResult = BindingCompiler.Init(Bindings, Log);
|
|
|
+ AITEST_TRUE("Expect init to succeed", bInitResult);
|
|
|
+
|
|
|
+ FEzAbilityBindableStructDesc SourceDesc;
|
|
|
+ SourceDesc.Name = FName(TEXT("Source"));
|
|
|
+ SourceDesc.Struct = TBaseStructure<FEzAbilityTest_PropertyRefSourceStruct>::Get();
|
|
|
+ SourceDesc.DataSource = EEzAbilityBindableStructSource::Parameter;
|
|
|
+ SourceDesc.DataHandle = FEzAbilityDataHandle(EEzAbilityDataSourceType::ContextData, 0);
|
|
|
+ SourceDesc.ID = FGuid::NewGuid();
|
|
|
+ BindingCompiler.AddSourceStruct(SourceDesc);
|
|
|
+
|
|
|
+ FEzAbilityBindableStructDesc TargetDesc;
|
|
|
+ TargetDesc.Name = FName(TEXT("Target"));
|
|
|
+ TargetDesc.Struct = TBaseStructure<FEzAbilityTest_PropertyRefTargetStruct>::Get();
|
|
|
+ TargetDesc.DataSource = EEzAbilityBindableStructSource::Parameter;
|
|
|
+ TargetDesc.ID = FGuid::NewGuid();
|
|
|
+
|
|
|
+ TArray<FEzAbilityPropertyPathBinding> PropertyBindings;
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceDesc.ID, TEXT("Item"), TargetDesc.ID, TEXT("RefToStruct")));
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceDesc.ID, TEXT("Item.A"), TargetDesc.ID, TEXT("RefToInt")));
|
|
|
+ PropertyBindings.Add(UE::EzAbility::Tests::MakeBinding(SourceDesc.ID, TEXT("Array"), TargetDesc.ID, TEXT("RefToStructArray")));
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyRefSourceStruct Source;
|
|
|
+ FEzAbilityDataView SourceView = FEzAbilityDataView(FStructView::Make(Source));
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyRefTargetStruct Target;
|
|
|
+ FEzAbilityDataView TargetView(FStructView::Make(Target));
|
|
|
+
|
|
|
+ TMap<FGuid, const FEzAbilityDataView> IDToStructValue;
|
|
|
+ IDToStructValue.Emplace(SourceDesc.ID, SourceView);
|
|
|
+ IDToStructValue.Emplace(TargetDesc.ID, TargetView);
|
|
|
+
|
|
|
+ const bool bCompileReferencesResult = BindingCompiler.CompileReferences(TargetDesc, PropertyBindings, TargetView);
|
|
|
+ AITEST_TRUE("CompileReferences should succeed", bCompileReferencesResult);
|
|
|
+
|
|
|
+ BindingCompiler.Finalize();
|
|
|
+
|
|
|
+ const bool bResolveResult = Bindings.ResolvePaths();
|
|
|
+ AITEST_TRUE("ResolvePaths should succeed", bResolveResult);
|
|
|
+
|
|
|
+ {
|
|
|
+ const FEzAbilityPropertyAccess* PropertyAccess = Bindings.GetPropertyAccess(Target.RefToStruct);
|
|
|
+ AITEST_NOT_NULL("GetPropertyAccess should succeed", PropertyAccess);
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyStruct* Reference = Bindings.GetMutablePropertyPtr<FEzAbilityTest_PropertyStruct>(SourceView, *PropertyAccess);
|
|
|
+ AITEST_EQUAL("Expect RefToStruct to point to SourceA.Item", Reference, &Source.Item);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ const FEzAbilityPropertyAccess* PropertyAccess = Bindings.GetPropertyAccess(Target.RefToInt);
|
|
|
+ AITEST_NOT_NULL("GetPropertyAccess should succeed", PropertyAccess);
|
|
|
+
|
|
|
+ int32* Reference = Bindings.GetMutablePropertyPtr<int32>(SourceView, *PropertyAccess);
|
|
|
+ AITEST_EQUAL("Expect RefToInt to point to SourceA.Item.A", Reference, &Source.Item);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ const FEzAbilityPropertyAccess* PropertyAccess = Bindings.GetPropertyAccess(Target.RefToStructArray);
|
|
|
+ AITEST_NOT_NULL("GetPropertyAccess should succeed", PropertyAccess);
|
|
|
+
|
|
|
+ TArray<FEzAbilityTest_PropertyStruct>* Reference = Bindings.GetMutablePropertyPtr<TArray<FEzAbilityTest_PropertyStruct>>(SourceView, *PropertyAccess);
|
|
|
+ AITEST_EQUAL("Expect RefToStructArray to point to SourceA.Array", Reference, &Source.Array);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_References, "System.EzAbility.References");
|
|
|
+
|
|
|
+struct FEzAbilityTest_ReferencesConstness : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityPropertyBindings Bindings;
|
|
|
+ FEzAbilityPropertyBindingCompiler BindingCompiler;
|
|
|
+
|
|
|
+ const bool bInitResult = BindingCompiler.Init(Bindings, Log);
|
|
|
+ AITEST_TRUE("Expect init to succeed", bInitResult);
|
|
|
+
|
|
|
+ FEzAbilityBindableStructDesc SourceAsTaskDesc;
|
|
|
+ SourceAsTaskDesc.Name = FName(TEXT("SourceTask"));
|
|
|
+ SourceAsTaskDesc.Struct = TBaseStructure<FEzAbilityTest_PropertyRefSourceStruct>::Get();
|
|
|
+ SourceAsTaskDesc.DataSource = EEzAbilityBindableStructSource::Task;
|
|
|
+ SourceAsTaskDesc.DataHandle = FEzAbilityDataHandle(EEzAbilityDataSourceType::ContextData, 0);
|
|
|
+ SourceAsTaskDesc.ID = FGuid::NewGuid();
|
|
|
+ BindingCompiler.AddSourceStruct(SourceAsTaskDesc);
|
|
|
+
|
|
|
+ FEzAbilityBindableStructDesc SourceAsContextDesc;
|
|
|
+ SourceAsContextDesc.Name = FName(TEXT("SourceContext"));
|
|
|
+ SourceAsContextDesc.Struct = TBaseStructure<FEzAbilityTest_PropertyRefSourceStruct>::Get();
|
|
|
+ SourceAsContextDesc.DataSource = EEzAbilityBindableStructSource::Context;
|
|
|
+ SourceAsContextDesc.DataHandle = FEzAbilityDataHandle(EEzAbilityDataSourceType::ContextData, 0);
|
|
|
+ SourceAsContextDesc.ID = FGuid::NewGuid();
|
|
|
+ BindingCompiler.AddSourceStruct(SourceAsContextDesc);
|
|
|
+
|
|
|
+ FEzAbilityBindableStructDesc TargetDesc;
|
|
|
+ TargetDesc.Name = FName(TEXT("Target"));
|
|
|
+ TargetDesc.Struct = TBaseStructure<FEzAbilityTest_PropertyRefTargetStruct>::Get();
|
|
|
+ TargetDesc.DataSource = EEzAbilityBindableStructSource::Parameter;
|
|
|
+ TargetDesc.ID = FGuid::NewGuid();
|
|
|
+
|
|
|
+ FEzAbilityPropertyPathBinding TaskPropertyBinding = UE::EzAbility::Tests::MakeBinding(SourceAsTaskDesc.ID, TEXT("Item"), TargetDesc.ID, TEXT("RefToStruct"));
|
|
|
+ FEzAbilityPropertyPathBinding TaskOutputPropertyBinding = UE::EzAbility::Tests::MakeBinding(SourceAsTaskDesc.ID, TEXT("OutputItem"), TargetDesc.ID, TEXT("RefToStruct"));
|
|
|
+
|
|
|
+ FEzAbilityPropertyPathBinding ContextPropertyBinding = UE::EzAbility::Tests::MakeBinding(SourceAsTaskDesc.ID, TEXT("Item"), TargetDesc.ID, TEXT("RefToStruct"));
|
|
|
+ FEzAbilityPropertyPathBinding ContextOutputPropertyBinding = UE::EzAbility::Tests::MakeBinding(SourceAsTaskDesc.ID, TEXT("Item"), TargetDesc.ID, TEXT("RefToStruct"));
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyRefSourceStruct SourceAsTask;
|
|
|
+ FEzAbilityDataView SourceAsTaskView(FStructView::Make(SourceAsTask));
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyRefSourceStruct SourceAsContext;
|
|
|
+ FEzAbilityDataView SourceAsContextView(FStructView::Make(SourceAsContext));
|
|
|
+
|
|
|
+ FEzAbilityTest_PropertyRefTargetStruct Target;
|
|
|
+ FEzAbilityDataView TargetView(FStructView::Make(Target));
|
|
|
+
|
|
|
+ TMap<FGuid, const FEzAbilityDataView> IDToStructValue;
|
|
|
+ IDToStructValue.Emplace(SourceAsTaskDesc.ID, SourceAsTaskView);
|
|
|
+ IDToStructValue.Emplace(SourceAsContextDesc.ID, SourceAsContextView);
|
|
|
+ IDToStructValue.Emplace(TargetDesc.ID, TargetView);
|
|
|
+
|
|
|
+ {
|
|
|
+ const bool bCompileReferenceResult = BindingCompiler.CompileReferences(TargetDesc, {TaskPropertyBinding}, TargetView);
|
|
|
+ AITEST_FALSE("CompileReferences should fail", bCompileReferenceResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ const bool bCompileReferenceResult = BindingCompiler.CompileReferences(TargetDesc, {TaskOutputPropertyBinding}, TargetView);
|
|
|
+ AITEST_TRUE("CompileReferences should succeed", bCompileReferenceResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ const bool bCompileReferenceResult = BindingCompiler.CompileReferences(TargetDesc, {ContextPropertyBinding}, TargetView);
|
|
|
+ AITEST_FALSE("CompileReferences should fail", bCompileReferenceResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ const bool bCompileReferenceResult = BindingCompiler.CompileReferences(TargetDesc, {ContextOutputPropertyBinding}, TargetView);
|
|
|
+ AITEST_FALSE("CompileReferences should fail", bCompileReferenceResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_ReferencesConstness, "System.EzAbility.ReferencesConstness");
|
|
|
+
|
|
|
+struct FEzAbilityTest_FollowTransitions : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ EditorData.RootParameters.Parameters.AddProperty(FName(TEXT("Int")), EPropertyBagPropertyType::Int32);
|
|
|
+ EditorData.RootParameters.Parameters.SetValueInt32(FName(TEXT("Int")), 1);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& StateTrans = Root.AddChildState(FName(TEXT("Trans")));
|
|
|
+ UEzAbilityState& StateA = Root.AddChildState(FName(TEXT("A")));
|
|
|
+ UEzAbilityState& StateB = Root.AddChildState(FName(TEXT("B")));
|
|
|
+ UEzAbilityState& StateC = Root.AddChildState(FName(TEXT("C")));
|
|
|
+
|
|
|
+ // Root
|
|
|
+
|
|
|
+ // Trans
|
|
|
+ {
|
|
|
+ StateTrans.SelectionBehavior = EEzAbilityStateSelectionBehavior::TryFollowTransitions;
|
|
|
+
|
|
|
+ {
|
|
|
+ // This transition should be skipped due to the condition
|
|
|
+ FEzAbilityTransition& TransA = StateTrans.AddTransition(EEzAbilityTransitionTrigger::OnTick, EEzAbilityTransitionType::GotoState, &StateA);
|
|
|
+ TEzAbilityEditorNode<FAbilityCompareIntCondition>& TransIntCond = TransA.AddCondition<FAbilityCompareIntCondition>(EGenericCheck::Equal);
|
|
|
+ TransIntCond.GetInstanceData().Right = 0;
|
|
|
+ EditorData.AddPropertyBinding(
|
|
|
+ FEzAbilityPropertyPath(EditorData.RootParameters.ID, TEXT("Int")),
|
|
|
+ FEzAbilityPropertyPath(TransIntCond.ID, TEXT("Left")));
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ // This transition leads to selection, but will be overridden.
|
|
|
+ FEzAbilityTransition& TransB = StateTrans.AddTransition(EEzAbilityTransitionTrigger::OnTick, EEzAbilityTransitionType::GotoState, &StateB);
|
|
|
+ TransB.Priority = EEzAbilityTransitionPriority::Normal;
|
|
|
+ TEzAbilityEditorNode<FAbilityCompareIntCondition>& TransIntCond = TransB.AddCondition<FAbilityCompareIntCondition>(EGenericCheck::Equal);
|
|
|
+ TransIntCond.GetInstanceData().Right = 1;
|
|
|
+ EditorData.AddPropertyBinding(
|
|
|
+ FEzAbilityPropertyPath(EditorData.RootParameters.ID, TEXT("Int")),
|
|
|
+ FEzAbilityPropertyPath(TransIntCond.ID, TEXT("Left")));
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ // This transition is selected, should override previous one due to priority.
|
|
|
+ FEzAbilityTransition& TransC = StateTrans.AddTransition(EEzAbilityTransitionTrigger::OnTick, EEzAbilityTransitionType::GotoState, &StateC);
|
|
|
+ TransC.Priority = EEzAbilityTransitionPriority::High;
|
|
|
+ TEzAbilityEditorNode<FAbilityCompareIntCondition>& TransIntCond = TransC.AddCondition<FAbilityCompareIntCondition>(EGenericCheck::Equal);
|
|
|
+ TransIntCond.GetInstanceData().Right = 1;
|
|
|
+ EditorData.AddPropertyBinding(
|
|
|
+ FEzAbilityPropertyPath(EditorData.RootParameters.ID, TEXT("Int")),
|
|
|
+ FEzAbilityPropertyPath(TransIntCond.ID, TEXT("Left")));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ auto& TaskA = StateA.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskA")));
|
|
|
+ auto& TaskB = StateB.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskB")));
|
|
|
+ auto& TaskC = StateC.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskC")));
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_FALSE("EzAbility TaskA should not enter state", Exec.Expect(TaskA.GetName(), EnterStateStr));
|
|
|
+ AITEST_FALSE("EzAbility TaskB should not enter state", Exec.Expect(TaskB.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility TaskC should enter state", Exec.Expect(TaskC.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_FollowTransitions, "System.EzAbility.FollowTransitions");
|
|
|
+
|
|
|
+struct FEzAbilityTest_InfiniteLoop : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ EditorData.RootParameters.Parameters.AddProperty(FName(TEXT("Int")), EPropertyBagPropertyType::Int32);
|
|
|
+ EditorData.RootParameters.Parameters.SetValueInt32(FName(TEXT("Int")), 1);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& StateA = Root.AddChildState(FName(TEXT("A")));
|
|
|
+ UEzAbilityState& StateB = StateA.AddChildState(FName(TEXT("B")));
|
|
|
+
|
|
|
+ // Root
|
|
|
+
|
|
|
+ // State A
|
|
|
+ {
|
|
|
+ StateA.SelectionBehavior = EEzAbilityStateSelectionBehavior::TryFollowTransitions;
|
|
|
+ {
|
|
|
+ // A -> B
|
|
|
+ FEzAbilityTransition& Trans = StateA.AddTransition(EEzAbilityTransitionTrigger::OnTick, EEzAbilityTransitionType::GotoState, &StateB);
|
|
|
+ TEzAbilityEditorNode<FAbilityCompareIntCondition>& TransIntCond = Trans.AddCondition<FAbilityCompareIntCondition>(EGenericCheck::Equal);
|
|
|
+ TransIntCond.GetInstanceData().Right = 1;
|
|
|
+ EditorData.AddPropertyBinding(
|
|
|
+ FEzAbilityPropertyPath(EditorData.RootParameters.ID, TEXT("Int")),
|
|
|
+ FEzAbilityPropertyPath(TransIntCond.ID, TEXT("Left")));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // State B
|
|
|
+ {
|
|
|
+ StateB.SelectionBehavior = EEzAbilityStateSelectionBehavior::TryFollowTransitions;
|
|
|
+ {
|
|
|
+ // B -> A
|
|
|
+ FEzAbilityTransition& Trans = StateB.AddTransition(EEzAbilityTransitionTrigger::OnTick, EEzAbilityTransitionType::GotoState, &StateA);
|
|
|
+ TEzAbilityEditorNode<FAbilityCompareIntCondition>& TransIntCond = Trans.AddCondition<FAbilityCompareIntCondition>(EGenericCheck::Equal);
|
|
|
+ TransIntCond.GetInstanceData().Right = 1;
|
|
|
+ EditorData.AddPropertyBinding(
|
|
|
+ FEzAbilityPropertyPath(EditorData.RootParameters.ID, TEXT("Int")),
|
|
|
+ FEzAbilityPropertyPath(TransIntCond.ID, TEXT("Left")));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ auto& TaskA = StateA.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskA")));
|
|
|
+ auto& TaskB = StateB.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskB")));
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+
|
|
|
+ GetTestRunner().AddExpectedError(TEXT("Loop detected when trying to select state"), EAutomationExpectedErrorFlags::Contains, 1);
|
|
|
+ GetTestRunner().AddExpectedError(TEXT("Failed to select initial state"), EAutomationExpectedErrorFlags::Contains, 1);
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_EQUAL("Start should fail", Status, EAbilityRunStatus::Failed);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_InfiniteLoop, "System.EzAbility.InfiniteLoop");
|
|
|
+
|
|
|
+
|
|
|
+//
|
|
|
+// The stop tests test how the combinations of execution path to stop the tree are reported on ExitState() transition.
|
|
|
+//
|
|
|
+struct FEzAbilityTest_Stop : FAITestBase
|
|
|
+{
|
|
|
+ UEzAbility& SetupTree()
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& StateA = Root.AddChildState(FName(TEXT("A")));
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& TaskA = StateA.AddTask<FAbilityTestTask_Stand>(TaskAName);
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& GlobalTask = EditorData.AddGlobalTask<FAbilityTestTask_Stand>(GlobalTaskName);
|
|
|
+
|
|
|
+ // Transition success
|
|
|
+ StateA.AddTransition(EEzAbilityTransitionTrigger::OnStateSucceeded, EEzAbilityTransitionType::Succeeded);
|
|
|
+ StateA.AddTransition(EEzAbilityTransitionTrigger::OnStateFailed, EEzAbilityTransitionType::Failed);
|
|
|
+
|
|
|
+ GlobalTask.GetNode().TicksToCompletion = GlobalTaskTicks;
|
|
|
+ GlobalTask.GetNode().TickCompletionResult = GlobalTaskStatus;
|
|
|
+ GlobalTask.GetNode().EnterStateResult = GlobalTaskEnterStatus;
|
|
|
+
|
|
|
+ TaskA.GetNode().TicksToCompletion = NormalTaskTicks;
|
|
|
+ TaskA.GetNode().TickCompletionResult = NormalTaskStatus;
|
|
|
+ TaskA.GetNode().EnterStateResult = NormalTaskEnterStatus;
|
|
|
+
|
|
|
+ return EzAbility;
|
|
|
+ }
|
|
|
+
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = SetupTree();
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_EQUAL("Start should be running", Status, EAbilityRunStatus::Running);
|
|
|
+ AITEST_TRUE("EzAbility GlobalTask should enter state", Exec.Expect(GlobalTaskName, EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility TaskA should enter state", Exec.Expect(TaskAName, EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_EQUAL("Tree should end expectedly", Status, ExpectedStatusAfterTick);
|
|
|
+ AITEST_TRUE("EzAbility GlobalTask should get exit state expectedly", Exec.Expect(GlobalTaskName, ExpectedExitStatusStr));
|
|
|
+ AITEST_TRUE("EzAbility TaskA should get exit state expectedly", Exec.Expect(TaskAName, ExpectedExitStatusStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+protected:
|
|
|
+
|
|
|
+ const FName GlobalTaskName = FName(TEXT("GlobalTask"));
|
|
|
+ const FName TaskAName = FName(TEXT("TaskA"));
|
|
|
+
|
|
|
+ EAbilityRunStatus NormalTaskStatus = EAbilityRunStatus::Succeeded;
|
|
|
+ EAbilityRunStatus NormalTaskEnterStatus = EAbilityRunStatus::Running;
|
|
|
+ int32 NormalTaskTicks = 1;
|
|
|
+
|
|
|
+ EAbilityRunStatus GlobalTaskStatus = EAbilityRunStatus::Succeeded;
|
|
|
+ EAbilityRunStatus GlobalTaskEnterStatus = EAbilityRunStatus::Running;
|
|
|
+ int32 GlobalTaskTicks = 1;
|
|
|
+
|
|
|
+ EAbilityRunStatus ExpectedStatusAfterTick = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+ FString ExpectedExitStatusStr = TEXT("ExitSucceeded");
|
|
|
+};
|
|
|
+
|
|
|
+struct FEzAbilityTest_Stop_NormalSucceeded : FEzAbilityTest_Stop
|
|
|
+{
|
|
|
+ virtual bool SetUp() override
|
|
|
+ {
|
|
|
+ // Normal task completes as succeeded.
|
|
|
+ NormalTaskStatus = EAbilityRunStatus::Succeeded;
|
|
|
+ NormalTaskTicks = 1;
|
|
|
+
|
|
|
+ // Global task completes later
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+ // Tree should complete as succeeded.
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+ // Tasks should have Transition.CurrentRunStatus as succeeded
|
|
|
+ ExpectedExitStatusStr = TEXT("ExitSucceeded");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_NormalSucceeded, "System.EzAbility.Stop.NormalSucceeded");
|
|
|
+
|
|
|
+struct FEzAbilityTest_Stop_NormalFailed : FEzAbilityTest_Stop
|
|
|
+{
|
|
|
+ virtual bool SetUp() override
|
|
|
+ {
|
|
|
+ // Normal task completes as failed.
|
|
|
+ NormalTaskStatus = EAbilityRunStatus::Failed;
|
|
|
+ NormalTaskTicks = 1;
|
|
|
+
|
|
|
+ // Global task completes later.
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+ // Tree should complete as failed.
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::Failed;
|
|
|
+
|
|
|
+ // Tasks should have Transition.CurrentRunStatus as failed.
|
|
|
+ ExpectedExitStatusStr = TEXT("ExitFailed");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_NormalFailed, "System.EzAbility.Stop.NormalFailed");
|
|
|
+
|
|
|
+
|
|
|
+struct FEzAbilityTest_Stop_GlobalSucceeded : FEzAbilityTest_Stop
|
|
|
+{
|
|
|
+ virtual bool SetUp() override
|
|
|
+ {
|
|
|
+ // Normal task completes later.
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+
|
|
|
+ // Global task completes as succeeded.
|
|
|
+ GlobalTaskStatus = EAbilityRunStatus::Succeeded;
|
|
|
+ GlobalTaskTicks = 1;
|
|
|
+
|
|
|
+ // Tree should complete as succeeded.
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+ // Tasks should have Transition.CurrentRunStatus as succeeded.
|
|
|
+ ExpectedExitStatusStr = TEXT("ExitSucceeded");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_GlobalSucceeded, "System.EzAbility.Stop.GlobalSucceeded");
|
|
|
+
|
|
|
+struct FEzAbilityTest_Stop_GlobalFailed : FEzAbilityTest_Stop
|
|
|
+{
|
|
|
+ virtual bool SetUp() override
|
|
|
+ {
|
|
|
+ // Normal task completes later
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+
|
|
|
+ // Global task completes as failed.
|
|
|
+ GlobalTaskStatus = EAbilityRunStatus::Failed;
|
|
|
+ GlobalTaskTicks = 1;
|
|
|
+
|
|
|
+ // Tree should complete as failed.
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::Failed;
|
|
|
+
|
|
|
+ // Tasks should have Transition.CurrentRunStatus as failed.
|
|
|
+ ExpectedExitStatusStr = TEXT("ExitFailed");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_GlobalFailed, "System.EzAbility.Stop.GlobalFailed");
|
|
|
+
|
|
|
+
|
|
|
+//
|
|
|
+// Tests combinations of completing the tree on EnterState.
|
|
|
+//
|
|
|
+struct FEzAbilityTest_StopEnterNormal : FEzAbilityTest_Stop
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = SetupTree();
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+
|
|
|
+ // If a normal task fails at start, the last tick status will be failed, but transition handling (and final execution status) will take place next tick.
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_EQUAL("Tree should be running after start", Status, EAbilityRunStatus::Running);
|
|
|
+ AITEST_EQUAL("Last execution status should be expected value", Exec.GetLastTickStatus(), ExpectedStatusAfterStart);
|
|
|
+
|
|
|
+ // Handles any transitions from failed transition
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_EQUAL("Start should be expected value", Status, ExpectedStatusAfterStart);
|
|
|
+ AITEST_TRUE("EzAbility GlobalTask should get exit state expectedly", Exec.Expect(GlobalTaskName, ExpectedExitStatusStr));
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility TaskA should enter state", Exec.Expect(TaskAName, EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility TaskA should report exit status", Exec.Expect(TaskAName, ExpectedExitStatusStr));
|
|
|
+
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ EAbilityRunStatus ExpectedStatusAfterStart = EAbilityRunStatus::Succeeded;
|
|
|
+ FString ExpectedExitStatusStr = TEXT("ExitSucceeded");
|
|
|
+ bool bExpectNormalTaskToRun = true;
|
|
|
+};
|
|
|
+
|
|
|
+struct FEzAbilityTest_Stop_NormalEnterSucceeded : FEzAbilityTest_StopEnterNormal
|
|
|
+{
|
|
|
+ virtual bool SetUp() override
|
|
|
+ {
|
|
|
+ // Tasks should complete later.
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+ // Normal task EnterState as succeeded, completion is handled using completion transitions.
|
|
|
+ NormalTaskEnterStatus = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+ // Tree should complete as succeeded.
|
|
|
+ ExpectedStatusAfterStart = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+ // Tasks should have Transition.CurrentRunStatus as succeeded.
|
|
|
+ ExpectedExitStatusStr = TEXT("ExitSucceeded");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_NormalEnterSucceeded, "System.EzAbility.Stop.NormalEnterSucceeded");
|
|
|
+
|
|
|
+struct FEzAbilityTest_Stop_NormalEnterFailed : FEzAbilityTest_StopEnterNormal
|
|
|
+{
|
|
|
+ virtual bool SetUp() override
|
|
|
+ {
|
|
|
+ // Tasks should complete later.
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+ // Normal task EnterState as failed, completion is handled using completion transitions.
|
|
|
+ NormalTaskEnterStatus = EAbilityRunStatus::Failed;
|
|
|
+
|
|
|
+ // Tree should complete as failed.
|
|
|
+ ExpectedStatusAfterStart = EAbilityRunStatus::Failed;
|
|
|
+
|
|
|
+ // Tasks should have Transition.CurrentRunStatus as failed.
|
|
|
+ ExpectedExitStatusStr = TEXT("ExitFailed");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_NormalEnterFailed, "System.EzAbility.Stop.NormalEnterFailed");
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+struct FEzAbilityTest_StopEnterGlobal : FEzAbilityTest_Stop
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = SetupTree();
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_EQUAL("Start should be expected value", Status, ExpectedStatusAfterStart);
|
|
|
+ AITEST_TRUE("EzAbility GlobalTask should get exit state expectedly", Exec.Expect(GlobalTaskName, ExpectedExitStatusStr));
|
|
|
+
|
|
|
+ // Normal tasks should not run
|
|
|
+ AITEST_FALSE("EzAbility TaskA should not enter state", Exec.Expect(TaskAName, EnterStateStr));
|
|
|
+ AITEST_FALSE("EzAbility TaskA should not report exit status", Exec.Expect(TaskAName, ExpectedExitStatusStr));
|
|
|
+
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ EAbilityRunStatus ExpectedStatusAfterStart = EAbilityRunStatus::Succeeded;
|
|
|
+ FString ExpectedExitStatusStr = TEXT("ExitSucceeded");
|
|
|
+};
|
|
|
+
|
|
|
+struct FEzAbilityTest_Stop_GlobalEnterSucceeded : FEzAbilityTest_StopEnterGlobal
|
|
|
+{
|
|
|
+ virtual bool SetUp() override
|
|
|
+ {
|
|
|
+ // Tasks should complete later.
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+ // Global task EnterState as succeeded, completion is handled directly based on the global task status.
|
|
|
+ GlobalTaskEnterStatus = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+ // Tree should complete as succeeded.
|
|
|
+ ExpectedStatusAfterStart = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+ // Tasks should have Transition.CurrentRunStatus as Succeeded.
|
|
|
+ ExpectedExitStatusStr = TEXT("ExitSucceeded");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_GlobalEnterSucceeded, "System.EzAbility.Stop.GlobalEnterSucceeded");
|
|
|
+
|
|
|
+struct FEzAbilityTest_Stop_GlobalEnterFailed : FEzAbilityTest_StopEnterGlobal
|
|
|
+{
|
|
|
+ virtual bool SetUp() override
|
|
|
+ {
|
|
|
+ // Tasks should complete later.
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+ // Global task EnterState as failed, completion is handled directly based on the global task status.
|
|
|
+ GlobalTaskEnterStatus = EAbilityRunStatus::Failed;
|
|
|
+
|
|
|
+ // Tree should complete as failed.
|
|
|
+ ExpectedStatusAfterStart = EAbilityRunStatus::Failed;
|
|
|
+
|
|
|
+ // Tasks should have Transition.CurrentRunStatus as failed.
|
|
|
+ ExpectedExitStatusStr = TEXT("ExitFailed");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_GlobalEnterFailed, "System.EzAbility.Stop.GlobalEnterFailed");
|
|
|
+
|
|
|
+struct FEzAbilityTest_Stop_ExternalStop : FEzAbilityTest_Stop
|
|
|
+{
|
|
|
+ virtual bool SetUp() override
|
|
|
+ {
|
|
|
+ // Tasks should complete later.
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+ // Tree should tick and keep on running.
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::Running;
|
|
|
+
|
|
|
+ // Tree should stop as stopped.
|
|
|
+ ExpectedStatusAfterStop = EAbilityRunStatus::Stopped;
|
|
|
+
|
|
|
+ // Tasks should have Transition.CurrentRunStatus as stopped.
|
|
|
+ ExpectedExitStatusStr = TEXT("ExitStopped");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = SetupTree();
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_EQUAL("Start should be running", Status, EAbilityRunStatus::Running);
|
|
|
+ AITEST_TRUE("EzAbility GlobalTask should enter state", Exec.Expect(GlobalTaskName, EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility TaskA should enter state", Exec.Expect(TaskAName, EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_EQUAL("Tree should end expectedly", Status, ExpectedStatusAfterTick);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ Status = Exec.Stop(EAbilityRunStatus::Stopped);
|
|
|
+ AITEST_EQUAL("Start should be running", Status, ExpectedStatusAfterStop);
|
|
|
+ if (!ExpectedExitStatusStr.IsEmpty())
|
|
|
+ {
|
|
|
+ AITEST_TRUE("EzAbility GlobalTask should get exit state expectedly", Exec.Expect(GlobalTaskName, ExpectedExitStatusStr));
|
|
|
+ AITEST_TRUE("EzAbility TaskA should get exit state expectedly", Exec.Expect(TaskAName, ExpectedExitStatusStr));
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ EAbilityRunStatus ExpectedStatusAfterStop = EAbilityRunStatus::Stopped;
|
|
|
+
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_ExternalStop, "System.EzAbility.Stop.ExternalStop");
|
|
|
+
|
|
|
+struct FEzAbilityTest_Stop_AlreadyStopped : FEzAbilityTest_Stop_ExternalStop
|
|
|
+{
|
|
|
+ virtual bool SetUp() override
|
|
|
+ {
|
|
|
+ // Normal task completes before stop.
|
|
|
+ NormalTaskTicks = 1;
|
|
|
+ NormalTaskStatus = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+ // Global task completes later
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+ // Tree should tick stop as succeeded.
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+ // Tree is already stopped, should keep the status (not Stopped).
|
|
|
+ ExpectedStatusAfterStop = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+ // Skip exit status check.
|
|
|
+ ExpectedExitStatusStr = TEXT("");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_AlreadyStopped, "System.EzAbility.Stop.AlreadyStopped");
|
|
|
+
|
|
|
+//
|
|
|
+// The deferred stop tests validates that the tree can be properly stopped if requested in the main entry points (Start, Tick, Stop).
|
|
|
+//
|
|
|
+struct FEzAbilityTest_DeferredStop : FAITestBase
|
|
|
+{
|
|
|
+ UEzAbility& SetupTree() const
|
|
|
+ {
|
|
|
+ UEzAbility& Ability = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(Ability.EditorData);
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+ UEzAbilityState& StateA = Root.AddChildState(FName(TEXT("A")));
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_StopTree>& TaskA = StateA.AddTask<FAbilityTestTask_StopTree>(TEXT("Task"));
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_StopTree>& GlobalTask = EditorData.AddGlobalTask<FAbilityTestTask_StopTree>(TEXT("GlobalTask"));
|
|
|
+
|
|
|
+ StateA.AddTransition(EEzAbilityTransitionTrigger::OnStateSucceeded, EEzAbilityTransitionType::Succeeded);
|
|
|
+ StateA.AddTransition(EEzAbilityTransitionTrigger::OnStateFailed, EEzAbilityTransitionType::Failed);
|
|
|
+
|
|
|
+ GlobalTask.GetNode().Phase = GlobalTaskPhase;
|
|
|
+ TaskA.GetNode().Phase = TaskPhase;
|
|
|
+
|
|
|
+ return Ability;
|
|
|
+ }
|
|
|
+
|
|
|
+ virtual bool RunDerivedTest(FTestEzAbilityExecutionContext& Exec) = 0;
|
|
|
+
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = SetupTree();
|
|
|
+ WeakAbility = &EzAbility;
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ return RunDerivedTest(Exec);
|
|
|
+ }
|
|
|
+
|
|
|
+protected:
|
|
|
+ TWeakObjectPtr<UEzAbility> WeakAbility;
|
|
|
+ EEzAbilityUpdatePhase GlobalTaskPhase = EEzAbilityUpdatePhase::Unset;
|
|
|
+ EEzAbilityUpdatePhase TaskPhase = EEzAbilityUpdatePhase::Unset;
|
|
|
+};
|
|
|
+
|
|
|
+struct FEzAbilityTest_DeferredStop_EnterGlobalTask : FEzAbilityTest_DeferredStop
|
|
|
+{
|
|
|
+ FEzAbilityTest_DeferredStop_EnterGlobalTask() { GlobalTaskPhase = EEzAbilityUpdatePhase::EnterStates; }
|
|
|
+ virtual bool RunDerivedTest(FTestEzAbilityExecutionContext& Exec) override
|
|
|
+ {
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(WeakAbility.Get(), Parameter, OutText);;
|
|
|
+ AITEST_EQUAL("Tree should be stopped", Status, EAbilityRunStatus::Stopped);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_DeferredStop_EnterGlobalTask, "System.EzAbility.DeferredStop.EnterGlobalTask");
|
|
|
+
|
|
|
+struct FEzAbilityTest_DeferredStop_TickGlobalTask : FEzAbilityTest_DeferredStop
|
|
|
+{
|
|
|
+ FEzAbilityTest_DeferredStop_TickGlobalTask() { GlobalTaskPhase = EEzAbilityUpdatePhase::Tick; }
|
|
|
+ virtual bool RunDerivedTest(FTestEzAbilityExecutionContext& Exec) override
|
|
|
+ {
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(WeakAbility.Get(), Parameter, OutText);;
|
|
|
+ AITEST_EQUAL("Tree should be running", Status, EAbilityRunStatus::Running);
|
|
|
+
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_EQUAL("Tree should be stopped", Status, EAbilityRunStatus::Stopped);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_DeferredStop_TickGlobalTask, "System.EzAbility.DeferredStop.TickGlobalTask");
|
|
|
+
|
|
|
+struct FEzAbilityTest_DeferredStop_ExitGlobalTask : FEzAbilityTest_DeferredStop
|
|
|
+{
|
|
|
+ FEzAbilityTest_DeferredStop_ExitGlobalTask() { GlobalTaskPhase = EEzAbilityUpdatePhase::ExitStates; }
|
|
|
+ virtual bool RunDerivedTest(FTestEzAbilityExecutionContext& Exec) override
|
|
|
+ {
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(WeakAbility.Get(), Parameter, OutText);;
|
|
|
+ AITEST_EQUAL("Tree should be running", Status, EAbilityRunStatus::Running);
|
|
|
+
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_EQUAL("Tree should be running", Status, EAbilityRunStatus::Running);
|
|
|
+
|
|
|
+ Status = Exec.Stop();
|
|
|
+ AITEST_EQUAL("Tree should be stopped", Status, EAbilityRunStatus::Stopped);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_DeferredStop_ExitGlobalTask, "System.EzAbility.DeferredStop.ExitGlobalTask");
|
|
|
+
|
|
|
+struct FEzAbilityTest_DeferredStop_EnterTask : FEzAbilityTest_DeferredStop
|
|
|
+{
|
|
|
+ FEzAbilityTest_DeferredStop_EnterTask() { TaskPhase = EEzAbilityUpdatePhase::EnterStates; }
|
|
|
+ virtual bool RunDerivedTest(FTestEzAbilityExecutionContext& Exec) override
|
|
|
+ {
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(WeakAbility.Get(), Parameter, OutText);;
|
|
|
+ AITEST_EQUAL("Tree should be running", Status, EAbilityRunStatus::Stopped);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_DeferredStop_EnterTask, "System.EzAbility.DeferredStop.EnterTask");
|
|
|
+
|
|
|
+struct FEzAbilityTest_DeferredStop_TickTask : FEzAbilityTest_DeferredStop
|
|
|
+{
|
|
|
+ FEzAbilityTest_DeferredStop_TickTask() { TaskPhase = EEzAbilityUpdatePhase::Tick; }
|
|
|
+ virtual bool RunDerivedTest(FTestEzAbilityExecutionContext& Exec) override
|
|
|
+ {
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(WeakAbility.Get(), Parameter, OutText);;
|
|
|
+ AITEST_EQUAL("Tree should be running", Status, EAbilityRunStatus::Running);
|
|
|
+
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_EQUAL("Tree should be stopped", Status, EAbilityRunStatus::Stopped);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_DeferredStop_TickTask, "System.EzAbility.DeferredStop.TickTask");
|
|
|
+
|
|
|
+struct FEzAbilityTest_DeferredStop_ExitTask : FEzAbilityTest_DeferredStop
|
|
|
+{
|
|
|
+ FEzAbilityTest_DeferredStop_ExitTask() { TaskPhase = EEzAbilityUpdatePhase::ExitStates; }
|
|
|
+ virtual bool RunDerivedTest(FTestEzAbilityExecutionContext& Exec) override
|
|
|
+ {
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(WeakAbility.Get(), Parameter, OutText);;
|
|
|
+ AITEST_EQUAL("Tree should be running", Status, EAbilityRunStatus::Running);
|
|
|
+
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_EQUAL("Tree should be running", Status, EAbilityRunStatus::Running);
|
|
|
+
|
|
|
+ Status = Exec.Stop();
|
|
|
+ AITEST_EQUAL("Tree should be stopped", Status, EAbilityRunStatus::Stopped);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_DeferredStop_ExitTask, "System.EzAbility.DeferredStop.ExitTask");
|
|
|
+
|
|
|
+// struct FEzAbilityTest_FailEnterLinkedAsset : FAITestBase
|
|
|
+// {
|
|
|
+// virtual bool InstantTest() override
|
|
|
+// {
|
|
|
+// FEzAbilityCompilerLog Log;
|
|
|
+//
|
|
|
+// // Asset 2
|
|
|
+// UEzAbility& EzAbility2 = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData2 = *Cast<UEzAbilityEditorData>(EzAbility2.EditorData);
|
|
|
+// UEzAbilityState& Root2 = EditorData2.AddSubTree(FName(TEXT("Root2")));
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task2 = Root2.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task2")));
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& GlobalTask2 = EditorData2.AddGlobalTask<FAbilityTestTask_Stand>(FName(TEXT("GlobalTask2")));
|
|
|
+// GlobalTask2.GetInstanceData().Value = 123;
|
|
|
+//
|
|
|
+// // Always failing enter condition
|
|
|
+// TEzAbilityEditorNode<FAbilityCompareIntCondition>& IntCond2 = Root2.AddEnterCondition<FAbilityCompareIntCondition>();
|
|
|
+// EditorData2.AddPropertyBinding(GlobalTask2, TEXT("Value"), IntCond2, TEXT("Left"));
|
|
|
+// IntCond2.GetInstanceData().Right = 0;
|
|
|
+//
|
|
|
+// FEzAbilityCompiler Compiler2(Log);
|
|
|
+// const bool bResult2 = Compiler2.Compile(EzAbility2);
|
|
|
+// AITEST_TRUE("EzAbility2 should get compiled", bResult2);
|
|
|
+//
|
|
|
+// // Main asset
|
|
|
+// UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+//
|
|
|
+// UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root1")));
|
|
|
+// UEzAbilityState& A1 = Root.AddChildState(FName(TEXT("A1")), EEzAbilityStateType::LinkedAsset);
|
|
|
+// A1.SetLinkedStateAsset(&EzAbility2);
|
|
|
+//
|
|
|
+// UEzAbilityState& B1 = Root.AddChildState(FName(TEXT("B1")), EEzAbilityStateType::State);
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task1 = B1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+//
|
|
|
+// FEzAbilityCompiler Compiler(Log);
|
|
|
+// const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+// AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+//
|
|
|
+// const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+// const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+//
|
|
|
+// {
|
|
|
+// EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+// FEzAbilityInstanceData InstanceData;
|
|
|
+// FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+// const bool bInitSucceeded = Exec.IsValid();
|
|
|
+// AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+//
|
|
|
+// FEzAbilityParameter Parameter;
|
|
|
+// FText OutText;
|
|
|
+// Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+// AITEST_EQUAL("Start should complete with Running", Status, EAbilityRunStatus::Running);
|
|
|
+// AITEST_TRUE("EzAbility should enter GlobalTask2", Exec.Expect(GlobalTask2.GetName(), EnterStateStr));
|
|
|
+// AITEST_TRUE("EzAbility should exit GlobalTask2", Exec.Expect(GlobalTask2.GetName(), ExitStateStr));
|
|
|
+// AITEST_FALSE("EzAbility should not enter Task2", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+// AITEST_TRUE("EzAbility should enter Task1", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+//
|
|
|
+// Exec.LogClear();
|
|
|
+// }
|
|
|
+//
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_FailEnterLinkedAsset, "System.EzAbility.FailEnterLinkedAsset");
|
|
|
+
|
|
|
+// struct FEzAbilityTest_EnterAndExitLinkedAsset : FAITestBase
|
|
|
+// {
|
|
|
+// virtual bool InstantTest() override
|
|
|
+// {
|
|
|
+// FEzAbilityCompilerLog Log;
|
|
|
+//
|
|
|
+// // Asset 2
|
|
|
+// UEzAbility& EzAbility2 = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData2 = *Cast<UEzAbilityEditorData>(EzAbility2.EditorData);
|
|
|
+// UEzAbilityState& Root2 = EditorData2.AddSubTree(FName(TEXT("Root2")));
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task2 = Root2.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task2")));
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& GlobalTask2 = EditorData2.AddGlobalTask<FAbilityTestTask_Stand>(FName(TEXT("GlobalTask2")));
|
|
|
+// GlobalTask2.GetNode().TicksToCompletion = 2;
|
|
|
+//
|
|
|
+// FEzAbilityCompiler Compiler2(Log);
|
|
|
+// const bool bResult2 = Compiler2.Compile(EzAbility2);
|
|
|
+// AITEST_TRUE("EzAbility2 should get compiled", bResult2);
|
|
|
+//
|
|
|
+// // Main asset
|
|
|
+// UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+//
|
|
|
+// UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root1")));
|
|
|
+// UEzAbilityState& A1 = Root.AddChildState(FName(TEXT("A1")), EEzAbilityStateType::LinkedAsset);
|
|
|
+// A1.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::NextState);
|
|
|
+// A1.SetLinkedStateAsset(&EzAbility2);
|
|
|
+//
|
|
|
+// UEzAbilityState& B1 = Root.AddChildState(FName(TEXT("B1")), EEzAbilityStateType::State);
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task1 = B1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+//
|
|
|
+// FEzAbilityCompiler Compiler(Log);
|
|
|
+// const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+// AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+//
|
|
|
+// const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+// const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+//
|
|
|
+// {
|
|
|
+// EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+// FEzAbilityInstanceData InstanceData;
|
|
|
+// FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+// const bool bInitSucceeded = Exec.IsValid();
|
|
|
+// AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+//
|
|
|
+// FEzAbilityParameter Parameter;
|
|
|
+// FText OutText;
|
|
|
+// Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+// AITEST_EQUAL("Start should complete with Running", Status, EAbilityRunStatus::Running);
|
|
|
+// AITEST_TRUE("EzAbility should enter GlobalTask2", Exec.Expect(GlobalTask2.GetName(), EnterStateStr));
|
|
|
+// AITEST_FALSE("EzAbility should not exit GlobalTask2", Exec.Expect(GlobalTask2.GetName(), ExitStateStr));
|
|
|
+// AITEST_TRUE("EzAbility should enter Task2", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+// AITEST_FALSE("EzAbility should not exit Task2", Exec.Expect(Task2.GetName(), ExitStateStr));
|
|
|
+// AITEST_FALSE("EzAbility should not enter Task1", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_EQUAL("Tick should complete with Running", Status, EAbilityRunStatus::Running);
|
|
|
+// AITEST_FALSE("EzAbility should not enter GlobalTask2", Exec.Expect(GlobalTask2.GetName(), EnterStateStr));
|
|
|
+// AITEST_TRUE("EzAbility should exit GlobalTask2", Exec.Expect(GlobalTask2.GetName(), ExitStateStr));
|
|
|
+// AITEST_FALSE("EzAbility should not enter Task2", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+// AITEST_TRUE("EzAbility should exit Task2", Exec.Expect(Task2.GetName(), ExitStateStr));
|
|
|
+// AITEST_TRUE("EzAbility should enter Task1", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+// }
|
|
|
+//
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_EnterAndExitLinkedAsset, "System.EzAbility.EnterAndExitLinkedAsset");
|
|
|
+
|
|
|
+// // Test nested tree overrides
|
|
|
+// struct FEzAbilityTest_NestedOverride : FAITestBase
|
|
|
+// {
|
|
|
+// virtual bool InstantTest() override
|
|
|
+// {
|
|
|
+// FEzAbilityCompilerLog Log;
|
|
|
+//
|
|
|
+// const FGameplayTag Tag = UE::EzAbility::Tests::FNativeGameplayTags::Get().TestTag;
|
|
|
+//
|
|
|
+// // Asset 2
|
|
|
+// UEzAbility& EzAbility2 = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData2 = *Cast<UEzAbilityEditorData>(EzAbility2.EditorData);
|
|
|
+// EditorData2.RootParameters.Parameters.AddProperty(FName(TEXT("Int")), EPropertyBagPropertyType::Int32);
|
|
|
+// UEzAbilityState& Root2 = EditorData2.AddSubTree(FName(TEXT("Root2")));
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& TaskRoot2 = Root2.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskRoot2")));
|
|
|
+//
|
|
|
+// FEzAbilityCompiler Compiler2(Log);
|
|
|
+// const bool bResult2 = Compiler2.Compile(EzAbility2);
|
|
|
+// AITEST_TRUE("EzAbility2 should get compiled", bResult2);
|
|
|
+//
|
|
|
+//
|
|
|
+// // Asset 3
|
|
|
+// UEzAbility& EzAbility3 = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData3 = *Cast<UEzAbilityEditorData>(EzAbility3.EditorData);
|
|
|
+// EditorData3.RootParameters.Parameters.AddProperty(FName(TEXT("Float")), EPropertyBagPropertyType::Float); // Different parameters
|
|
|
+// UEzAbilityState& Root3 = EditorData3.AddSubTree(FName(TEXT("Root3")));
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& TaskRoot3 = Root3.AddTask<FAbilityTestTask_Stand>(FName(TEXT("TaskRoot3")));
|
|
|
+//
|
|
|
+// FEzAbilityCompiler Compiler3(Log);
|
|
|
+// const bool bResult3 = Compiler3.Compile(EzAbility3);
|
|
|
+// AITEST_TRUE("EzAbility3 should get compiled", bResult3);
|
|
|
+//
|
|
|
+// // Main asset
|
|
|
+// UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+//
|
|
|
+// EditorData.RootParameters.Parameters.AddProperty(FName(TEXT("Int")), EPropertyBagPropertyType::Int32);
|
|
|
+//
|
|
|
+// UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root1")));
|
|
|
+// UEzAbilityState& StateA = Root.AddChildState(FName(TEXT("A1")), EEzAbilityStateType::LinkedAsset);
|
|
|
+// StateA.Tag = Tag;
|
|
|
+// StateA.SetLinkedStateAsset(&EzAbility2);
|
|
|
+//
|
|
|
+// FEzAbilityCompiler Compiler(Log);
|
|
|
+// const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+// AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+//
|
|
|
+// const FString TickStr(TEXT("Tick"));
|
|
|
+// const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+// const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+//
|
|
|
+// // Without overrides
|
|
|
+// {
|
|
|
+// EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+// FEzAbilityInstanceData InstanceData;
|
|
|
+// FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+// const bool bInitSucceeded = Exec.IsValid();
|
|
|
+// AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+//
|
|
|
+// FEzAbilityParameter Parameter;
|
|
|
+// FText OutText;
|
|
|
+// Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+// AITEST_EQUAL("Start should complete with Running", Status, EAbilityRunStatus::Running);
|
|
|
+// AITEST_TRUE("EzAbility should enter TaskRoot2", Exec.Expect(TaskRoot2.GetName(), EnterStateStr));
|
|
|
+//
|
|
|
+// Exec.LogClear();
|
|
|
+// }
|
|
|
+//
|
|
|
+// // With overrides
|
|
|
+// {
|
|
|
+// EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+// FEzAbilityInstanceData InstanceData;
|
|
|
+//
|
|
|
+// FEzAbilityReferenceOverrides Overrides;
|
|
|
+// FEzAbilityReference OverrideRef;
|
|
|
+// OverrideRef.SetEzAbility(&EzAbility3);
|
|
|
+// Overrides.AddOverride(Tag, OverrideRef);
|
|
|
+//
|
|
|
+// FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+// Exec.SetLinkedEzAbilityOverrides(&Overrides);
|
|
|
+//
|
|
|
+// const bool bInitSucceeded = Exec.IsValid();
|
|
|
+// AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+//
|
|
|
+// FEzAbilityParameter Parameter;
|
|
|
+// FText OutText;
|
|
|
+// Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+// AITEST_EQUAL("Start should complete with Running", Status, EAbilityRunStatus::Running);
|
|
|
+// AITEST_TRUE("EzAbility should enter TaskRoot3", Exec.Expect(TaskRoot3.GetName(), EnterStateStr));
|
|
|
+//
|
|
|
+// Exec.LogClear();
|
|
|
+// }
|
|
|
+//
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_NestedOverride, "System.EzAbility.NestedOverride");
|
|
|
+
|
|
|
+// // Test parallel tree event priority handling.
|
|
|
+// struct FEzAbilityTest_ParallelEventPriority : FAITestBase
|
|
|
+// {
|
|
|
+// EEzAbilityTransitionPriority ParallelTreePriority = EEzAbilityTransitionPriority::Normal;
|
|
|
+//
|
|
|
+// virtual bool InstantTest() override
|
|
|
+// {
|
|
|
+// FEzAbilityCompilerLog Log;
|
|
|
+//
|
|
|
+// const FGameplayTag EventTag = UE::EzAbility::Tests::FNativeGameplayTags::Get().TestTag;
|
|
|
+//
|
|
|
+// // Parallel tree
|
|
|
+// // - Root
|
|
|
+// // - State1 ?-> State2
|
|
|
+// // - State2
|
|
|
+// UEzAbility& EzAbilityPar = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorDataPar = *Cast<UEzAbilityEditorData>(EzAbilityPar.EditorData);
|
|
|
+//
|
|
|
+// UEzAbilityState& RootPar = EditorDataPar.AddSubTree(FName(TEXT("Root")));
|
|
|
+// UEzAbilityState& State1 = RootPar.AddChildState(FName(TEXT("State1")));
|
|
|
+// UEzAbilityState& State2 = RootPar.AddChildState(FName(TEXT("State2")));
|
|
|
+//
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task1 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+// Task1.GetNode().TicksToCompletion = 100;
|
|
|
+// State1.AddTransition(EEzAbilityTransitionTrigger::OnEvent, EventTag, EEzAbilityTransitionType::NextState);
|
|
|
+//
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task2 = State2.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task2")));
|
|
|
+// Task2.GetNode().TicksToCompletion = 100;
|
|
|
+//
|
|
|
+// {
|
|
|
+// FEzAbilityCompiler Compiler(Log);
|
|
|
+// const bool bResult = Compiler.Compile(EzAbilityPar);
|
|
|
+// AITEST_TRUE("EzAbilityPar should get compiled", bResult);
|
|
|
+// }
|
|
|
+//
|
|
|
+// // Main asset
|
|
|
+// // - Root [EzAbilityPar]
|
|
|
+// // - State3 ?-> State4
|
|
|
+// // - State4
|
|
|
+// UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+// UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+//
|
|
|
+// UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+// UEzAbilityState& State3 = Root.AddChildState(FName(TEXT("State3")));
|
|
|
+// UEzAbilityState& State4 = Root.AddChildState(FName(TEXT("State4")));
|
|
|
+//
|
|
|
+// TEzAbilityEditorNode<FEzAbilityRunParallelEzAbilityTask>& TaskPar = Root.AddTask<FEzAbilityRunParallelEzAbilityTask>();
|
|
|
+// TaskPar.GetNode().SetEventHandlingPriority(ParallelTreePriority);
|
|
|
+//
|
|
|
+// TaskPar.GetInstanceData().EzAbility.SetEzAbility(&EzAbilityPar);
|
|
|
+//
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task3 = State3.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task3")));
|
|
|
+// Task3.GetNode().TicksToCompletion = 100;
|
|
|
+// State3.AddTransition(EEzAbilityTransitionTrigger::OnEvent, EventTag, EEzAbilityTransitionType::NextState);
|
|
|
+//
|
|
|
+// TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task4 = State4.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task4")));
|
|
|
+// Task4.GetNode().TicksToCompletion = 100;
|
|
|
+//
|
|
|
+// {
|
|
|
+// FEzAbilityCompiler Compiler(Log);
|
|
|
+// const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+// AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+// }
|
|
|
+//
|
|
|
+// const FString TickStr(TEXT("Tick"));
|
|
|
+// const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+// const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+//
|
|
|
+// // Run EzAbilityPar in parallel with the main tree.
|
|
|
+// // Both trees have a transition on same event.
|
|
|
+// // Setting the priority to Low, should make the main tree to take the transition.
|
|
|
+// EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+// FEzAbilityInstanceData InstanceData;
|
|
|
+// FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+// const bool bInitSucceeded = Exec.IsValid();
|
|
|
+// AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+//
|
|
|
+// FEzAbilityParameter Parameter;
|
|
|
+// FText OutText;
|
|
|
+// Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+// AITEST_EQUAL("Start should complete with Running", Status, EAbilityRunStatus::Running);
|
|
|
+// AITEST_TRUE("EzAbility should enter Task1, Task3", Exec.Expect(Task1.GetName(), EnterStateStr).Then(Task3.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_EQUAL("Tick should complete with Running", Status, EAbilityRunStatus::Running);
|
|
|
+// AITEST_TRUE("EzAbility should tick Task1, Task3", Exec.Expect(Task1.GetName(), TickStr).Then(Task3.GetName(), TickStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// Exec.SendEvent(EventTag);
|
|
|
+//
|
|
|
+// // If the parallel tree priority is < Normal, then it should always be handled after the main tree.
|
|
|
+// // If the parallel tree priority is Normal, then the state order decides (leaf to root)
|
|
|
+// // If the parallel tree priority is > Normal, then it should always be handled before the main tree.
|
|
|
+// if (ParallelTreePriority <= EEzAbilityTransitionPriority::Normal)
|
|
|
+// {
|
|
|
+// // Main tree should do the transition.
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_EQUAL("Tick should complete with Running", Status, EAbilityRunStatus::Running);
|
|
|
+// AITEST_TRUE("EzAbility should enter Task4", Exec.Expect(Task4.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_EQUAL("Tick should complete with Running", Status, EAbilityRunStatus::Running);
|
|
|
+// AITEST_TRUE("EzAbility should tick Task1, Task4", Exec.Expect(Task1.GetName(), TickStr).Then(Task4.GetName(), TickStr));
|
|
|
+// Exec.LogClear();
|
|
|
+// }
|
|
|
+// else
|
|
|
+// {
|
|
|
+// // Parallel tree should do the transition.
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_EQUAL("Tick should complete with Running", Status, EAbilityRunStatus::Running);
|
|
|
+// AITEST_TRUE("EzAbility should enter Task2", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+// Exec.LogClear();
|
|
|
+//
|
|
|
+// Status = Exec.Tick(0.1f);
|
|
|
+// AITEST_EQUAL("Tick should complete with Running", Status, EAbilityRunStatus::Running);
|
|
|
+// AITEST_TRUE("EzAbility should tick Task2, Task3", Exec.Expect(Task2.GetName(), TickStr).Then(Task3.GetName(), TickStr));
|
|
|
+// Exec.LogClear();
|
|
|
+// }
|
|
|
+//
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_ParallelEventPriority, "System.EzAbility.ParallelEventPriority");
|
|
|
+
|
|
|
+
|
|
|
+// struct FEzAbilityTest_ParallelEventPriority_Low : FEzAbilityTest_ParallelEventPriority
|
|
|
+// {
|
|
|
+// FEzAbilityTest_ParallelEventPriority_Low()
|
|
|
+// {
|
|
|
+// ParallelTreePriority = EEzAbilityTransitionPriority::Low;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_ParallelEventPriority_Low, "System.EzAbility.ParallelEventPriority.Low");
|
|
|
+//
|
|
|
+// struct FEzAbilityTest_ParallelEventPriority_High : FEzAbilityTest_ParallelEventPriority
|
|
|
+// {
|
|
|
+// FEzAbilityTest_ParallelEventPriority_High()
|
|
|
+// {
|
|
|
+// ParallelTreePriority = EEzAbilityTransitionPriority::High;
|
|
|
+// }
|
|
|
+// };
|
|
|
+// IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_ParallelEventPriority_High, "System.EzAbility.ParallelEventPriority.High");
|
|
|
+
|
|
|
+struct FEzAbilityTest_SubTreeTransition : FAITestBase
|
|
|
+{
|
|
|
+ virtual bool InstantTest() override
|
|
|
+ {
|
|
|
+ UEzAbility& EzAbility = UE::EzAbility::Tests::NewAbility(&GetWorld());
|
|
|
+ UEzAbilityEditorData& EditorData = *Cast<UEzAbilityEditorData>(EzAbility.EditorData);
|
|
|
+
|
|
|
+ /*
|
|
|
+ - Root
|
|
|
+ - PreLastStand [Task1] -> Reinforcements
|
|
|
+ - BusinessAsUsual [Task2]
|
|
|
+ - LastStand [Task3]
|
|
|
+ - Reinforcements>TimeoutChecker
|
|
|
+ - (f)TimeoutChecker
|
|
|
+ - RemainingCount [Task4]
|
|
|
+ */
|
|
|
+
|
|
|
+ UEzAbilityState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
+
|
|
|
+ UEzAbilityState& PreLastStand = Root.AddChildState(FName(TEXT("PreLastStand")));
|
|
|
+ UEzAbilityState& BusinessAsUsual = PreLastStand.AddChildState(FName(TEXT("BusinessAsUsual")));
|
|
|
+
|
|
|
+ UEzAbilityState& LastStand = Root.AddChildState(FName(TEXT("LastStand")));
|
|
|
+ UEzAbilityState& Reinforcements = LastStand.AddChildState(FName(TEXT("Reinforcements")), EEzAbilityStateType::Linked);
|
|
|
+
|
|
|
+ UEzAbilityState& TimeoutChecker = LastStand.AddChildState(FName(TEXT("TimeoutChecker")), EEzAbilityStateType::Subtree);
|
|
|
+ UEzAbilityState& RemainingCount = TimeoutChecker.AddChildState(FName(TEXT("RemainingCount")));
|
|
|
+
|
|
|
+ Reinforcements.LinkedSubtree = TimeoutChecker.GetLinkToState();
|
|
|
+
|
|
|
+
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task1 = PreLastStand.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+ PreLastStand.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::GotoState, &Reinforcements);
|
|
|
+ Task1.GetInstanceData().Value = 1; // This should finish before the child state
|
|
|
+
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task2 = BusinessAsUsual.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task2")));
|
|
|
+ Task2.GetInstanceData().Value = 2;
|
|
|
+
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task3 = LastStand.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task3")));
|
|
|
+ Task3.GetInstanceData().Value = 2;
|
|
|
+
|
|
|
+ TEzAbilityEditorNode<FAbilityTestTask_Stand>& Task4 = LastStand.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task4")));
|
|
|
+ Task4.GetInstanceData().Value = 2;
|
|
|
+
|
|
|
+ FEzAbilityCompilerLog Log;
|
|
|
+ FEzAbilityCompiler Compiler(Log);
|
|
|
+ const bool bResult = Compiler.Compile(EzAbility);
|
|
|
+ AITEST_TRUE("EzAbility should get compiled", bResult);
|
|
|
+
|
|
|
+ EAbilityRunStatus Status = EAbilityRunStatus::Unset;
|
|
|
+ FEzAbilityInstanceData InstanceData;
|
|
|
+ FTestEzAbilityExecutionContext Exec(EzAbility, EzAbility, InstanceData);
|
|
|
+ const bool bInitSucceeded = Exec.IsValid();
|
|
|
+ AITEST_TRUE("EzAbility should init", bInitSucceeded);
|
|
|
+
|
|
|
+ const FString TickStr(TEXT("Tick"));
|
|
|
+ const FString EnterStateStr(TEXT("EnterState"));
|
|
|
+ const FString ExitStateStr(TEXT("ExitState"));
|
|
|
+ const FString StateCompletedStr(TEXT("StateCompleted"));
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ // Start and enter state
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+
|
|
|
+ AITEST_TRUE("EzAbility Active States should be in Root/PreLastStand/BusinessAsUsual", Exec.ExpectInActiveStates(Root.Name, PreLastStand.Name, BusinessAsUsual.Name));
|
|
|
+ AITEST_TRUE("EzAbility Task1 should enter state", Exec.Expect(Task1.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task2 should enter state", Exec.Expect(Task2.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ // Transition to Reinforcements
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility Active States should be in Root/LastStand/Reinforcements/TimeoutChecker/RemainingCount", Exec.ExpectInActiveStates(Root.Name, LastStand.Name, Reinforcements.Name, TimeoutChecker.Name, RemainingCount.Name));
|
|
|
+ AITEST_TRUE("EzAbility Task3 should enter state", Exec.Expect(Task3.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility Task4 should enter state", Exec.Expect(Task4.GetName(), EnterStateStr));
|
|
|
+ AITEST_TRUE("EzAbility should be running", Status == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_SubTreeTransition, "System.EzAbility.SubTreeTransition");
|
|
|
+
|
|
|
+
|
|
|
UE_ENABLE_OPTIMIZATION_SHIP
|
|
|
#undef LOCTEXT_NAMESPACE
|