Files
UnrealEngineUWP/Engine/Source/Programs/AutomationTool/Gauntlet/Unreal/Utils/Gauntlet.AutomationLogParser.cs
Jerome Delattre 60839c1816 Improve test exclusion list mechanic
* Rename blacklist to excludelist (requested by high management)
* Support section exclusion rule to be able to exclude entire section of tests (ie PathTracing is only supported on Win64 for now)
* Mark excluded test as skipped in the report instead of entirely removed for test list. Check for exclusion just before running the test.
* Remove NotEnoughParticipant state in favor of Skipped (same conditions lead to Skipped state with appropriate messaging)
* Add support for exclusion management from the Test Automation window. (added a column at the end of each row)
* Expose device information to UE test report
* Add support for metadata in Gauntlet test report for Horde

Limitations:
* Management through the UI is limited to which test is available through in the active worker node. That's mean Runtime only tests are not listed from a worker that is Editor(the default) and platform specific are not clearly identified.
* For platforms, the mechanic to access their config and save it will remain to be done. In the meantime, it needs to be done manually through the target platform config file.

#jira UE-125960
#jira UE-125974
#rb Chris.Constantinescu, Eric.Knapik, Louise.Rasmussen

[CL 17607554 by Jerome Delattre in ue5-main branch]
2021-09-23 09:34:55 -04:00

164 lines
5.5 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Gauntlet
{
/// <summary>
/// Helper class for parsing AutomationTest results from either an UnrealLogParser or log contents
/// </summary>
public class AutomationLogParser
{
protected UnrealLogParser Parser;
public string AutomationReportPath { get; protected set; } = "";
public string AutomationReportURL { get; protected set; } = "";
/// <summary>
/// Returns entries in the log file related to automation
/// </summary>
public IEnumerable<UnrealLog.LogEntry> AutomationLogEntries { get; protected set; }
/// <summary>
/// Returns warning/errors in the logfile related to automation
/// </summary>
public IEnumerable<UnrealLog.LogEntry> AutomationWarningsAndErrors
{
get
{
return AutomationLogEntries.Where(E => E.Level == UnrealLog.LogLevel.Error || E.Level == UnrealLog.LogLevel.Warning);
}
}
/// <summary>
/// Constructor that uses an existing log parser
/// </summary>
/// <param name="InParser"></param>
public AutomationLogParser(UnrealLogParser InParser)
{
Parser = InParser;
IEnumerable<Match> ReportPathMatch = Parser.GetAllMatches(@"LogAutomationController.+Report can be opened.+'(.+)'");
if (ReportPathMatch.Any())
{
AutomationReportPath = Path.GetFullPath(ReportPathMatch.First().Groups[1].ToString());
}
IEnumerable<Match> ReportUrlMatch = Parser.GetAllMatches(@"LogAutomationController.+Report can be viewed.+'(.+)'");
if (ReportUrlMatch.Any())
{
AutomationReportURL = ReportUrlMatch.First().Groups[1].ToString();
}
AutomationLogEntries = Parser.LogEntries.Where(
E => E.Category.StartsWith("Automation", StringComparison.OrdinalIgnoreCase)
|| E.Category.StartsWith("FunctionalTest", StringComparison.OrdinalIgnoreCase)
)
.ToArray();
}
/// <summary>
/// Constructor that takes raw log contents
/// </summary>
/// <param name="InContents"></param>
public AutomationLogParser(string InContents)
: this(new UnrealLogParser(InContents))
{
}
/// <summary>
/// Returns all results found in our construction content.
/// </summary>
/// <returns></returns>
public IEnumerable<UnrealAutomatedTestResult> GetResults()
{
IEnumerable<Match> TestStarts = Parser.GetAllMatches(@"LogAutomationController.+Test Started. Name={(.+?)}\s+Path={(.+?)}");
// Find all automation results that succeeded/failed
// [00:10:54.148] LogAutomationController: Display: Test Started. Name={ST_PR04} Path={Project.Functional Tests./Game/Tests/Rendering/PlanarReflection.ST_PR04}
// [2019.04.30-18.49.51:329][244]LogAutomationController: Display: Test Completed With Success. Name={ST_PR04} Path={Project.Functional Tests./Game/Tests/Rendering/PlanarReflection.ST_PR04}
// [2019.04.30-18.49.51:330] [244] LogAutomationController: BeginEvents: Project.Functional Tests./Game/Tests/Rendering/PlanarReflection.ST_PR04
// [2019.04.30 - 18.49.51:331][244] LogAutomationController: Screenshot 'ST_PR04' was similar! Global Difference = 0.001377, Max Local Difference = 0.037953
// [2019.04.30 - 18.49.51:332][244]LogAutomationController: EndEvents: Project.Functional Tests./Game/Tests/Rendering/PlanarReflection.ST_PR04
IEnumerable<Match> TestResults = Parser.GetAllMatches(@"LogAutomationController.+Test Completed. Result={(.+?)}\s+Name={(.+?)}\s+Path={(.+?)}");
string[] AutomationChannel = Parser.GetLogChannel("AutomationController").ToArray();
Func<string, string> SantizeLine = (L) =>
{
L = L.Replace(": Error: ", "");
L = L.Replace(": Warning: ", "");
L = L.Replace("LogAutomationController: ", "");
return L;
};
// Convert these lines into results by parsing out the details and then adding the events
IEnumerable<UnrealAutomatedTestResult> Results = TestStarts.Select(TestMatch =>
{
UnrealAutomatedTestResult Result = new UnrealAutomatedTestResult();
string DisplayName = TestMatch.Groups[1].ToString();
string LongName = TestMatch.Groups[2].ToString(); ;
TestStateType State = TestStateType.InProcess;
Match ResultMatch = TestResults.Where(M => M.Groups[3].ToString() == LongName).FirstOrDefault();
if (ResultMatch != null)
{
switch(ResultMatch.Groups[1].ToString().ToLower())
{
case "skipped":
State = TestStateType.Skipped;
break;
case "success":
State = TestStateType.Success;
break;
default:
State = TestStateType.Fail;
break;
}
string EventName = string.Format("BeginEvents: {0}", LongName);
int EventIndex = Array.FindIndex(AutomationChannel, S => S.Contains(EventName)) + 1;
if (EventIndex > 0)
{
while (EventIndex < AutomationChannel.Length)
{
string Event = AutomationChannel[EventIndex++];
if (Event.Contains("EndEvents"))
{
break;
}
EventType EventType = Event.Contains(": Error: ") ? EventType.Error : Event.Contains(": Warning: ") ? EventType.Warning : EventType.Info;
Result.AddEvent(EventType, SantizeLine(Event));
}
}
}
else
{
Result.AddEvent(EventType.Error, string.Format("Test {0} incomplete.", DisplayName));
}
Result.TestDisplayName = DisplayName;
Result.FullTestPath = LongName;
Result.State = State;
return Result;
});
return Results;
}
}
}