1
2
3
4
5 package mockit.integration;
6
7 import static mockit.internal.reflection.ParameterReflection.getParameterCount;
8
9 import edu.umd.cs.findbugs.annotations.NonNull;
10 import edu.umd.cs.findbugs.annotations.Nullable;
11
12 import java.lang.reflect.Method;
13 import java.util.Collections;
14 import java.util.List;
15
16 import mockit.internal.expectations.RecordAndReplayExecution;
17 import mockit.internal.expectations.invocation.MissingInvocation;
18 import mockit.internal.expectations.invocation.UnexpectedInvocation;
19 import mockit.internal.expectations.mocking.FieldTypeRedefinitions;
20 import mockit.internal.expectations.mocking.ParameterTypeRedefinitions;
21 import mockit.internal.expectations.mocking.TypeRedefinitions;
22 import mockit.internal.injection.InjectionProvider;
23 import mockit.internal.injection.TestedClassInstantiations;
24 import mockit.internal.injection.TestedParameters;
25 import mockit.internal.state.SavePoint;
26 import mockit.internal.state.TestRun;
27 import mockit.internal.util.ParameterNameExtractor;
28 import mockit.internal.util.StackTrace;
29 import mockit.internal.util.TestMethod;
30
31
32
33
34
35 public class TestRunnerDecorator {
36 @Nullable
37 private static SavePoint savePointForTestClass;
38 @Nullable
39 private static SavePoint savePointForTest;
40
41
42
43
44
45 protected volatile boolean shouldPrepareForNextTest;
46
47 protected TestRunnerDecorator() {
48 shouldPrepareForNextTest = true;
49 }
50
51 protected static void updateTestClassState(@Nullable Object target, @NonNull Class<?> testClass) {
52 testClass = getActualTestClass(testClass);
53
54 try {
55 handleSwitchToNewTestClassIfApplicable(testClass);
56
57 if (target != null) {
58 handleMockFieldsForWholeTestClass(target);
59 }
60 } catch (Error e) {
61 try {
62 rollbackForTestClass();
63 } catch (Error err) {
64 StackTrace.filterStackTrace(err);
65 throw err;
66 }
67
68 throw e;
69 } catch (RuntimeException e) {
70 rollbackForTestClass();
71 StackTrace.filterStackTrace(e);
72 throw e;
73 }
74 }
75
76 @NonNull
77 private static Class<?> getActualTestClass(@NonNull Class<?> testClass) {
78 return testClass.isSynthetic() ? testClass.getSuperclass() : testClass;
79 }
80
81 private static void handleSwitchToNewTestClassIfApplicable(@NonNull Class<?> testClass) {
82 Class<?> currentTestClass = TestRun.getCurrentTestClass();
83
84 if (testClass != currentTestClass) {
85 if (currentTestClass == null) {
86 savePointForTestClass = new SavePoint();
87 } else if (!currentTestClass.isAssignableFrom(testClass)) {
88 cleanUpMocksFromPreviousTestClass();
89 savePointForTestClass = new SavePoint();
90 }
91
92 TestRun.setCurrentTestClass(testClass);
93 }
94 }
95
96 public static void cleanUpMocksFromPreviousTestClass() {
97 cleanUpMocks(true);
98 }
99
100 protected static void cleanUpMocksFromPreviousTest() {
101 cleanUpMocks(false);
102 }
103
104 public static void cleanUpAllMocks() {
105 cleanUpMocks(true);
106 TestRun.getFakeClasses().discardStartupFakes();
107 }
108
109 private static void cleanUpMocks(boolean forTestClassAsWell) {
110 discardTestLevelMockedTypes();
111
112 if (forTestClassAsWell) {
113 rollbackForTestClass();
114 }
115
116 clearFieldTypeRedefinitions();
117 }
118
119 private static void rollbackForTestClass() {
120 SavePoint savePoint = savePointForTestClass;
121
122 if (savePoint != null) {
123 savePoint.rollback();
124 savePointForTestClass = null;
125 }
126 }
127
128 protected static void clearFieldTypeRedefinitions() {
129 TypeRedefinitions fieldTypeRedefinitions = TestRun.getFieldTypeRedefinitions();
130
131 if (fieldTypeRedefinitions != null) {
132 fieldTypeRedefinitions.cleanUp();
133 TestRun.setFieldTypeRedefinitions(null);
134 }
135 }
136
137 protected static void prepareForNextTest() {
138 if (savePointForTest == null) {
139 savePointForTest = new SavePoint();
140 }
141
142 TestRun.prepareForNextTest();
143 }
144
145 protected static void discardTestLevelMockedTypes() {
146 SavePoint savePoint = savePointForTest;
147
148 if (savePoint != null) {
149 savePoint.rollback();
150 savePointForTest = null;
151 }
152 }
153
154 protected static void handleMockFieldsForWholeTestClass(@NonNull Object target) {
155 Class<?> testClass = getActualTestClass(target.getClass());
156 FieldTypeRedefinitions fieldTypeRedefinitions = TestRun.getFieldTypeRedefinitions();
157
158 if (fieldTypeRedefinitions == null) {
159 ParameterNameExtractor.extractNames(testClass);
160
161 fieldTypeRedefinitions = new FieldTypeRedefinitions(testClass);
162 TestRun.setFieldTypeRedefinitions(fieldTypeRedefinitions);
163
164 TestedClassInstantiations testedClassInstantiations = new TestedClassInstantiations();
165
166 if (!testedClassInstantiations.findTestedAndInjectableMembers(testClass)) {
167 testedClassInstantiations = null;
168 }
169
170 TestRun.setTestedClassInstantiations(testedClassInstantiations);
171 }
172
173
174 if (target != TestRun.getCurrentTestInstance()) {
175 fieldTypeRedefinitions.assignNewInstancesToMockFields(target);
176 }
177 }
178
179 protected static void createInstancesForTestedFieldsFromBaseClasses(@NonNull Object testClassInstance) {
180 TestedClassInstantiations testedClasses = TestRun.getTestedClassInstantiations();
181
182 if (testedClasses != null) {
183 TestRun.enterNoMockingZone();
184
185 try {
186 testedClasses.assignNewInstancesToTestedFieldsFromBaseClasses(testClassInstance);
187 } finally {
188 TestRun.exitNoMockingZone();
189 }
190 }
191 }
192
193 protected static void createInstancesForTestedFieldsBeforeSetup(@NonNull Object testClassInstance) {
194 TestedClassInstantiations testedClasses = TestRun.getTestedClassInstantiations();
195
196 if (testedClasses != null) {
197 TestRun.enterNoMockingZone();
198
199 try {
200 testedClasses.assignNewInstancesToTestedFields(testClassInstance, true,
201 Collections.<InjectionProvider> emptyList());
202 } finally {
203 TestRun.exitNoMockingZone();
204 }
205 }
206 }
207
208 protected static void createInstancesForTestedFields(@NonNull Object testClassInstance) {
209 TestedClassInstantiations testedClasses = TestRun.getTestedClassInstantiations();
210
211 if (testedClasses != null) {
212 List<? extends InjectionProvider> injectableParameters = Collections.emptyList();
213 ParameterTypeRedefinitions paramTypeRedefs = TestRun.getExecutingTest().getParameterRedefinitions();
214
215 if (paramTypeRedefs != null) {
216 injectableParameters = paramTypeRedefs.getInjectableParameters();
217 }
218
219 TestRun.enterNoMockingZone();
220
221 try {
222 testedClasses.assignNewInstancesToTestedFields(testClassInstance, false, injectableParameters);
223 } finally {
224 TestRun.exitNoMockingZone();
225 }
226 }
227 }
228
229 @Nullable
230 protected static Object[] createInstancesForAnnotatedParameters(@NonNull Object testClassInstance,
231 @NonNull Method testMethod, @Nullable Object[] parameterValues) {
232 int numParameters = getParameterCount(testMethod);
233
234 if (numParameters == 0) {
235 return null;
236 }
237
238 if (parameterValues == null || parameterValues.length != numParameters) {
239
240 parameterValues = new Object[numParameters];
241 }
242
243 TestMethod methodInfo = new TestMethod(testMethod, parameterValues);
244
245 TestRun.enterNoMockingZone();
246
247 try {
248 ParameterTypeRedefinitions redefinitions = new ParameterTypeRedefinitions(methodInfo, parameterValues);
249 TestRun.getExecutingTest().setParameterRedefinitions(redefinitions);
250
251 TestedParameters testedParameters = new TestedParameters(methodInfo);
252 List<? extends InjectionProvider> injectableParameters = redefinitions.getInjectableParameters();
253 testedParameters.createTestedParameters(testClassInstance, injectableParameters);
254 } finally {
255 TestRun.exitNoMockingZone();
256 }
257
258 return parameterValues;
259 }
260
261 protected static void concludeTestMethodExecution(@NonNull SavePoint savePoint, @Nullable Throwable thrownByTest,
262 boolean thrownAsExpected) throws Throwable {
263 TestRun.enterNoMockingZone();
264
265 Error expectationsFailure = RecordAndReplayExecution.endCurrentReplayIfAny();
266
267 try {
268 clearTestedObjectsIfAny();
269 } finally {
270 savePoint.rollback();
271 TestRun.exitNoMockingZone();
272 }
273
274 if (thrownByTest != null) {
275 if (expectationsFailure == null || !thrownAsExpected || isUnexpectedOrMissingInvocation(thrownByTest)) {
276 throw thrownByTest;
277 }
278
279 Throwable expectationsFailureCause = expectationsFailure.getCause();
280
281 if (expectationsFailureCause != null) {
282 expectationsFailureCause.initCause(thrownByTest);
283 }
284 }
285
286 if (expectationsFailure != null) {
287 throw expectationsFailure;
288 }
289 }
290
291 protected static void clearTestedObjectsIfAny() {
292 TestedClassInstantiations testedClasses = TestRun.getTestedClassInstantiations();
293
294 if (testedClasses != null) {
295 testedClasses.clearTestedObjects();
296 }
297 }
298
299 protected static void clearTestedObjectsCreatedDuringSetup() {
300 TestedClassInstantiations testedClasses = TestRun.getTestedClassInstantiations();
301
302 if (testedClasses != null) {
303 testedClasses.clearTestedObjectsCreatedDuringSetup();
304 }
305 }
306
307 private static boolean isUnexpectedOrMissingInvocation(@NonNull Throwable error) {
308 Class<?> errorType = error.getClass();
309 return errorType == UnexpectedInvocation.class || errorType == MissingInvocation.class;
310 }
311 }