Files

375 lines
16 KiB
C++
Raw Permalink Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
#if WITH_LOW_LEVEL_TESTS
#include "ObjectPtrTestClass.h"
#include "UObject/ObjectHandle.h"
#include "UObject/ObjectPtr.h"
#include "UObject/Package.h"
#include "UObject/ObjectResource.h"
#include "UObject/MetaData.h"
#include "HAL/PlatformProperties.h"
#include "ObjectRefTrackingTestBase.h"
#include "IO/IoDispatcher.h"
#include "TestHarness.h"
#include "UObject/ObjectRef.h"
#include "UObject/ObjectPathId.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:
#if UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
void TestResolveFailure(UE::CoreUObject::Private::FPackedObjectRef PackedRef)
{
FSnapshotObjectRefMetrics ObjectRefMetrics(*this);
FObjectHandle TargetHandle = { PackedRef.EncodedRef };
UObject* ResolvedObject = FObjectPtr(TargetHandle).Get();
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);
CHECK(ResolvedObject == nullptr);
ObjectRefMetrics.TestNumFailedResolves(TEXT("NumFailedResolves should be incremented by one after a failed resolve attempt"), 1);
}
#endif
#if UE_WITH_OBJECT_HANDLE_LATE_RESOLVE || UE_WITH_OBJECT_HANDLE_TRACKING
void TestResolvableNonNull(const ANSICHAR* PackageName, const ANSICHAR* ObjectName, bool bExpectSubRefReads)
{
FSnapshotObjectRefMetrics ObjectRefMetrics(*this);
FObjectRef TargetRef(FName(PackageName), NAME_None, NAME_None, UE::CoreUObject::Private::FObjectPathId(ObjectName));
UObject* ResolvedObject = TargetRef.Resolve();
FObjectPtr Ptr(ResolvedObject);
Ptr.Get();
TEST_TRUE(TEXT("expected not null"), ResolvedObject != nullptr);
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*/);
ObjectRefMetrics.TestNumFailedResolves(TEXT("NumFailedResolves should not change after a successful resolve attempt"), 0);
}
void TestResolveFailure(const ANSICHAR* PackageName, const ANSICHAR* ObjectName)
{
FSnapshotObjectRefMetrics ObjectRefMetrics(*this);
FObjectRef TargetRef(FName(PackageName), NAME_None, NAME_None, UE::CoreUObject::Private::FObjectPathId(ObjectName));
const UObject* ResolvedObject = TargetRef.Resolve();
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);
CHECK(ResolvedObject == nullptr);
ObjectRefMetrics.TestNumFailedResolves(TEXT("NumFailedResolves should be incremented by one after a failed resolve attempt"), 1);
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
}
#endif
};
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::FObjectHandle::Null Behavior", "[CoreUObject][ObjectHandle]")
{
FObjectHandle TargetHandle = UE::CoreUObject::Private::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 = UE::CoreUObject::Private::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 = UE::CoreUObject::Private::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 = UE::CoreUObject::Private::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);
}
#if UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
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<UObjectPtrTestClass>(TestPackage, TEXT("DefaultSerializeObject"));
UObject* TestSubObject = NewObject<UObjectPtrTestClass>(TestSoftObject, TEXT("SubObject"));
ON_SCOPE_EXIT{
TestPackage->RemoveFromRoot();
};
TestResolvableNonNull("/Engine/Test/ObjectPtrDefaultSerialize/Transient", "DefaultSerializeObject.SubObject", true);
TestResolvableNonNull("/Engine/Test/ObjectPtrDefaultSerialize/Transient", "DefaultSerializeObject", false);
}
// TODO: Disabled until warnings and errors related to loading a non-existent package have been fixed.
DISABLED_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<UObjectPtrTestClass>(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", true);
}
#endif
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::TObjectPtr::HandleNullGetClass", "[CoreUObject][ObjectHandle]")
{
TObjectPtr<UObject> Ptr = nullptr;
TEST_TRUE(TEXT("TObjectPtr.GetClass should return null on a null object"), Ptr.GetClass() == nullptr);
}
#if UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
TEST_CASE("CoreUObject::FObjectHandle::Names")
{
const FName TestPackageName(TEXT("/Engine/Test/PackageResolve/Transient"));
UPackage* TestPackage = NewObject<UPackage>(nullptr, TestPackageName, RF_Transient);
TestPackage->AddToRoot();
UObject* Obj1 = NewObject<UObjectPtrTestClass>(TestPackage, TEXT("DefaultSerializeObject"));
ON_SCOPE_EXIT{
TestPackage->RemoveFromRoot();
};
FObjectPtr Test;
FObjectPtr PackagePtr(MakeUnresolvedHandle(TestPackage));
FObjectPtr Obj1Ptr(MakeUnresolvedHandle(Obj1));
CHECK(!PackagePtr.IsResolved());
CHECK(TestPackage->GetPathName() == PackagePtr.GetPathName());
CHECK(TestPackage->GetFName() == PackagePtr.GetFName());
CHECK(TestPackage->GetName() == PackagePtr.GetName());
CHECK(TestPackage->GetFullName() == PackagePtr.GetFullName());
CHECK(!PackagePtr.IsResolved());
CHECK(!Obj1Ptr.IsResolved());
CHECK(Obj1->GetPathName() == Obj1Ptr.GetPathName());
CHECK(Obj1->GetFName() == Obj1Ptr.GetFName());
CHECK(Obj1->GetName() == Obj1Ptr.GetName());
CHECK(Obj1->GetFullName() == Obj1Ptr.GetFullName());
CHECK(!Obj1Ptr.IsResolved());
}
#endif
#if UE_WITH_OBJECT_HANDLE_TRACKING || UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
TEST_CASE("CoreUObject::ObjectRef")
{
const FName TestPackageName(TEXT("/Engine/Test/ObjectRef/Transient"));
UPackage* TestPackage = NewObject<UPackage>(nullptr, TestPackageName, RF_Transient);
TestPackage->AddToRoot();
UObject* Obj1 = NewObject<UObjectPtrTestClass>(TestPackage, TEXT("DefaultSerializeObject"));
UObject* Inner1 = NewObject<UObjectPtrTestClass>(Obj1, TEXT("Inner"));
ON_SCOPE_EXIT{
TestPackage->RemoveFromRoot();
};
{
FObjectImport ObjectImport(Obj1);
FObjectRef ObjectRef(Obj1);
CHECK(ObjectImport.ClassPackage == ObjectRef.ClassPackageName);
CHECK(ObjectImport.ClassName == ObjectRef.ClassName);
CHECK(TestPackage->GetFName() == ObjectRef.PackageName);
}
{
FObjectImport ObjectImport(Inner1);
FObjectRef ObjectRef(Inner1);
CHECK(ObjectImport.ClassPackage == ObjectRef.ClassPackageName);
CHECK(ObjectImport.ClassName == ObjectRef.ClassName);
CHECK(TestPackage->GetFName() == ObjectRef.PackageName);
}
}
#endif
#if UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::TObjectPtr::Null Behavior", "[CoreUObject][ObjectHandle]")
{
TObjectPtr<UObject> Ptr = nullptr;
UObjectPtrTestClass* TestObject = nullptr;
uint32 ResolveCount = 0;
auto ResolveDelegate = [&ResolveCount](const FObjectRef& SourceRef, UPackage* ObjectPackage, UObject* Object)
{
++ResolveCount;
};
auto Handle = UE::CoreUObject::AddObjectHandleReferenceResolvedCallback(ResolveDelegate);
ON_SCOPE_EXIT
{
UE::CoreUObject::RemoveObjectHandleReferenceResolvedCallback(Handle);
};
//compare against all flavours of nullptr, should not try and resolve this pointer
CHECK(Ptr == nullptr); CHECK(ResolveCount == 0u);
CHECK(nullptr == Ptr); CHECK(ResolveCount == 0u);
CHECK_FALSE(Ptr != nullptr); CHECK(ResolveCount == 0u);
CHECK_FALSE(nullptr != Ptr); CHECK(ResolveCount == 0u);
CHECK(!Ptr); CHECK(ResolveCount == 0u);
//using an if otherwise the macros try to convert to a pointer and not use the bool operator
if (Ptr)
{
CHECK(false);
}
else
{
CHECK(true);
}
CHECK(ResolveCount == 0u);
CHECK(Ptr == TestObject); CHECK(ResolveCount == 0u);
CHECK(TestObject == Ptr); CHECK(ResolveCount == 0u);
CHECK_FALSE(Ptr != TestObject); CHECK(ResolveCount == 0u);
CHECK_FALSE(TestObject != Ptr); CHECK(ResolveCount == 0u);
FObjectRef TargetRef(FName("SomePackage"), FName("ClassPackageName"), FName("ClassName"), UE::CoreUObject::Private::FObjectPathId("ObjectName"));
UE::CoreUObject::Private::FPackedObjectRef PackedObjectRef = UE::CoreUObject::Private::MakePackedObjectRef(TargetRef);
FObjectPtr ObjectPtr({ PackedObjectRef.EncodedRef });
REQUIRE(!ObjectPtr.IsResolved()); //make sure not resolved
//an unresolved pointers compared against nullptr should still not resolve
Ptr = *reinterpret_cast<TObjectPtr<UObject>*>(&ObjectPtr);
CHECK_FALSE(Ptr == nullptr); CHECK(ResolveCount == 0u);
CHECK_FALSE(nullptr == Ptr); CHECK(ResolveCount == 0u);
CHECK(Ptr != nullptr); CHECK(ResolveCount == 0u);
CHECK(nullptr != Ptr); CHECK(ResolveCount == 0u);
CHECK_FALSE(!Ptr); CHECK(ResolveCount == 0u);
//using an if otherwise the macros try to convert to a pointer and not use the bool operator
if (Ptr)
{
CHECK(true);
}
else
{
CHECK(false);
}
CHECK(ResolveCount == 0u);
//test an unresolve pointer against a null raw pointer
CHECK_FALSE(Ptr == TestObject); CHECK(ResolveCount == 0u);
CHECK_FALSE(TestObject == Ptr); CHECK(ResolveCount == 0u);
CHECK(Ptr != TestObject); CHECK(ResolveCount == 0u);
CHECK(TestObject != Ptr); CHECK(ResolveCount == 0u);
//creating a real object for something that can resolve
const FName TestPackageName(TEXT("/Engine/Test/ObjectPtrDefaultSerialize/Transient"));
UPackage* TestPackage = NewObject<UPackage>(nullptr, TestPackageName, RF_Transient);
TestPackage->AddToRoot();
const FName TestObjectName(TEXT("MyObject"));
TestObject = NewObject<UObjectPtrTestClass>(TestPackage, TestObjectName, RF_Transient);
TObjectPtr<UObject> TestNotLazyObject = NewObject<UObjectPtrNotLazyTestClass>(TestPackage, TEXT("NotLazy"), RF_Transient);
//compare resolved ptr against nullptr
TObjectPtr<UObject> ResolvedPtr = TestObject;
CHECK(ResolvedPtr.IsResolved());
CHECK(Ptr != ResolvedPtr); CHECK(ResolveCount == 0u);
CHECK(ResolvedPtr != Ptr); CHECK(ResolveCount == 0u);
CHECK_FALSE(Ptr == ResolvedPtr); CHECK(ResolveCount == 0u);
CHECK_FALSE(ResolvedPtr == Ptr); CHECK(ResolveCount == 0u);
//compare unresolved against nullptr
FObjectPtr FPtr(MakeUnresolvedHandle(TestObject));
TObjectPtr<UObject> UnResolvedPtr = *reinterpret_cast<TObjectPtr<UObject>*>(&FPtr);
CHECK(!UnResolvedPtr.IsResolved());
CHECK_FALSE(Ptr == UnResolvedPtr); CHECK(ResolveCount == 0u);
CHECK_FALSE(UnResolvedPtr == Ptr); CHECK(ResolveCount == 0u);
CHECK(Ptr != UnResolvedPtr); CHECK(ResolveCount == 0u);
CHECK(UnResolvedPtr != Ptr); CHECK(ResolveCount == 0u);
//compare unresolved against resolved not equal
CHECK_FALSE(TestNotLazyObject == UnResolvedPtr); CHECK(ResolveCount == 0u);
CHECK_FALSE(UnResolvedPtr == TestNotLazyObject); CHECK(ResolveCount == 0u);
CHECK(TestNotLazyObject != UnResolvedPtr); CHECK(ResolveCount == 0u);
CHECK(UnResolvedPtr != TestNotLazyObject); CHECK(ResolveCount == 0u);
//compare resolved against naked pointer
Ptr = TestObject;
REQUIRE(Ptr.IsResolved());
CHECK(Ptr == TestObject); CHECK(ResolveCount == 0u);
CHECK(TestObject == Ptr); CHECK(ResolveCount == 0u);
CHECK_FALSE(Ptr != TestObject); CHECK(ResolveCount == 0u);
CHECK_FALSE(TestObject != Ptr); CHECK(ResolveCount == 0u);
//compare resolved pointer and unresolved of the same object
CHECK(Ptr == UnResolvedPtr); CHECK(ResolveCount == 0u);
CHECK(UnResolvedPtr == Ptr); CHECK(ResolveCount == 0u);
CHECK_FALSE(Ptr != UnResolvedPtr); CHECK(ResolveCount == 0u);
CHECK_FALSE(UnResolvedPtr != Ptr); CHECK(ResolveCount == 0u);
TestObject = nullptr;
CHECK_FALSE(Ptr == TestObject); CHECK(ResolveCount == 0u);
CHECK_FALSE(TestObject == Ptr); CHECK(ResolveCount == 0u);
CHECK(Ptr != TestObject); CHECK(ResolveCount == 0u);
CHECK(TestObject != Ptr); CHECK(ResolveCount == 0u);
TestObject = static_cast<UObjectPtrTestClass*>(Ptr.Get());
Ptr = nullptr;
CHECK_FALSE(Ptr == TestObject); CHECK(ResolveCount == 0u);
CHECK_FALSE(TestObject == Ptr); CHECK(ResolveCount == 0u);
CHECK(Ptr != TestObject); CHECK(ResolveCount == 0u);
CHECK(TestObject != Ptr); CHECK(ResolveCount == 0u);
support for TObjectPtr handling renames and GetTypeHash(TObjectPtr) no long has to resolve GetTypeHash will create a packaged object ref for hashing if the class is defined as lazy load otherwise raw pointer is hashed a map was added for moved objects and ObjectPathId can be a WeakObjectPtr the map allows UObjects to be mapped back to packed object refs #rb zousar.shaker https://p4-swarm.epicgames.net/reviews/23356491 #preflight 63b5de641c35d1cbdbccecf7 #preflight 63b70406e26e31879b8aa6d3 code cleanup of ObjectHandle made ObjectPathId to private trimmed down the public API as much as possible https://p4-swarm.epicgames.net/reviews/23658342 #rb zousar.shaker #preflight 63c5c8922a6acaf1625fcf25 #[robomerge] FNMain changed ObjectHandleTracking to use Functions instead of delegates as the performance of delegate is poor https://p4-swarm.epicgames.net/reviews/23751084 #rb zousar.shaker #preflight 63c9ae14d45afa2a8fc37d9d changed FObjectPropertyBase::CheckValidObject to not resolve FObjectPtr fields to do validation this is needed for feature work with validation of accessing asset pointers during serialization https://p4-swarm.epicgames.net/reviews/23782822 #rb zousar.shaker #[fyi] francis.hurteau #preflight 63cf06efd83c1837b14e2aeb fix for ObjectHandleTracking initialization. order of global variable initialization was problematic move globals into a struct added a singleton for the struct to do lazy initialization https://p4-swarm.epicgames.net/reviews/23877016 #rb zousar.shaker #preflight 63d308a7f626715201408881 changed `FClassProperty::Identical` to use TObjectPtr to avoid causing unnecessary reads #rb zousar.shaker #preflight 63d804493656ea96dc13d296 changed FObjectProperty::SerializeItem to not cause object handle reads #preflight 63d837e91f0aa8a2897477ee #ushell-cherrypick of 23589497 by joe.pribele #ushell-cherrypick of 23734208 by joe.pribele #ushell-cherrypick of 23781117 by joe.pribele #ushell-cherrypick of 23823212 by joe.pribele #ushell-cherrypick of 23878025 by joe.pribele #ushell-cherrypick of 23912609 by joe.pribele #ushell-cherrypick of 23916733 by joe.pribele [CL 24493715 by joe pribele in ue5-main branch]
2023-03-02 20:28:39 -05:00
}
#endif
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
{
// make one packed ref guarantee something is in the object handle index
FObjectRef TargetRef(FName("/Test/DummyPackage"), FName("ClassPackageName"), FName("ClassName"), UE::CoreUObject::Private::FObjectPathId("DummyObjectName"));
UE::CoreUObject::Private::MakePackedObjectRef(TargetRef);
uint32 ObjectId = ~0u;
UPTRINT PackedId = ObjectId << 1 | 1;
UE::CoreUObject::Private::FPackedObjectRef PackedObjectRef = { PackedId };
TestResolveFailure(PackedObjectRef); // packed ref has a valid package id but invalid object id
TestResolveFailure(UE::CoreUObject::Private::FPackedObjectRef { 0xFFFF'FFFF'FFFF'FFFFull });
TestResolveFailure(UE::CoreUObject::Private::FPackedObjectRef { 0xEFEF'EFEF'EFEF'EFEFull });
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
}
#endif // UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::FObjectHandle::Hash Object Without Index", "[CoreUObject][ObjectHandle]")
{
UObject DummyObjectWithInvalidIndex(EC_StaticConstructor, RF_NoFlags);
CHECK(DummyObjectWithInvalidIndex.GetUniqueID() == -1);
FObjectHandle DummyObjectHandle = UE::CoreUObject::Private::MakeObjectHandle(&DummyObjectWithInvalidIndex);
CHECK(GetTypeHash(DummyObjectHandle) == GetTypeHash(&DummyObjectWithInvalidIndex));
}
#endif