2021-09-28 13:33:17 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "CoreMinimal.h"
# include "AITestsCommon.h"
# include "Engine/World.h"
# include "EngineDefines.h"
2022-01-19 14:16:16 -05:00
# include "MassExecutor.h"
# include "MassEntitySubsystem.h"
2021-09-28 13:33:17 -04:00
# include "SmartObjectSubsystem.h"
# include "SmartObjectComponent.h"
# include "SmartObjectTestTypes.h"
# define LOCTEXT_NAMESPACE "AITestSuite_SmartObjectsTest"
PRAGMA_DISABLE_OPTIMIZATION
namespace FSmartObjectTest
{
struct FSmartObjectTestBase : FAITestBase
{
FSmartObjectRequestFilter TestFilter ;
USmartObjectSubsystem * Subsystem = nullptr ;
TArray < USmartObjectComponent * > SOList ;
virtual bool SetUp ( ) override
{
UWorld * World = FAITestHelpers : : GetWorld ( ) ;
Subsystem = USmartObjectSubsystem : : GetCurrent ( World ) ;
if ( Subsystem = = nullptr )
{
return false ;
}
2022-01-19 14:16:16 -05:00
// Setup main definition
2021-11-26 09:52:13 -05:00
USmartObjectDefinition * Definition = NewAutoDestroyObject < USmartObjectDefinition > ( ) ;
2022-01-19 14:16:16 -05:00
FSmartObjectSlotDefinition & Slot = Definition - > DebugAddSlot ( ) ;
// Add some test behavior definition
Slot . BehaviorDefinitions . Add ( NewAutoDestroyObject < USmartObjectTestBehaviorDefinition > ( ) ) ;
// Add some test slot definition data
FSmartObjectSlotTestDefinitionData DefinitionData ;
DefinitionData . SomeSharedFloat = 123.456f ;
Slot . Data . Add ( FInstancedStruct : : Make ( DefinitionData ) ) ;
2021-09-28 13:33:17 -04:00
// Setup filter
2022-01-19 14:16:16 -05:00
TestFilter . BehaviorDefinitionClass = USmartObjectTestBehaviorDefinition : : StaticClass ( ) ;
2021-09-28 13:33:17 -04:00
// Create some smart objects
SOList =
{
NewAutoDestroyObject < USmartObjectComponent > ( World ) ,
NewAutoDestroyObject < USmartObjectComponent > ( World )
} ;
// Register all to the subsystem
for ( USmartObjectComponent * SO : SOList )
{
if ( SO ! = nullptr )
{
2021-11-26 09:52:13 -05:00
SO - > SetDefinition ( Definition ) ;
2021-09-28 13:33:17 -04:00
Subsystem - > RegisterSmartObject ( * SO ) ;
}
}
# if WITH_SMARTOBJECT_DEBUG
// Force registration to the runtime simulation
Subsystem - > DebugRegisterAllSmartObjects ( ) ;
# endif
2022-01-19 14:16:16 -05:00
if ( UMassEntitySubsystem * System = UWorld : : GetSubsystem < UMassEntitySubsystem > ( World ) )
{
FMassProcessingContext ProcessingContext ( * System , /* DeltaSeconds */ 0.f ) ;
UE : : Mass : : Executor : : RunProcessorsView ( TArrayView < UMassProcessor * > ( ) , ProcessingContext ) ;
}
2021-09-28 13:33:17 -04:00
return true ;
}
virtual void TearDown ( ) override
{
if ( Subsystem = = nullptr )
{
return ;
}
# if WITH_SMARTOBJECT_DEBUG
// Force removal from the runtime simulation
Subsystem - > DebugUnregisterAllSmartObjects ( ) ;
# endif
// Unregister all from the current test
for ( USmartObjectComponent * SO : SOList )
{
if ( SO ! = nullptr )
{
Subsystem - > UnregisterSmartObject ( * SO ) ;
}
}
FAITestBase : : TearDown ( ) ;
}
} ;
struct FFindSmartObject : FSmartObjectTestBase
{
virtual bool InstantTest ( ) override
{
const FSmartObjectRequest Request ( FBox ( EForceInit : : ForceInit ) . ExpandBy ( FVector ( HALF_WORLD_MAX ) , FVector ( HALF_WORLD_MAX ) ) , TestFilter ) ;
// Find object
const FSmartObjectRequestResult FindResult = Subsystem - > FindSmartObject ( Request ) ;
AITEST_TRUE ( " Result is expected to be valid " , FindResult . IsValid ( ) ) ;
2022-01-19 14:16:16 -05:00
AITEST_TRUE ( " ID is expected to be valid " , FindResult . SmartObjectHandle . IsValid ( ) ) ;
2021-09-28 13:33:17 -04:00
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FFindSmartObject , " System.AI.SmartObjects.Find " ) ;
struct FClaimAndReleaseSmartObject : FSmartObjectTestBase
{
virtual bool InstantTest ( ) override
{
const FSmartObjectRequest Request ( FBox ( EForceInit : : ForceInit ) . ExpandBy ( FVector ( HALF_WORLD_MAX ) , FVector ( HALF_WORLD_MAX ) ) , TestFilter ) ;
// Find object
const FSmartObjectRequestResult FirstFindResult = Subsystem - > FindSmartObject ( Request ) ;
AITEST_TRUE ( " Result is expected to be valid " , FirstFindResult . IsValid ( ) ) ;
2022-01-19 14:16:16 -05:00
AITEST_TRUE ( " ID is expected to be valid " , FirstFindResult . SmartObjectHandle . IsValid ( ) ) ;
2021-09-28 13:33:17 -04:00
// Claim object
const FSmartObjectClaimHandle ClaimHandle = Subsystem - > Claim ( FirstFindResult ) ;
AITEST_TRUE ( " Claim Handle is expected to be valid " , ClaimHandle . IsValid ( ) ) ;
// Try to find valid use in claimed object
2022-01-19 14:16:16 -05:00
const FSmartObjectRequestResult FirstFindValidUseResult = Subsystem - > FindSlot ( FirstFindResult . SmartObjectHandle , TestFilter ) ;
2021-09-28 13:33:17 -04:00
AITEST_FALSE ( " Result is expected to be invalid since ID was claimed " , FirstFindValidUseResult . IsValid ( ) ) ;
// Release claimed object
const bool bSuccess = Subsystem - > Release ( ClaimHandle ) ;
AITEST_TRUE ( " Handle is expected to be unclaimed successfully " , bSuccess ) ;
// Try to find valid use in unclaimed object
2022-01-19 14:16:16 -05:00
const FSmartObjectRequestResult SecondFindValidUseResult = Subsystem - > FindSlot ( FirstFindResult . SmartObjectHandle , TestFilter ) ;
2021-09-28 13:33:17 -04:00
AITEST_TRUE ( " Result is expected to be valid since ID was released " , SecondFindValidUseResult . IsValid ( ) ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FClaimAndReleaseSmartObject , " System.AI.SmartObjects.Claim & Release " ) ;
struct FFindAfterClaimSmartObject : FSmartObjectTestBase
{
virtual bool InstantTest ( ) override
{
const FSmartObjectRequest Request ( FBox ( EForceInit : : ForceInit ) . ExpandBy ( FVector ( HALF_WORLD_MAX ) , FVector ( HALF_WORLD_MAX ) ) , TestFilter ) ;
// Find first object
const FSmartObjectRequestResult FirstFindResult = Subsystem - > FindSmartObject ( Request ) ;
AITEST_TRUE ( " Result is expected to be valid " , FirstFindResult . IsValid ( ) ) ;
2022-01-19 14:16:16 -05:00
AITEST_TRUE ( " ID is expected to be valid " , FirstFindResult . SmartObjectHandle . IsValid ( ) ) ;
2021-09-28 13:33:17 -04:00
// Claim first object
const FSmartObjectClaimHandle FirstClaimHandle = Subsystem - > Claim ( FirstFindResult ) ;
AITEST_TRUE ( " Claim Handle is expected to be valid for the first claim " , FirstClaimHandle . IsValid ( ) ) ;
// Find second object
const FSmartObjectRequestResult SecondFindResult = Subsystem - > FindSmartObject ( Request ) ;
AITEST_TRUE ( " Result is expected to be valid " , SecondFindResult . IsValid ( ) ) ;
2022-01-19 14:16:16 -05:00
AITEST_TRUE ( " ID is expected to be valid " , SecondFindResult . SmartObjectHandle . IsValid ( ) ) ;
AITEST_NOT_EQUAL ( " Result is expected to point to a different ID since first ID was claimed " , FirstFindResult . SmartObjectHandle , SecondFindResult . SmartObjectHandle ) ;
2021-09-28 13:33:17 -04:00
// Claim second object
const FSmartObjectClaimHandle SecondClaimHandle = Subsystem - > Claim ( SecondFindResult ) ;
AITEST_TRUE ( " Claim Handle is expected to be valid for the second claim " , SecondClaimHandle . IsValid ( ) ) ;
// Try to find an available object
const FSmartObjectRequestResult ThirdFindResult = Subsystem - > FindSmartObject ( Request ) ;
AITEST_FALSE ( " Result is expected to be invalid: all objects are claimed " , ThirdFindResult . IsValid ( ) ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FFindAfterClaimSmartObject , " System.AI.SmartObjects.Find after Claim " ) ;
struct FDoubleClaimSmartObject : FSmartObjectTestBase
{
virtual bool InstantTest ( ) override
{
const FSmartObjectRequest Request ( FBox ( EForceInit : : ForceInit ) . ExpandBy ( FVector ( HALF_WORLD_MAX ) , FVector ( HALF_WORLD_MAX ) ) , TestFilter ) ;
// Find object
const FSmartObjectRequestResult PreClaimResult = Subsystem - > FindSmartObject ( Request ) ;
AITEST_TRUE ( " Result is expected to be valid " , PreClaimResult . IsValid ( ) ) ;
2022-01-19 14:16:16 -05:00
AITEST_TRUE ( " ID is expected to be valid " , PreClaimResult . SmartObjectHandle . IsValid ( ) ) ;
2021-09-28 13:33:17 -04:00
// Claim first object
const FSmartObjectClaimHandle FirstHdl = Subsystem - > Claim ( PreClaimResult ) ;
AITEST_TRUE ( " Claim Handle is expected to be valid for the first claim " , FirstHdl . IsValid ( ) ) ;
// Claim first object again
const FSmartObjectClaimHandle SecondHdl = Subsystem - > Claim ( PreClaimResult ) ;
AITEST_FALSE ( " Claim Handle is expected to be invalid for the second claim " , SecondHdl . IsValid ( ) ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FDoubleClaimSmartObject , " System.AI.SmartObjects.Double Claim " ) ;
struct FUseAndReleaseSmartObject : FSmartObjectTestBase
{
virtual bool InstantTest ( ) override
{
const FSmartObjectRequest Request ( FBox ( EForceInit : : ForceInit ) . ExpandBy ( FVector ( HALF_WORLD_MAX ) , FVector ( HALF_WORLD_MAX ) ) , TestFilter ) ;
// Find object
const FSmartObjectRequestResult PreClaimResult = Subsystem - > FindSmartObject ( Request ) ;
AITEST_TRUE ( " Result is expected to be valid " , PreClaimResult . IsValid ( ) ) ;
2022-01-19 14:16:16 -05:00
AITEST_TRUE ( " ID is expected to be valid " , PreClaimResult . SmartObjectHandle . IsValid ( ) ) ;
2021-09-28 13:33:17 -04:00
// Claim & Use object
const FSmartObjectClaimHandle Hdl = Subsystem - > Claim ( PreClaimResult ) ;
AITEST_TRUE ( " Claim Handle is expected to be valid " , Hdl . IsValid ( ) ) ;
2021-11-26 12:55:06 -05:00
const USmartObjectBehaviorDefinition * BehaviorDefinition = Subsystem - > Use < USmartObjectBehaviorDefinition > ( Hdl ) ;
AITEST_NOT_NULL ( " Bahavior definition is expected to be valid " , BehaviorDefinition ) ;
2021-09-28 13:33:17 -04:00
// Release object
const bool bSuccess = Subsystem - > Release ( Hdl ) ;
AITEST_TRUE ( " Handle is expected to be released successfully " , bSuccess ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FUseAndReleaseSmartObject , " System.AI.SmartObjects.Use & Release " ) ;
struct FFindAfterUseSmartObject : FSmartObjectTestBase
{
virtual bool InstantTest ( ) override
{
const FSmartObjectRequest Request ( FBox ( EForceInit : : ForceInit ) . ExpandBy ( FVector ( HALF_WORLD_MAX ) , FVector ( HALF_WORLD_MAX ) ) , TestFilter ) ;
2022-01-19 14:16:16 -05:00
constexpr uint32 ExpectedNumRegisteredObjects = 2 ;
2021-09-28 13:33:17 -04:00
AITEST_EQUAL ( " Test expects only %s registerd smart objects " , SOList . Num ( ) , ExpectedNumRegisteredObjects ) ;
// Find first object
const FSmartObjectRequestResult FirstFindResult = Subsystem - > FindSmartObject ( Request ) ;
AITEST_TRUE ( " Result is expected to be valid " , FirstFindResult . IsValid ( ) ) ;
2022-01-19 14:16:16 -05:00
AITEST_TRUE ( " ID is expected to be valid " , FirstFindResult . SmartObjectHandle . IsValid ( ) ) ;
2021-09-28 13:33:17 -04:00
// Claim & Use first object
const FSmartObjectClaimHandle FirstClaimHandle = Subsystem - > Claim ( FirstFindResult ) ;
AITEST_TRUE ( " Claim Handle is expected to be valid for the first claim " , FirstClaimHandle . IsValid ( ) ) ;
2021-11-26 12:55:06 -05:00
const USmartObjectBehaviorDefinition * FirstDefinition = Subsystem - > Use < USmartObjectBehaviorDefinition > ( FirstClaimHandle ) ;
AITEST_NOT_NULL ( " Behavior definition is expected to be valid " , FirstDefinition ) ;
2021-09-28 13:33:17 -04:00
// Find second object
const FSmartObjectRequestResult SecondFindResult = Subsystem - > FindSmartObject ( Request ) ;
AITEST_TRUE ( " Result is expected to be valid " , SecondFindResult . IsValid ( ) ) ;
2022-01-19 14:16:16 -05:00
AITEST_TRUE ( " ID is expected to be valid " , SecondFindResult . SmartObjectHandle . IsValid ( ) ) ;
AITEST_NOT_EQUAL ( " Result is expected to point to a different ID since first ID was claimed " , FirstFindResult . SmartObjectHandle , SecondFindResult . SmartObjectHandle ) ;
2021-09-28 13:33:17 -04:00
// Claim & use second object
const FSmartObjectClaimHandle SecondClaimHandle = Subsystem - > Claim ( SecondFindResult ) ;
AITEST_TRUE ( " Claim Handle is expected to be valid for the second claim " , SecondClaimHandle . IsValid ( ) ) ;
2021-11-26 12:55:06 -05:00
const USmartObjectBehaviorDefinition * SecondDefinition = Subsystem - > Use < USmartObjectBehaviorDefinition > ( SecondClaimHandle ) ;
AITEST_NOT_NULL ( " Behavior definition is expected to be valid " , SecondDefinition ) ;
2021-09-28 13:33:17 -04:00
// Try to find a third one
const FSmartObjectRequestResult ThirdFindResult = Subsystem - > FindSmartObject ( Request ) ;
AITEST_FALSE ( " Result is expected to be invalid; all objects are claimed " , ThirdFindResult . IsValid ( ) ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FFindAfterUseSmartObject , " System.AI.SmartObjects.Find after Use " ) ;
2022-01-19 14:16:16 -05:00
struct FSlotCustomData : FSmartObjectTestBase
{
virtual bool InstantTest ( ) override
{
const FSmartObjectRequest Request ( FBox ( EForceInit : : ForceInit ) . ExpandBy ( FVector ( HALF_WORLD_MAX ) , FVector ( HALF_WORLD_MAX ) ) , TestFilter ) ;
// Find an object
const FSmartObjectRequestResult FindResult = Subsystem - > FindSmartObject ( Request ) ;
AITEST_TRUE ( " Result is expected to be valid " , FindResult . IsValid ( ) ) ;
AITEST_TRUE ( " ID is expected to be valid " , FindResult . SmartObjectHandle . IsValid ( ) ) ;
AITEST_TRUE ( " SlotHandle is expected to be valid " , FindResult . SlotHandle . IsValid ( ) ) ;
const FSmartObjectSlotView SlotView = Subsystem - > GetSlotView ( FindResult ) ;
AITEST_TRUE ( " Slot Handle is expected to be valid " , SlotView . GetSlotHandle ( ) . IsValid ( ) ) ;
const FSmartObjectSlotTestDefinitionData * DefinitionData = SlotView . GetDefinitionDataPtr < FSmartObjectSlotTestDefinitionData > ( ) ;
AITEST_NOT_NULL ( " Data definition for cooldown is expected to be valid " , DefinitionData ) ;
const FSmartObjectSlotTestRuntimeData * RuntimeData = SlotView . GetStateDataPtr < FSmartObjectSlotTestRuntimeData > ( ) ;
AITEST_NULL ( " Runtime data is expected to be not valid " , RuntimeData ) ;
// Claim
const FSmartObjectClaimHandle ClaimHandle = Subsystem - > Claim ( FindResult ) ;
AITEST_TRUE ( " Claim Handle is expected to be valid for the first claim " , ClaimHandle . IsValid ( ) ) ;
// Add new data, note that this will invalidate the view...
FSmartObjectSlotTestRuntimeData NewRuntimeData ;
constexpr float SomeFloatConstant = 654.321f ;
NewRuntimeData . SomePerInstanceSharedFloat = SomeFloatConstant ;
Subsystem - > AddSlotDataDeferred ( ClaimHandle , FConstStructView : : Make ( NewRuntimeData ) ) ;
// We need to run Mass to flush deferred commands
if ( UMassEntitySubsystem * System = UWorld : : GetSubsystem < UMassEntitySubsystem > ( FAITestHelpers : : GetWorld ( ) ) )
{
FMassProcessingContext ProcessingContext ( * System , /* DeltaSeconds */ 0.f ) ;
UE : : Mass : : Executor : : RunProcessorsView ( TArrayView < UMassProcessor * > ( ) , ProcessingContext ) ;
}
// Fetch a fresh slot view
const FSmartObjectSlotView SlotViewAfter = Subsystem - > GetSlotView ( ClaimHandle ) ;
const FSmartObjectSlotTestRuntimeData * RuntimeDataAfter = SlotViewAfter . GetStateDataPtr < FSmartObjectSlotTestRuntimeData > ( ) ;
AITEST_NOT_NULL ( " Runtime data is expected to be valid " , RuntimeDataAfter ) ;
AITEST_EQUAL ( " Runtime data float from view is expected to be the same as the 'pushed' one " , RuntimeDataAfter - > SomePerInstanceSharedFloat , SomeFloatConstant ) ;
return true ;
}
} ;
IMPLEMENT_AI_INSTANT_TEST ( FSlotCustomData , " System.AI.SmartObjects.Slot custom data " ) ;
2021-09-28 13:33:17 -04:00
} // namespace FSmartObjectTest
PRAGMA_ENABLE_OPTIMIZATION
# undef LOCTEXT_NAMESPACE