154 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			154 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;
 | |
| 
 | |
| namespace UnityEngine.TestRunner.NUnitExtensions.Runner
 | |
| {
 | |
|     internal class UnityLogCheckDelegatingCommand : DelegatingTestCommand, IEnumerableTestMethodCommand
 | |
|     {
 | |
|         private 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;
 | |
| 
 | |
|                 var innerCommandIsTask = enumerableTestMethodCommand is TaskTestMethodCommand;
 | |
|                 foreach (var step in executeEnumerable)
 | |
|                 {
 | |
|                     // do not check expected logs here - we want to permit expecting and receiving messages to run
 | |
|                     // across frames. This means that we break on failing logs and fail on next frame.
 | |
|                     // An exception is async (Task), in which case we first fail after the task has run, as we cannot cancel the task. 
 | |
|                     if (!innerCommandIsTask && !CheckFailingLogs(logScope, context.CurrentResult))
 | |
|                     {
 | |
|                         yield break;
 | |
|                     }
 | |
| 
 | |
|                     yield return step;
 | |
|                 }
 | |
| 
 | |
|                 if (!CheckLogs(context.CurrentResult, logScope))
 | |
|                     yield break;
 | |
| 
 | |
|                 PostTestValidation(logScope, innerCommand, context.CurrentResult);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private static bool CaptureException(TestResult result, Action action)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 action();
 | |
|                 return true;
 | |
|             }
 | |
|             catch (Exception e)
 | |
|             {
 | |
|                 result.RecordException(e);
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private static bool ExecuteAndCheckLog(LogScope logScope, TestResult result, Action action)
 | |
|             => CaptureException(result, action) && CheckLogs(result, logScope);
 | |
| 
 | |
|         private static void PostTestValidation(LogScope logScope, TestCommand command, TestResult result)
 | |
|         {
 | |
|             if (MustExpect(command.Test.Method.MethodInfo))
 | |
|                 CaptureException(result, logScope.NoUnexpectedReceived);
 | |
|         }
 | |
| 
 | |
|         private static bool CheckLogs(TestResult result, LogScope logScope)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 logScope.EvaluateLogScope(true);
 | |
|             }
 | |
|             catch (Exception e)
 | |
|             {
 | |
|                 result.RecordException(e);
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         private static bool CheckFailingLogs(LogScope logScope, TestResult result)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 logScope.EvaluateLogScope(false);
 | |
|             }
 | |
|             catch (Exception e)
 | |
|             {
 | |
|                 result.RecordException(e);
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         private 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;
 | |
|         }
 | |
|     }
 | |
| }
 |