// Copyright Epic Games, Inc. All Rights Reserved. #include "HttpDerivedDataBackend.h" #include "Async/ParallelFor.h" #include "DerivedDataBackendInterface.h" #include "Misc/AutomationTest.h" #include "Misc/SecureHash.h" // Test is targeted at HttpDerivedDataBackend but with some backend test interface it could be generalized // to function against all backends. #if WITH_DEV_AUTOMATION_TESTS && WITH_HTTP_DDC_BACKEND #define TEST_NAME_ROOT TEXT("System.DerivedDataCache.HttpDerivedDataBackend") #define IMPLEMENT_HTTPDERIVEDDATA_AUTOMATION_TEST( TClass, PrettyName, TFlags ) \ IMPLEMENT_CUSTOM_COMPLEX_AUTOMATION_TEST(TClass, FHttpDerivedDataTestBase, TEST_NAME_ROOT PrettyName, TFlags) \ void TClass::GetTests(TArray& OutBeautifiedNames, TArray & OutTestCommands) const \ { \ if (CheckPrequisites()) \ { \ OutBeautifiedNames.Add(TEST_NAME_ROOT PrettyName); \ OutTestCommands.Add(FString()); \ } \ } namespace HttpDerivedDataBackendTest { class FHttpDerivedDataTestBase : public FAutomationTestBase { public: FHttpDerivedDataTestBase(const FString& InName, const bool bInComplexTask) : FAutomationTestBase(InName, bInComplexTask) { } bool CheckPrequisites() const { if (UE::DerivedData::Backends::FHttpDerivedDataBackend* Backend = GetTestBackend()) { if (Backend->IsUsable()) { return true; } } return false; } protected: UE::DerivedData::Backends::FHttpDerivedDataBackend* GetTestBackend() const { static UE::DerivedData::Backends::FHttpDerivedDataBackend* CachedBackend = FetchTestBackend_Internal(); return CachedBackend; } private: UE::DerivedData::Backends::FHttpDerivedDataBackend* FetchTestBackend_Internal() const { return UE::DerivedData::Backends::FHttpDerivedDataBackend::GetAny(); } }; IMPLEMENT_HTTPDERIVEDDATA_AUTOMATION_TEST(FConcurrentCachedDataProbablyExistsBatch, TEXT(".FHeavyConcurrentCachedDataProbablyExistsBatch"), EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter) bool FConcurrentCachedDataProbablyExistsBatch::RunTest(const FString& Parameters) { UE::DerivedData::Backends::FHttpDerivedDataBackend* TestBackend = GetTestBackend(); const int32 ParallelTasks = 32; const uint32 Iterations = 20; const uint32 KeysInBatch = 4; TArray Keys; TArray KeyContents; KeyContents.Add(42); FSHA1 HashState; HashState.Update(KeyContents.GetData(), KeyContents.Num()); HashState.Final(); uint8 Hash[FSHA1::DigestSize]; HashState.GetHash(Hash); const FString HashString = BytesToHex(Hash, FSHA1::DigestSize); for (uint32 KeyIndex = 0; KeyIndex < KeysInBatch; ++KeyIndex) { FString NewKey = FString::Printf(TEXT("__AutoTest_Dummy_%u__%s"), KeyIndex, *HashString); Keys.Add(NewKey); TestBackend->PutCachedData(*NewKey, KeyContents, false); } std::atomic MismatchedResults = 0; ParallelFor(ParallelTasks, [&](int32 TaskIndex) { for (uint32 Iteration = 0; Iteration < Iterations; ++Iteration) { TConstArrayView BatchView = MakeArrayView(Keys.GetData(), KeysInBatch); TBitArray<> Result = TestBackend->CachedDataProbablyExistsBatch(BatchView); if (Result.CountSetBits() != BatchView.Num()) { MismatchedResults.fetch_add(BatchView.Num() - Result.CountSetBits(), std::memory_order_relaxed); } } } ); TestEqual(TEXT("Concurrent calls to CachedDataProbablyExistsBatch for a batch of keys that were put are not reliably found"), MismatchedResults, 0); return true; } } #endif // WITH_DEV_AUTOMATION_TESTS