// 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
{
///
/// Test that waits for the client and server to get to the front-end then quits
///
public class BootTest : UnrealTestNode
{
///
/// Used to track progress via logging
///
UnrealLogStreamParser LogReader = null;
///
/// Time we last saw a change in logging
///
DateTime LastLogTime = DateTime.Now;
///
/// Set to true once we detect the game has launched correctly
///
bool DidDetectLaunch = false;
///
/// Log idle timeout in seconds
///
private float kTimeOutDuration = 10 * 60;
///
/// Default constructor
///
///
public BootTest(Gauntlet.UnrealTestContext InContext)
: base(InContext)
{
}
///
/// Returns the configuration description for this test
///
///
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;
}
///
/// Called to begin the test.
///
///
///
///
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;
}
///
/// String that we search for to be considered "Booted"
///
///
protected virtual string GetCompletionString()
{
// Intentionally setup with no completion string as UnrealTestNode verified initialization string already
return null;
}
///
/// Called periodically while the test is running to allow code to monitor health.
///
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 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);
}
}
}
///
/// Called after a test finishes to create an overall summary based on looking at the artifacts
///
///
///
/// ITestReport
///
///
///
public override ITestReport CreateReport(TestResult Result, UnrealTestContext Context, UnrealBuildSource Build, IEnumerable 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());
}
}
///
/// Test that verifies the editor boots
///
public class EditorBootTest : BootTest
{
public EditorBootTest(Gauntlet.UnrealTestContext InContext)
: base(InContext)
{
}
///
/// Returns the configuration description for this test
///
///
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;
}
}
///
/// Test that verifies a target boots
///
public class TargetBootTest : BootTest
{
public TargetBootTest(Gauntlet.UnrealTestContext InContext)
: base(InContext)
{
}
///
/// Returns the configuration description for this test
///
///
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
{
///
/// Default constructor
///
///
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");
}
}
}