Files
UnrealEngineUWP/Engine/Source/Runtime/CoreUObject/Tests/ObjectHandleTest.cpp

210 lines
8.7 KiB
C++
Raw Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
#if WITH_LOW_LEVEL_TESTS
#include "UObject/ObjectHandle.h"
#include "HAL/PlatformProperties.h"
#include "ObjectRefTrackingTestBase.h"
#include "IO/IoDispatcher.h"
#include "TestHarness.h"
static_assert(sizeof(FObjectHandle) == sizeof(void*), "FObjectHandle type must always compile to something equivalent to a pointer size.");
class FObjectHandleTestBase : public FObjectRefTrackingTestBase
{
public:
protected:
Revision 2 (with handling for uninitialized object references): -Compile in lazy resolve functionality for editor binaries -Add "-LazyResolveAllImports" commandline switch to allow lazy RESOLVE of all wrapped object references -Functionality confirmed: load default map in ShooterGame Editor, cook ShooterGame, load LumenReflectiveTest in FortGPUTestbed, load default map and P_Construct in Frosty -Renamed "-DisableLoadingAllImports" to "-LazyLoadAllImports" commandline switch to allow lazy LOAD of all wrapped object references and ensured it works with AsyncLoading code path -Non functional when combined with lazy resolve - will be worked on afterwards -Added CPU timing scopes to measure performance impact of lazy resolve Fixed lazy resolve bugs: -Ensure null check on an unresolved object reference resolves the object reference (a non-null unresolved reference can become a null resolved reference) -Ensure hash of an unresolved object reference resolves the object reference and hashes the resultant address (otherwise we can't reliably ensure hash consistency in the face of object redirection or stale references) -Avoid using package name hash internally as it immediately manifested in hash collisions on a moderately sized project -Ensure StaticFindObjectFastInternal instead of StaticFindObjectFast to ensure we can find/resolve not fully loaded objects -Ensure UObjectRedirectors are handled when resolving wrapped object pointers -Ensure we handle the possibility that a package has been partially loaded and we may not find our target object within it because the object hasn't been created yet Automated testing: -Adding hash consistency and redirector resolve tests to ObjectPtr unit tests #rb devin.doucette [CL 15571874 by Zousar Shaker in ue5-main branch]
2021-03-02 14:39:53 -04:00
UObject* ResolveHandle(FObjectHandle& TargetHandle)
{
#if UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
// Late resolved handles cannot be null or resolved at this point
bool bValue = IsObjectHandleNull(TargetHandle);
TEST_FALSE(TEXT("Handle to target is null"), bValue);
if (bValue)
{
return nullptr;
}
bValue = IsObjectHandleResolved(TargetHandle);
TEST_FALSE(TEXT("Handle to target is resolved"), bValue);
if (bValue)
{
return nullptr;
}
#else
bool bValue = IsObjectHandleResolved(TargetHandle);
// Immediately resolved handles may be null (if the target is invalid) and must be resolved at this point
TEST_TRUE(TEXT("Handle to target is not resolved"), bValue);
if (!bValue)
{
return nullptr;
}
#endif
return ResolveObjectHandle(TargetHandle);
}
Revision 2 (with handling for uninitialized object references): -Compile in lazy resolve functionality for editor binaries -Add "-LazyResolveAllImports" commandline switch to allow lazy RESOLVE of all wrapped object references -Functionality confirmed: load default map in ShooterGame Editor, cook ShooterGame, load LumenReflectiveTest in FortGPUTestbed, load default map and P_Construct in Frosty -Renamed "-DisableLoadingAllImports" to "-LazyLoadAllImports" commandline switch to allow lazy LOAD of all wrapped object references and ensured it works with AsyncLoading code path -Non functional when combined with lazy resolve - will be worked on afterwards -Added CPU timing scopes to measure performance impact of lazy resolve Fixed lazy resolve bugs: -Ensure null check on an unresolved object reference resolves the object reference (a non-null unresolved reference can become a null resolved reference) -Ensure hash of an unresolved object reference resolves the object reference and hashes the resultant address (otherwise we can't reliably ensure hash consistency in the face of object redirection or stale references) -Avoid using package name hash internally as it immediately manifested in hash collisions on a moderately sized project -Ensure StaticFindObjectFastInternal instead of StaticFindObjectFast to ensure we can find/resolve not fully loaded objects -Ensure UObjectRedirectors are handled when resolving wrapped object pointers -Ensure we handle the possibility that a package has been partially loaded and we may not find our target object within it because the object hasn't been created yet Automated testing: -Adding hash consistency and redirector resolve tests to ObjectPtr unit tests #rb devin.doucette [CL 15571874 by Zousar Shaker in ue5-main branch]
2021-03-02 14:39:53 -04:00
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)};
bool bValue = IsObjectRefNull(TargetRef);
TEST_FALSE(TEXT("Reference to target is null"), bValue);
if (bValue)
Revision 2 (with handling for uninitialized object references): -Compile in lazy resolve functionality for editor binaries -Add "-LazyResolveAllImports" commandline switch to allow lazy RESOLVE of all wrapped object references -Functionality confirmed: load default map in ShooterGame Editor, cook ShooterGame, load LumenReflectiveTest in FortGPUTestbed, load default map and P_Construct in Frosty -Renamed "-DisableLoadingAllImports" to "-LazyLoadAllImports" commandline switch to allow lazy LOAD of all wrapped object references and ensured it works with AsyncLoading code path -Non functional when combined with lazy resolve - will be worked on afterwards -Added CPU timing scopes to measure performance impact of lazy resolve Fixed lazy resolve bugs: -Ensure null check on an unresolved object reference resolves the object reference (a non-null unresolved reference can become a null resolved reference) -Ensure hash of an unresolved object reference resolves the object reference and hashes the resultant address (otherwise we can't reliably ensure hash consistency in the face of object redirection or stale references) -Avoid using package name hash internally as it immediately manifested in hash collisions on a moderately sized project -Ensure StaticFindObjectFastInternal instead of StaticFindObjectFast to ensure we can find/resolve not fully loaded objects -Ensure UObjectRedirectors are handled when resolving wrapped object pointers -Ensure we handle the possibility that a package has been partially loaded and we may not find our target object within it because the object hasn't been created yet Automated testing: -Adding hash consistency and redirector resolve tests to ObjectPtr unit tests #rb devin.doucette [CL 15571874 by Zousar Shaker in ue5-main branch]
2021-03-02 14:39:53 -04:00
{
return nullptr;
}
FObjectHandle TargetHandle = MakeObjectHandle(TargetRef);
return ResolveHandle(TargetHandle);
}
UObject* ConstructAndResolveHandle(const FPackedObjectRef& PackedTargetRef)
{
bool bValue = IsPackedObjectRefNull(PackedTargetRef);
TEST_FALSE(TEXT("Reference to target is null"), bValue);
if (bValue)
Revision 2 (with handling for uninitialized object references): -Compile in lazy resolve functionality for editor binaries -Add "-LazyResolveAllImports" commandline switch to allow lazy RESOLVE of all wrapped object references -Functionality confirmed: load default map in ShooterGame Editor, cook ShooterGame, load LumenReflectiveTest in FortGPUTestbed, load default map and P_Construct in Frosty -Renamed "-DisableLoadingAllImports" to "-LazyLoadAllImports" commandline switch to allow lazy LOAD of all wrapped object references and ensured it works with AsyncLoading code path -Non functional when combined with lazy resolve - will be worked on afterwards -Added CPU timing scopes to measure performance impact of lazy resolve Fixed lazy resolve bugs: -Ensure null check on an unresolved object reference resolves the object reference (a non-null unresolved reference can become a null resolved reference) -Ensure hash of an unresolved object reference resolves the object reference and hashes the resultant address (otherwise we can't reliably ensure hash consistency in the face of object redirection or stale references) -Avoid using package name hash internally as it immediately manifested in hash collisions on a moderately sized project -Ensure StaticFindObjectFastInternal instead of StaticFindObjectFast to ensure we can find/resolve not fully loaded objects -Ensure UObjectRedirectors are handled when resolving wrapped object pointers -Ensure we handle the possibility that a package has been partially loaded and we may not find our target object within it because the object hasn't been created yet Automated testing: -Adding hash consistency and redirector resolve tests to ObjectPtr unit tests #rb devin.doucette [CL 15571874 by Zousar Shaker in ue5-main branch]
2021-03-02 14:39:53 -04:00
{
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)
{
FAIL_CHECK(FString::Printf(TEXT("Expected '%s.%s' to resolve to non null."), ANSI_TO_TCHAR(PackageName), ANSI_TO_TCHAR(ObjectName)));
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)
{
FAIL_CHECK(FString::Printf(TEXT("Expected '%s.%s' to resolve to null."), ANSI_TO_TCHAR(PackageName), ANSI_TO_TCHAR(ObjectName)));
return false;
}
ObjectRefMetrics.TestNumFailedResolves(TEXT("NumFailedResolves should be incremented by one after a failed resolve attempt"), 1);
return true;
}
Revision 2 (with handling for uninitialized object references): -Compile in lazy resolve functionality for editor binaries -Add "-LazyResolveAllImports" commandline switch to allow lazy RESOLVE of all wrapped object references -Functionality confirmed: load default map in ShooterGame Editor, cook ShooterGame, load LumenReflectiveTest in FortGPUTestbed, load default map and P_Construct in Frosty -Renamed "-DisableLoadingAllImports" to "-LazyLoadAllImports" commandline switch to allow lazy LOAD of all wrapped object references and ensured it works with AsyncLoading code path -Non functional when combined with lazy resolve - will be worked on afterwards -Added CPU timing scopes to measure performance impact of lazy resolve Fixed lazy resolve bugs: -Ensure null check on an unresolved object reference resolves the object reference (a non-null unresolved reference can become a null resolved reference) -Ensure hash of an unresolved object reference resolves the object reference and hashes the resultant address (otherwise we can't reliably ensure hash consistency in the face of object redirection or stale references) -Avoid using package name hash internally as it immediately manifested in hash collisions on a moderately sized project -Ensure StaticFindObjectFastInternal instead of StaticFindObjectFast to ensure we can find/resolve not fully loaded objects -Ensure UObjectRedirectors are handled when resolving wrapped object pointers -Ensure we handle the possibility that a package has been partially loaded and we may not find our target object within it because the object hasn't been created yet Automated testing: -Adding hash consistency and redirector resolve tests to ObjectPtr unit tests #rb devin.doucette [CL 15571874 by Zousar Shaker in ue5-main branch]
2021-03-02 14:39:53 -04:00
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)
{
FAIL_CHECK(FString::Printf(TEXT("Expected PACKEDREF(%" UPTRINT_X_FMT ") to resolve to null."), PackedRef.EncodedRef));
Revision 2 (with handling for uninitialized object references): -Compile in lazy resolve functionality for editor binaries -Add "-LazyResolveAllImports" commandline switch to allow lazy RESOLVE of all wrapped object references -Functionality confirmed: load default map in ShooterGame Editor, cook ShooterGame, load LumenReflectiveTest in FortGPUTestbed, load default map and P_Construct in Frosty -Renamed "-DisableLoadingAllImports" to "-LazyLoadAllImports" commandline switch to allow lazy LOAD of all wrapped object references and ensured it works with AsyncLoading code path -Non functional when combined with lazy resolve - will be worked on afterwards -Added CPU timing scopes to measure performance impact of lazy resolve Fixed lazy resolve bugs: -Ensure null check on an unresolved object reference resolves the object reference (a non-null unresolved reference can become a null resolved reference) -Ensure hash of an unresolved object reference resolves the object reference and hashes the resultant address (otherwise we can't reliably ensure hash consistency in the face of object redirection or stale references) -Avoid using package name hash internally as it immediately manifested in hash collisions on a moderately sized project -Ensure StaticFindObjectFastInternal instead of StaticFindObjectFast to ensure we can find/resolve not fully loaded objects -Ensure UObjectRedirectors are handled when resolving wrapped object pointers -Ensure we handle the possibility that a package has been partially loaded and we may not find our target object within it because the object hasn't been created yet Automated testing: -Adding hash consistency and redirector resolve tests to ObjectPtr unit tests #rb devin.doucette [CL 15571874 by Zousar Shaker in ue5-main branch]
2021-03-02 14:39:53 -04:00
return false;
}
ObjectRefMetrics.TestNumFailedResolves(TEXT("NumFailedResolves should be incremented by one after a failed resolve attempt"), 1);
return true;
}
};
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::FObjectHandle::Null Behavior", "[CoreUObject][ObjectHandle]")
{
FObjectHandle TargetHandle = MakeObjectHandle(nullptr);
TEST_TRUE(TEXT("Handle to target is null"), IsObjectHandleNull(TargetHandle));
TEST_TRUE(TEXT("Handle to target is resolved"), IsObjectHandleResolved(TargetHandle));
FSnapshotObjectRefMetrics ObjectRefMetrics(*this);
UObject* ResolvedObject = ResolveObjectHandle(TargetHandle);
TEST_EQUAL(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);
}
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::FObjectHandle::Pointer Behavior", "[CoreUObject][ObjectHandle]")
{
FObjectHandle TargetHandle = MakeObjectHandle((UObject*)0x0042);
TEST_FALSE(TEXT("Handle to target is null"), IsObjectHandleNull(TargetHandle));
TEST_TRUE(TEXT("Handle to target is resolved"), IsObjectHandleResolved(TargetHandle));
FSnapshotObjectRefMetrics ObjectRefMetrics(*this);
UObject* ResolvedObject = ResolveObjectHandle(TargetHandle);
TEST_EQUAL(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);
}
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::FObjectHandle::Resolve Engine Content Target", "[CoreUObject][ObjectHandle]")
{
const FName TestPackageName(TEXT("/Engine/Test/ObjectPtrDefaultSerialize/Transient"));
UPackage* TestPackage = NewObject<UPackage>(nullptr, TestPackageName, RF_Transient);
TestPackage->AddToRoot();
UObject* TestSoftObject = NewObject<UMetaData>(TestPackage, TEXT("DefaultSerializeObject"));
UObject* TestSubObject = NewObject<UMetaData>(TestSoftObject, TEXT("SubObject"));
ON_SCOPE_EXIT{
TestPackage->RemoveFromRoot();
};
// Confirm we successfully resolve a correct reference to a subobject
TestResolvableNonNull("/Engine/Test/ObjectPtrDefaultSerialize/Transient", "DefaultSerializeObject.SubObject", nullptr, nullptr, true);
TestResolvableNonNull("/Engine/Test/ObjectPtrDefaultSerialize/Transient", "DefaultSerializeObject");
}
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::FObjectHandle::Resolve Non Existent Target", "[CoreUObject][ObjectHandle]")
{
// Confirm we don't successfully resolve an incorrect reference to engine content
TestResolveFailure("/Engine/EngineResources/NonExistentPackageName_0", "DefaultTexture");
const FName TestPackageName(TEXT("/Engine/Test/ObjectPtrDefaultSerialize/Transient"));
UPackage* TestPackage = NewObject<UPackage>(nullptr, TestPackageName, RF_Transient);
TestPackage->AddToRoot();
UObject* TestSoftObject = NewObject<UMetaData>(TestPackage, TEXT("DefaultSerializeObject"));
ON_SCOPE_EXIT{
TestPackage->RemoveFromRoot();
};
TestResolveFailure("/Engine/Test/ObjectPtrDefaultSerialize/Transient", "DefaultSerializeObject_DoesNotExist");
}
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::FObjectHandle::Resolve Script Target", "[CoreUObject][ObjectHandle]")
{
// Confirm we successfully resolve a correct reference to engine content
TestResolvableNonNull("/Script/CoreUObject", "MetaData");
}
Revision 2 (with handling for uninitialized object references): -Compile in lazy resolve functionality for editor binaries -Add "-LazyResolveAllImports" commandline switch to allow lazy RESOLVE of all wrapped object references -Functionality confirmed: load default map in ShooterGame Editor, cook ShooterGame, load LumenReflectiveTest in FortGPUTestbed, load default map and P_Construct in Frosty -Renamed "-DisableLoadingAllImports" to "-LazyLoadAllImports" commandline switch to allow lazy LOAD of all wrapped object references and ensured it works with AsyncLoading code path -Non functional when combined with lazy resolve - will be worked on afterwards -Added CPU timing scopes to measure performance impact of lazy resolve Fixed lazy resolve bugs: -Ensure null check on an unresolved object reference resolves the object reference (a non-null unresolved reference can become a null resolved reference) -Ensure hash of an unresolved object reference resolves the object reference and hashes the resultant address (otherwise we can't reliably ensure hash consistency in the face of object redirection or stale references) -Avoid using package name hash internally as it immediately manifested in hash collisions on a moderately sized project -Ensure StaticFindObjectFastInternal instead of StaticFindObjectFast to ensure we can find/resolve not fully loaded objects -Ensure UObjectRedirectors are handled when resolving wrapped object pointers -Ensure we handle the possibility that a package has been partially loaded and we may not find our target object within it because the object hasn't been created yet Automated testing: -Adding hash consistency and redirector resolve tests to ObjectPtr unit tests #rb devin.doucette [CL 15571874 by Zousar Shaker in ue5-main branch]
2021-03-02 14:39:53 -04:00
#if UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::FObjectHandle::Resolve Malformed Handle", "[CoreUObject][ObjectHandle]")
Revision 2 (with handling for uninitialized object references): -Compile in lazy resolve functionality for editor binaries -Add "-LazyResolveAllImports" commandline switch to allow lazy RESOLVE of all wrapped object references -Functionality confirmed: load default map in ShooterGame Editor, cook ShooterGame, load LumenReflectiveTest in FortGPUTestbed, load default map and P_Construct in Frosty -Renamed "-DisableLoadingAllImports" to "-LazyLoadAllImports" commandline switch to allow lazy LOAD of all wrapped object references and ensured it works with AsyncLoading code path -Non functional when combined with lazy resolve - will be worked on afterwards -Added CPU timing scopes to measure performance impact of lazy resolve Fixed lazy resolve bugs: -Ensure null check on an unresolved object reference resolves the object reference (a non-null unresolved reference can become a null resolved reference) -Ensure hash of an unresolved object reference resolves the object reference and hashes the resultant address (otherwise we can't reliably ensure hash consistency in the face of object redirection or stale references) -Avoid using package name hash internally as it immediately manifested in hash collisions on a moderately sized project -Ensure StaticFindObjectFastInternal instead of StaticFindObjectFast to ensure we can find/resolve not fully loaded objects -Ensure UObjectRedirectors are handled when resolving wrapped object pointers -Ensure we handle the possibility that a package has been partially loaded and we may not find our target object within it because the object hasn't been created yet Automated testing: -Adding hash consistency and redirector resolve tests to ObjectPtr unit tests #rb devin.doucette [CL 15571874 by Zousar Shaker in ue5-main branch]
2021-03-02 14:39:53 -04:00
{
TestResolveFailure(FPackedObjectRef { 0xFFFF'FFFF'FFFF'FFFFull });
TestResolveFailure(FPackedObjectRef { 0xEFEF'EFEF'EFEF'EFEFull });
}
#endif // UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
#endif