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; | ||
|  |         } | ||
|  |     } | ||
|  | } |