EM_Task/UnrealEd/Private/Cooker/AsyncIODeleteTest.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

169 lines
8.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "AsyncIODelete.h"
#include "HAL/FileManager.h"
#include "Misc/AutomationTest.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "Misc/ScopeExit.h"
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FAsyncIODeleteTest, "System.Core.Misc.AsyncIODelete", EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::EngineFilter);
bool FAsyncIODeleteTest::RunTest(const FString& Parameters)
{
#if ASYNCIODELETE_ASYNC_ENABLED
UE_LOG(LogCore, Display, TEXT("FAsyncIODeleteTest::RunTest, ASYNCIODELETE is ENABLED"));
#else
UE_LOG(LogCore, Display, TEXT("FAsyncIODeleteTest::RunTest, ASYNCIODELETE is DISABLED"));
#endif
FString TestRoot = FPaths::CreateTempFilename(FPlatformProcess::UserTempDir(), TEXT("AsyncIODelete"), TEXT(""));
IFileManager& FileManager = IFileManager::Get();
const bool bApplyToTreeTrue = true;
ON_SCOPE_EXIT
{
const bool bRequireExists = false;
FileManager.DeleteDirectory(*TestRoot, bRequireExists, bApplyToTreeTrue);
};
FString TempRoot = FPaths::Combine(TestRoot, TEXT("TempRoot"));
FString TempRoot2 = FPaths::Combine(TestRoot, TEXT("TempRoot2"));
FString TempRoot3 = FPaths::Combine(TestRoot, TEXT("TempRoot3"));
FString TempRoot4 = FPaths::Combine(TestRoot, TEXT("TempRoot4"));
FString TestFile1 = FPaths::Combine(TestRoot, TEXT("TestFile1"));
FString TestDir1 = FPaths::Combine(TestRoot, TEXT("TestDir1"));
int32 NumFiles = 0;
int32 NumDirs = 0;
const TCHAR* TestText = TEXT("Test");
auto CreateTestPathsToDelete = [=, &TestFile1, &TestDir1, &FileManager]()
{
FFileHelper::SaveStringToFile(TestText, *TestFile1);
FileManager.MakeDirectory(*TestDir1, bApplyToTreeTrue);
};
IPlatformFile::FDirectoryVisitorFunc CountFilesAndDirs = [&NumFiles, &NumDirs](const TCHAR* VisitFilename, bool VisitIsDir)
{
NumFiles += VisitIsDir ? 1 : 0;
NumDirs += VisitIsDir ? 0 : 1;
return true;
};
auto TestTempRootCountsEqual = [&](const FString& RootDir, int32 ExpectedFileCount, int32 ExpectedDirCount, const TCHAR* TestDescription)
{
NumFiles = 0;
NumDirs = 0;
FileManager.IterateDirectory(*RootDir, CountFilesAndDirs);
TestTrue(TestDescription, NumFiles == ExpectedFileCount && NumDirs == ExpectedDirCount);
};
auto TestRequestedPathsDeleted = [&](const TCHAR* TestDescription)
{
TestTrue(TestDescription, !FileManager.FileExists(*TestFile1) && !FileManager.DirectoryExists(*TestDir1));
};
FileManager.MakeDirectory(*TestRoot);
const bool bVerbose = true;
auto StartSection = [bVerbose](const TCHAR* Section)
{
if (bVerbose)
{
UE_LOG(LogCore, Display, TEXT("%s"), Section);
}
};
const float MaxWaitTime = 5.0f;
auto WaitForAllTasksAndVerify = [this, MaxWaitTime](FAsyncIODelete& InAsyncIODelete)
{
bool WaitResult = InAsyncIODelete.WaitForAllTasks(MaxWaitTime);
TestTrue(TEXT("WaitForAllTasks timed out"), WaitResult);
};
{
StartSection(TEXT("Constructing first FAsyncIODelete"));
FAsyncIODelete AsyncIODelete(TempRoot);
StartSection(TEXT("Waiting for tasks to complete when none have been launched should succeed"));
WaitForAllTasksAndVerify(AsyncIODelete);
StartSection(TEXT("Moving file and directory from the source location should be finished by the time DeleteFile/DeleteDirectory returns"));
CreateTestPathsToDelete();
AsyncIODelete.DeleteFile(TestFile1);
AsyncIODelete.DeleteDirectory(TestDir1);
TestRequestedPathsDeleted(TEXT("AsyncIODelete::Delete should have moved the deleted paths before returning."));
StartSection(TEXT("Deleting the temporary files/directories should be finished before WaitForAllTasks returns"));
WaitForAllTasksAndVerify(AsyncIODelete);
TestTempRootCountsEqual(TempRoot, 0, 0, TEXT("AsyncIODelete should have deleted the moved paths before WaitForAllTasks returned."));
StartSection(TEXT("Two FAsyncIODelete constructed at once should be legal, as long as they have different TempRoots"));
FAsyncIODelete AsyncIODelete2(TempRoot2);
StartSection(TEXT("Use the pause feature to verify that the paths are indeed moved into the TempRoot"));
AsyncIODelete2.SetDeletesPaused(true);
CreateTestPathsToDelete();
AsyncIODelete2.DeleteFile(TestFile1);
AsyncIODelete2.DeleteDirectory(TestDir1);
TestRequestedPathsDeleted(TEXT("AsyncIODelete::Delete should have moved the deleted paths before returning even when paused."));
WaitForAllTasksAndVerify(AsyncIODelete2);
#if ASYNCIODELETE_ASYNC_ENABLED
TestTempRootCountsEqual(TempRoot2, 1, 1, TEXT("AsyncIODelete should not have deleted the moved paths because it is paused."));
#endif
AsyncIODelete2.SetDeletesPaused(false);
WaitForAllTasksAndVerify(AsyncIODelete2);
#if ASYNCIODELETE_ASYNC_ENABLED
TestTempRootCountsEqual(TempRoot2, 0, 0, TEXT("AsyncIODelete should have deleted the moved paths after unpausing."));
#endif
StartSection(TEXT("Verify Teardown() deletes the TempRoot and Setup() creates it"));
AsyncIODelete2.Teardown();
#if ASYNCIODELETE_ASYNC_ENABLED
TestTrue(TEXT("AsyncIODelete::Teardown should have deleted its TempRoot."), !FileManager.DirectoryExists(*TempRoot2));
#endif
AsyncIODelete2.Setup();
#if ASYNCIODELETE_ASYNC_ENABLED
TestTrue(TEXT("AsyncIODelete::Setup should have created its TempRoot."), FileManager.DirectoryExists(*TempRoot2));
#endif
StartSection(TEXT("Manual setup works as long as you call SetTempRoot before Setup"));
FAsyncIODelete AsyncIODelete3;
AsyncIODelete3.SetTempRoot(TempRoot3);
AsyncIODelete3.Setup();
#if ASYNCIODELETE_ASYNC_ENABLED
TestTrue(TEXT("Setup should have created the TempRoot."), FileManager.DirectoryExists(*TempRoot3));
#endif
StartSection(TEXT("Check that even after Setup, waiting for tasks to complete when none have been launched should succeed"));
WaitForAllTasksAndVerify(AsyncIODelete);
StartSection(TEXT("Changing TempRoot and then deleting a file works"));
CreateTestPathsToDelete();
AsyncIODelete3.DeleteFile(TestFile1);
AsyncIODelete3.DeleteDirectory(TestDir1);
AsyncIODelete3.SetTempRoot(TempRoot4);
#if ASYNCIODELETE_ASYNC_ENABLED
TestTrue(TEXT("SetTempRoot should have deleted the old TempRoot."), !FileManager.DirectoryExists(*TempRoot3));
#endif
CreateTestPathsToDelete();
AsyncIODelete3.DeleteFile(TestFile1);
AsyncIODelete3.DeleteDirectory(TestDir1);
TestRequestedPathsDeleted(TEXT("AsyncIODelete::Delete should have worked after changing the TempRoot."));
#if ASYNCIODELETE_ASYNC_ENABLED
TestTrue(TEXT("Delete should have created the new TempRoot after SetTempRoot."), FileManager.DirectoryExists(*TempRoot4));
#endif
WaitForAllTasksAndVerify(AsyncIODelete3);
#if ASYNCIODELETE_ASYNC_ENABLED
TestTempRootCountsEqual(TempRoot4, 0, 0, TEXT("AsyncIODelete::Delete should have created the tasks to delete moved files after changing the TempRoot."));
#endif
StartSection(TEXT("Attempting to delete a parent directory of the temproot, the temproot itself, or a child inside of it fails"));
FString SubDirInTempRoot4 = FPaths::Combine(TempRoot4, TEXT("SubDir"));
FileManager.MakeDirectory(*SubDirInTempRoot4, bApplyToTreeTrue); // Note it's illegal to add files into TempRoot, but we're not currently checking for it and we're not colliding with the DeleteN paths AsyncIODelete uses, so this breaking of the rule will not cause problems
TestFalse(TEXT("AsyncIODelete should refuse to delete a parent of its TempRoot."), AsyncIODelete3.DeleteDirectory(TestRoot));
TestFalse(TEXT("AsyncIODelete should refuse to delete its TempRoot."), AsyncIODelete3.DeleteDirectory(TempRoot4));
TestFalse(TEXT("AsyncIODelete should refuse to delete a child of its TempRoot."), AsyncIODelete3.DeleteDirectory(SubDirInTempRoot4));
}
#if ASYNCIODELETE_ASYNC_ENABLED
TestTrue(TEXT("AsyncIODelete destructor should have deleted its TempRoot."),
!FileManager.DirectoryExists(*TempRoot) && !FileManager.DirectoryExists(*TempRoot2) && !FileManager.DirectoryExists(*TempRoot3) && !FileManager.DirectoryExists(*TempRoot4));
#endif
return true;
}