1
2
3
4
5
6 package mockit;
7
8 import static org.junit.jupiter.api.Assertions.assertEquals;
9 import static org.junit.jupiter.api.Assertions.assertFalse;
10 import static org.junit.jupiter.api.Assertions.assertNotNull;
11 import static org.junit.jupiter.api.Assertions.assertNull;
12 import static org.junit.jupiter.api.Assertions.assertThrows;
13 import static org.junit.jupiter.api.Assertions.assertTrue;
14
15 import java.io.File;
16 import java.nio.file.Path;
17
18 import org.junit.jupiter.api.Test;
19 import org.junit.jupiter.api.Timeout;
20
21
22
23
24 final class ReentrantFakeTest {
25
26
27
28
29 public static class RealClass {
30
31
32
33
34
35
36 public String foo() {
37 return "real value";
38 }
39
40
41
42
43
44
45
46
47
48 protected static int staticRecursiveMethod(int i) {
49 return i <= 0 ? 0 : 2 + staticRecursiveMethod(i - 1);
50 }
51
52
53
54
55
56
57
58
59
60 public int recursiveMethod(int i) {
61 return i <= 0 ? 0 : 2 + recursiveMethod(i - 1);
62 }
63
64
65
66
67
68
69
70
71
72 protected static int nonRecursiveStaticMethod(int i) {
73 return -i;
74 }
75
76
77
78
79
80
81
82
83
84 public int nonRecursiveMethod(int i) {
85 return -i;
86 }
87 }
88
89
90
91
92 public static class AnnotatedFakeClass extends MockUp<RealClass> {
93
94
95 private static Boolean fakeIt;
96
97
98
99
100
101
102
103
104
105 @Mock
106 public String foo(Invocation inv) {
107 if (fakeIt == null) {
108 throw new IllegalStateException("null fakeIt");
109 } else if (fakeIt) {
110 return "fake value";
111 } else {
112 return inv.proceed();
113 }
114 }
115 }
116
117
118
119
120 @Test
121 void callFakeMethod() {
122 new AnnotatedFakeClass();
123 AnnotatedFakeClass.fakeIt = true;
124
125 String foo = new RealClass().foo();
126
127 assertEquals("fake value", foo);
128 }
129
130
131
132
133 @Test
134 void callOriginalMethod() {
135 new AnnotatedFakeClass();
136 AnnotatedFakeClass.fakeIt = false;
137
138 String foo = new RealClass().foo();
139
140 assertEquals("real value", foo);
141 }
142
143
144
145
146 @Test
147 void calledFakeThrowsException() {
148 assertThrows(IllegalStateException.class, () -> {
149 new AnnotatedFakeClass();
150 AnnotatedFakeClass.fakeIt = null;
151
152 new RealClass().foo();
153 });
154 }
155
156
157
158
159 public static class FakeRuntime extends MockUp<Runtime> {
160
161
162 private int runFinalizationCount;
163
164
165
166
167
168
169
170 @Mock
171 public void runFinalization(Invocation inv) {
172 if (runFinalizationCount < 2) {
173 inv.proceed();
174 }
175
176 runFinalizationCount++;
177 }
178
179
180
181
182
183
184
185
186
187
188
189 @Mock
190 public boolean removeShutdownHook(Invocation inv, Thread hook) {
191 if (hook == null) {
192
193 hook = Thread.currentThread();
194 }
195
196 return inv.proceed(hook);
197 }
198 }
199
200
201
202
203 @Test
204 void callFakeMethodForJREClass() {
205 Runtime runtime = Runtime.getRuntime();
206 new FakeRuntime();
207
208 runtime.runFinalization();
209 runtime.runFinalization();
210 runtime.runFinalization();
211
212 assertFalse(runtime.removeShutdownHook(null));
213 }
214
215
216
217
218 public static class ReentrantFakeForNativeMethod extends MockUp<Runtime> {
219
220
221
222
223
224
225
226
227
228 @Mock
229 public int availableProcessors(Invocation inv) {
230 assertNotNull(inv.getInvokedInstance());
231 return 5;
232 }
233 }
234
235
236
237
238 @Test
239 void applyReentrantFakeForNativeJREMethod() {
240 new ReentrantFakeForNativeMethod();
241
242 assertEquals(5, Runtime.getRuntime().availableProcessors());
243 }
244
245
246
247
248 @SuppressWarnings("SynchronizeOnThis")
249 static class MultiThreadedFake extends MockUp<RealClass> {
250
251
252 private static boolean nobodyEntered = true;
253
254
255
256
257
258
259
260
261
262
263
264
265 @Mock
266 public String foo(Invocation inv) throws InterruptedException {
267 String value = inv.proceed();
268
269 synchronized (MultiThreadedFake.class) {
270 if (nobodyEntered) {
271 nobodyEntered = false;
272
273 MultiThreadedFake.class.wait(5000);
274 } else {
275 MultiThreadedFake.class.notifyAll();
276 }
277 }
278
279 return value.replace("real", "fake");
280 }
281 }
282
283
284
285
286
287
288
289 @Test
290 @Timeout(1000)
291 void twoConcurrentThreadsCallingTheSameReentrantFake() throws Exception {
292 new MultiThreadedFake();
293
294 final StringBuilder first = new StringBuilder();
295 final StringBuilder second = new StringBuilder();
296
297 Thread thread1 = new Thread(() -> first.append(new RealClass().foo()));
298 thread1.start();
299
300 Thread thread2 = new Thread(() -> second.append(new RealClass().foo()));
301 thread2.start();
302
303 thread1.join();
304 thread2.join();
305
306 assertEquals("fake value", first.toString());
307 assertEquals("fake value", second.toString());
308 }
309
310
311
312
313 public static final class RealClass2 {
314
315
316
317
318
319
320 public int firstMethod() {
321 return 1;
322 }
323
324
325
326
327
328
329 public int secondMethod() {
330 return 2;
331 }
332 }
333
334
335
336
337 @Test
338 void reentrantFakeForNonJREClassWhichCallsAnotherFromADifferentThread() {
339 new MockUp<RealClass2>() {
340 int value;
341
342 @Mock
343 int firstMethod(Invocation inv) {
344 return inv.proceed();
345 }
346
347 @Mock
348 int secondMethod(Invocation inv) throws InterruptedException {
349 final RealClass2 it = inv.getInvokedInstance();
350
351 Thread t = new Thread() {
352 @Override
353 public void run() {
354 value = it.firstMethod();
355 }
356 };
357 t.start();
358 t.join();
359 return value;
360 }
361 };
362
363 RealClass2 r = new RealClass2();
364 assertEquals(1, r.firstMethod());
365 assertEquals(1, r.secondMethod());
366 }
367
368
369
370
371 @Test
372 void reentrantFakeForJREClassWhichCallsAnotherFromADifferentThread() {
373 System.setProperty("a", "1");
374 System.setProperty("b", "2");
375
376 new MockUp<System>() {
377 String property;
378
379 @Mock
380 String getProperty(Invocation inv, String key) {
381 return inv.proceed();
382 }
383
384 @Mock
385 String clearProperty(final String key) throws InterruptedException {
386 Thread t = new Thread() {
387 @Override
388 public void run() {
389 property = System.getProperty(key);
390 }
391 };
392 t.start();
393 t.join();
394 return property;
395 }
396 };
397
398 assertEquals("1", System.getProperty("a"));
399 assertEquals("2", System.clearProperty("b"));
400 }
401
402
403
404
405 @Test
406 void fakeFileAndForceJREToCallReentrantFakedMethod() {
407 new MockUp<File>() {
408 @Mock
409 boolean exists(Invocation inv) {
410 boolean exists = inv.proceed();
411 return !exists;
412 }
413 };
414
415
416 new Runnable() {
417 @Override
418 public void run() {
419 }
420 };
421
422 assertTrue(Path.of("noFile").toFile().exists());
423 }
424
425
426
427
428 public static final class RealClass3 {
429
430
431
432
433
434
435 public RealClass3 newInstance() {
436 return new RealClass3();
437 }
438 }
439
440
441
442
443 @Test
444 void reentrantFakeForMethodWhichInstantiatesAndReturnsNewInstanceOfTheFakedClass() {
445 new MockUp<RealClass3>() {
446 @Mock
447 RealClass3 newInstance(Invocation inv) {
448 return null;
449 }
450 };
451
452 assertNull(new RealClass3().newInstance());
453 }
454
455
456
457
458 public static final class FakeClassWithReentrantFakeForRecursiveMethod extends MockUp<RealClass> {
459
460
461
462
463
464
465
466
467
468
469
470 @Mock
471 int recursiveMethod(Invocation inv, int i) {
472 int j = inv.proceed();
473 return 1 + j;
474 }
475
476
477
478
479
480
481
482
483
484
485
486 @Mock
487 static int staticRecursiveMethod(Invocation inv, int i) {
488 int j = inv.proceed();
489 return 1 + j;
490 }
491 }
492
493
494
495
496 @Test
497 void reentrantFakeMethodForRecursiveMethods() {
498 assertEquals(0, RealClass.staticRecursiveMethod(0));
499 assertEquals(2, RealClass.staticRecursiveMethod(1));
500
501 RealClass r = new RealClass();
502 assertEquals(0, r.recursiveMethod(0));
503 assertEquals(2, r.recursiveMethod(1));
504
505 new FakeClassWithReentrantFakeForRecursiveMethod();
506
507 assertEquals(1, RealClass.staticRecursiveMethod(0));
508 assertEquals(1 + 2 + 1, RealClass.staticRecursiveMethod(1));
509 assertEquals(1, r.recursiveMethod(0));
510 assertEquals(4, r.recursiveMethod(1));
511 }
512
513
514
515
516 @Test
517 void fakeThatProceedsIntoRecursiveMethod() {
518 RealClass r = new RealClass();
519 assertEquals(0, r.recursiveMethod(0));
520 assertEquals(2, r.recursiveMethod(1));
521
522 new MockUp<RealClass>() {
523 @Mock
524 int recursiveMethod(Invocation inv, int i) {
525 int ret = inv.proceed();
526 return 1 + ret;
527 }
528 };
529
530 assertEquals(1, r.recursiveMethod(0));
531 assertEquals(4, r.recursiveMethod(1));
532 }
533
534
535
536
537 @Test
538 void recursiveFakeMethodWithoutInvocationParameter() {
539 new MockUp<RealClass>() {
540 @Mock
541 int nonRecursiveStaticMethod(int i) {
542 if (i > 1) {
543 return i;
544 }
545 return RealClass.nonRecursiveStaticMethod(i + 1);
546 }
547 };
548
549 int result = RealClass.nonRecursiveStaticMethod(1);
550 assertEquals(2, result);
551 }
552
553
554
555
556 @Test
557 void recursiveFakeMethodWithInvocationParameterNotUsedForProceeding() {
558 new MockUp<RealClass>() {
559 @Mock
560 int nonRecursiveMethod(Invocation inv, int i) {
561 if (i > 1) {
562 return i;
563 }
564 RealClass it = inv.getInvokedInstance();
565 return it.nonRecursiveMethod(i + 1);
566 }
567 };
568
569 int result = new RealClass().nonRecursiveMethod(1);
570 assertEquals(2, result);
571 }
572
573
574
575
576 @Test
577 void nonRecursiveFakeMethodWithInvocationParameterUsedForProceeding() {
578 new MockUp<RealClass>() {
579 @Mock
580 int nonRecursiveMethod(Invocation inv, int i) {
581 if (i > 1) {
582 return i;
583 }
584 return inv.proceed(i + 1);
585 }
586 };
587
588 int result = new RealClass().nonRecursiveMethod(1);
589 assertEquals(-2, result);
590 }
591 }