|
@@ -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;
|
|
|
+
|
|
|
+ auto& Task1 = State1.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task1")));
|
|
|
+ Task1.GetNode().TicksToCompletion = 3;
|
|
|
+
|
|
|
+ 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"));
|
|
|
+
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ 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"));
|
|
|
+
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ 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"));
|
|
|
+
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ Status = Exec.Tick(0.1f);
|
|
|
+ AITEST_TRUE("EzAbility exec status should be running", Exec.GetLastTickStatus() == EAbilityRunStatus::Running);
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+
|
|
|
+ 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"));
|
|
|
+
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ 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
|
|
|
+ - 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);
|
|
|
+
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+
|
|
|
+ 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"));
|
|
|
+
|
|
|
+
|
|
|
+ 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));
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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());
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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"));
|
|
|
+
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ 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"));
|
|
|
+
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Task0 should enter state", Exec.Expect(Task0.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+ Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Task0 should enter state", Exec.Expect(Task0.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+
|
|
|
+ Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Task0 should enter state", Exec.Expect(Task0.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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_TransitionGlobalDataView : 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& 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;
|
|
|
+
|
|
|
+
|
|
|
+ auto& Task0 = StateA.AddTask<FAbilityTestTask_Stand>(FName(TEXT("Task0")));
|
|
|
+ StateA.AddTransition(EEzAbilityTransitionTrigger::OnStateCompleted, EEzAbilityTransitionType::GotoState, &StateB);
|
|
|
+
|
|
|
+
|
|
|
+ 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"));
|
|
|
+
|
|
|
+
|
|
|
+ FEzAbilityParameter Parameter;
|
|
|
+ FText OutText;
|
|
|
+ Status = Exec.Start(&EzAbility, Parameter, OutText);;
|
|
|
+ AITEST_TRUE("EzAbility Task0 should enter state", Exec.Expect(Task0.GetName(), EnterStateStr));
|
|
|
+ Exec.LogClear();
|
|
|
+
|
|
|
+
|
|
|
+ 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_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(""));
|
|
|
+ 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);
|
|
|
+ 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);
|
|
|
+ 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);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ 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_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);
|
|
|
+ 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;
|
|
|
+
|
|
|
+ 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")));
|
|
|
+
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+
|
|
|
+ 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")));
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ {
|
|
|
+ StateTrans.SelectionBehavior = EEzAbilityStateSelectionBehavior::TryFollowTransitions;
|
|
|
+
|
|
|
+ {
|
|
|
+
|
|
|
+ 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")));
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+
|
|
|
+ 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")));
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+
|
|
|
+ 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")));
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ {
|
|
|
+ StateA.SelectionBehavior = EEzAbilityStateSelectionBehavior::TryFollowTransitions;
|
|
|
+ {
|
|
|
+
|
|
|
+ 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")));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ {
|
|
|
+ StateB.SelectionBehavior = EEzAbilityStateSelectionBehavior::TryFollowTransitions;
|
|
|
+ {
|
|
|
+
|
|
|
+ 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");
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+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);
|
|
|
+
|
|
|
+
|
|
|
+ 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
|
|
|
+ {
|
|
|
+
|
|
|
+ NormalTaskStatus = EAbilityRunStatus::Succeeded;
|
|
|
+ NormalTaskTicks = 1;
|
|
|
+
|
|
|
+
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::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
|
|
|
+ {
|
|
|
+
|
|
|
+ NormalTaskStatus = EAbilityRunStatus::Failed;
|
|
|
+ NormalTaskTicks = 1;
|
|
|
+
|
|
|
+
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::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
|
|
|
+ {
|
|
|
+
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+
|
|
|
+
|
|
|
+ GlobalTaskStatus = EAbilityRunStatus::Succeeded;
|
|
|
+ GlobalTaskTicks = 1;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::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
|
|
|
+ {
|
|
|
+
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+
|
|
|
+
|
|
|
+ GlobalTaskStatus = EAbilityRunStatus::Failed;
|
|
|
+ GlobalTaskTicks = 1;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::Failed;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedExitStatusStr = TEXT("ExitFailed");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_GlobalFailed, "System.EzAbility.Stop.GlobalFailed");
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+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;
|
|
|
+
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+
|
|
|
+ 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
|
|
|
+ {
|
|
|
+
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+
|
|
|
+ NormalTaskEnterStatus = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterStart = EAbilityRunStatus::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
|
|
|
+ {
|
|
|
+
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+
|
|
|
+ NormalTaskEnterStatus = EAbilityRunStatus::Failed;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterStart = EAbilityRunStatus::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));
|
|
|
+
|
|
|
+
|
|
|
+ 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
|
|
|
+ {
|
|
|
+
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+
|
|
|
+ GlobalTaskEnterStatus = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterStart = EAbilityRunStatus::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
|
|
|
+ {
|
|
|
+
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+
|
|
|
+ GlobalTaskEnterStatus = EAbilityRunStatus::Failed;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterStart = EAbilityRunStatus::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
|
|
|
+ {
|
|
|
+
|
|
|
+ NormalTaskTicks = 2;
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::Running;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterStop = EAbilityRunStatus::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
|
|
|
+ {
|
|
|
+
|
|
|
+ NormalTaskTicks = 1;
|
|
|
+ NormalTaskStatus = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+
|
|
|
+ GlobalTaskTicks = 2;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterTick = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedStatusAfterStop = EAbilityRunStatus::Succeeded;
|
|
|
+
|
|
|
+
|
|
|
+ ExpectedExitStatusStr = TEXT("");
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+};
|
|
|
+IMPLEMENT_AI_INSTANT_TEST(FEzAbilityTest_Stop_AlreadyStopped, "System.EzAbility.Stop.AlreadyStopped");
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+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_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;
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+
|
|
|
+ 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
|