You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb zousar.shaker #p4v-preflight-copy 22194027 [CL 22388016 by joe pribele in ue5-main branch]
314 lines
13 KiB
C++
314 lines
13 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#if WITH_LOW_LEVEL_TESTS
|
|
|
|
#include "UObject/ObjectHandle.h"
|
|
#include "UObject/ObjectPtr.h"
|
|
#include "UObject/MetaData.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:
|
|
|
|
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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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));
|
|
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");
|
|
}
|
|
|
|
// 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<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");
|
|
}
|
|
|
|
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::TObjectPtr::HandleNullGetClass", "[CoreUObject][ObjectHandle][.Engine]")
|
|
{
|
|
TObjectPtr<UObject> Ptr = nullptr;
|
|
TEST_TRUE(TEXT("TObjectPtr.GetClass should return null on a null object"), Ptr.GetClass() == nullptr);
|
|
}
|
|
|
|
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::TObjectPtr::Null Behavior", "[CoreUObject][ObjectHandle]")
|
|
{
|
|
TObjectPtr<UObject> Ptr = nullptr;
|
|
UMetaData* TestObject = nullptr;
|
|
|
|
uint32 ResolveCount = 0;
|
|
auto ResolveDelegate = FObjectHandleReferenceResolvedDelegate::CreateLambda([&ResolveCount](const FObjectRef& SourceRef, UPackage* ObjectPackage, UObject* Object)
|
|
{
|
|
++ResolveCount;
|
|
});
|
|
auto Handle = AddObjectHandleReferenceResolvedCallback(ResolveDelegate);
|
|
|
|
//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"), FObjectPathId("ObjectName") };
|
|
FObjectPtr ObjectPtr(TargetRef);
|
|
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<UMetaData>(TestPackage, TestObjectName, RF_Transient);
|
|
TestObject->AddToRoot();
|
|
|
|
//ObjectPtr = FObjectPtr(MakePackedObjectRef(TestObject));
|
|
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);
|
|
|
|
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<UMetaData*>(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);
|
|
|
|
RemoveObjectHandleReferenceResolvedCallback(Handle);
|
|
}
|
|
|
|
#if UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
|
|
|
|
TEST_CASE_METHOD(FObjectHandleTestBase, "CoreUObject::FObjectHandle::Resolve Malformed Handle", "[CoreUObject][ObjectHandle]")
|
|
{
|
|
TestResolveFailure(FPackedObjectRef { 0xFFFF'FFFF'FFFF'FFFFull });
|
|
TestResolveFailure(FPackedObjectRef { 0xEFEF'EFEF'EFEF'EFEFull });
|
|
}
|
|
#endif // UE_WITH_OBJECT_HANDLE_LATE_RESOLVE
|
|
|
|
#endif |