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