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.Interfaces; 7using NUnit.Framework.Internal; 8using NUnit.Framework.Internal.Commands; 9using UnityEngine.TestRunner.NUnitExtensions; 10using UnityEngine.TestRunner.NUnitExtensions.Runner; 11using UnityEngine.TestTools.Logging; 12using UnityEngine.TestTools.TestRunner; 13 14namespace UnityEngine.TestTools 15{ 16 internal abstract class BeforeAfterTestCommandBase<T> : DelegatingTestCommand, IEnumerableTestMethodCommand where T : class 17 { 18 private string m_BeforeErrorPrefix; 19 private string m_AfterErrorPrefix; 20 protected BeforeAfterTestCommandBase(TestCommand innerCommand, string beforeErrorPrefix, string afterErrorPrefix) 21 : base(innerCommand) 22 { 23 m_BeforeErrorPrefix = beforeErrorPrefix; 24 m_AfterErrorPrefix = afterErrorPrefix; 25 } 26 27 protected T[] BeforeActions = new T[0]; 28 29 protected T[] AfterActions = new T[0]; 30 31 protected static MethodInfo[] GetActions(IDictionary<Type, List<MethodInfo>> cacheStorage, Type fixtureType, Type attributeType, Type[] returnTypes) 32 { 33 if (cacheStorage.TryGetValue(fixtureType, out var result)) 34 { 35 return result.ToArray(); 36 } 37 38 cacheStorage[fixtureType] = GetMethodsWithAttributeFromFixture(fixtureType, attributeType, returnTypes); 39 40 return cacheStorage[fixtureType].ToArray(); 41 } 42 43 protected static T[] GetTestActions(IDictionary<MethodInfo, List<T>> cacheStorage, MethodInfo methodInfo) 44 { 45 if (cacheStorage.TryGetValue(methodInfo, out var result)) 46 { 47 return result.ToArray(); 48 } 49 50 var attributesForMethodInfo = new List<T>(); 51 var attributes = methodInfo.GetCustomAttributes(false); 52 foreach (var attribute in attributes) 53 { 54 if (attribute is T attribute1) 55 { 56 attributesForMethodInfo.Add(attribute1); 57 } 58 } 59 60 cacheStorage[methodInfo] = attributesForMethodInfo; 61 62 return cacheStorage[methodInfo].ToArray(); 63 } 64 65 private static List<MethodInfo> GetMethodsWithAttributeFromFixture(Type fixtureType, Type setUpType, Type[] returnTypes) 66 { 67 MethodInfo[] methodsWithAttribute = Reflect.GetMethodsWithAttribute(fixtureType, setUpType, true); 68 var methodsInfo = new List<MethodInfo>(); 69 methodsInfo.AddRange(methodsWithAttribute.Where(method => returnTypes.Any(type => type == method.ReturnType))); 70 return methodsInfo; 71 } 72 73 protected abstract IEnumerator InvokeBefore(T action, Test test, UnityTestExecutionContext context); 74 75 protected abstract IEnumerator InvokeAfter(T action, Test test, UnityTestExecutionContext context); 76 77 protected virtual bool MoveBeforeEnumerator(IEnumerator enumerator, Test test) 78 { 79 return enumerator.MoveNext(); 80 } 81 82 protected virtual bool MoveAfterEnumerator(IEnumerator enumerator, Test test) 83 { 84 return enumerator.MoveNext(); 85 } 86 87 protected abstract BeforeAfterTestCommandState GetState(UnityTestExecutionContext context); 88 89 protected virtual bool AllowFrameSkipAfterAction(T action) 90 { 91 return true; 92 } 93 94 public IEnumerable ExecuteEnumerable(ITestExecutionContext context) 95 { 96 var unityContext = (UnityTestExecutionContext)context; 97 var state = GetState(unityContext); 98 if (state == null) 99 { 100 throw new Exception($"No state in context for {GetType().Name}."); 101 } 102 103 if(state.ShouldRestore) 104 { 105 state.ApplyContext(unityContext); 106 } 107 108 while (state.NextBeforeStepIndex < BeforeActions.Length) 109 { 110 var action = BeforeActions[state.NextBeforeStepIndex]; 111 IEnumerator enumerator; 112 try 113 { 114 enumerator = EnumeratorHelper.UnpackNestedEnumerators(InvokeBefore(action, Test, unityContext)); 115 EnumeratorHelper.SetEnumeratorPC(state.NextBeforeStepPc); 116 } 117 catch (Exception ex) 118 { 119 state.TestHasRun = true; 120 context.CurrentResult.RecordPrefixedException(m_BeforeErrorPrefix, ex); 121 break; 122 } 123 124 using (var logScope = new LogScope()) 125 { 126 while (true) 127 { 128 try 129 { 130 if (!enumerator.MoveNext()) 131 { 132 logScope.EvaluateLogScope(true); 133 break; 134 } 135 136 if (!AllowFrameSkipAfterAction(action)) // Evaluate the log scope right away for the commands where we do not yield 137 { 138 logScope.EvaluateLogScope(true); 139 } 140 if (enumerator.Current is IEditModeTestYieldInstruction) 141 { 142 if (unityContext.TestMode == TestPlatform.PlayMode) 143 { 144 throw new Exception($"PlayMode test are not allowed to yield {enumerator.Current.GetType().Name}"); 145 } 146 147 if (EnumeratorHelper.IsRunningNestedEnumerator) 148 { 149 throw new Exception($"Nested enumerators are not allowed to yield {enumerator.Current.GetType().Name}"); 150 } 151 } 152 } 153 catch (Exception ex) 154 { 155 state.TestHasRun = true; 156 context.CurrentResult.RecordPrefixedException(m_BeforeErrorPrefix, ex); 157 state.StoreContext(unityContext); 158 break; 159 } 160 161 if (!EnumeratorHelper.IsRunningNestedEnumerator) 162 { 163 // Only store the state in the main enumerator. Domain reloads are not supported from nested enumerators. 164 state.NextBeforeStepPc = EnumeratorHelper.GetEnumeratorPC(); 165 state.StoreContext(unityContext); 166 } 167 168 if (!AllowFrameSkipAfterAction(action)) 169 { 170 break; 171 } 172 173 yield return enumerator.Current; 174 } 175 } 176 177 state.NextBeforeStepIndex++; 178 state.NextBeforeStepPc = 0; 179 } 180 181 if (!state.TestHasRun) 182 { 183 state.ShouldRestore = false; // Any inner commands that can perform domain reloads are responsible for restoring the context 184 if (innerCommand is IEnumerableTestMethodCommand) 185 { 186 var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context); 187 foreach (var iterator in executeEnumerable) 188 { 189 yield return iterator; 190 } 191 } 192 else 193 { 194 context.CurrentResult = innerCommand.Execute(context); 195 } 196 197 state.TestHasRun = true; 198 } 199 200 while (state.NextAfterStepIndex < AfterActions.Length) 201 { 202 state.TestAfterStarted = true; 203 var action = AfterActions[state.NextAfterStepIndex]; 204 IEnumerator enumerator; 205 try 206 { 207 enumerator = EnumeratorHelper.UnpackNestedEnumerators(InvokeAfter(action, Test, unityContext)); 208 EnumeratorHelper.SetEnumeratorPC(state.NextAfterStepPc); 209 } 210 catch (Exception ex) 211 { 212 context.CurrentResult.RecordPrefixedException(m_AfterErrorPrefix, ex); 213 state.StoreContext(unityContext); 214 break; 215 } 216 217 using (var logScope = new LogScope()) 218 { 219 while (true) 220 { 221 try 222 { 223 if (!enumerator.MoveNext()) 224 { 225 logScope.EvaluateLogScope(true); 226 break; 227 } 228 229 if (!AllowFrameSkipAfterAction(action)) // Evaluate the log scope right away for the commands where we do not yield 230 { 231 logScope.EvaluateLogScope(true); 232 } 233 if (enumerator.Current is IEditModeTestYieldInstruction) 234 { 235 if (unityContext.TestMode == TestPlatform.PlayMode) 236 { 237 throw new Exception($"PlayMode test are not allowed to yield {enumerator.Current.GetType().Name}"); 238 } 239 240 if (EnumeratorHelper.IsRunningNestedEnumerator) 241 { 242 throw new Exception($"Nested enumerators are not allowed to yield {enumerator.Current.GetType().Name}"); 243 } 244 } 245 } 246 catch (Exception ex) 247 { 248 context.CurrentResult.RecordPrefixedException(m_AfterErrorPrefix, ex); 249 state.StoreContext(unityContext); 250 break; 251 } 252 253 if (!EnumeratorHelper.IsRunningNestedEnumerator) 254 { 255 // Only store the state in the main enumerator. Domain reloads are not supported from nested enumerators. 256 state.NextAfterStepPc = EnumeratorHelper.GetEnumeratorPC(); 257 state.StoreContext(unityContext); 258 } 259 260 if (!AllowFrameSkipAfterAction(action)) 261 { 262 break; 263 } 264 265 yield return enumerator.Current; 266 } 267 } 268 269 state.NextAfterStepIndex++; 270 state.NextAfterStepPc = 0; 271 } 272 273 state.Reset(); 274 } 275 276 public override TestResult Execute(ITestExecutionContext context) 277 { 278 throw new NotImplementedException("Use ExecuteEnumerable"); 279 } 280 } 281}