A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections; 3using System.Collections.Generic; 4using System.Linq; 5using System.Reflection; 6using NUnit.Framework.Internal; 7using NUnit.Framework.Internal.Commands; 8using UnityEngine.TestTools; 9using UnityEngine.TestTools.Logging; 10 11namespace UnityEngine.TestRunner.NUnitExtensions.Runner 12{ 13 internal class UnityLogCheckDelegatingCommand : DelegatingTestCommand, IEnumerableTestMethodCommand 14 { 15 private static Dictionary<object, bool?> s_AttributeCache = new Dictionary<object, bool?>(); 16 17 public UnityLogCheckDelegatingCommand(TestCommand innerCommand) 18 : base(innerCommand) {} 19 20 public override TestResult Execute(ITestExecutionContext context) 21 { 22 using (var logScope = new LogScope()) 23 { 24 if (ExecuteAndCheckLog(logScope, context.CurrentResult, () => innerCommand.Execute(context))) 25 PostTestValidation(logScope, innerCommand, context.CurrentResult); 26 } 27 28 return context.CurrentResult; 29 } 30 31 public IEnumerable ExecuteEnumerable(ITestExecutionContext context) 32 { 33 if (!(innerCommand is IEnumerableTestMethodCommand enumerableTestMethodCommand)) 34 { 35 Execute(context); 36 yield break; 37 } 38 39 using (var logScope = new LogScope()) 40 { 41 IEnumerable executeEnumerable = null; 42 43 if (!ExecuteAndCheckLog(logScope, context.CurrentResult, 44 () => executeEnumerable = enumerableTestMethodCommand.ExecuteEnumerable(context))) 45 yield break; 46 47 var innerCommandIsTask = enumerableTestMethodCommand is TaskTestMethodCommand; 48 foreach (var step in executeEnumerable) 49 { 50 // do not check expected logs here - we want to permit expecting and receiving messages to run 51 // across frames. This means that we break on failing logs and fail on next frame. 52 // An exception is async (Task), in which case we first fail after the task has run, as we cannot cancel the task. 53 if (!innerCommandIsTask && !CheckFailingLogs(logScope, context.CurrentResult)) 54 { 55 yield break; 56 } 57 58 yield return step; 59 } 60 61 if (!CheckLogs(context.CurrentResult, logScope)) 62 yield break; 63 64 PostTestValidation(logScope, innerCommand, context.CurrentResult); 65 } 66 } 67 68 private static bool CaptureException(TestResult result, Action action) 69 { 70 try 71 { 72 action(); 73 return true; 74 } 75 catch (Exception e) 76 { 77 result.RecordException(e); 78 return false; 79 } 80 } 81 82 private static bool ExecuteAndCheckLog(LogScope logScope, TestResult result, Action action) 83 => CaptureException(result, action) && CheckLogs(result, logScope); 84 85 private static void PostTestValidation(LogScope logScope, TestCommand command, TestResult result) 86 { 87 if (MustExpect(command.Test.Method.MethodInfo)) 88 CaptureException(result, logScope.NoUnexpectedReceived); 89 } 90 91 private static bool CheckLogs(TestResult result, LogScope logScope) 92 { 93 try 94 { 95 logScope.EvaluateLogScope(true); 96 } 97 catch (Exception e) 98 { 99 result.RecordException(e); 100 return false; 101 } 102 103 return true; 104 } 105 106 private static bool CheckFailingLogs(LogScope logScope, TestResult result) 107 { 108 try 109 { 110 logScope.EvaluateLogScope(false); 111 } 112 catch (Exception e) 113 { 114 result.RecordException(e); 115 return false; 116 } 117 118 return true; 119 } 120 121 private static bool MustExpect(MemberInfo method) 122 { 123 // method 124 125 var methodAttr = method.GetCustomAttributes<TestMustExpectAllLogsAttribute>(true).FirstOrDefault(); 126 if (methodAttr != null) 127 return methodAttr.MustExpect; 128 129 // fixture 130 131 var fixture = method.DeclaringType; 132 if (!s_AttributeCache.TryGetValue(fixture, out var mustExpect)) 133 { 134 var fixtureAttr = fixture.GetCustomAttributes<TestMustExpectAllLogsAttribute>(true).FirstOrDefault(); 135 mustExpect = s_AttributeCache[fixture] = fixtureAttr?.MustExpect; 136 } 137 138 if (mustExpect != null) 139 return mustExpect.Value; 140 141 // assembly 142 143 var assembly = fixture.Assembly; 144 if (!s_AttributeCache.TryGetValue(assembly, out mustExpect)) 145 { 146 var assemblyAttr = assembly.GetCustomAttributes<TestMustExpectAllLogsAttribute>().FirstOrDefault(); 147 mustExpect = s_AttributeCache[assembly] = assemblyAttr?.MustExpect; 148 } 149 150 return mustExpect == true; 151 } 152 } 153}