Files
UnrealEngineUWP/Engine/Source/Developer/FunctionalTesting/Private/FunctionalTestingModule.cpp
matt peters 64658cf6ae GetAssetRegistryTags deprecation: Make the old GetAssetRegistryTags and related functions deprecated.
Upgrade the overrides and callsites in all of Epic's code.
Every class that can be subclassed by licensees that has its override change needs to keep the old version of the function as deprecated. Otherwise subclasses that call Super::GetAssetRegistryTags will have a compile error instead of a deprecation warning.
Several classes had their own extension method for GetAssetRegistryTags being passed on to user data classes. Updated all of those to take FAssetRegistryTagsContext just like GetAssetRegistryTags does.
#rnx
#rb Francis.Hurteau

[CL 30224166 by matt peters in ue5-main branch]
2023-12-08 21:45:21 -05:00

331 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "FunctionalTestingModule.h"
#include "AssetRegistry/AssetData.h"
#include "FunctionalTestingManager.h"
#include "Misc/CoreMisc.h"
#include "Misc/ConfigCacheIni.h"
#include "Engine/World.h"
#include "Engine/Engine.h"
#include "EngineUtils.h"
#include "FunctionalTest.h"
#include "EngineGlobals.h"
#include "AssetRegistry/ARFilter.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Misc/CommandLine.h"
#include "IAutomationControllerModule.h"
#include "UObject/AssetRegistryTagsContext.h"
#define LOCTEXT_NAMESPACE "FunctionalTesting"
DEFINE_LOG_CATEGORY(LogFunctionalTest);
class FFunctionalTestingModule : public IFunctionalTestingModule
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
virtual void RunAllTestsOnMap(bool bClearLog, bool bRunLooped) override;
virtual void RunTestOnMap(const FString& TestName, bool bClearLog, bool bRunLooped) override;
virtual void MarkPendingActivation() override;
virtual bool IsActivationPending() const override;
virtual bool IsRunning() const override;
virtual bool IsFinished() const override;
virtual void SetManager(class UFunctionalTestingManager* NewManager) override;
virtual class UFunctionalTestingManager* GetCurrentManager();
virtual void SetLooping(const bool bLoop) override;
virtual void GetMapTests(bool bEditorOnlyTests, TArray<FString>& OutBeautifiedNames, TArray<FString>& OutTestCommands, TArray<FString>& OutTestMapAssets) const override;
private:
UWorld* GetTestWorld();
void OnGetAssetTagsForWorld(const UWorld* World, FAssetRegistryTagsContext Context);
TWeakObjectPtr<class UFunctionalTestingManager> TestManager;
bool bPendingActivation;
};
void FFunctionalTestingModule::StartupModule()
{
bPendingActivation = false;
#if WITH_EDITOR
FWorldDelegates::GetAssetTagsWithContext.AddRaw(this, &FFunctionalTestingModule::OnGetAssetTagsForWorld);
#endif
}
void FFunctionalTestingModule::ShutdownModule()
{
#if WITH_EDITOR
FWorldDelegates::GetAssetTagsWithContext.RemoveAll(this);
#endif
}
void FFunctionalTestingModule::OnGetAssetTagsForWorld(const UWorld* World, FAssetRegistryTagsContext Context)
{
#if WITH_EDITOR
TArray<FString> TestNamesRuntime;
TArray<FString> TestNamesEditor;
for (TActorIterator<AFunctionalTest> ActorItr(const_cast<UWorld*>(World), AFunctionalTest::StaticClass(), EActorIteratorFlags::AllActors); ActorItr; ++ActorItr)
{
AFunctionalTest* FunctionalTest = *ActorItr;
if (!FunctionalTest->IsPackageExternal())
{
// Only include enabled tests in the list of functional tests to run.
if (FunctionalTest->IsEnabled())
{
TArray<FString>& TestNames = IsEditorOnlyObject(FunctionalTest) ? TestNamesEditor : TestNamesRuntime;
TestNames.Add(FString::Printf(TEXT("%s|%s;"), *FunctionalTest->GetActorLabel(), *FunctionalTest->GetName()));
}
}
}
auto AddTestNames = [&Context](const TCHAR* TagName, TArray<FString>& TestNames)
{
if (!TestNames.IsEmpty())
{
TestNames.Sort();
FString TestNamesStr = FString::Join(TestNames, TEXT(""));
Context.AddTag(UObject::FAssetRegistryTag(TagName, MoveTemp(TestNamesStr), UObject::FAssetRegistryTag::TT_Hidden));
}
};
AddTestNames(TEXT("TestNames"), TestNamesRuntime);
AddTestNames(TEXT("TestNamesEditor"), TestNamesEditor);
#endif
}
void FFunctionalTestingModule::GetMapTests(bool bEditorOnlyTests, TArray<FString>& OutBeautifiedNames, TArray<FString>& OutTestCommands, TArray<FString>& OutTestMapAssets) const
{
IAssetRegistry& AssetRegistry = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
if (!AssetRegistry.IsLoadingAssets())
{
#if WITH_EDITOR
static bool bDidScan = false;
if (!GIsEditor && !bDidScan)
{
// For editor build -game, we need to do a full scan
AssetRegistry.SearchAllAssets(true);
bDidScan = true;
}
#endif
TArray<FAssetData> MapList;
FARFilter Filter;
Filter.ClassPaths.Add(UWorld::StaticClass()->GetClassPathName());
Filter.bRecursiveClasses = true;
Filter.bIncludeOnlyOnDiskAssets = true;
if (AssetRegistry.GetAssets(Filter, /*out*/ MapList))
{
IAutomationControllerModule& AutomationControllerModule = FModuleManager::LoadModuleChecked<IAutomationControllerModule>(TEXT("AutomationController"));
IAutomationControllerManagerPtr AutomationController = AutomationControllerModule.GetAutomationController();
bool IsDeveloperDirectoryIncluded = AutomationController->IsDeveloperDirectoryIncluded();
for (const FAssetData& MapAsset : MapList)
{
FString MapAssetPath = MapAsset.GetObjectPathString();
FString MapPackageName = MapAsset.PackageName.ToString();
if (!IsDeveloperDirectoryIncluded && MapPackageName.Find(TEXT("/Game/Developers")) == 0) continue;
FString PartialSuiteName = MapPackageName.RightChop(1).Replace(TEXT("/"), TEXT(".")); // use dot syntax
if (MapPackageName.StartsWith(TEXT("/Game/")))
{
PartialSuiteName = PartialSuiteName.RightChop(5); // Remove "/Game/" from the name
}
FString AllTestNames;
FAssetDataTagMapSharedView::FFindTagResult MapTestNames = MapAsset.TagsAndValues.FindTag(bEditorOnlyTests ? TEXT("TestNamesEditor") : TEXT("TestNames"));
if (MapTestNames.IsSet())
{
AllTestNames = MapTestNames.GetValue();
}
#if WITH_EDITOR
// Also append external functional test actors
if (ULevel::GetIsLevelUsingExternalActorsFromAsset(MapAsset))
{
const FString LevelExternalActorsPath = ULevel::GetExternalActorsPath(MapPackageName);
// Do a synchronous scan of the level external actors path.
AssetRegistry.ScanPathsSynchronous({ LevelExternalActorsPath }, /*bForceRescan*/false, /*bIgnoreDenyListScanFilters*/false);
FARFilter ActorsFilter;
ActorsFilter.bRecursivePaths = true;
ActorsFilter.bIncludeOnlyOnDiskAssets = true;
ActorsFilter.PackagePaths.Add(*LevelExternalActorsPath);
TArray<FAssetData> ActorList;
AssetRegistry.GetAssets(ActorsFilter, ActorList);
for (const FAssetData& ActorAsset : ActorList)
{
FAssetDataTagMapSharedView::FFindTagResult ActorTestName = ActorAsset.TagsAndValues.FindTag(bEditorOnlyTests ? TEXT("TestNameEditor") : TEXT("TestName"));
if (ActorTestName.IsSet())
{
if (!AllTestNames.IsEmpty())
{
AllTestNames += TEXT(";");
}
AllTestNames += ActorTestName.GetValue();
}
}
}
#endif
if (!AllTestNames.IsEmpty())
{
TArray<FString> MapTests;
AllTestNames.ParseIntoArray(MapTests, TEXT(";"), true);
for (const FString& MapTest : MapTests)
{
FString BeautifulTestName;
FString RealTestName;
if (MapTest.Split(TEXT("|"), &BeautifulTestName, &RealTestName))
{
OutBeautifiedNames.Add(PartialSuiteName + TEXT(".") + *BeautifulTestName);
OutTestCommands.Add(MapAssetPath + TEXT(";") + MapPackageName + TEXT(";") + *RealTestName);
OutTestMapAssets.AddUnique(MapAssetPath);
}
}
}
else if (!bEditorOnlyTests && MapAsset.AssetName.ToString().Find(TEXT("FTEST_")) == 0)
{
OutBeautifiedNames.Add(MapAsset.AssetName.ToString());
OutTestCommands.Add(MapAssetPath + TEXT(";") + MapPackageName);
OutTestMapAssets.AddUnique(MapAssetPath);
}
}
}
}
}
void FFunctionalTestingModule::SetManager(class UFunctionalTestingManager* NewManager)
{
TestManager = NewManager;
}
UFunctionalTestingManager* FFunctionalTestingModule::GetCurrentManager()
{
return TestManager.Get();
}
bool FFunctionalTestingModule::IsRunning() const
{
return TestManager.IsValid() && TestManager->IsRunning();
}
bool FFunctionalTestingModule::IsFinished() const
{
return (!TestManager.IsValid() || TestManager->IsFinished());
}
void FFunctionalTestingModule::MarkPendingActivation()
{
bPendingActivation = true;
}
bool FFunctionalTestingModule::IsActivationPending() const
{
return bPendingActivation;
}
void FFunctionalTestingModule::SetLooping(const bool bLoop)
{
if (TestManager.IsValid())
{
TestManager->SetLooped(bLoop);
}
}
UWorld* FFunctionalTestingModule::GetTestWorld()
{
#if WITH_EDITOR
const TIndirectArray<FWorldContext>& WorldContexts = GEngine->GetWorldContexts();
for (const FWorldContext& Context : WorldContexts)
{
if (Context.World() != nullptr)
{
if (Context.WorldType == EWorldType::PIE /*&& Context.PIEInstance == 0*/)
{
return Context.World();
}
if (Context.WorldType == EWorldType::Game)
{
return Context.World();
}
}
}
#endif
UWorld* TestWorld = GWorld;
if (GIsEditor)
{
UE_LOG(LogFunctionalTest, Warning, TEXT("Functional Test using GWorld. Not correct for PIE"));
}
return TestWorld;
}
void FFunctionalTestingModule::RunAllTestsOnMap(bool bClearLog, bool bRunLooped)
{
if (UWorld* TestWorld = GetTestWorld())
{
bPendingActivation = false;
if (UFunctionalTestingManager::RunAllFunctionalTests(TestWorld, bClearLog, bRunLooped) == false)
{
UE_LOG(LogFunctionalTest, Error, TEXT("No functional testing script on map."));
}
}
}
void FFunctionalTestingModule::RunTestOnMap(const FString& TestName, bool bClearLog, bool bRunLooped)
{
if (UWorld* TestWorld = GetTestWorld())
{
bPendingActivation = false;
if (UFunctionalTestingManager::RunAllFunctionalTests(TestWorld, bClearLog, bRunLooped, TestName) == false)
{
UE_LOG(LogFunctionalTest, Error, TEXT("No functional testing script on map."));
}
}
}
//////////////////////////////////////////////////////////////////////////
// Exec
//////////////////////////////////////////////////////////////////////////
static bool FuncTestExec(UWorld* InWorld, const TCHAR* Command, FOutputDevice& Ar)
{
if (FParse::Command(&Command, TEXT("ftest")))
{
if (FParse::Command(&Command, TEXT("start")))
{
const bool bLooped = FParse::Command(&Command, TEXT("loop"));
//instead of allowing straight use of the functional test framework, this should go through the automation framework and kick off one of the Editor/Client functional tests
IFunctionalTestingModule& Module = IFunctionalTestingModule::Get();
if (!Module.IsRunning() && !Module.IsActivationPending())
{
Module.RunAllTestsOnMap(/*bClearLog=*/true, bLooped);
}
}
return true;
}
return false;
}
FStaticSelfRegisteringExec FuncTestExecRegistration(FuncTestExec);
IMPLEMENT_MODULE( FFunctionalTestingModule, FunctionalTesting );
#undef LOCTEXT_NAMESPACE