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}