2021-09-28 13:33:17 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "StateTreeTest.h"
# include "AITestsCommon.h"
2023-01-25 02:42:36 -05:00
# include "StateTreeCompilerLog.h"
2021-09-28 13:33:17 -04:00
# include "StateTreeEditorData.h"
2022-04-05 09:44:28 -04:00
# include "StateTreeCompiler.h"
2022-08-23 13:04:52 -04:00
# include "Conditions/StateTreeCommonConditions.h"
2021-09-28 13:33:17 -04:00
# include "StateTreeTestTypes.h"
2021-11-17 18:06:10 -05:00
# include "Engine/World.h"
2022-09-07 17:12:18 -04:00
# include "Async/ParallelFor.h"
2023-03-14 10:40:47 -04:00
# include "GameplayTagsManager.h"
2021-09-28 13:33:17 -04:00
2022-09-28 01:06:15 -04:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(StateTreeTest)
2021-09-28 13:33:17 -04:00
# define LOCTEXT_NAMESPACE "AITestSuite_StateTreeTest"
2022-11-22 08:13:54 -05:00
UE_DISABLE_OPTIMIZATION_SHIP
2022-09-07 17:12:18 -04:00
std : : atomic < int32 > FStateTreeTestConditionInstanceData : : GlobalCounter = 0 ;
2021-09-28 13:33:17 -04:00
namespace UE : : StateTree : : Tests
{
2021-11-17 18:06:10 -05:00
UStateTree & NewStateTree ( UObject * Outer = GetTransientPackage ( ) )
2021-09-28 13:33:17 -04:00
{
2021-11-17 18:06:10 -05:00
UStateTree * StateTree = NewObject < UStateTree > ( Outer ) ;
2021-09-28 13:33:17 -04:00
check ( StateTree ) ;
UStateTreeEditorData * EditorData = NewObject < UStateTreeEditorData > ( StateTree ) ;
check ( EditorData ) ;
StateTree - > EditorData = EditorData ;
2022-04-12 15:55:39 -04:00
EditorData - > Schema = NewObject < UStateTreeTestSchema > ( ) ;
2021-09-28 13:33:17 -04:00
return * StateTree ;
}
2022-04-05 03:20:57 -04:00
2023-03-14 10:40:47 -04:00
// Helper struct to define some test tags
struct FNativeGameplayTags : public FGameplayTagNativeAdder
{
virtual ~ FNativeGameplayTags ( ) { }
FGameplayTag TestTag ;
virtual void AddTags ( ) override
{
UGameplayTagsManager & Manager = UGameplayTagsManager : : Get ( ) ;
TestTag = Manager . AddNativeGameplayTag ( TEXT ( " Test.StateTree.Tag " ) ) ;
}
FORCEINLINE static const FNativeGameplayTags & Get ( )
{
return StaticInstance ;
}
static FNativeGameplayTags StaticInstance ;
} ;
FNativeGameplayTags FNativeGameplayTags : : StaticInstance ;
2021-09-28 13:33:17 -04:00
}
struct FStateTreeTest_MakeAndBakeStateTree : FAITestBase
{
virtual bool InstantTest ( ) override
{
2021-11-17 18:06:10 -05:00
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
2021-09-28 13:33:17 -04:00
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & StateA = Root . AddChildState ( FName ( TEXT ( " A " ) ) ) ;
UStateTreeState & StateB = Root . AddChildState ( FName ( TEXT ( " B " ) ) ) ;
// Root
2022-05-16 05:13:27 -04:00
auto & EvalA = EditorData . AddEvaluator < FTestEval_A > ( ) ;
2021-10-21 04:08:20 -04:00
2021-09-28 13:33:17 -04:00
// State A
2021-11-12 05:48:11 -05:00
auto & TaskB1 = StateA . AddTask < FTestTask_B > ( ) ;
2023-02-10 07:22:48 -05:00
EditorData . AddPropertyBinding ( EvalA , TEXT ( " IntA " ) , TaskB1 , TEXT ( " IntB " ) ) ;
2021-09-28 13:33:17 -04:00
2022-08-23 13:04:52 -04:00
auto & IntCond = StateA . AddEnterCondition < FStateTreeCompareIntCondition > ( EGenericAICheck : : Less ) ;
2022-09-07 17:12:18 -04:00
IntCond . GetInstanceData ( ) . Right = 2 ;
2021-11-12 05:48:11 -05:00
2023-02-10 07:22:48 -05:00
EditorData . AddPropertyBinding ( EvalA , TEXT ( " IntA " ) , IntCond , TEXT ( " Left " ) ) ;
2021-09-28 13:33:17 -04:00
2022-09-01 09:06:53 -04:00
StateA . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : GotoState , & StateB ) ;
2021-09-28 13:33:17 -04:00
// State B
2021-11-12 05:48:11 -05:00
auto & TaskB2 = StateB . AddTask < FTestTask_B > ( ) ;
2023-02-10 07:22:48 -05:00
EditorData . AddPropertyBinding ( EvalA , TEXT ( " bBoolA " ) , TaskB2 , TEXT ( " bBoolB " ) ) ;
2021-09-28 13:33:17 -04:00
2022-08-23 13:04:52 -04:00
FStateTreeTransition & Trans = StateB . AddTransition ( { } , EStateTreeTransitionType : : GotoState , & Root ) ;
auto & TransFloatCond = Trans . AddCondition < FStateTreeCompareFloatCondition > ( EGenericAICheck : : Less ) ;
2022-09-07 17:12:18 -04:00
TransFloatCond . GetInstanceData ( ) . Right = 13.0f ;
2023-02-10 07:22:48 -05:00
EditorData . AddPropertyBinding ( EvalA , TEXT ( " FloatA " ) , TransFloatCond , TEXT ( " Left " ) ) ;
2021-09-28 13:33:17 -04:00
2022-09-01 09:06:53 -04:00
StateB . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : Succeeded ) ;
2021-09-28 13:33:17 -04:00
2021-10-29 04:33:21 -04:00
FStateTreeCompilerLog Log ;
2022-04-05 09:44:28 -04:00
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
2021-09-28 13:33:17 -04:00
2022-04-05 09:44:28 -04:00
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
2021-09-28 13:33:17 -04:00
return true ;
}
} ;
2021-12-09 05:26:00 -05:00
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_MakeAndBakeStateTree , " System.StateTree.MakeAndBakeStateTree " ) ;
2021-09-28 13:33:17 -04:00
struct FStateTreeTest_Sequence : FAITestBase
{
virtual bool InstantTest ( ) override
{
2021-11-17 18:06:10 -05:00
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
2021-09-28 13:33:17 -04:00
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & State1 = Root . AddChildState ( FName ( TEXT ( " State1 " ) ) ) ;
UStateTreeState & State2 = Root . AddChildState ( FName ( TEXT ( " State2 " ) ) ) ;
2021-11-12 05:48:11 -05:00
auto & Task1 = State1 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1 " ) ) ) ;
2022-09-01 09:06:53 -04:00
State1 . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : NextState ) ;
2021-09-28 13:33:17 -04:00
2021-11-12 05:48:11 -05:00
auto & Task2 = State2 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task2 " ) ) ) ;
2022-09-01 09:06:53 -04:00
State2 . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : Succeeded ) ;
2021-09-28 13:33:17 -04:00
2021-10-29 04:33:21 -04:00
FStateTreeCompilerLog Log ;
2022-04-05 09:44:28 -04:00
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
2021-09-28 13:33:17 -04:00
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
2022-09-23 20:02:42 -04:00
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
2021-09-28 13:33:17 -04:00
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
2021-11-22 12:11:19 -05:00
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree Task1 should enter state " , Exec . Expect ( Task1 . GetName ( ) , EnterStateStr ) ) ;
AITEST_FALSE ( " StateTree Task1 should not tick " , Exec . Expect ( Task1 . GetName ( ) , TickStr ) ) ;
Exec . LogClear ( ) ;
2021-09-28 13:33:17 -04:00
Status = Exec . Tick ( 0.1f ) ;
2021-11-22 12:11:19 -05:00
AITEST_TRUE ( " StateTree Task1 should tick, and exit state " , Exec . Expect ( Task1 . GetName ( ) , TickStr ) . Then ( Task1 . GetName ( ) , ExitStateStr ) ) ;
AITEST_TRUE ( " StateTree Task2 should enter state " , Exec . Expect ( Task2 . GetName ( ) , EnterStateStr ) ) ;
2021-11-12 05:48:11 -05:00
AITEST_FALSE ( " StateTree Task2 should not tick " , Exec . Expect ( Task2 . GetName ( ) , TickStr ) ) ;
2021-09-28 13:33:17 -04:00
AITEST_TRUE ( " StateTree should be running " , Status = = EStateTreeRunStatus : : Running ) ;
Exec . LogClear ( ) ;
Status = Exec . Tick ( 0.1f ) ;
2021-11-22 12:11:19 -05:00
AITEST_TRUE ( " StateTree Task2 should tick, and exit state " , Exec . Expect ( Task2 . GetName ( ) , TickStr ) . Then ( Task2 . GetName ( ) , ExitStateStr ) ) ;
2021-11-12 05:48:11 -05:00
AITEST_FALSE ( " StateTree Task1 should not tick " , Exec . Expect ( Task1 . GetName ( ) , TickStr ) ) ;
2021-09-28 13:33:17 -04:00
AITEST_TRUE ( " StateTree should be completed " , Status = = EStateTreeRunStatus : : Succeeded ) ;
2021-11-22 12:11:19 -05:00
Exec . LogClear ( ) ;
Status = Exec . Tick ( 0.1f ) ;
AITEST_FALSE ( " StateTree Task1 should not tick " , Exec . Expect ( Task1 . GetName ( ) , TickStr ) ) ;
AITEST_FALSE ( " StateTree Task2 should not tick " , Exec . Expect ( Task2 . GetName ( ) , TickStr ) ) ;
Exec . LogClear ( ) ;
2021-09-28 13:33:17 -04:00
return true ;
}
} ;
2021-12-09 05:26:00 -05:00
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Sequence , " System.StateTree.Sequence " ) ;
2021-09-28 13:33:17 -04:00
struct FStateTreeTest_Select : FAITestBase
{
virtual bool InstantTest ( ) override
{
2021-11-17 18:06:10 -05:00
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
2021-09-28 13:33:17 -04:00
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & State1 = Root . AddChildState ( FName ( TEXT ( " State1 " ) ) ) ;
UStateTreeState & State1A = State1 . AddChildState ( FName ( TEXT ( " State1A " ) ) ) ;
2021-11-12 05:48:11 -05:00
auto & TaskRoot = Root . AddTask < FTestTask_Stand > ( FName ( TEXT ( " TaskRoot " ) ) ) ;
2022-11-22 08:13:54 -05:00
TaskRoot . GetNode ( ) . TicksToCompletion = 3 ; // let Task1A to complete first
2021-09-28 13:33:17 -04:00
2021-11-12 05:48:11 -05:00
auto & Task1 = State1 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1 " ) ) ) ;
2022-11-22 08:13:54 -05:00
Task1 . GetNode ( ) . TicksToCompletion = 3 ; // let Task1A to complete first
2021-09-28 13:33:17 -04:00
2021-11-12 05:48:11 -05:00
auto & Task1A = State1A . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1A " ) ) ) ;
2022-09-07 17:12:18 -04:00
Task1A . GetNode ( ) . TicksToCompletion = 2 ;
2022-09-01 09:06:53 -04:00
State1A . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : GotoState , & State1 ) ;
2021-09-28 13:33:17 -04:00
2021-10-29 04:33:21 -04:00
FStateTreeCompilerLog Log ;
2022-04-05 09:44:28 -04:00
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
2021-09-28 13:33:17 -04:00
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
2022-09-23 20:02:42 -04:00
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
2021-09-28 13:33:17 -04:00
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
2021-11-22 12:11:19 -05:00
// Start and enter state
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree TaskRoot should enter state " , Exec . Expect ( TaskRoot . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree Task1 should enter state " , Exec . Expect ( Task1 . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree Task1A should enter state " , Exec . Expect ( Task1A . GetName ( ) , EnterStateStr ) ) ;
AITEST_FALSE ( " StateTree TaskRoot should not tick " , Exec . Expect ( TaskRoot . GetName ( ) , TickStr ) ) ;
AITEST_FALSE ( " StateTree Task1 should not tick " , Exec . Expect ( Task1 . GetName ( ) , TickStr ) ) ;
AITEST_FALSE ( " StateTree Task1A should not tick " , Exec . Expect ( Task1A . GetName ( ) , TickStr ) ) ;
2021-09-28 13:33:17 -04:00
AITEST_TRUE ( " StateTree should be running " , Status = = EStateTreeRunStatus : : Running ) ;
Exec . LogClear ( ) ;
// Regular tick
Status = Exec . Tick ( 0.1f ) ;
2021-11-12 05:48:11 -05:00
AITEST_TRUE ( " StateTree tasks should update in order " , Exec . Expect ( TaskRoot . GetName ( ) , TickStr ) . Then ( Task1 . GetName ( ) , TickStr ) . Then ( Task1A . GetName ( ) , TickStr ) ) ;
AITEST_FALSE ( " StateTree TaskRoot should not EnterState " , Exec . Expect ( TaskRoot . GetName ( ) , EnterStateStr ) ) ;
AITEST_FALSE ( " StateTree Task1 should not EnterState " , Exec . Expect ( Task1 . GetName ( ) , EnterStateStr ) ) ;
AITEST_FALSE ( " StateTree Task1A should not EnterState " , Exec . Expect ( Task1A . GetName ( ) , EnterStateStr ) ) ;
AITEST_FALSE ( " StateTree TaskRoot should not ExitState " , Exec . Expect ( TaskRoot . GetName ( ) , ExitStateStr ) ) ;
AITEST_FALSE ( " StateTree Task1 should not ExitState " , Exec . Expect ( Task1 . GetName ( ) , ExitStateStr ) ) ;
AITEST_FALSE ( " StateTree Task1A should not ExitState " , Exec . Expect ( Task1A . GetName ( ) , ExitStateStr ) ) ;
2021-09-28 13:33:17 -04:00
AITEST_TRUE ( " StateTree should be running " , Status = = EStateTreeRunStatus : : Running ) ;
Exec . LogClear ( ) ;
// Partial reselect, Root should not get EnterState
Status = Exec . Tick ( 0.1f ) ;
2021-11-12 05:48:11 -05:00
AITEST_FALSE ( " StateTree TaskRoot should not enter state " , Exec . Expect ( TaskRoot . GetName ( ) , EnterStateStr ) ) ;
2021-11-22 12:11:19 -05:00
AITEST_TRUE ( " StateTree Task1 should tick, exit state, and enter state " , Exec . Expect ( Task1 . GetName ( ) , TickStr ) . Then ( Task1 . GetName ( ) , ExitStateStr ) . Then ( Task1 . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree Task1A should tick, exit state, and enter state " , Exec . Expect ( Task1A . GetName ( ) , TickStr ) . Then ( Task1A . GetName ( ) , ExitStateStr ) . Then ( Task1A . GetName ( ) , EnterStateStr ) ) ;
2021-09-28 13:33:17 -04:00
AITEST_TRUE ( " StateTree should be running " , Status = = EStateTreeRunStatus : : Running ) ;
Exec . LogClear ( ) ;
return true ;
}
} ;
2021-12-09 05:26:00 -05:00
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Select , " System.StateTree.Select " ) ;
struct FStateTreeTest_FailEnterState : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & State1 = Root . AddChildState ( FName ( TEXT ( " State1 " ) ) ) ;
UStateTreeState & State1A = State1 . AddChildState ( FName ( TEXT ( " State1A " ) ) ) ;
auto & TaskRoot = Root . AddTask < FTestTask_Stand > ( FName ( TEXT ( " TaskRoot " ) ) ) ;
auto & Task1 = State1 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1 " ) ) ) ;
auto & Task2 = State1 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task2 " ) ) ) ;
2022-09-07 17:12:18 -04:00
Task2 . GetNode ( ) . EnterStateResult = EStateTreeRunStatus : : Failed ;
2021-12-09 05:26:00 -05:00
auto & Task3 = State1 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task3 " ) ) ) ;
auto & Task1A = State1A . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1A " ) ) ) ;
2022-09-01 09:06:53 -04:00
State1A . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : GotoState , & State1 ) ;
2021-12-09 05:26:00 -05:00
FStateTreeCompilerLog Log ;
2022-04-05 09:44:28 -04:00
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
2021-12-09 05:26:00 -05:00
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
2022-09-23 20:02:42 -04:00
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
2021-12-09 05:26:00 -05:00
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
const FString StateCompletedStr ( TEXT ( " StateCompleted " ) ) ;
// Start and enter state
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree TaskRoot should enter state " , Exec . Expect ( TaskRoot . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree Task1 should enter state " , Exec . Expect ( Task1 . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree Task2 should enter state " , Exec . Expect ( Task2 . GetName ( ) , EnterStateStr ) ) ;
AITEST_FALSE ( " StateTree Task3 should not enter state " , Exec . Expect ( Task3 . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree Should execute StateCompleted in reverse order " , Exec . Expect ( Task2 . GetName ( ) , StateCompletedStr ) . Then ( Task1 . GetName ( ) , StateCompletedStr ) . Then ( TaskRoot . GetName ( ) , StateCompletedStr ) ) ;
AITEST_FALSE ( " StateTree Task3 should not state complete " , Exec . Expect ( Task3 . GetName ( ) , StateCompletedStr ) ) ;
AITEST_TRUE ( " StateTree exec status should be failed " , Exec . GetLastTickStatus ( ) = = EStateTreeRunStatus : : Failed ) ;
Exec . LogClear ( ) ;
// Stop and exit state
Status = Exec . Stop ( ) ;
AITEST_TRUE ( " StateTree TaskRoot should exit state " , Exec . Expect ( TaskRoot . GetName ( ) , ExitStateStr ) ) ;
AITEST_TRUE ( " StateTree Task1 should exit state " , Exec . Expect ( Task1 . GetName ( ) , ExitStateStr ) ) ;
AITEST_TRUE ( " StateTree Task2 should exit state " , Exec . Expect ( Task2 . GetName ( ) , ExitStateStr ) ) ;
AITEST_FALSE ( " StateTree Task3 should not exit state " , Exec . Expect ( Task3 . GetName ( ) , ExitStateStr ) ) ;
2023-05-10 09:30:13 -04:00
AITEST_TRUE ( " StateTree status should be stopped " , Status = = EStateTreeRunStatus : : Stopped ) ;
2021-12-09 05:26:00 -05:00
Exec . LogClear ( ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_FailEnterState , " System.StateTree.FailEnterState " ) ;
2021-09-28 13:33:17 -04:00
2022-11-23 09:22:14 -05:00
struct FStateTreeTest_Restart : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & State1 = Root . AddChildState ( FName ( TEXT ( " State1 " ) ) ) ;
auto & Task1 = State1 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1 " ) ) ) ;
Task1 . GetNode ( ) . TicksToCompletion = 2 ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
const FString StateCompletedStr ( TEXT ( " StateCompleted " ) ) ;
// Start and enter state
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree Task1 should enter state " , Exec . Expect ( Task1 . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree exec status should be running " , Exec . GetLastTickStatus ( ) = = EStateTreeRunStatus : : Running ) ;
Exec . LogClear ( ) ;
// Tick
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree exec status should be running " , Exec . GetLastTickStatus ( ) = = EStateTreeRunStatus : : Running ) ;
Exec . LogClear ( ) ;
// Call Start again, should stop and start the tree.
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree Task1 should exit state " , Exec . Expect ( Task1 . GetName ( ) , ExitStateStr ) ) ;
AITEST_TRUE ( " StateTree Task1 should enter state " , Exec . Expect ( Task1 . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree exec status should be running " , Exec . GetLastTickStatus ( ) = = EStateTreeRunStatus : : Running ) ;
Exec . LogClear ( ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Restart , " System.StateTree.Restart " ) ;
2022-04-05 03:20:57 -04:00
struct FStateTreeTest_SubTree : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & State1 = Root . AddChildState ( FName ( TEXT ( " State1 " ) ) , EStateTreeStateType : : Linked ) ;
UStateTreeState & State2 = Root . AddChildState ( FName ( TEXT ( " State2 " ) ) ) ;
2022-07-21 08:19:56 -04:00
UStateTreeState & State3 = Root . AddChildState ( FName ( TEXT ( " State3 " ) ) , EStateTreeStateType : : Subtree ) ;
2022-04-05 03:20:57 -04:00
UStateTreeState & State3A = State3 . AddChildState ( FName ( TEXT ( " State3A " ) ) ) ;
UStateTreeState & State3B = State3 . AddChildState ( FName ( TEXT ( " State3B " ) ) ) ;
2023-01-23 12:48:04 -05:00
State1 . LinkedSubtree = State3 . GetLinkToState ( ) ;
2022-04-05 03:20:57 -04:00
2022-11-23 09:22:14 -05:00
State1 . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : GotoState , & State2 ) ;
2022-04-05 03:20:57 -04:00
auto & Task2 = State2 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task2 " ) ) ) ;
2022-11-23 09:22:14 -05:00
State2 . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : Succeeded ) ;
2022-04-05 03:20:57 -04:00
auto & Task3A = State3A . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task3A " ) ) ) ;
2022-09-01 09:06:53 -04:00
State3A . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : GotoState , & State3B ) ;
2022-04-05 03:20:57 -04:00
auto & Task3B = State3B . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task3B " ) ) ) ;
2022-11-23 09:22:14 -05:00
State3B . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : Succeeded ) ;
2022-04-05 03:20:57 -04:00
FStateTreeCompilerLog Log ;
2022-04-05 09:44:28 -04:00
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
2022-04-05 03:20:57 -04:00
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
2022-09-23 20:02:42 -04:00
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
2022-04-05 03:20:57 -04:00
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
const FString StateCompletedStr ( TEXT ( " StateCompleted " ) ) ;
// Start and enter state
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree Active States should be in Root/State1/State3/State3A " , Exec . ExpectInActiveStates ( Root . Name , State1 . Name , State3 . Name , State3A . Name ) ) ;
2023-06-12 07:34:11 -04:00
AITEST_FALSE ( " StateTree Task2 should not enter state " , Exec . Expect ( Task2 . GetName ( ) , EnterStateStr ) ) ;
2022-04-05 03:20:57 -04:00
AITEST_TRUE ( " StateTree Task3A should enter state " , Exec . Expect ( Task3A . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree should be running " , Status = = EStateTreeRunStatus : : Running ) ;
Exec . LogClear ( ) ;
2022-11-23 09:22:14 -05:00
// Transition within subtree
2022-04-05 03:20:57 -04:00
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree Active States should be in Root/State1/State3/State3B " , Exec . ExpectInActiveStates ( Root . Name , State1 . Name , State3 . Name , State3B . Name ) ) ;
AITEST_TRUE ( " StateTree Task3B should enter state " , Exec . Expect ( Task3B . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree should be running " , Status = = EStateTreeRunStatus : : Running ) ;
2022-11-23 09:22:14 -05:00
Exec . LogClear ( ) ;
// Complete subtree
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree Active States should be in Root/State2 " , Exec . ExpectInActiveStates ( Root . Name , State2 . Name ) ) ;
AITEST_TRUE ( " StateTree Task2 should enter state " , Exec . Expect ( Task2 . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree should be running " , Status = = EStateTreeRunStatus : : Running ) ;
Exec . LogClear ( ) ;
// Complete the whole tree
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree should complete in succeeded " , Status = = EStateTreeRunStatus : : Succeeded ) ;
Exec . LogClear ( ) ;
2022-04-05 03:20:57 -04:00
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_SubTree , " System.StateTree.SubTree " ) ;
2023-06-12 07:34:11 -04:00
struct FStateTreeTest_SubTree_CascadedSucceeded : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
// - Root [TaskA]
// - LinkedState>SubTreeState -> (F)Failed
// - SubTreeState [TaskB]
// - SubLinkedState>SubSubTreeState -> (S)Failed
// - SubSubTreeState
// - SubSubLeaf [TaskC] -> (S)Succeeded
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & LinkedState = Root . AddChildState ( FName ( TEXT ( " Linked " ) ) , EStateTreeStateType : : Linked ) ;
UStateTreeState & SubTreeState = Root . AddChildState ( FName ( TEXT ( " SubTreeState " ) ) , EStateTreeStateType : : Subtree ) ;
UStateTreeState & SubLinkedState = SubTreeState . AddChildState ( FName ( TEXT ( " SubLinkedState " ) ) , EStateTreeStateType : : Linked ) ;
UStateTreeState & SubSubTreeState = Root . AddChildState ( FName ( TEXT ( " SubSubTreeState " ) ) , EStateTreeStateType : : Subtree ) ;
UStateTreeState & SubSubLeaf = SubSubTreeState . AddChildState ( FName ( TEXT ( " SubSubLeaf " ) ) ) ;
LinkedState . LinkedSubtree = SubTreeState . GetLinkToState ( ) ;
SubLinkedState . LinkedSubtree = SubSubTreeState . GetLinkToState ( ) ;
LinkedState . AddTransition ( EStateTreeTransitionTrigger : : OnStateFailed , EStateTreeTransitionType : : Failed ) ;
SubLinkedState . AddTransition ( EStateTreeTransitionTrigger : : OnStateSucceeded , EStateTreeTransitionType : : Failed ) ;
SubSubLeaf . AddTransition ( EStateTreeTransitionTrigger : : OnStateSucceeded , EStateTreeTransitionType : : Succeeded ) ;
TStateTreeEditorNode < FTestTask_Stand > & TaskA = Root . AddTask < FTestTask_Stand > ( FName ( TEXT ( " TaskA " ) ) ) ;
TStateTreeEditorNode < FTestTask_Stand > & TaskB = SubTreeState . AddTask < FTestTask_Stand > ( FName ( TEXT ( " TaskB " ) ) ) ;
TStateTreeEditorNode < FTestTask_Stand > & TaskC = SubSubLeaf . AddTask < FTestTask_Stand > ( FName ( TEXT ( " TaskC " ) ) ) ;
TaskA . GetNode ( ) . TicksToCompletion = 2 ;
TaskB . GetNode ( ) . TicksToCompletion = 2 ;
TaskC . GetNode ( ) . TicksToCompletion = 1 ; // The deepest task completes first.
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
const FString StateCompletedStr ( TEXT ( " StateCompleted " ) ) ;
// Start and enter state
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree 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 ( " StateTree should be running " , Status = = EStateTreeRunStatus : : Running ) ;
Exec . LogClear ( ) ;
// Subtrees completes, and it completes the whole tree too.
// There's no good way to observe this externally. We switch the return along the way to make sure the transition does not happen directly from the leaf to failed.
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree should be Failed " , Status = = EStateTreeRunStatus : : Failed ) ;
Exec . LogClear ( ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_SubTree_CascadedSucceeded , " System.StateTree.SubTree.CascadedSucceeded " ) ;
2022-09-07 17:12:18 -04:00
struct FStateTreeTest_SharedInstanceData : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
auto & IntCond = Root . AddEnterCondition < FStateTreeTestCondition > ( ) ;
IntCond . GetInstanceData ( ) . Count = 1 ;
auto & Task = Root . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task " ) ) ) ;
Task . GetNode ( ) . TicksToCompletion = 2 ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
// Init, nothing should access the shared data.
constexpr int32 NumConcurrent = 100 ;
FStateTreeTestConditionInstanceData : : GlobalCounter = 0 ;
bool bInitSucceeded = true ;
2022-09-23 20:02:42 -04:00
TArray < FStateTreeInstanceData > InstanceDatas ;
InstanceDatas . SetNum ( NumConcurrent ) ;
for ( int32 Index = 0 ; Index < NumConcurrent ; Index + + )
2022-09-07 17:12:18 -04:00
{
2022-09-23 20:02:42 -04:00
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceDatas [ Index ] ) ;
bInitSucceeded & = Exec . IsValid ( ) ;
2022-09-07 17:12:18 -04:00
}
AITEST_TRUE ( " All StateTree contexts should init " , bInitSucceeded ) ;
AITEST_EQUAL ( " Test condition global counter should be 0 " , ( int32 ) FStateTreeTestConditionInstanceData : : GlobalCounter , 0 ) ;
// Start in parallel
// This should create shared data per thread.
// We expect that ParallelForWithTaskContext() creates a context per thread.
TArray < FStateTreeTestRunContext > RunContexts ;
ParallelForWithTaskContext (
RunContexts ,
2022-09-23 20:02:42 -04:00
InstanceDatas . Num ( ) ,
[ & InstanceDatas , & StateTree ] ( FStateTreeTestRunContext & RunContext , int32 Index )
2022-09-07 17:12:18 -04:00
{
2022-09-23 20:02:42 -04:00
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceDatas [ Index ] ) ;
const EStateTreeRunStatus Status = Exec . Start ( ) ;
2022-09-07 17:12:18 -04:00
if ( Status = = EStateTreeRunStatus : : Running )
{
RunContext . Count + + ;
}
}
) ;
int32 StartTotalRunning = 0 ;
for ( FStateTreeTestRunContext RunContext : RunContexts )
{
StartTotalRunning + = RunContext . Count ;
}
AITEST_EQUAL ( " All StateTree contexts should be running after Start " , StartTotalRunning , NumConcurrent ) ;
2022-09-23 20:02:42 -04:00
AITEST_EQUAL ( " Test condition global counter should equal context count after Start " , ( int32 ) FStateTreeTestConditionInstanceData : : GlobalCounter , InstanceDatas . Num ( ) ) ;
2022-09-07 17:12:18 -04:00
// Tick in parallel
// This should not recreate the data, so FStateTreeTestConditionInstanceData::GlobalCounter should stay as is.
for ( FStateTreeTestRunContext RunContext : RunContexts )
{
RunContext . Count = 0 ;
}
ParallelForWithTaskContext (
RunContexts ,
2022-09-23 20:02:42 -04:00
InstanceDatas . Num ( ) ,
[ & InstanceDatas , & StateTree ] ( FStateTreeTestRunContext & RunContext , int32 Index )
2022-09-07 17:12:18 -04:00
{
2022-09-23 20:02:42 -04:00
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceDatas [ Index ] ) ;
const EStateTreeRunStatus Status = Exec . Tick ( 0.1f ) ;
2022-09-07 17:12:18 -04:00
if ( Status = = EStateTreeRunStatus : : Running )
{
RunContext . Count + + ;
}
}
) ;
int32 TickTotalRunning = 0 ;
for ( FStateTreeTestRunContext RunContext : RunContexts )
{
TickTotalRunning + = RunContext . Count ;
}
AITEST_EQUAL ( " All StateTree contexts should be running after Tick " , TickTotalRunning , NumConcurrent ) ;
2022-09-23 20:02:42 -04:00
AITEST_EQUAL ( " Test condition global counter should equal context count after Tick " , ( int32 ) FStateTreeTestConditionInstanceData : : GlobalCounter , InstanceDatas . Num ( ) ) ;
2022-09-07 17:12:18 -04:00
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_SharedInstanceData , " System.StateTree.SharedInstanceData " ) ;
2022-11-22 08:13:54 -05:00
struct FStateTreeTest_TransitionPriority : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & State1 = Root . AddChildState ( FName ( TEXT ( " State1 " ) ) ) ;
UStateTreeState & State1A = State1 . AddChildState ( FName ( TEXT ( " State1A " ) ) ) ;
UStateTreeState & State1B = State1 . AddChildState ( FName ( TEXT ( " State1B " ) ) ) ;
UStateTreeState & State1C = State1 . AddChildState ( FName ( TEXT ( " State1C " ) ) ) ;
auto & Task1 = State1 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1 " ) ) ) ;
Task1 . GetNode ( ) . TicksToCompletion = 2 ;
State1 . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : Succeeded ) ;
auto & Task1A = State1A . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1A " ) ) ) ;
Task1A . GetNode ( ) . TicksToCompletion = 1 ;
State1A . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : NextState ) ;
auto & Task1B = State1B . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1B " ) ) ) ;
Task1B . GetNode ( ) . TicksToCompletion = 2 ;
State1B . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : NextState ) ;
auto & Task1C = State1C . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1C " ) ) ) ;
Task1C . GetNode ( ) . TicksToCompletion = 2 ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
const FString StateCompletedStr ( TEXT ( " StateCompleted " ) ) ;
// Start and enter state
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree Task1 should enter state " , Exec . Expect ( Task1 . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree Task1A should enter state " , Exec . Expect ( Task1A . GetName ( ) , EnterStateStr ) ) ;
Exec . LogClear ( ) ;
// Transition from Task1A to Task1B
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree Task1A should complete " , Exec . Expect ( Task1A . GetName ( ) , StateCompletedStr ) ) ;
AITEST_TRUE ( " StateTree Task1B should enter state " , Exec . Expect ( Task1B . GetName ( ) , EnterStateStr ) ) ;
Exec . LogClear ( ) ;
// Task1 completes, and we should take State1 transition.
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree Task1 should complete " , Exec . Expect ( Task1 . GetName ( ) , StateCompletedStr ) ) ;
AITEST_EQUAL ( " Tree execution should stop on success " , Status , EStateTreeRunStatus : : Succeeded ) ;
Exec . LogClear ( ) ;
return true ;
}
} ;
2023-02-09 04:20:59 -05:00
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_TransitionPriority , " System.StateTree.Transition.Priority " ) ;
2022-11-22 08:13:54 -05:00
struct FStateTreeTest_TransitionPriorityEnterState : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
2023-01-23 12:48:04 -05:00
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
2022-11-22 08:13:54 -05:00
UStateTreeState & State0 = Root . AddChildState ( FName ( TEXT ( " State0 " ) ) ) ;
UStateTreeState & State1 = Root . AddChildState ( FName ( TEXT ( " State1 " ) ) ) ;
UStateTreeState & State1A = State1 . AddChildState ( FName ( TEXT ( " State1A " ) ) ) ;
UStateTreeState & State2 = Root . AddChildState ( FName ( TEXT ( " State2 " ) ) ) ;
UStateTreeState & State3 = Root . AddChildState ( FName ( TEXT ( " State3 " ) ) ) ;
auto & Task0 = State0 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task0 " ) ) ) ;
State0 . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : GotoState , & State1 ) ;
auto & Task1 = State1 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1 " ) ) ) ;
Task1 . GetNode ( ) . EnterStateResult = EStateTreeRunStatus : : Failed ;
State1 . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : GotoState , & State2 ) ;
auto & Task1A = State1A . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1A " ) ) ) ;
State1A . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : GotoState , & State3 ) ;
auto & Task2 = State2 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task2 " ) ) ) ;
State2 . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : Succeeded ) ;
auto & Task3 = State3 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task3 " ) ) ) ;
State3 . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : Succeeded ) ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
const FString StateCompletedStr ( TEXT ( " StateCompleted " ) ) ;
// Start and enter state
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree Task0 should enter state " , Exec . Expect ( Task0 . GetName ( ) , EnterStateStr ) ) ;
Exec . LogClear ( ) ;
// Transition from State0 to State1, it should fail (Task1), and the transition on State1->State2 (and not State1A->State3)
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree Task0 should complete " , Exec . Expect ( Task0 . GetName ( ) , StateCompletedStr ) ) ;
AITEST_TRUE ( " StateTree Task2 should enter state " , Exec . Expect ( Task2 . GetName ( ) , EnterStateStr ) ) ;
AITEST_FALSE ( " StateTree Task3 should not enter state " , Exec . Expect ( Task3 . GetName ( ) , EnterStateStr ) ) ;
Exec . LogClear ( ) ;
return true ;
}
} ;
2023-02-09 04:20:59 -05:00
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_TransitionPriorityEnterState , " System.StateTree.Transition.PriorityEnterState " ) ;
2023-04-04 08:25:50 -04:00
struct FStateTreeTest_LastConditionWithIndent : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & State1 = Root . AddChildState ( FName ( TEXT ( " State1 " ) ) ) ;
auto & Task1 = State1 . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1 " ) ) ) ;
State1 . AddEnterCondition < FStateTreeTestCondition > ( ) ;
auto & LastCondition = State1 . AddEnterCondition < FStateTreeTestCondition > ( ) ;
// Last condition has Indent
LastCondition . ConditionIndent = 1 ;
State1 . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : Succeeded ) ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree Task1 should enter state " , Exec . Expect ( Task1 . GetName ( ) , EnterStateStr ) ) ;
AITEST_FALSE ( " StateTree Task1 should not tick " , Exec . Expect ( Task1 . GetName ( ) , TickStr ) ) ;
Exec . LogClear ( ) ;
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree Task1 should tick, and exit state " , Exec . Expect ( Task1 . GetName ( ) , TickStr ) . Then ( Task1 . GetName ( ) , ExitStateStr ) ) ;
AITEST_TRUE ( " StateTree should be completed " , Status = = EStateTreeRunStatus : : Succeeded ) ;
Exec . LogClear ( ) ;
Status = Exec . Tick ( 0.1f ) ;
AITEST_FALSE ( " StateTree Task1 should not tick " , Exec . Expect ( Task1 . GetName ( ) , TickStr ) ) ;
Exec . LogClear ( ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_LastConditionWithIndent , " System.StateTree.LastConditionWithIndent " ) ;
2023-02-09 04:20:59 -05:00
struct FStateTreeTest_TransitionGlobalDataView : FAITestBase
{
// Tests that the global eval and task dataviews are kept up to date when transitioning from
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & StateA = Root . AddChildState ( FName ( TEXT ( " A " ) ) ) ;
UStateTreeState & StateB = Root . AddChildState ( FName ( TEXT ( " B " ) ) ) ;
auto & EvalA = EditorData . AddEvaluator < FTestEval_A > ( FName ( TEXT ( " Eval " ) ) ) ;
EvalA . GetInstanceData ( ) . IntA = 42 ;
auto & GlobalTask = EditorData . AddGlobalTask < FTestTask_PrintValue > ( FName ( TEXT ( " Global " ) ) ) ;
GlobalTask . GetInstanceData ( ) . Value = 123 ;
// State A
auto & Task0 = StateA . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task0 " ) ) ) ;
StateA . AddTransition ( EStateTreeTransitionTrigger : : OnStateCompleted , EStateTreeTransitionType : : GotoState , & StateB ) ;
// State B
auto & Task1 = StateB . AddTask < FTestTask_PrintValue > ( FName ( TEXT ( " Task1 " ) ) ) ;
2023-02-10 07:22:48 -05:00
EditorData . AddPropertyBinding ( EvalA , TEXT ( " IntA " ) , Task1 , TEXT ( " Value " ) ) ;
2023-02-09 04:20:59 -05:00
auto & Task2 = StateB . AddTask < FTestTask_PrintValue > ( FName ( TEXT ( " Task2 " ) ) ) ;
2023-02-10 07:22:48 -05:00
EditorData . AddPropertyBinding ( GlobalTask , TEXT ( " Value " ) , Task2 , TEXT ( " Value " ) ) ;
2023-02-09 04:20:59 -05:00
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString EnterState42Str ( TEXT ( " EnterState42 " ) ) ;
const FString EnterState123Str ( TEXT ( " EnterState123 " ) ) ;
// Start and enter state
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree Task0 should enter state " , Exec . Expect ( Task0 . GetName ( ) , EnterStateStr ) ) ;
Exec . LogClear ( ) ;
// Transition from StateA to StateB, Task0 should enter state with evaluator value copied.
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree Task0 should enter state with value 42 " , Exec . Expect ( Task1 . GetName ( ) , EnterState42Str ) ) ;
AITEST_TRUE ( " StateTree Task1 should enter state with value 123 " , Exec . Expect ( Task2 . GetName ( ) , EnterState123Str ) ) ;
Exec . LogClear ( ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_TransitionGlobalDataView , " System.StateTree.Transition.GlobalDataView " ) ;
2022-11-22 08:13:54 -05:00
2023-03-14 10:40:47 -04:00
struct FStateTreeTest_TransitionDelay : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
const FGameplayTag Tag = UE : : StateTree : : Tests : : FNativeGameplayTags : : Get ( ) . TestTag ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & StateA = Root . AddChildState ( FName ( TEXT ( " A " ) ) ) ;
UStateTreeState & StateB = Root . AddChildState ( FName ( TEXT ( " B " ) ) ) ;
// State A
auto & Task0 = StateA . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task0 " ) ) ) ;
Task0 . GetNode ( ) . TicksToCompletion = 100 ;
FStateTreeTransition & Transition = StateA . AddTransition ( EStateTreeTransitionTrigger : : OnEvent , EStateTreeTransitionType : : GotoState , & StateB ) ;
Transition . bDelayTransition = true ;
Transition . DelayDuration = 0.15f ;
Transition . DelayRandomVariance = 0.0f ;
Transition . EventTag = Tag ;
// State B
auto & Task1 = StateB . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1 " ) ) ) ;
Task1 . GetNode ( ) . TicksToCompletion = 100 ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
const FString StateCompletedStr ( TEXT ( " StateCompleted " ) ) ;
// Start and enter state
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree Task0 should enter state " , Exec . Expect ( Task0 . GetName ( ) , EnterStateStr ) ) ;
Exec . LogClear ( ) ;
// This should cause delayed transition.
Exec . SendEvent ( Tag ) ;
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree Task0 should tick " , Exec . Expect ( Task0 . GetName ( ) , TickStr ) ) ;
Exec . LogClear ( ) ;
// Should have delayed transitions
const int32 NumDelayedTransitions0 = InstanceData . GetStruct ( 0 ) . Get < const FStateTreeExecutionState > ( ) . DelayedTransitions . Num ( ) ;
AITEST_EQUAL ( " Should have a delayed transition " , NumDelayedTransitions0 , 1 ) ;
// Tick and expect a delayed transition.
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree Task0 should tick " , Exec . Expect ( Task0 . GetName ( ) , TickStr ) ) ;
Exec . LogClear ( ) ;
const int32 NumDelayedTransitions1 = InstanceData . GetStruct ( 0 ) . Get < const FStateTreeExecutionState > ( ) . DelayedTransitions . Num ( ) ;
AITEST_EQUAL ( " Should have a delayed transition " , NumDelayedTransitions1 , 1 ) ;
// Should complete delayed transition.
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree Task0 should exit state " , Exec . Expect ( Task0 . GetName ( ) , ExitStateStr ) ) ;
AITEST_TRUE ( " StateTree Task1 should enter state " , Exec . Expect ( Task1 . GetName ( ) , EnterStateStr ) ) ;
Exec . LogClear ( ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_TransitionDelay , " System.StateTree.TransitionDelay " ) ;
struct FStateTreeTest_TransitionDelayZero : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
const FGameplayTag Tag = UE : : StateTree : : Tests : : FNativeGameplayTags : : Get ( ) . TestTag ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & StateA = Root . AddChildState ( FName ( TEXT ( " A " ) ) ) ;
UStateTreeState & StateB = Root . AddChildState ( FName ( TEXT ( " B " ) ) ) ;
// State A
auto & Task0 = StateA . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task0 " ) ) ) ;
Task0 . GetNode ( ) . TicksToCompletion = 100 ;
FStateTreeTransition & Transition = StateA . AddTransition ( EStateTreeTransitionTrigger : : OnEvent , EStateTreeTransitionType : : GotoState , & StateB ) ;
Transition . bDelayTransition = true ;
Transition . DelayDuration = 0.0f ;
Transition . DelayRandomVariance = 0.0f ;
Transition . EventTag = Tag ;
// State B
auto & Task1 = StateB . AddTask < FTestTask_Stand > ( FName ( TEXT ( " Task1 " ) ) ) ;
Task1 . GetNode ( ) . TicksToCompletion = 100 ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
const FString StateCompletedStr ( TEXT ( " StateCompleted " ) ) ;
// Start and enter state
Status = Exec . Start ( ) ;
AITEST_TRUE ( " StateTree Task0 should enter state " , Exec . Expect ( Task0 . GetName ( ) , EnterStateStr ) ) ;
Exec . LogClear ( ) ;
// This should cause delayed transition. Because the time is 0, it should happen immediately.
Exec . SendEvent ( Tag ) ;
Status = Exec . Tick ( 0.1f ) ;
AITEST_TRUE ( " StateTree Task0 should exit state " , Exec . Expect ( Task0 . GetName ( ) , ExitStateStr ) ) ;
AITEST_TRUE ( " StateTree Task1 should enter state " , Exec . Expect ( Task1 . GetName ( ) , EnterStateStr ) ) ;
Exec . LogClear ( ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_TransitionDelayZero , " System.StateTree.TransitionDelayZero " ) ;
2023-02-10 07:22:48 -05:00
struct FStateTreeTest_PropertyPathOffset : FAITestBase
{
virtual bool InstantTest ( ) override
{
FStateTreePropertyPath 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 < FStateTreePropertyPathIndirection > Indirections ;
const bool bResolveResult = Path . ResolveIndirections ( FStateTreeTest_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 ( ) , EStateTreePropertyAccessType : : Offset ) ;
AITEST_EQUAL ( " Indirection 1 should be Offset type " , Indirections [ 1 ] . GetAccessType ( ) , EStateTreePropertyAccessType : : Offset ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_PropertyPathOffset , " System.StateTree.PropertyPath.Offset " ) ;
struct FStateTreeTest_PropertyPathParseFail : FAITestBase
{
virtual bool InstantTest ( ) override
{
{
FStateTreePropertyPath Path ;
const bool bParseResult = Path . FromString ( TEXT ( " " ) ) ; // empty is valid.
AITEST_TRUE ( " Parsing path should succeed " , bParseResult ) ;
}
{
FStateTreePropertyPath Path ;
const bool bParseResult = Path . FromString ( TEXT ( " StructB.[0]B " ) ) ;
AITEST_FALSE ( " Parsing path should fail " , bParseResult ) ;
}
{
FStateTreePropertyPath Path ;
const bool bParseResult = Path . FromString ( TEXT ( " StructB..NoThere " ) ) ;
AITEST_FALSE ( " Parsing path should fail " , bParseResult ) ;
}
{
FStateTreePropertyPath Path ;
const bool bParseResult = Path . FromString ( TEXT ( " . " ) ) ;
AITEST_FALSE ( " Parsing path should fail " , bParseResult ) ;
}
{
FStateTreePropertyPath Path ;
const bool bParseResult = Path . FromString ( TEXT ( " StructB..B " ) ) ;
AITEST_FALSE ( " Parsing path should fail " , bParseResult ) ;
}
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_PropertyPathParseFail , " System.StateTree.PropertyPath.ParseFail " ) ;
struct FStateTreeTest_PropertyPathOffsetFail : FAITestBase
{
virtual bool InstantTest ( ) override
{
FStateTreePropertyPath 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 < FStateTreePropertyPathIndirection > Indirections ;
const bool bResolveResult = Path . ResolveIndirections ( FStateTreeTest_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 ( FStateTreeTest_PropertyPathOffsetFail , " System.StateTree.PropertyPath.OffsetFail " ) ;
struct FStateTreeTest_PropertyPathObject : FAITestBase
{
virtual bool InstantTest ( ) override
{
FStateTreePropertyPath 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 ) ;
UStateTreeTest_PropertyObject * Object = NewObject < UStateTreeTest_PropertyObject > ( ) ;
Object - > InstancedObject = NewObject < UStateTreeTest_PropertyObjectInstanced > ( ) ;
2023-06-21 10:27:51 -04:00
const bool bUpdateResult = Path . UpdateSegmentsFromValue ( FStateTreeDataView ( Object ) ) ;
2023-02-10 07:22:48 -05:00
AITEST_TRUE ( " Update instance types should succeeed " , bUpdateResult ) ;
AITEST_TRUE ( " Path segment 0 instance type should be UStateTreeTest_PropertyObjectInstanced " , Path . GetSegment ( 0 ) . GetInstanceStruct ( ) = = UStateTreeTest_PropertyObjectInstanced : : StaticClass ( ) ) ;
AITEST_TRUE ( " Path segment 1 instance type should be nullptr " , Path . GetSegment ( 1 ) . GetInstanceStruct ( ) = = nullptr ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_PropertyPathObject , " System.StateTree.PropertyPath.Object " ) ;
struct FStateTreeTest_PropertyPathWrongObject : FAITestBase
{
virtual bool InstantTest ( ) override
{
FStateTreePropertyPath 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 ) ;
UStateTreeTest_PropertyObject * Object = NewObject < UStateTreeTest_PropertyObject > ( ) ;
Object - > InstancedObject = NewObject < UStateTreeTest_PropertyObjectInstancedWithB > ( ) ;
{
FString ResolveErrors ;
TArray < FStateTreePropertyPathIndirection > Indirections ;
const bool bResolveResult = Path . ResolveIndirectionsWithValue ( FStateTreeDataView ( 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 ( ) = = EStateTreePropertyAccessType : : ObjectInstance ) ;
AITEST_TRUE ( " Object " , Indirections [ 0 ] . GetContainerStruct ( ) = = Object - > GetClass ( ) ) ;
AITEST_TRUE ( " Object " , Indirections [ 0 ] . GetInstanceStruct ( ) = = UStateTreeTest_PropertyObjectInstancedWithB : : StaticClass ( ) ) ;
AITEST_EQUAL ( " Should not have error " , ResolveErrors . Len ( ) , 0 ) ;
}
Object - > InstancedObject = NewObject < UStateTreeTest_PropertyObjectInstanced > ( ) ;
{
FString ResolveErrors ;
TArray < FStateTreePropertyPathIndirection > Indirections ;
const bool bResolveResult = Path . ResolveIndirectionsWithValue ( FStateTreeDataView ( 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 ( FStateTreeTest_PropertyPathWrongObject , " System.StateTree.PropertyPath.WrongObject " ) ;
struct FStateTreeTest_PropertyPathArray : FAITestBase
{
virtual bool InstantTest ( ) override
{
FStateTreePropertyPath 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 ) ;
UStateTreeTest_PropertyObject * Object = NewObject < UStateTreeTest_PropertyObject > ( ) ;
Object - > ArrayOfInts . Add ( 42 ) ;
Object - > ArrayOfInts . Add ( 123 ) ;
FString ResolveErrors ;
TArray < FStateTreePropertyPathIndirection > Indirections ;
const bool bResolveResult = Path . ResolveIndirectionsWithValue ( FStateTreeDataView ( 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 ( ) , EStateTreePropertyAccessType : : IndexArray ) ;
AITEST_EQUAL ( " Indirection 1 should be Offset type " , Indirections [ 1 ] . GetAccessType ( ) , EStateTreePropertyAccessType : : 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 ( FStateTreeTest_PropertyPathArray , " System.StateTree.PropertyPath.Array " ) ;
struct FStateTreeTest_PropertyPathArrayInvalidIndex : FAITestBase
{
virtual bool InstantTest ( ) override
{
FStateTreePropertyPath 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 ) ;
UStateTreeTest_PropertyObject * Object = NewObject < UStateTreeTest_PropertyObject > ( ) ;
Object - > ArrayOfInts . Add ( 42 ) ;
Object - > ArrayOfInts . Add ( 123 ) ;
FString ResolveErrors ;
TArray < FStateTreePropertyPathIndirection > Indirections ;
const bool bResolveResult = Path . ResolveIndirectionsWithValue ( FStateTreeDataView ( Object ) , Indirections , & ResolveErrors ) ;
AITEST_FALSE ( " Resolve path should fail " , bResolveResult ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_PropertyPathArrayInvalidIndex , " System.StateTree.PropertyPath.ArrayInvalidIndex " ) ;
struct FStateTreeTest_PropertyPathArrayOfStructs : FAITestBase
{
virtual bool InstantTest ( ) override
{
FStateTreePropertyPath Path1 ;
Path1 . FromString ( TEXT ( " ArrayOfStruct[0].B " ) ) ;
FStateTreePropertyPath Path2 ;
Path2 . FromString ( TEXT ( " ArrayOfStruct[2].StructB.B " ) ) ;
UStateTreeTest_PropertyObject * Object = NewObject < UStateTreeTest_PropertyObject > ( ) ;
Object - > ArrayOfStruct . AddDefaulted_GetRef ( ) . B = 3 ;
Object - > ArrayOfStruct . AddDefaulted ( ) ;
Object - > ArrayOfStruct . AddDefaulted_GetRef ( ) . StructB . B = 42 ;
{
FString ResolveErrors ;
TArray < FStateTreePropertyPathIndirection > Indirections ;
const bool bResolveResult = Path1 . ResolveIndirectionsWithValue ( FStateTreeDataView ( 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 ( ) , EStateTreePropertyAccessType : : IndexArray ) ;
AITEST_EQUAL ( " Indirection 1 should be Offset type " , Indirections [ 1 ] . GetAccessType ( ) , EStateTreePropertyAccessType : : Offset ) ;
AITEST_EQUAL ( " Indirection 2 should be Offset type " , Indirections [ 2 ] . GetAccessType ( ) , EStateTreePropertyAccessType : : Offset ) ;
const int32 Value = * reinterpret_cast < const int32 * > ( Indirections [ 2 ] . GetPropertyAddress ( ) ) ;
AITEST_EQUAL ( " Value should be 3 " , Value , 3 ) ;
}
{
FString ResolveErrors ;
TArray < FStateTreePropertyPathIndirection > Indirections ;
const bool bResolveResult = Path2 . ResolveIndirectionsWithValue ( FStateTreeDataView ( 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 ( ) , EStateTreePropertyAccessType : : IndexArray ) ;
AITEST_EQUAL ( " Indirection 1 should be Offset type " , Indirections [ 1 ] . GetAccessType ( ) , EStateTreePropertyAccessType : : Offset ) ;
AITEST_EQUAL ( " Indirection 2 should be Offset type " , Indirections [ 2 ] . GetAccessType ( ) , EStateTreePropertyAccessType : : Offset ) ;
AITEST_EQUAL ( " Indirection 3 should be Offset type " , Indirections [ 3 ] . GetAccessType ( ) , EStateTreePropertyAccessType : : 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 ( FStateTreeTest_PropertyPathArrayOfStructs , " System.StateTree.PropertyPath.ArrayOfStructs " ) ;
struct FStateTreeTest_PropertyPathArrayOfInstancedObjects : FAITestBase
{
virtual bool InstantTest ( ) override
{
FStateTreePropertyPath Path ;
Path . FromString ( TEXT ( " ArrayOfInstancedStructs[0].B " ) ) ;
FStateTreeTest_PropertyStruct Struct ;
Struct . B = 123 ;
UStateTreeTest_PropertyObject * Object = NewObject < UStateTreeTest_PropertyObject > ( ) ;
Object - > ArrayOfInstancedStructs . Emplace ( FConstStructView : : Make ( Struct ) ) ;
2023-06-21 10:27:51 -04:00
const bool bUpdateResult = Path . UpdateSegmentsFromValue ( FStateTreeDataView ( Object ) ) ;
2023-02-10 07:22:48 -05:00
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 FStateTreeTest_PropertyStruct " , Path . GetSegment ( 0 ) . GetInstanceStruct ( ) = = FStateTreeTest_PropertyStruct : : StaticStruct ( ) ) ;
AITEST_TRUE ( " Path segment 1 instance type should be nullptr " , Path . GetSegment ( 1 ) . GetInstanceStruct ( ) = = nullptr ) ;
{
FString ResolveErrors ;
TArray < FStateTreePropertyPathIndirection > Indirections ;
const bool bResolveResult = Path . ResolveIndirections ( UStateTreeTest_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 ( ) , EStateTreePropertyAccessType : : IndexArray ) ;
AITEST_EQUAL ( " Indirection 1 should be StructInstance type " , Indirections [ 1 ] . GetAccessType ( ) , EStateTreePropertyAccessType : : StructInstance ) ;
AITEST_EQUAL ( " Indirection 2 should be Offset type " , Indirections [ 2 ] . GetAccessType ( ) , EStateTreePropertyAccessType : : Offset ) ;
}
{
FString ResolveErrors ;
TArray < FStateTreePropertyPathIndirection > Indirections ;
const bool bResolveResult = Path . ResolveIndirectionsWithValue ( FStateTreeDataView ( 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 ( ) , EStateTreePropertyAccessType : : IndexArray ) ;
AITEST_EQUAL ( " Indirection 1 should be StructInstance type " , Indirections [ 1 ] . GetAccessType ( ) , EStateTreePropertyAccessType : : StructInstance ) ;
AITEST_EQUAL ( " Indirection 2 should be Offset type " , Indirections [ 2 ] . GetAccessType ( ) , EStateTreePropertyAccessType : : 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 ( FStateTreeTest_PropertyPathArrayOfInstancedObjects , " System.StateTree.PropertyPath.ArrayOfInstancedObjects " ) ;
2023-02-17 04:00:32 -05:00
struct FStateTreeTest_BindingsCompiler : FAITestBase
{
virtual bool InstantTest ( ) override
{
FStateTreeCompilerLog Log ;
FStateTreePropertyBindings Bindings ;
FStateTreePropertyBindingCompiler BindingCompiler ;
const bool bInitResult = BindingCompiler . Init ( Bindings , Log ) ;
AITEST_TRUE ( " Expect init to succeed " , bInitResult ) ;
FStateTreeBindableStructDesc SourceADesc ;
SourceADesc . Name = FName ( TEXT ( " SourceA " ) ) ;
SourceADesc . Struct = TBaseStructure < FStateTreeTest_PropertyCopy > : : Get ( ) ;
SourceADesc . DataSource = EStateTreeBindableStructSource : : Parameter ;
SourceADesc . ID = FGuid : : NewGuid ( ) ;
FStateTreeBindableStructDesc SourceBDesc ;
SourceBDesc . Name = FName ( TEXT ( " SourceB " ) ) ;
SourceBDesc . Struct = TBaseStructure < FStateTreeTest_PropertyCopy > : : Get ( ) ;
SourceBDesc . DataSource = EStateTreeBindableStructSource : : Parameter ;
SourceBDesc . ID = FGuid : : NewGuid ( ) ;
FStateTreeBindableStructDesc TargetDesc ;
TargetDesc . Name = FName ( TEXT ( " Target " ) ) ;
TargetDesc . Struct = TBaseStructure < FStateTreeTest_PropertyCopy > : : Get ( ) ;
TargetDesc . DataSource = EStateTreeBindableStructSource : : Parameter ;
TargetDesc . ID = FGuid : : NewGuid ( ) ;
const int32 SourceAIndex = BindingCompiler . AddSourceStruct ( SourceADesc ) ;
const int32 SourceBIndex = BindingCompiler . AddSourceStruct ( SourceBDesc ) ;
auto MakeBinding = [ ] ( const FGuid & SourceID , const FString & Source , const FGuid & TargetID , const FString & Target )
{
FStateTreePropertyPath SourcePath ;
SourcePath . FromString ( Source ) ;
SourcePath . SetStructID ( SourceID ) ;
FStateTreePropertyPath TargetPath ;
TargetPath . FromString ( Target ) ;
TargetPath . SetStructID ( TargetID ) ;
return FStateTreePropertyPathBinding ( SourcePath , TargetPath ) ;
} ;
TArray < FStateTreePropertyPathBinding > PropertyBindings ;
PropertyBindings . Add ( MakeBinding ( SourceBDesc . ID , TEXT ( " Item " ) , TargetDesc . ID , TEXT ( " Array[1] " ) ) ) ;
PropertyBindings . Add ( MakeBinding ( SourceADesc . ID , TEXT ( " Item.B " ) , TargetDesc . ID , TEXT ( " Array[1].B " ) ) ) ;
PropertyBindings . Add ( 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 ) ;
FStateTreeTest_PropertyCopy SourceA ;
SourceA . Item . B = 123 ;
SourceA . Array . AddDefaulted_GetRef ( ) . A = 1 ;
SourceA . Array . AddDefaulted_GetRef ( ) . B = 2 ;
FStateTreeTest_PropertyCopy SourceB ;
SourceB . Item . A = 41 ;
SourceB . Item . B = 42 ;
FStateTreeTest_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 < FStateTreeDataView > SourceViews ;
SourceViews . SetNum ( Bindings . GetSourceStructNum ( ) ) ;
SourceViews [ SourceAIndex ] = FStateTreeDataView ( TBaseStructure < FStateTreeTest_PropertyCopy > : : Get ( ) , ( uint8 * ) & SourceA ) ;
SourceViews [ SourceBIndex ] = FStateTreeDataView ( TBaseStructure < FStateTreeTest_PropertyCopy > : : Get ( ) , ( uint8 * ) & SourceB ) ;
FStateTreeDataView TargetView ( TBaseStructure < FStateTreeTest_PropertyCopy > : : Get ( ) , ( uint8 * ) & Target ) ;
const bool bCopyResult = Bindings . CopyTo ( SourceViews , FStateTreeIndex16 ( CopyBatchIndex ) , TargetView ) ;
AITEST_TRUE ( " CopyTo should succeed " , bCopyResult ) ;
// Due to binding sorting, we expect them to executed in this order (sorted based on target access, earliest to latest)
// SourceA.Array -> Target.Array
// SourceB.Item -> Target.Array[1]
// SourceA.Item.B -> Target.Array[1].B
AITEST_EQUAL ( " Expect TargetArray to be copied from SourceA " , Target . Array . Num ( ) , SourceA . Array . Num ( ) ) ;
AITEST_EQUAL ( " Expect Target.Array[0].A copied from SourceA.Array[0].A " , Target . Array [ 0 ] . A , SourceA . Array [ 0 ] . A ) ;
AITEST_EQUAL ( " Expect Target.Array[0].B copied from SourceA.Array[0].B " , Target . Array [ 0 ] . B , SourceA . Array [ 0 ] . B ) ;
AITEST_EQUAL ( " Expect Target.Array[1].A copied from SourceB.Item.A " , Target . Array [ 1 ] . A , SourceB . Item . A ) ;
AITEST_EQUAL ( " Expect Target.Array[1].B copied from SourceA.Item.B " , Target . Array [ 1 ] . B , SourceA . Item . B ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_BindingsCompiler , " System.StateTree.BindingsCompiler " ) ;
2023-04-12 07:59:16 -04:00
struct FStateTreeTest_FollowTransitions : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
EditorData . RootParameters . Parameters . AddProperty ( FName ( TEXT ( " Int " ) ) , EPropertyBagPropertyType : : Int32 ) ;
EditorData . RootParameters . Parameters . SetValueInt32 ( FName ( TEXT ( " Int " ) ) , 1 ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & StateTrans = Root . AddChildState ( FName ( TEXT ( " Trans " ) ) ) ;
UStateTreeState & StateA = Root . AddChildState ( FName ( TEXT ( " A " ) ) ) ;
UStateTreeState & StateB = Root . AddChildState ( FName ( TEXT ( " B " ) ) ) ;
UStateTreeState & StateC = Root . AddChildState ( FName ( TEXT ( " C " ) ) ) ;
// Root
// Trans
{
StateTrans . SelectionBehavior = EStateTreeStateSelectionBehavior : : TryFollowTransitions ;
{
// This transition should be skipped due to the condition
FStateTreeTransition & TransA = StateTrans . AddTransition ( EStateTreeTransitionTrigger : : OnTick , EStateTreeTransitionType : : GotoState , & StateA ) ;
TStateTreeEditorNode < FStateTreeCompareIntCondition > & TransIntCond = TransA . AddCondition < FStateTreeCompareIntCondition > ( EGenericAICheck : : Equal ) ;
TransIntCond . GetInstanceData ( ) . Right = 0 ;
EditorData . AddPropertyBinding (
FStateTreePropertyPath ( EditorData . RootParameters . ID , TEXT ( " Int " ) ) ,
FStateTreePropertyPath ( TransIntCond . ID , TEXT ( " Left " ) ) ) ;
}
{
// This transition leads to selection, but will be overridden.
FStateTreeTransition & TransB = StateTrans . AddTransition ( EStateTreeTransitionTrigger : : OnTick , EStateTreeTransitionType : : GotoState , & StateB ) ;
TransB . Priority = EStateTreeTransitionPriority : : Normal ;
TStateTreeEditorNode < FStateTreeCompareIntCondition > & TransIntCond = TransB . AddCondition < FStateTreeCompareIntCondition > ( EGenericAICheck : : Equal ) ;
TransIntCond . GetInstanceData ( ) . Right = 1 ;
EditorData . AddPropertyBinding (
FStateTreePropertyPath ( EditorData . RootParameters . ID , TEXT ( " Int " ) ) ,
FStateTreePropertyPath ( TransIntCond . ID , TEXT ( " Left " ) ) ) ;
}
{
// This transition is selected, should override previous one due to priority.
FStateTreeTransition & TransC = StateTrans . AddTransition ( EStateTreeTransitionTrigger : : OnTick , EStateTreeTransitionType : : GotoState , & StateC ) ;
TransC . Priority = EStateTreeTransitionPriority : : High ;
TStateTreeEditorNode < FStateTreeCompareIntCondition > & TransIntCond = TransC . AddCondition < FStateTreeCompareIntCondition > ( EGenericAICheck : : Equal ) ;
TransIntCond . GetInstanceData ( ) . Right = 1 ;
EditorData . AddPropertyBinding (
FStateTreePropertyPath ( EditorData . RootParameters . ID , TEXT ( " Int " ) ) ,
FStateTreePropertyPath ( TransIntCond . ID , TEXT ( " Left " ) ) ) ;
}
}
auto & TaskA = StateA . AddTask < FTestTask_Stand > ( FName ( TEXT ( " TaskA " ) ) ) ;
auto & TaskB = StateB . AddTask < FTestTask_Stand > ( FName ( TEXT ( " TaskB " ) ) ) ;
auto & TaskC = StateC . AddTask < FTestTask_Stand > ( FName ( TEXT ( " TaskC " ) ) ) ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
Status = Exec . Start ( ) ;
AITEST_FALSE ( " StateTree TaskA should not enter state " , Exec . Expect ( TaskA . GetName ( ) , EnterStateStr ) ) ;
AITEST_FALSE ( " StateTree TaskB should not enter state " , Exec . Expect ( TaskB . GetName ( ) , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree TaskC should enter state " , Exec . Expect ( TaskC . GetName ( ) , EnterStateStr ) ) ;
Exec . LogClear ( ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_FollowTransitions , " System.StateTree.FollowTransitions " ) ;
struct FStateTreeTest_InfiniteLoop : FAITestBase
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
EditorData . RootParameters . Parameters . AddProperty ( FName ( TEXT ( " Int " ) ) , EPropertyBagPropertyType : : Int32 ) ;
EditorData . RootParameters . Parameters . SetValueInt32 ( FName ( TEXT ( " Int " ) ) , 1 ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & StateA = Root . AddChildState ( FName ( TEXT ( " A " ) ) ) ;
UStateTreeState & StateB = StateA . AddChildState ( FName ( TEXT ( " B " ) ) ) ;
// Root
// State A
{
StateA . SelectionBehavior = EStateTreeStateSelectionBehavior : : TryFollowTransitions ;
{
// A -> B
FStateTreeTransition & Trans = StateA . AddTransition ( EStateTreeTransitionTrigger : : OnTick , EStateTreeTransitionType : : GotoState , & StateB ) ;
TStateTreeEditorNode < FStateTreeCompareIntCondition > & TransIntCond = Trans . AddCondition < FStateTreeCompareIntCondition > ( EGenericAICheck : : Equal ) ;
TransIntCond . GetInstanceData ( ) . Right = 1 ;
EditorData . AddPropertyBinding (
FStateTreePropertyPath ( EditorData . RootParameters . ID , TEXT ( " Int " ) ) ,
FStateTreePropertyPath ( TransIntCond . ID , TEXT ( " Left " ) ) ) ;
}
}
// State B
{
StateB . SelectionBehavior = EStateTreeStateSelectionBehavior : : TryFollowTransitions ;
{
// B -> A
FStateTreeTransition & Trans = StateB . AddTransition ( EStateTreeTransitionTrigger : : OnTick , EStateTreeTransitionType : : GotoState , & StateA ) ;
TStateTreeEditorNode < FStateTreeCompareIntCondition > & TransIntCond = Trans . AddCondition < FStateTreeCompareIntCondition > ( EGenericAICheck : : Equal ) ;
TransIntCond . GetInstanceData ( ) . Right = 1 ;
EditorData . AddPropertyBinding (
FStateTreePropertyPath ( EditorData . RootParameters . ID , TEXT ( " Int " ) ) ,
FStateTreePropertyPath ( TransIntCond . ID , TEXT ( " Left " ) ) ) ;
}
}
auto & TaskA = StateA . AddTask < FTestTask_Stand > ( FName ( TEXT ( " TaskA " ) ) ) ;
auto & TaskB = StateB . AddTask < FTestTask_Stand > ( FName ( TEXT ( " TaskB " ) ) ) ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree 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 ) ;
Status = Exec . Start ( ) ;
AITEST_EQUAL ( " Start should fail " , Status , EStateTreeRunStatus : : Failed ) ;
Exec . LogClear ( ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_InfiniteLoop , " System.StateTree.InfiniteLoop " ) ;
2023-02-17 04:00:32 -05:00
2023-06-05 06:33:07 -04:00
//
// The stop tests test how the combinations of execution path to stop the tree are reported on ExitState() transition.
//
struct FStateTreeTest_Stop : FAITestBase
{
UStateTree & SetupTree ( )
{
UStateTree & StateTree = UE : : StateTree : : Tests : : NewStateTree ( & GetWorld ( ) ) ;
UStateTreeEditorData & EditorData = * Cast < UStateTreeEditorData > ( StateTree . EditorData ) ;
UStateTreeState & Root = EditorData . AddSubTree ( FName ( TEXT ( " Root " ) ) ) ;
UStateTreeState & StateA = Root . AddChildState ( FName ( TEXT ( " A " ) ) ) ;
TStateTreeEditorNode < FTestTask_Stand > & TaskA = StateA . AddTask < FTestTask_Stand > ( TaskAName ) ;
TStateTreeEditorNode < FTestTask_Stand > & GlobalTask = EditorData . AddGlobalTask < FTestTask_Stand > ( GlobalTaskName ) ;
// Transition success
StateA . AddTransition ( EStateTreeTransitionTrigger : : OnStateSucceeded , EStateTreeTransitionType : : Succeeded ) ;
StateA . AddTransition ( EStateTreeTransitionTrigger : : OnStateFailed , EStateTreeTransitionType : : 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 StateTree ;
}
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = SetupTree ( ) ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
Status = Exec . Start ( ) ;
AITEST_EQUAL ( " Start should be running " , Status , EStateTreeRunStatus : : Running ) ;
AITEST_TRUE ( " StateTree GlobalTask should enter state " , Exec . Expect ( GlobalTaskName , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree 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 ( " StateTree GlobalTask should get exit state expectedly " , Exec . Expect ( GlobalTaskName , ExpectedExitStatusStr ) ) ;
AITEST_TRUE ( " StateTree 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 " ) ) ;
EStateTreeRunStatus NormalTaskStatus = EStateTreeRunStatus : : Succeeded ;
EStateTreeRunStatus NormalTaskEnterStatus = EStateTreeRunStatus : : Running ;
int32 NormalTaskTicks = 1 ;
EStateTreeRunStatus GlobalTaskStatus = EStateTreeRunStatus : : Succeeded ;
EStateTreeRunStatus GlobalTaskEnterStatus = EStateTreeRunStatus : : Running ;
int32 GlobalTaskTicks = 1 ;
EStateTreeRunStatus ExpectedStatusAfterTick = EStateTreeRunStatus : : Succeeded ;
FString ExpectedExitStatusStr = TEXT ( " ExitSucceeded " ) ;
} ;
struct FStateTreeTest_Stop_NormalSucceeded : FStateTreeTest_Stop
{
virtual bool SetUp ( ) override
{
// Normal task completes as succeeded.
NormalTaskStatus = EStateTreeRunStatus : : Succeeded ;
NormalTaskTicks = 1 ;
// Global task completes later
GlobalTaskTicks = 2 ;
// Tree should complete as succeeded.
ExpectedStatusAfterTick = EStateTreeRunStatus : : Succeeded ;
// Tasks should have Transition.CurrentRunStatus as succeeded
ExpectedExitStatusStr = TEXT ( " ExitSucceeded " ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Stop_NormalSucceeded , " System.StateTree.Stop.NormalSucceeded " ) ;
struct FStateTreeTest_Stop_NormalFailed : FStateTreeTest_Stop
{
virtual bool SetUp ( ) override
{
// Normal task completes as failed.
NormalTaskStatus = EStateTreeRunStatus : : Failed ;
NormalTaskTicks = 1 ;
// Global task completes later.
GlobalTaskTicks = 2 ;
// Tree should complete as failed.
ExpectedStatusAfterTick = EStateTreeRunStatus : : Failed ;
// Tasks should have Transition.CurrentRunStatus as failed.
ExpectedExitStatusStr = TEXT ( " ExitFailed " ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Stop_NormalFailed , " System.StateTree.Stop.NormalFailed " ) ;
struct FStateTreeTest_Stop_GlobalSucceeded : FStateTreeTest_Stop
{
virtual bool SetUp ( ) override
{
// Normal task completes later.
NormalTaskTicks = 2 ;
// Global task completes as succeeded.
GlobalTaskStatus = EStateTreeRunStatus : : Succeeded ;
GlobalTaskTicks = 1 ;
// Tree should complete as succeeded.
ExpectedStatusAfterTick = EStateTreeRunStatus : : Succeeded ;
// Tasks should have Transition.CurrentRunStatus as succeeded.
ExpectedExitStatusStr = TEXT ( " ExitSucceeded " ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Stop_GlobalSucceeded , " System.StateTree.Stop.GlobalSucceeded " ) ;
struct FStateTreeTest_Stop_GlobalFailed : FStateTreeTest_Stop
{
virtual bool SetUp ( ) override
{
// Normal task completes later
NormalTaskTicks = 2 ;
// Global task completes as failed.
GlobalTaskStatus = EStateTreeRunStatus : : Failed ;
GlobalTaskTicks = 1 ;
// Tree should complete as failed.
ExpectedStatusAfterTick = EStateTreeRunStatus : : Failed ;
// Tasks should have Transition.CurrentRunStatus as failed.
ExpectedExitStatusStr = TEXT ( " ExitFailed " ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Stop_GlobalFailed , " System.StateTree.Stop.GlobalFailed " ) ;
//
// Tests combinations of completing the tree on EnterState.
//
struct FStateTreeTest_StopEnterNormal : FStateTreeTest_Stop
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = SetupTree ( ) ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
// If a normal task fails at start, the last tick status will be failed, but transition handling (and final execution status) will take place next tick.
Status = Exec . Start ( ) ;
AITEST_EQUAL ( " Tree should be running after start " , Status , EStateTreeRunStatus : : Running ) ;
AITEST_EQUAL ( " Last execution status should be expected value " , Exec . GetLastTickStatus ( ) , ExpectedStatusAfterStart ) ;
// Handles any transitions from failed transition
Status = Exec . Tick ( 0.1f ) ;
AITEST_EQUAL ( " Start should be expected value " , Status , ExpectedStatusAfterStart ) ;
AITEST_TRUE ( " StateTree GlobalTask should get exit state expectedly " , Exec . Expect ( GlobalTaskName , ExpectedExitStatusStr ) ) ;
AITEST_TRUE ( " StateTree TaskA should enter state " , Exec . Expect ( TaskAName , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree TaskA should report exit status " , Exec . Expect ( TaskAName , ExpectedExitStatusStr ) ) ;
Exec . LogClear ( ) ;
return true ;
}
EStateTreeRunStatus ExpectedStatusAfterStart = EStateTreeRunStatus : : Succeeded ;
FString ExpectedExitStatusStr = TEXT ( " ExitSucceeded " ) ;
bool bExpectNormalTaskToRun = true ;
} ;
struct FStateTreeTest_Stop_NormalEnterSucceeded : FStateTreeTest_StopEnterNormal
{
virtual bool SetUp ( ) override
{
// Tasks should complete later.
NormalTaskTicks = 2 ;
GlobalTaskTicks = 2 ;
// Normal task EnterState as succeeded, completion is handled using completion transitions.
NormalTaskEnterStatus = EStateTreeRunStatus : : Succeeded ;
// Tree should complete as succeeded.
ExpectedStatusAfterStart = EStateTreeRunStatus : : Succeeded ;
// Tasks should have Transition.CurrentRunStatus as succeeded.
ExpectedExitStatusStr = TEXT ( " ExitSucceeded " ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Stop_NormalEnterSucceeded , " System.StateTree.Stop.NormalEnterSucceeded " ) ;
struct FStateTreeTest_Stop_NormalEnterFailed : FStateTreeTest_StopEnterNormal
{
virtual bool SetUp ( ) override
{
// Tasks should complete later.
NormalTaskTicks = 2 ;
GlobalTaskTicks = 2 ;
// Normal task EnterState as failed, completion is handled using completion transitions.
NormalTaskEnterStatus = EStateTreeRunStatus : : Failed ;
// Tree should complete as failed.
ExpectedStatusAfterStart = EStateTreeRunStatus : : Failed ;
// Tasks should have Transition.CurrentRunStatus as failed.
ExpectedExitStatusStr = TEXT ( " ExitFailed " ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Stop_NormalEnterFailed , " System.StateTree.Stop.NormalEnterFailed " ) ;
struct FStateTreeTest_StopEnterGlobal : FStateTreeTest_Stop
{
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = SetupTree ( ) ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
Status = Exec . Start ( ) ;
AITEST_EQUAL ( " Start should be expected value " , Status , ExpectedStatusAfterStart ) ;
AITEST_TRUE ( " StateTree GlobalTask should get exit state expectedly " , Exec . Expect ( GlobalTaskName , ExpectedExitStatusStr ) ) ;
// Normal tasks should not run
AITEST_FALSE ( " StateTree TaskA should not enter state " , Exec . Expect ( TaskAName , EnterStateStr ) ) ;
AITEST_FALSE ( " StateTree TaskA should not report exit status " , Exec . Expect ( TaskAName , ExpectedExitStatusStr ) ) ;
Exec . LogClear ( ) ;
return true ;
}
EStateTreeRunStatus ExpectedStatusAfterStart = EStateTreeRunStatus : : Succeeded ;
FString ExpectedExitStatusStr = TEXT ( " ExitSucceeded " ) ;
} ;
struct FStateTreeTest_Stop_GlobalEnterSucceeded : FStateTreeTest_StopEnterGlobal
{
virtual bool SetUp ( ) override
{
// Tasks should complete later.
NormalTaskTicks = 2 ;
GlobalTaskTicks = 2 ;
// Global task EnterState as succeeded, completion is handled directly based on the global task status.
GlobalTaskEnterStatus = EStateTreeRunStatus : : Succeeded ;
// Tree should complete as succeeded.
ExpectedStatusAfterStart = EStateTreeRunStatus : : Succeeded ;
// Tasks should have Transition.CurrentRunStatus as Succeeded.
ExpectedExitStatusStr = TEXT ( " ExitSucceeded " ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Stop_GlobalEnterSucceeded , " System.StateTree.Stop.GlobalEnterSucceeded " ) ;
struct FStateTreeTest_Stop_GlobalEnterFailed : FStateTreeTest_StopEnterGlobal
{
virtual bool SetUp ( ) override
{
// Tasks should complete later.
NormalTaskTicks = 2 ;
GlobalTaskTicks = 2 ;
// Global task EnterState as failed, completion is handled directly based on the global task status.
GlobalTaskEnterStatus = EStateTreeRunStatus : : Failed ;
// Tree should complete as failed.
ExpectedStatusAfterStart = EStateTreeRunStatus : : Failed ;
// Tasks should have Transition.CurrentRunStatus as failed.
ExpectedExitStatusStr = TEXT ( " ExitFailed " ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Stop_GlobalEnterFailed , " System.StateTree.Stop.GlobalEnterFailed " ) ;
struct FStateTreeTest_Stop_ExternalStop : FStateTreeTest_Stop
{
virtual bool SetUp ( ) override
{
// Tasks should complete later.
NormalTaskTicks = 2 ;
GlobalTaskTicks = 2 ;
// Tree should tick and keep on running.
ExpectedStatusAfterTick = EStateTreeRunStatus : : Running ;
// Tree should stop as stopped.
ExpectedStatusAfterStop = EStateTreeRunStatus : : Stopped ;
// Tasks should have Transition.CurrentRunStatus as stopped.
ExpectedExitStatusStr = TEXT ( " ExitStopped " ) ;
return true ;
}
virtual bool InstantTest ( ) override
{
UStateTree & StateTree = SetupTree ( ) ;
FStateTreeCompilerLog Log ;
FStateTreeCompiler Compiler ( Log ) ;
const bool bResult = Compiler . Compile ( StateTree ) ;
AITEST_TRUE ( " StateTree should get compiled " , bResult ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
FStateTreeInstanceData InstanceData ;
FTestStateTreeExecutionContext Exec ( StateTree , StateTree , InstanceData ) ;
const bool bInitSucceeded = Exec . IsValid ( ) ;
AITEST_TRUE ( " StateTree should init " , bInitSucceeded ) ;
const FString TickStr ( TEXT ( " Tick " ) ) ;
const FString EnterStateStr ( TEXT ( " EnterState " ) ) ;
const FString ExitStateStr ( TEXT ( " ExitState " ) ) ;
Status = Exec . Start ( ) ;
AITEST_EQUAL ( " Start should be running " , Status , EStateTreeRunStatus : : Running ) ;
AITEST_TRUE ( " StateTree GlobalTask should enter state " , Exec . Expect ( GlobalTaskName , EnterStateStr ) ) ;
AITEST_TRUE ( " StateTree 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 ( EStateTreeRunStatus : : Stopped ) ;
AITEST_EQUAL ( " Start should be running " , Status , ExpectedStatusAfterStop ) ;
if ( ! ExpectedExitStatusStr . IsEmpty ( ) )
{
AITEST_TRUE ( " StateTree GlobalTask should get exit state expectedly " , Exec . Expect ( GlobalTaskName , ExpectedExitStatusStr ) ) ;
AITEST_TRUE ( " StateTree TaskA should get exit state expectedly " , Exec . Expect ( TaskAName , ExpectedExitStatusStr ) ) ;
}
return true ;
}
EStateTreeRunStatus ExpectedStatusAfterStop = EStateTreeRunStatus : : Stopped ;
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Stop_ExternalStop , " System.StateTree.Stop.ExternalStop " ) ;
struct FStateTreeTest_Stop_AlreadyStopped : FStateTreeTest_Stop_ExternalStop
{
virtual bool SetUp ( ) override
{
// Normal task completes before stop.
NormalTaskTicks = 1 ;
NormalTaskStatus = EStateTreeRunStatus : : Succeeded ;
// Global task completes later
GlobalTaskTicks = 2 ;
// Tree should tick stop as succeeded.
ExpectedStatusAfterTick = EStateTreeRunStatus : : Succeeded ;
// Tree is already stopped, should keep the status (not Stopped).
ExpectedStatusAfterStop = EStateTreeRunStatus : : Succeeded ;
// Skip exit status check.
ExpectedExitStatusStr = TEXT ( " " ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FStateTreeTest_Stop_AlreadyStopped , " System.StateTree.Stop.AlreadyStopped " ) ;
2022-11-22 08:13:54 -05:00
UE_ENABLE_OPTIMIZATION_SHIP
2021-09-28 13:33:17 -04:00
# undef LOCTEXT_NAMESPACE
2022-09-28 01:06:15 -04:00