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