You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb #lockdown Nick.Penwarden ============================ MAJOR FEATURES & CHANGES ============================ Change 3776794 by Chris.Bunner Fixed inverted check. Change 3778396 by Chris.Bunner By default functional screenshot tests now enable camera cut and a fixed tonemap/exposure. Existing tests in PostProcessing have had the FixedTonemapping flag disabled. Updated all existing screenshots against the new fixed gamma ground truth. Change 3778592 by Chris.Bunner Updating PostProcess screenshots that managed to collide mid P4 add. Change 3778793 by Chris.Bunner Override the secondary screen percentage (used for high DPI) when taking screenshots. This is necessary for consistent results between machines. Change 3781715 by Chris.Bunner Updating Windows screenshots. Change 3781717 by Chris.Bunner Fixes for eye adaptation and tonemapping override consistency between test types and run modes. Change 3783199 by Chris.Bunner Added separate add/replace screenshot buttons to know if we're working with a matching platform tier or a fallback. Change 3783228 by Chris.Bunner When incoming screenshots are different sizes create a delta of the minimum shared dimensions but still force a failure. The UI is more consistent showing any delta, even if it's almost pure white. Change 3783712 by Chris.Bunner Rebuilt translucency lighting test map and updated screenshot results. Change 3784010 by Chris.Bunner Adding Mac-specific PostProcessing screenshots. Change 3787456 by Chris.Bunner Improving name matching consistency of test blacklist entries. Change 3787522 by Chris.Bunner Updating Mac-specific screenshots. Change 3787583 by Chris.Bunner Updating Mac-Specific screenshots. Change 3787832 by Chris.Bunner Fixing-up NaNs in two saved level's HLOD world settings. Change 3789147 by Chris.Bunner Updating Sequencer sub-levels which still had NaNs in World Settings. Change 3791454 by Chris.Bunner Deleted ancient screenshot re-introduced by Fortnite merge. Change 3781713 by Chris.Bunner Updating platform unique screenshots - Tessellation, fixed vs variable screenshot size, Niagara simulation. Change 3776756 by Chris.Bunner Initial pass at hierarchical screenshot testing. Deleted existing platforms automated test screenshots. Change 3784051 by Chris.Bunner Updated CableActor screenshots for new capture defaults. Change 3787092 by Chris.Bunner Added blacklist as "AutomationTestBlacklist" in Engine.ini. Formatting fix-up in existing automation test config. Removed workaround cvar for physicalized animation tests failing on platforms. [CL 3792526 by Chris Bunner in Main branch]
350 lines
10 KiB
C++
350 lines
10 KiB
C++
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "FunctionalTestingModule.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 "ARFilter.h"
|
|
#include "AssetRegistryModule.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, TArray<UObject::FAssetRegistryTag>& OutTags);
|
|
|
|
void BuildTestBlacklistFromConfig();
|
|
bool IsBlacklisted(const FString& MapName, const FString& TestName) const;
|
|
|
|
TArray<FString> TestBlacklist;
|
|
TWeakObjectPtr<class UFunctionalTestingManager> TestManager;
|
|
bool bPendingActivation;
|
|
};
|
|
|
|
void FFunctionalTestingModule::StartupModule()
|
|
{
|
|
bPendingActivation = false;
|
|
#if WITH_EDITOR
|
|
FWorldDelegates::GetAssetTags.AddRaw(this, &FFunctionalTestingModule::OnGetAssetTagsForWorld);
|
|
#endif
|
|
BuildTestBlacklistFromConfig();
|
|
}
|
|
|
|
void FFunctionalTestingModule::ShutdownModule()
|
|
{
|
|
#if WITH_EDITOR
|
|
FWorldDelegates::GetAssetTags.RemoveAll(this);
|
|
#endif
|
|
}
|
|
|
|
void FFunctionalTestingModule::OnGetAssetTagsForWorld(const UWorld* World, TArray<UObject::FAssetRegistryTag>& OutTags)
|
|
{
|
|
#if WITH_EDITOR
|
|
int32 Tests = 0;
|
|
FString TestNames, TestNamesEditor;
|
|
for (TActorIterator<AFunctionalTest> ActorItr(const_cast<UWorld*>(World), AFunctionalTest::StaticClass(), EActorIteratorFlags::AllActors); ActorItr; ++ActorItr)
|
|
{
|
|
AFunctionalTest* FunctionalTest = *ActorItr;
|
|
|
|
// Only include enabled tests in the list of functional tests to run.
|
|
if (FunctionalTest->IsEnabled())
|
|
{
|
|
bool bIsEditorOnly = IsEditorOnlyObject(FunctionalTest);
|
|
|
|
// Check if this class is editor only
|
|
FString& NamesAppend = bIsEditorOnly ? TestNamesEditor : TestNames;
|
|
|
|
Tests++;
|
|
NamesAppend.Append(FunctionalTest->GetActorLabel() + TEXT("|") + FunctionalTest->GetName());
|
|
NamesAppend.Append(TEXT(";"));
|
|
}
|
|
}
|
|
|
|
if (Tests > 0)
|
|
{
|
|
OutTags.Add(UObject::FAssetRegistryTag("Tests", FString::FromInt(Tests), UObject::FAssetRegistryTag::TT_Numerical));
|
|
}
|
|
|
|
if (!TestNames.IsEmpty())
|
|
{
|
|
OutTags.Add(UObject::FAssetRegistryTag("TestNames", TestNames, UObject::FAssetRegistryTag::TT_Hidden));
|
|
}
|
|
|
|
if (!TestNamesEditor.IsEmpty())
|
|
{
|
|
OutTags.Add(UObject::FAssetRegistryTag("TestNamesEditor", TestNamesEditor, UObject::FAssetRegistryTag::TT_Hidden));
|
|
}
|
|
#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.ClassNames.Add(UWorld::StaticClass()->GetFName());
|
|
Filter.bRecursiveClasses = true;
|
|
Filter.bIncludeOnlyOnDiskAssets = true;
|
|
if (AssetRegistry.GetAssets(Filter, /*out*/ MapList))
|
|
{
|
|
for (const FAssetData& MapAsset : MapList)
|
|
{
|
|
FString MapAssetPath = MapAsset.ObjectPath.ToString();
|
|
|
|
const FString* Tests = MapAsset.TagsAndValues.Find(TEXT("Tests"));
|
|
const FString* TestNames = MapAsset.TagsAndValues.Find(bEditorOnlyTests ? TEXT("TestNamesEditor") : TEXT("TestNames"));
|
|
|
|
if (Tests && TestNames)
|
|
{
|
|
int32 TestCount = FCString::Atoi(**Tests);
|
|
if (TestCount > 0)
|
|
{
|
|
TArray<FString> MapTests;
|
|
(*TestNames).ParseIntoArray(MapTests, TEXT(";"), true);
|
|
|
|
for (const FString& MapTest : MapTests)
|
|
{
|
|
FString BeautifulTestName;
|
|
FString RealTestName;
|
|
FString MapPackageName = MapAsset.PackageName.ToString();
|
|
|
|
if (MapTest.Split(TEXT("|"), &BeautifulTestName, &RealTestName))
|
|
{
|
|
if (!IsBlacklisted(MapPackageName, RealTestName))
|
|
{
|
|
OutBeautifiedNames.Add(MapPackageName + TEXT(".") + *BeautifulTestName);
|
|
OutTestCommands.Add(MapAssetPath + TEXT(";") + MapAsset.PackageName.ToString() + TEXT(";") + *RealTestName);
|
|
OutTestMapAssets.AddUnique(MapAssetPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!bEditorOnlyTests && MapAsset.AssetName.ToString().Find(TEXT("FTEST_")) == 0)
|
|
{
|
|
OutBeautifiedNames.Add(MapAsset.AssetName.ToString());
|
|
OutTestCommands.Add(MapAssetPath + TEXT(";") + MapAsset.PackageName.ToString());
|
|
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()
|
|
{
|
|
UWorld* TestWorld = nullptr;
|
|
|
|
#if WITH_EDITOR
|
|
const TIndirectArray<FWorldContext>& WorldContexts = GEngine->GetWorldContexts();
|
|
for (const FWorldContext& Context : WorldContexts)
|
|
{
|
|
if ((Context.WorldType == EWorldType::PIE) && (Context.World() != nullptr))
|
|
{
|
|
TestWorld = Context.World();
|
|
}
|
|
}
|
|
#endif
|
|
if (!TestWorld)
|
|
{
|
|
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."));
|
|
}
|
|
}
|
|
}
|
|
|
|
void FFunctionalTestingModule::BuildTestBlacklistFromConfig()
|
|
{
|
|
TestBlacklist.Empty();
|
|
if (GConfig)
|
|
{
|
|
for (const TPair<FString,FConfigFile>& Config : *GConfig)
|
|
{
|
|
FConfigSection* BlacklistSection = GConfig->GetSectionPrivate(TEXT("AutomationTestBlacklist"), false, true, Config.Key);
|
|
if (BlacklistSection)
|
|
{
|
|
// Parse all blacklist definitions of the format "BlacklistTest=(Map=/Game/Tests/MapName, Test=TestName)"
|
|
for (FConfigSection::TIterator Section(*BlacklistSection); Section; ++Section)
|
|
{
|
|
if (Section.Key() == TEXT("BlacklistTest"))
|
|
{
|
|
FString BlacklistValue = Section.Value().GetValue();
|
|
FString Map, Test;
|
|
bool bSuccess = false;
|
|
|
|
if (FParse::Value(*BlacklistValue, TEXT("Map="), Map, true) && FParse::Value(*BlacklistValue, TEXT("Test="), Test, true))
|
|
{
|
|
// These are used as folders so ensure they match the expected layout
|
|
if (Map.StartsWith(TEXT("/")))
|
|
{
|
|
FString ListName = Map + TEXT("/") + Test;
|
|
ListName.RemoveSpacesInline();
|
|
|
|
TestBlacklist.AddUnique(ListName);
|
|
bSuccess = true;
|
|
}
|
|
}
|
|
|
|
if (!bSuccess)
|
|
{
|
|
UE_LOG(LogFunctionalTest, Error, TEXT("Invalid blacklisted test definition: '%s'"), *BlacklistValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (TestBlacklist.Num() > 0)
|
|
{
|
|
UE_LOG(LogFunctionalTest, Log, TEXT("Automated Test Blacklist:"));
|
|
for (FString& Test : TestBlacklist)
|
|
{
|
|
UE_LOG(LogFunctionalTest, Log, TEXT("\tTest: %s"), *Test);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FFunctionalTestingModule::IsBlacklisted(const FString& MapName, const FString& TestName) const
|
|
{
|
|
FString ListName = MapName + TEXT("/") + TestName;
|
|
ListName.RemoveSpacesInline();
|
|
return TestBlacklist.Contains(ListName);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// 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
|