Files
UnrealEngine/Engine/Source/Developer/FunctionalTesting/Private/TraceQueryTestResults.cpp
2025-05-18 13:04:45 +08:00

234 lines
9.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "TraceQueryTestResults.h"
#include "FunctionalTest.h"
#include "Components/PrimitiveComponent.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(TraceQueryTestResults)
FString FTraceQueryTestNames::ToString() const
{
return FString::Printf(TEXT("Component:%s, Actor:%s, PhysicalMaterial:%s"), *ComponentName.ToString(), *ActorName.ToString(), *PhysicalMaterialName.ToString());
}
FString FTraceQueryTestResultsInnerMost::ToString() const
{
FString OutSingle = FString::Printf(TEXT("----SingleResult:%d %s {%s}"), bSingleResult, *SingleNames.ToString(), *SingleHit.ToString());
FString OutMulti = FString::Printf(TEXT("\n----MultiResult:%d"), bMultiResult);
for(int32 Counter = 0; Counter < MultiHits.Num(); ++Counter)
{
OutMulti.Append(FString::Printf(TEXT("\n----[%d] %s {%s}"), Counter, *MultiNames[Counter].ToString(), *MultiHits[Counter].ToString()));
}
return OutSingle.Append(OutMulti);
}
FString FTraceQueryTestResultsInner::ToString(const FTraceChannelTestBatchOptions& BatchOptions) const
{
FString LineResultsStr = BatchOptions.bLineTrace ? FString::Printf(TEXT("--LineResults:\n%s\n\n"), *LineResults.ToString()) : "";
FString SphereResultsStr = BatchOptions.bSphereTrace ? FString::Printf(TEXT("--SphereResults:\n%s\n\n"), *SphereResults.ToString()) : "";
FString CapsuleResultsStr = BatchOptions.bCapsuleTrace ? FString::Printf(TEXT("--CapsuleResults:\n%s\n\n"), *CapsuleResults.ToString()) : "";
FString BoxResultsStr = BatchOptions.bBoxTrace ? FString::Printf(TEXT("--BoxResults:\n%s\n\n"), *BoxResults.ToString()) : "";
return FString::Printf(TEXT("%s%s%s%s"), *LineResultsStr, *SphereResultsStr, *CapsuleResultsStr, *BoxResultsStr);
}
FString UTraceQueryTestResults::ToString()
{
CaptureNames();
FString ChannelResultsStr = BatchOptions.bChannelTrace ? FString::Printf(TEXT("ChannelResults:\n%s\n\n"), *ChannelResults.ToString(BatchOptions)) : "";
FString ObjectResultsStr = BatchOptions.bObjectsTrace ? FString::Printf(TEXT("ObjectResults:\n%s\n\n"), *ObjectResults.ToString(BatchOptions)) : "";
FString ProfileResultsStr = BatchOptions.bProfileTrace ? FString::Printf(TEXT("ProfileResults:\n%s\n\n"), *ProfileResults.ToString(BatchOptions)) : "";
return FString::Printf(TEXT("%s%s%s"), *ChannelResultsStr, *ObjectResultsStr, *ProfileResultsStr);
}
void CaptureNameHelper(FTraceQueryTestNames& Names, FHitResult& HitResult)
{
UPrimitiveComponent* HitComp = HitResult.GetComponent();
UPhysicalMaterial* PhysMat = HitResult.PhysMaterial.Get();
Names.ComponentName = HitComp ? HitComp->GetFName() : NAME_None;
Names.ActorName = HitResult.HitObjectHandle.IsValid() ? HitResult.HitObjectHandle.GetFName() : NAME_None;
Names.PhysicalMaterialName = PhysMat ? PhysMat->GetFName() : NAME_None;
}
void FTraceQueryTestResultsInnerMost::CaptureNames()
{
CaptureNameHelper(SingleNames, SingleHit);
MultiNames.Reset();
MultiNames.AddZeroed(MultiHits.Num());
for(int32 HitIdx = 0; HitIdx < MultiHits.Num(); ++HitIdx)
{
CaptureNameHelper(MultiNames[HitIdx], MultiHits[HitIdx]);
}
}
void FTraceQueryTestResultsInner::CaptureNames()
{
LineResults.CaptureNames();
SphereResults.CaptureNames();
CapsuleResults.CaptureNames();
BoxResults.CaptureNames();
}
void UTraceQueryTestResults::CaptureNames()
{
ChannelResults.CaptureNames();
ObjectResults.CaptureNames();
ProfileResults.CaptureNames();
}
FString FTraceChannelTestBatchOptions::ToString() const
{
return FString::Printf(TEXT("bLineTrace:%d, bSphereTrace:%d, bCapsuleTrace:%d, bBoxTrace:%d, bChannelTrace:%d, bObjectsTrace:%d, bProfileTrace:%d"), bLineTrace, bSphereTrace, bCapsuleTrace, bBoxTrace, bChannelTrace, bObjectsTrace, bProfileTrace);
}
#define TEST_IMPL(X, Type) FunctionalTest.AssertEqual_##Type(Actual.X, Expected.X, FString::Printf(TEXT("%s:"#X), *What), ContextObject)
#define TEST_IMPL_TOLERANCE(X, Type) FunctionalTest.AssertEqual_##Type(Actual.X, Expected.X, FString::Printf(TEXT("%s:"#X), *What), KINDA_SMALL_NUMBER, ContextObject)
#define TEST_BOOL(X) TEST_IMPL(X, Bool)
#define TEST_INT(X) TEST_IMPL(X, Int)
#define TEST_NAME(X) TEST_IMPL(X, Name)
#define TEST_FLOAT(X) TEST_IMPL_TOLERANCE(X, Float)
#define TEST_VECTOR(X) TEST_IMPL_TOLERANCE(X, Vector)
bool HelperAssertNamesEqual(const FTraceQueryTestNames& Actual, const FTraceQueryTestNames& Expected, const FString& What, const UObject* ContextObject, AFunctionalTest& FunctionalTest)
{
bool bResults[] = {
TEST_NAME(ComponentName),
TEST_NAME(ActorName),
TEST_NAME(PhysicalMaterialName)
};
for (bool bSuccess : bResults)
{
if (!bSuccess)
{
return false;
}
}
return true;
}
bool HelperAssertFHitResultEqual(const FHitResult& Actual, const FHitResult& Expected, const FString& What, const UObject* ContextObject, AFunctionalTest& FunctionalTest)
{
bool bResults[] = {
TEST_BOOL(bBlockingHit),
TEST_BOOL(bStartPenetrating),
TEST_FLOAT(Time),
TEST_FLOAT(Distance),
TEST_VECTOR(Location),
TEST_VECTOR(ImpactPoint),
TEST_VECTOR(Normal),
TEST_VECTOR(ImpactNormal),
TEST_VECTOR(TraceStart),
TEST_VECTOR(TraceEnd),
TEST_FLOAT(PenetrationDepth),
TEST_INT(Item),
TEST_NAME(BoneName),
TEST_INT(FaceIndex)
};
for(bool bSuccess : bResults)
{
if(!bSuccess)
{
return false;
}
}
return true;
}
#undef TEST_IMPL
#undef TEST_IMPL_TOLERANCE
#undef TEST_BOOL
#undef TEST_INT
#undef TEST_FLOAT
#undef TEST_VECTOR
bool FTraceQueryTestResultsInnerMost::AssertEqual(const FTraceQueryTestResultsInnerMost& Expected, const FString& What, const UObject* ContextObject, AFunctionalTest& FunctionalTest) const
{
bool bSuccess = true;
if(FunctionalTest.AssertEqual_Bool(bSingleResult, Expected.bSingleResult, FString::Printf(TEXT("%s:bSingleResult"), *What), ContextObject))
{
bSuccess = HelperAssertFHitResultEqual(SingleHit, Expected.SingleHit, FString::Printf(TEXT("%s_SingleHit"), *What), ContextObject, FunctionalTest);
bSuccess &= HelperAssertNamesEqual(SingleNames, Expected.SingleNames, FString::Printf(TEXT("%s_SingleHit"), *What), ContextObject, FunctionalTest);
}
else
{
bSuccess = false;
}
if (FunctionalTest.AssertEqual_Bool(bMultiResult, Expected.bMultiResult, FString::Printf(TEXT("%s:bMultiResult"), *What), ContextObject))
{
if(FunctionalTest.AssertValue_Int(MultiHits.Num(), EComparisonMethod::Equal_To, Expected.MultiHits.Num(), FString::Printf(TEXT("%s:MultiHitsNum"), *What), ContextObject))
{
for (int32 HitIdx = 0; HitIdx < MultiHits.Num(); ++HitIdx)
{
bSuccess &= HelperAssertFHitResultEqual(MultiHits[HitIdx], Expected.MultiHits[HitIdx], FString::Printf(TEXT("%s_MultiHit[%d]"), *What, HitIdx), ContextObject, FunctionalTest);
bSuccess &= HelperAssertNamesEqual(MultiNames[HitIdx], Expected.MultiNames[HitIdx], FString::Printf(TEXT("%s_MultiHitHit[%d]"), *What, HitIdx), ContextObject, FunctionalTest);
}
}
else
{
bSuccess = false;
}
}
else
{
bSuccess = false;
}
return bSuccess;
}
bool FTraceQueryTestResultsInner::AssertEqual(const FTraceQueryTestResultsInner& Expected, const FString& What, const UObject* ContextObject, AFunctionalTest& FunctionalTest) const
{
const bool bLineResults = LineResults.AssertEqual(Expected.LineResults, FString::Printf(TEXT("%s_Line"), *What), ContextObject, FunctionalTest);
const bool bSphereResults = SphereResults.AssertEqual(Expected.SphereResults, FString::Printf(TEXT("%s_Sphere"), *What), ContextObject, FunctionalTest);
const bool bCapsuleResults = CapsuleResults.AssertEqual(Expected.CapsuleResults, FString::Printf(TEXT("%s_Capsule"), *What), ContextObject, FunctionalTest);
const bool bBoxResults = BoxResults.AssertEqual(Expected.BoxResults, FString::Printf(TEXT("%s_Box"), *What), ContextObject, FunctionalTest);
return bLineResults && bSphereResults && bCapsuleResults && bBoxResults;
}
bool UTraceQueryTestResults::AssertEqual(const UTraceQueryTestResults* Expected, const FString& What, const UObject* ContextObject, AFunctionalTest& FunctionalTest) const
{
if(Expected == nullptr)
{
FunctionalTest.LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Expected '%s' 'Expected != nullptr' for context '%s'"), *What, ContextObject ? *ContextObject->GetName() : TEXT("")));
return false;
}
else if(Expected->BatchOptions != BatchOptions)
{
FunctionalTest.LogStep(ELogVerbosity::Error, FString::Printf(TEXT("Expected '%s' to be {%s} but it was {%s} for context '%s'"), *What, *Expected->BatchOptions.ToString(), *BatchOptions.ToString(), ContextObject ? *ContextObject->GetName() : TEXT("")));
return false;
}
else
{
//Purposely do all 3 so we get errors for as much as possible
const bool bChannelResults = ChannelResults.AssertEqual(Expected->ChannelResults, FString::Printf(TEXT("%s_Channel"), *What), ContextObject, FunctionalTest);
const bool bObjectResults = ObjectResults.AssertEqual(Expected->ObjectResults, FString::Printf(TEXT("%s_Object"), *What), ContextObject, FunctionalTest);
const bool bProfileResults = ProfileResults.AssertEqual(Expected->ProfileResults, FString::Printf(TEXT("%s_Profile"), *What), ContextObject, FunctionalTest);
if( bChannelResults && bObjectResults && bProfileResults)
{
FunctionalTest.LogStep(ELogVerbosity::Log, FString::Printf(TEXT("TraceQueryTestResults assertion passed (%s)"), *What));
return true;
}
else
{
FunctionalTest.LogStep(ELogVerbosity::Error, FString::Printf(TEXT("'%s' comparison failed for context '%s'"), *What, ContextObject ? *ContextObject->GetName() : TEXT("")));
return false;
}
}
}