Files
UnrealEngineUWP/Engine/Source/Runtime/CoreUObject/Tests/ObjectHandleTest.cpp
chris constantinescu ae281656f2 Tests converted from 18848115 made to work multi-platform.
Not all were converted because they were designed for packaged applications whereas low level tests are designed to run as "native" non-packaged applications.
- reporting support for non-desktop platforms, for the moment Catch2 report type "console" is used that is sent to a .out file
- most number of tests possible running multi platform, slower tests excluded on incremental builds
- slow tests are moved to run on the monolithic build
- Catch2 report failure event listener such that Horde detects them as build errors. Must add new Horde matcher for Catch2 failures

#rb Mark.Lintott
#preflight 623c8de389625f0612dabd64

[CL 19498448 by chris constantinescu in ue5-main branch]
2022-03-24 13:28:25 -04:00

216 lines
9.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "UObject/ObjectHandle.h"
#include "HAL/PlatformProperties.h"
#include "ObjectRefTrackingTestBase.h"
#include "IO/IoDispatcher.h"
static_assert(sizeof(FObjectHandle) == sizeof(void*), "FObjectHandle type must always compile to something equivalent to a pointer size.");
#if WITH_DEV_AUTOMATION_TESTS
class FObjectHandleTestBase : public FObjectRefTrackingTestBase
{
public:
FObjectHandleTestBase(const FString& InName, const bool bInComplexTask)
: FObjectRefTrackingTestBase(InName, bInComplexTask)
{
}
protected:
UObject* ResolveHandle(FObjectHandle& TargetHandle)
{
#if UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
// Late resolved handles cannot be null or resolved at this point
if (!TestFalse(TEXT("Handle to target is null"), IsObjectHandleNull(TargetHandle)))
{
return nullptr;
}
if (!TestFalse(TEXT("Handle to target is resolved"), IsObjectHandleResolved(TargetHandle)))
{
return nullptr;
}
#else
// Immediately resolved handles may be null (if the target is invalid) and must be resolved at this point
if (!TestTrue(TEXT("Handle to target is not resolved"), IsObjectHandleResolved(TargetHandle)))
{
return nullptr;
}
#endif
return ResolveObjectHandle(TargetHandle);
}
UObject* ConstructAndResolveHandle(const ANSICHAR* PackageName, const ANSICHAR* ObjectName, const ANSICHAR* ClassPackageName = nullptr, const ANSICHAR* ClassName = nullptr)
{
FObjectRef TargetRef{FName(PackageName), FName(ClassPackageName), FName(ClassName), FObjectPathId(ObjectName)};
if (!TestFalse(TEXT("Reference to target is null"), IsObjectRefNull(TargetRef)))
{
return nullptr;
}
FObjectHandle TargetHandle = MakeObjectHandle(TargetRef);
return ResolveHandle(TargetHandle);
}
UObject* ConstructAndResolveHandle(const FPackedObjectRef& PackedTargetRef)
{
if (!TestFalse(TEXT("Reference to target is null"), IsPackedObjectRefNull(PackedTargetRef)))
{
return nullptr;
}
FObjectHandle TargetHandle = MakeObjectHandle(PackedTargetRef);
return ResolveHandle(TargetHandle);
}
bool TestResolvableNonNull(const ANSICHAR* PackageName, const ANSICHAR* ObjectName, const ANSICHAR* ClassPackageName = nullptr, const ANSICHAR* ClassName = nullptr, bool bExpectSubRefReads = false)
{
FSnapshotObjectRefMetrics ObjectRefMetrics(*this);
UObject* ResolvedObject = ConstructAndResolveHandle(PackageName, ObjectName, ClassPackageName, ClassName);
ObjectRefMetrics.TestNumResolves(TEXT("NumResolves should be incremented by one after a resolve attempt"), 1);
ObjectRefMetrics.TestNumReads(TEXT("NumReads should be incremented by one after a resolve attempt"), 1, bExpectSubRefReads /*bAllowAdditionalReads*/);
if (!ResolvedObject)
{
AddError(FString::Printf(TEXT("Expected '%s.%s' to resolve to non null."), ANSI_TO_TCHAR(PackageName), ANSI_TO_TCHAR(ObjectName)), 1);
return false;
}
ObjectRefMetrics.TestNumFailedResolves(TEXT("NumFailedResolves should not change after a successful resolve attempt"), 0);
return true;
}
bool TestResolveFailure(const ANSICHAR* PackageName, const ANSICHAR* ObjectName, const ANSICHAR* ClassPackageName = nullptr, const ANSICHAR* ClassName = nullptr)
{
FSnapshotObjectRefMetrics ObjectRefMetrics(*this);
UObject* ResolvedObject = ConstructAndResolveHandle(PackageName, ObjectName, ClassPackageName, ClassName);
ObjectRefMetrics.TestNumResolves(TEXT("NumResolves should be incremented by one after a resolve attempt"), 1);
ObjectRefMetrics.TestNumReads(TEXT("NumReads should be incremented by one after a resolve attempt"), 1);
if (ResolvedObject)
{
AddError(FString::Printf(TEXT("Expected '%s.%s' to resolve to null."), ANSI_TO_TCHAR(PackageName), ANSI_TO_TCHAR(ObjectName)), 1);
return false;
}
ObjectRefMetrics.TestNumFailedResolves(TEXT("NumFailedResolves should be incremented by one after a failed resolve attempt"), 1);
return true;
}
bool TestResolveFailure(FPackedObjectRef PackedRef)
{
FSnapshotObjectRefMetrics ObjectRefMetrics(*this);
UObject* ResolvedObject = ConstructAndResolveHandle(PackedRef);
ObjectRefMetrics.TestNumResolves(TEXT("NumResolves should be incremented by one after a resolve attempt"), 1);
ObjectRefMetrics.TestNumReads(TEXT("NumReads should be incremented by one after a resolve attempt"), 1);
if (ResolvedObject)
{
AddError(FString::Printf(TEXT("Expected PACKEDREF(%" UPTRINT_X_FMT ") to resolve to null."), PackedRef.EncodedRef), 1);
return false;
}
ObjectRefMetrics.TestNumFailedResolves(TEXT("NumFailedResolves should be incremented by one after a failed resolve attempt"), 1);
return true;
}
};
#define TEST_NAME_ROOT TEXT("System.CoreUObject.ObjectHandle")
constexpr const uint32 ObjectHandleTestFlags = EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::EngineFilter;
IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FObjectHandleTestNullBehavior, FObjectHandleTestBase, TEST_NAME_ROOT TEXT(".NullBehavior"), ObjectHandleTestFlags)
bool FObjectHandleTestNullBehavior::RunTest(const FString& Parameters)
{
FObjectHandle TargetHandle = MakeObjectHandle(nullptr);
TestTrue(TEXT("Handle to target is null"), IsObjectHandleNull(TargetHandle));
TestTrue(TEXT("Handle to target is resolved"), IsObjectHandleResolved(TargetHandle));
FSnapshotObjectRefMetrics ObjectRefMetrics(*this);
UObject* ResolvedObject = ResolveObjectHandle(TargetHandle);
TestEqual(TEXT("Resolved object is equal to original object"), (UObject*)nullptr, ResolvedObject);
ObjectRefMetrics.TestNumFailedResolves(TEXT("NumFailedResolves should not change after a resolve attempt on a null handle"), 0);
ObjectRefMetrics.TestNumResolves(TEXT("NumResolves should not change after a resolve attempt on a null handle"), 0);
ObjectRefMetrics.TestNumReads(TEXT("NumReads should be incremented by one after a resolve attempt on a null handle"), 1);
return true;
}
IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FObjectHandleTestPointerBehavior, FObjectHandleTestBase, TEST_NAME_ROOT TEXT(".PointerBehavior"), ObjectHandleTestFlags)
bool FObjectHandleTestPointerBehavior::RunTest(const FString& Parameters)
{
FObjectHandle TargetHandle = MakeObjectHandle((UObject*)0x0042);
TestFalse(TEXT("Handle to target is null"), IsObjectHandleNull(TargetHandle));
TestTrue(TEXT("Handle to target is resolved"), IsObjectHandleResolved(TargetHandle));
FSnapshotObjectRefMetrics ObjectRefMetrics(*this);
UObject* ResolvedObject = ResolveObjectHandle(TargetHandle);
TestEqual(TEXT("Resolved object is equal to original object"), (UObject*)0x0042, ResolvedObject);
ObjectRefMetrics.TestNumResolves(TEXT("NumResolves should not change after a resolve attempt on a pointer handle"), 0);
ObjectRefMetrics.TestNumFailedResolves(TEXT("NumFailedResolves should not change after a resolve attempt on a pointer handle"), 0);
ObjectRefMetrics.TestNumReads(TEXT("NumReads should be incremented by one after a resolve attempt on a pointer handle"),1);
return true;
}
IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FObjectHandleTestResolveEngineContentTarget, FObjectHandleTestBase, TEST_NAME_ROOT TEXT(".ResolveEngineContentTarget"), ObjectHandleTestFlags)
bool FObjectHandleTestResolveEngineContentTarget::RunTest(const FString& Parameters)
{
// Confirm we successfully resolve a correct reference to engine content
TestResolvableNonNull("/Engine/EngineResources/DefaultTexture", "DefaultTexture");
// @TODO: OBJPTR: These assets aren't in a standard cook of EngineTest, so avoid testing them when using cooked content. Should look for other assets to use instead.
if (!FPlatformProperties::RequiresCookedData())
{
// Confirm we successfully resolve a correct reference to a subobject in engine content
TestResolvableNonNull("/Engine/FunctionalTesting/Blueprints/AITesting_MoveGoal", "AITesting_MoveGoal.EventGraph.K2Node_VariableGet_142", nullptr, nullptr, true);
// Attempt to load something that uses a User Defined Enum
TestResolvableNonNull("/Engine/ArtTools/RenderToTexture/Macros/RenderToTextureMacros", "RenderToTextureMacros:Array to HLSL Float Array.K2Node_Select_1", nullptr, nullptr, true);
}
return true;
}
IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FObjectHandleTestResolveNonExistentTarget, FObjectHandleTestBase, TEST_NAME_ROOT TEXT(".ResolveNonExistentTarget"), ObjectHandleTestFlags)
bool FObjectHandleTestResolveNonExistentTarget::RunTest(const FString& Parameters)
{
// Confirm we don't successfully resolve an incorrect reference to engine content
TestResolveFailure("/Engine/EngineResources/NonExistentPackageName_0", "DefaultTexture");
TestResolveFailure("/Engine/EngineResources/DefaultTexture", "NonExistentObject_0");
return true;
}
IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FObjectHandleTestResolveScriptTarget, FObjectHandleTestBase, TEST_NAME_ROOT TEXT(".ResolveScriptTarget"), ObjectHandleTestFlags)
bool FObjectHandleTestResolveScriptTarget::RunTest(const FString& Parameters)
{
// Confirm we successfully resolve a correct reference to engine content
TestResolvableNonNull("/Script/Engine", "Default__Actor");
TestResolvableNonNull("/Script/Engine", "DefaultPawn");
return true;
}
#if UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FObjectHandleTestResolveMalformedHandle, FObjectHandleTestBase, TEST_NAME_ROOT TEXT(".ResolveMalformedHandle"), ObjectHandleTestFlags)
bool FObjectHandleTestResolveMalformedHandle::RunTest(const FString& Parameters)
{
TestResolveFailure(FPackedObjectRef { 0xFFFF'FFFF'FFFF'FFFFull });
TestResolveFailure(FPackedObjectRef { 0xEFEF'EFEF'EFEF'EFEFull });
return true;
}
#endif // UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
#undef TEST_NAME_ROOT
#endif // WITH_DEV_AUTOMATION_TESTS