06/05/2021
自动化系统
别名自动测试(AutoTest),由UE4提供的。首先看一下UE4的例子。打开Window->DeveloperTool->SessionFrontend->Automation。
可以选择System->Engine->FileSystem 和Attachment选项,然后开始测试
创建自动的自动化
此系统存在于”UObject”生态系统之外,所以它对
蓝图
或引擎的
反射系统
不可见
简单测试
创建一个空的[Name]Test.cpp文件
//UE4官方例子
#include "Misc/AutomationTest.h"
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FPlaceholderTest, "TestGroup.TestSubgroup.Placeholder Test", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FPlaceholderTest::RunTest(const FString& Parameters)
{
// 通过返回"真(true)"使测试通过,或者返回"假(false)"使测试失败。
return true;
}
打开Session Frontend可以看到测试函数,因为
EAutomationTestFlags::EditorContext
//在Console command 也可以运行自动化,按照下面写法
automation runtest TestGroup.TestSubgroup.Placeholder Test
下面列子存在Runtime\Engine\Private\Tests\EngineAutomationTests.cpp,和官网提供的一致
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FSetResTest, "System.Windows.Set Resolution", EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter)
bool FSetResTest::RunTest(const FString& Parameters)
{
//Gets the default map that the game uses.
const UGameMapsSettings* GameMapsSettings = GetDefault<UGameMapsSettings>();
const FString& MapName = GameMapsSettings->GetGameDefaultMap();
//Opens the actual default map in game.
GEngine->Exec(GetSimpleEngineAutomationTestGameWorld(GetTestFlags()), *FString::Printf(TEXT("Open %s"), *MapName));
//Gets the current resolution.
int32 ResX = GSystemResolution.ResX;
int32 ResY = GSystemResolution.ResY;
FString RestoreResolutionString = FString::Printf(TEXT("setres %dx%d"), ResX, ResY);
//Change the resolution and then restore it.
ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(2.0f));
ADD_LATENT_AUTOMATION_COMMAND(FExecStringLatentCommand(TEXT("setres 640x480")));
ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(2.0f));
ADD_LATENT_AUTOMATION_COMMAND(FExecStringLatentCommand(RestoreResolutionString));
return true;
}
复杂测试
复杂测试可以用来测试多个重复的任务,相当于简单测试中有一个for-loop,GetTests需要获取每张地图的名字并加入到OutBeautifiedNames和OutTestCommands,然后每一张地图都会传给RunTests.
//加载所有地图并获取 性能报告
IMPLEMENT_COMPLEX_AUTOMATION_TEST(FLoadAllMapsInGameTest, "Project.Maps.Load All In Game", EAutomationTestFlags::ClientContext | EAutomationTestFlags::PerfFilter)
/**
* Requests a enumeration of all maps to be loaded
*/
void FLoadAllMapsInGameTest::GetTests(TArray<FString>& OutBeautifiedNames, TArray <FString>& OutTestCommands) const
{
TArray<FString> FileList;
#if WITH_EDITOR
FEditorFileUtils::FindAllPackageFiles(FileList);
#else
// Look directly on disk. Very slow!
FPackageName::FindPackagesInDirectory(FileList, *FPaths::ProjectContentDir());
#endif
// Iterate over all files, adding the ones with the map extension..
for (int32 FileIndex = 0; FileIndex < FileList.Num(); FileIndex++)
{
const FString& Filename = FileList[FileIndex];
// Disregard filenames that don't have the map extension if we're in MAPSONLY mode.
if (FPaths::GetExtension(Filename, true) == FPackageName::GetMapPackageExtension())
{
if (FAutomationTestFramework::Get().ShouldTestContent(Filename))
{
OutBeautifiedNames.Add(FPaths::GetBaseFilename(Filename));
OutTestCommands.Add(Filename);
}
}
}
//也可以使用AssetRegistry 模块代码获取地图类信息
}
UWorld* GetSimpleEngineAutomationTestGameWorld(const int32 TestFlags)
{
// Accessing the game world is only valid for game-only
check((TestFlags & EAutomationTestFlags::ApplicationContextMask) == EAutomationTestFlags::ClientContext);
check(GEngine->GetWorldContexts().Num() == 1);
check(GEngine->GetWorldContexts()[0].WorldType == EWorldType::Game);
return GEngine->GetWorldContexts()[0].World();
}
bool FLoadAllMapsInGameTest::RunTest(const FString& Parameters)
{
FString MapName = Parameters;
//Open the map
GEngine->Exec(GetSimpleEngineAutomationTestGameWorld(GetTestFlags()), *FString::Printf(TEXT("Open %s"), *MapName));
//if( FAutomationTestFramework::Get().IsScreenshotAllowed() )
{
//Generate the screen shot name and path
FString ScreenshotFileName;
const FString LoadAllMapsTestName = FString::Printf(TEXT("LoadAllMaps_Game/%s"), *FPaths::GetBaseFilename(MapName));
ScreenshotFileName = AutomationCommon::GetLocalPathForScreenshot(AutomationCommon::GetScreenshotName(LoadAllMapsTestName));
//Give the map some time to load
ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(1.5f));
//Take the screen shot
ADD_LATENT_AUTOMATION_COMMAND(FTakeViewportScreenshotCommand(ScreenshotFileName));
//Give the screen shot a chance to capture the scene
ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(0.5f));
}
//Kick off any Automation matinees that are in this map
ADD_LATENT_AUTOMATION_COMMAND(FEnqueuePerformanceCaptureCommands());
return true;
}
ADD_LATENT_AUTOMATION_COMMAND 潜在指令
潜在指令用于辅助Runtests,让指令不在一帧完成,而是可以分为好几帧完成,需要通过DEFINE_LATENT_AUTOMATION_COMMAND声明,才能使用
执行命令
不光可以从Editor启动,也可以从命令控制台启动或者命令行启动
TestWorld.exe -execcmds="Automation runtests Project.Maps.Load All In Game;Quit" //启动并完成后退出游戏
-execcmds="automation list;runtests [....]" //查看定义过的Autotest,并执行某个
-execcmds="automation list;runall" 查看定义过的Autotest,并执行所有
其他潜在命令
ADD_LATENT_AUTOMATION_COMMAND(FExecStringLatentCommand(TEXT("stat game")));
//StartFPSChart
ADD_LATENT_AUTOMATION_COMMAND(FExecWorldStringLatentCommand(TEXT("StartFPSChart")));
ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(10.0f));
ADD_LATENT_AUTOMATION_COMMAND(FExecWorldStringLatentCommand(TEXT("StopFPSChart")));
//Start memory report
其他辅助命令
// NOTE: In the absence of "-ddc=noshared", a VPN connection can cause UE4 to take a long time to startup
// NOTE: Without '-CrashForUAT'/'-unattended' the auto-reporter can pop up
// NOTE: Without '-UseAutoReporter' the crash report executable is launched
-stdout -FullStdOutLogOutput -ddc=noshared -unattended -CrashForUAT -UseAutoReporter -NUTServer
-unattended: 设置为无人管理的状态。禁用需要从用户获得反馈的任何东西。
-crashreports 总是显示崩溃信息
-logcmds="logonline verbose,LogAutomationCommandLine verbose/display/log"