Files
UnrealEngine/Engine/Source/Programs/AutomationTool/Gauntlet/Unreal/Automation/UE.BootTest.cs
2025-05-18 13:04:45 +08:00

261 lines
7.0 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>
UnrealLogStreamParser LogReader = null;
/// <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>
/// Log idle timeout in seconds
/// </summary>
private float kTimeOutDuration = 10 * 60;
/// <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);
if (string.IsNullOrEmpty(GetCompletionString()))
{
Client.CommandLineParams.Add("ExecCmds", "Automation SoftQuit");
}
float logIdleTimeout = Globals.Params.ParseValue("LogIdleTimeout", kTimeOutDuration);
if (logIdleTimeout > 0)
{
kTimeOutDuration = logIdleTimeout;
}
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;
LogReader = null;
DidDetectLaunch = false;
return true;
}
/// <summary>
/// String that we search for to be considered "Booted"
/// </summary>
/// <returns></returns>
protected virtual string GetCompletionString()
{
// Intentionally setup with no completion string as UnrealTestNode verified initialization string already
return null;
}
/// <summary>
/// Called periodically while the test is running to allow code to monitor health.
/// </summary>
public override void TickTest()
{
// run the base class tick;
base.TickTest();
// Get the log of the first client app
IAppInstance RunningInstance = this.TestInstance.RunningRoles.First().AppInstance;
if (LogReader == null)
{
LogReader = new UnrealLogStreamParser(RunningInstance.GetLogBufferReader());
}
LogReader.ReadStream();
IEnumerable<string> BusyLogLines = LogReader.GetLogFromEditorBusyChannels();
if (BusyLogLines.Any())
{
LastLogTime = DateTime.Now;
// log new entries so people have something to look at
BusyLogLines.ToList().ForEach(S => Log.Info("{0}", S));
}
// 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).TotalSeconds > kTimeOutDuration)
{
Log.Error("No logfile activity observed in last {Time:0.00} minutes. Ending test", kTimeOutDuration / 60);
MarkTestComplete();
SetUnrealTestResult(TestResult.TimedOut);
}
string CompletionString = GetCompletionString();
if (!string.IsNullOrEmpty(CompletionString))
{
if (LogReader.GetLogLinesContaining(CompletionString).Any())
{
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>
/// <param name="Context"></param>
/// <returns>ITestReport</returns>
/// <param name="Build"></param>
/// <param name="InResults"></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 (!string.IsNullOrEmpty(GetCompletionString()) && !DidDetectLaunch)
{
ReportError("Failed to detect completion of launch");
}
else
{
// find a logfile or something that indicates the process ran successsfully
bool MissingLogs = false;
foreach (var RoleResult in InResults)
{
if (!File.Exists(RoleResult.Artifacts.LogPath))
{
MissingLogs = true;
ReportError("No log files found for {0}. Were they not retrieved from the device?", RoleResult.Artifacts.SessionRole);
}
}
if (!MissingLogs)
{
Log.Info("Found valid log 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(Config.CookedEditor ? UnrealTargetRole.CookedEditor : 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");
}
}
}