Files
UnrealEngineUWP/Engine/Source/Programs/AutomationTool/Gauntlet/Unreal/Automation/UE.BootTest.cs
eric knapik 1ee860257e #jira: FORT-429595
Allow cooked editor to run in cooked for in gauntlet and uncooked form.
Switch to using the -cookedEditor commandline argument for game specific cooked editor.
Adjusted parameters to allow the game to override exes to look for and name their module type

#preflight: 620df1e0f44e27e076620baf
[REVIEW] [at]Andrew.Grant [at]Jerome.Delattre [at]Chris.Constantinescu

#ROBOMERGE-AUTHOR: eric.knapik
#ROBOMERGE-SOURCE: CL 19035750 via CL 19037678 via CL 19038828 via CL 19039124 via CL 19039805
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v918-19018356)

[CL 19040295 by eric knapik in ue5-main branch]
2022-02-17 15:08:03 -05:00

267 lines
7.5 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using AutomationTool;
using EpicGame;
using Gauntlet;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace UE
{
/// <summary>
/// Test that waits for the client and server to get to the front-end then quits
/// </summary>
public class BootTest : UnrealTestNode<UnrealTestConfiguration>
{
/// <summary>
/// Used to track progress via logging
/// </summary>
int LogLinesLastTick = 0;
/// <summary>
/// Time we last saw a change in logging
/// </summary>
DateTime LastLogTime = DateTime.Now;
/// <summary>
/// Set to true once we detect the game has launched correctly
/// </summary>
bool DidDetectLaunch = false;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="InContext"></param>
public BootTest(Gauntlet.UnrealTestContext InContext)
: base(InContext)
{
}
/// <summary>
/// Returns the configuration description for this test
/// </summary>
/// <returns></returns>
public override UnrealTestConfiguration GetConfiguration()
{
UnrealTestConfiguration Config = base.GetConfiguration();
UnrealTestRole Client = Config.RequireRole(UnrealTargetRole.Client);
return Config;
}
/// <summary>
/// Called to begin the test.
/// </summary>
/// <param name="Pass"></param>
/// <param name="InNumPasses"></param>
/// <returns></returns>
public override bool StartTest(int Pass, int InNumPasses)
{
// Call the base class to actually start the test running
if (!base.StartTest(Pass, InNumPasses))
{
return false;
}
// track our starting condition
LastLogTime = DateTime.Now;
LogLinesLastTick = 0;
DidDetectLaunch = true;
return true;
}
/// <summary>
/// String that we search for to be considered "Booted"
/// </summary>
/// <returns></returns>
protected virtual string GetCompletionString()
{
return "Engine is initialized. Leaving FEngineLoop::Init()";
}
/// <summary>
/// Called periodically while the test is running to allow code to monitor health.
/// </summary>
public override void TickTest()
{
const int kTimeOutDuration = 10;
// run the base class tick;
base.TickTest();
// Get the log of the first client app
IAppInstance RunningInstance = this.TestInstance.RunningRoles.First().AppInstance;
UnrealLogParser LogParser = new UnrealLogParser(RunningInstance.StdOut);
IEnumerable<string> BusyLogLines = LogParser.GetEditorBusyChannels();
int BusyLineCount = BusyLogLines.Count();
if (BusyLineCount > LogLinesLastTick)
{
LastLogTime = DateTime.Now;
// log new entries so people have something to look at
BusyLogLines.Skip(LogLinesLastTick).ToList().ForEach(S => Log.Info("{0}", S));
LogLinesLastTick = BusyLineCount;
}
// Gauntlet will timeout tests based on the -timeout argument, but we have greater insight here so can bail earlier to save
// tests from idling on the farm needlessly.
if ((DateTime.Now - LastLogTime).TotalMinutes > kTimeOutDuration)
{
Log.Error("No logfile activity observed in last {0} minutes. Ending test", kTimeOutDuration);
MarkTestComplete();
SetUnrealTestResult(TestResult.TimedOut);
}
// now see if the game has brought the first world up for play
IEnumerable<string> LogWorldLines = LogParser.GetLogChannel("World");
string CompletionString = GetCompletionString();
if (!string.IsNullOrEmpty(CompletionString))
{
if (LogParser.Content.IndexOf(CompletionString, StringComparison.OrdinalIgnoreCase) > 0)
{
Log.Info("Found '{0}'. Ending Test", GetCompletionString());
MarkTestComplete();
DidDetectLaunch = true;
SetUnrealTestResult(TestResult.Passed);
}
}
}
/// <summary>
/// Called after a test finishes to create an overall summary based on looking at the artifacts
/// </summary>
/// <param name="Result"></param>
/// <returns>ITestReport</returns>
/// <param name="Build"></param>
/// <param name="Artifacts"></param>
/// <param name="InArtifactPath"></param>
public override ITestReport CreateReport(TestResult Result, UnrealTestContext Context, UnrealBuildSource Build, IEnumerable<UnrealRoleResult> InResults, string InArtifactPath)
{
if (Result == TestResult.Passed)
{
if (!DidDetectLaunch)
{
ReportError("Failed to detect completion of launch");
}
else
{
// find a logfile or something that indicates the process ran successsfully
bool MissingFiles = false;
foreach (var RoleResult in InResults)
{
DirectoryInfo RoleDir = new DirectoryInfo(RoleResult.Artifacts.ArtifactPath);
IEnumerable<FileInfo> ArtifactFiles = RoleDir.EnumerateFiles("*.*", SearchOption.AllDirectories);
// user may not have cleared paths between runs, so throw away anything that's older than 2m
ArtifactFiles = ArtifactFiles.Where(F => (DateTime.Now - F.LastWriteTime).TotalMinutes < 2);
if (ArtifactFiles.Any() == false)
{
MissingFiles = true;
ReportError("No artifact files found for {0}. Were they not retrieved from the device?", RoleResult.Artifacts.SessionRole);
}
IEnumerable<FileInfo> LogFiles = ArtifactFiles.Where(F => F.Extension.Equals(".log", StringComparison.OrdinalIgnoreCase));
if (LogFiles.Any() == false)
{
MissingFiles = true;
ReportError("No log files found for {0}. Were they not retrieved from the device?", RoleResult.Artifacts.SessionRole);
}
}
if (MissingFiles)
{
SetUnrealTestResult(TestResult.Failed);
ReportError("One or more roles did not generated any artifacts");
}
Log.Info("Found valid artifacts for test");
}
}
return base.CreateReport(GetTestResult());
}
}
/// <summary>
/// Test that verifies the editor boots
/// </summary>
public class EditorBootTest : BootTest
{
public EditorBootTest(Gauntlet.UnrealTestContext InContext)
: base(InContext)
{
}
/// <summary>
/// Returns the configuration description for this test
/// </summary>
/// <returns></returns>
public override UnrealTestConfiguration GetConfiguration()
{
UnrealTestConfiguration Config = base.GetConfiguration();
// currently needed as BootTest isn't an abstract class. Can be changed for 4.27
Config.ClearRoles();
UnrealTestRole EditorRole = Config.RequireRole(UnrealTargetRole.Editor);
EditorRole.CommandLineParams.Add("execcmds", "QUIT_EDITOR");
return Config;
}
protected override string GetCompletionString()
{
return null;
}
}
/// <summary>
/// Test that verifies a target boots
/// </summary>
public class TargetBootTest : BootTest
{
public TargetBootTest(Gauntlet.UnrealTestContext InContext)
: base(InContext)
{
}
/// <summary>
/// Returns the configuration description for this test
/// </summary>
/// <returns></returns>
public override UnrealTestConfiguration GetConfiguration()
{
UnrealTestConfiguration Config = base.GetConfiguration();
Config.RequireRole(UnrealTargetRole.Client);
return Config;
}
}
}
// Provided for backwards compatibility with scripts
namespace Gauntlet.UnrealTest
{
class BootTest : UE.BootTest
{
/// <summary>
/// Default constructor
/// </summary>
/// <param name="InContext"></param>
public BootTest(Gauntlet.UnrealTestContext InContext)
: base(InContext)
{
Log.Warning("Gauntlet.UnrealTest.BootTest is deprecated and will be removed in 4.27. Use UE.BootTest. All arguments are the same");
}
}
}