Singularity/Library/PackageCache/com.unity.test-framework@1..../UnityEngine.TestRunner/NUnitExtensions/Runner/UnityLogCheckDelegatingComm...
2024-05-06 11:45:45 -07:00

144 lines
5.1 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NUnit.Framework.Internal;
using NUnit.Framework.Internal.Commands;
using UnityEngine.TestTools;
using UnityEngine.TestTools.Logging;
using UnityEngine.TestTools.TestRunner;
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
{
class UnityLogCheckDelegatingCommand : DelegatingTestCommand, IEnumerableTestMethodCommand
{
static Dictionary<object, bool?> s_AttributeCache = new Dictionary<object, bool?>();
public UnityLogCheckDelegatingCommand(TestCommand innerCommand)
: base(innerCommand) {}
public override TestResult Execute(ITestExecutionContext context)
{
using (var logScope = new LogScope())
{
if (ExecuteAndCheckLog(logScope, context.CurrentResult, () => innerCommand.Execute(context)))
PostTestValidation(logScope, innerCommand, context.CurrentResult);
}
return context.CurrentResult;
}
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
{
if (!(innerCommand is IEnumerableTestMethodCommand enumerableTestMethodCommand))
{
Execute(context);
yield break;
}
using (var logScope = new LogScope())
{
IEnumerable executeEnumerable = null;
if (!ExecuteAndCheckLog(logScope, context.CurrentResult,
() => executeEnumerable = enumerableTestMethodCommand.ExecuteEnumerable(context)))
yield break;
foreach (var step in executeEnumerable)
{
// do not check expected logs here - we want to permit expecting and receiving messages to run
// across frames. (but we do always want to catch a fail immediately.)
if (!CheckFailingLogs(logScope, context.CurrentResult))
yield break;
yield return step;
}
if (!CheckLogs(context.CurrentResult, logScope))
yield break;
PostTestValidation(logScope, innerCommand, context.CurrentResult);
}
}
static bool CaptureException(TestResult result, Action action)
{
try
{
action();
return true;
}
catch (Exception e)
{
result.RecordException(e);
return false;
}
}
static bool ExecuteAndCheckLog(LogScope logScope, TestResult result, Action action)
=> CaptureException(result, action) && CheckLogs(result, logScope);
static void PostTestValidation(LogScope logScope, TestCommand command, TestResult result)
{
if (MustExpect(command.Test.Method.MethodInfo))
CaptureException(result, logScope.NoUnexpectedReceived);
}
static bool CheckLogs(TestResult result, LogScope logScope)
=> CheckFailingLogs(logScope, result) && CheckExpectedLogs(logScope, result);
static bool CheckFailingLogs(LogScope logScope, TestResult result)
{
if (!logScope.AnyFailingLogs())
return true;
var failingLog = logScope.FailingLogs.First();
result.RecordException(new UnhandledLogMessageException(failingLog));
return false;
}
static bool CheckExpectedLogs(LogScope logScope, TestResult result)
{
if (!logScope.ExpectedLogs.Any())
return true;
var expectedLog = logScope.ExpectedLogs.Peek();
result.RecordException(new UnexpectedLogMessageException(expectedLog));
return false;
}
static bool MustExpect(MemberInfo method)
{
// method
var methodAttr = method.GetCustomAttributes<TestMustExpectAllLogsAttribute>(true).FirstOrDefault();
if (methodAttr != null)
return methodAttr.MustExpect;
// fixture
var fixture = method.DeclaringType;
if (!s_AttributeCache.TryGetValue(fixture, out var mustExpect))
{
var fixtureAttr = fixture.GetCustomAttributes<TestMustExpectAllLogsAttribute>(true).FirstOrDefault();
mustExpect = s_AttributeCache[fixture] = fixtureAttr?.MustExpect;
}
if (mustExpect != null)
return mustExpect.Value;
// assembly
var assembly = fixture.Assembly;
if (!s_AttributeCache.TryGetValue(assembly, out mustExpect))
{
var assemblyAttr = assembly.GetCustomAttributes<TestMustExpectAllLogsAttribute>().FirstOrDefault();
mustExpect = s_AttributeCache[assembly] = assemblyAttr?.MustExpect;
}
return mustExpect == true;
}
}
}