1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.internal.utils;
21
22 import static org.junit.jupiter.api.Assertions.assertThrows;
23
24 import java.io.OutputStream;
25 import java.lang.reflect.Constructor;
26 import java.lang.reflect.Field;
27 import java.lang.reflect.Method;
28 import java.lang.reflect.Modifier;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.Map;
34 import java.util.Objects;
35 import java.util.Optional;
36 import java.util.Set;
37 import java.util.concurrent.Callable;
38 import java.util.concurrent.FutureTask;
39 import java.util.concurrent.TimeUnit;
40 import java.util.function.Predicate;
41 import java.util.function.Supplier;
42 import java.util.regex.Pattern;
43 import java.util.stream.Stream;
44
45 import org.junit.jupiter.api.function.Executable;
46 import org.mockito.internal.util.Checks;
47
48 import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean;
49 import com.puppycrawl.tools.checkstyle.PackageNamesLoader;
50 import com.puppycrawl.tools.checkstyle.PackageObjectFactory;
51 import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent;
52 import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
53 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
54 import com.puppycrawl.tools.checkstyle.api.AuditListener;
55 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
56 import com.puppycrawl.tools.checkstyle.api.DetailAST;
57 import com.puppycrawl.tools.checkstyle.api.TextBlock;
58
59 public final class TestUtil {
60
61
62
63
64
65
66
67
68
69 private static final int MINIMAL_STACK_SIZE = 147456;
70
71 private TestUtil() {
72 }
73
74
75
76
77
78
79
80 public static boolean isUtilsClassHasPrivateConstructor(final Class<?> utilClass)
81 throws ReflectiveOperationException {
82 final Constructor<?> constructor = utilClass.getDeclaredConstructor();
83 final boolean result = Modifier.isPrivate(constructor.getModifiers());
84 constructor.setAccessible(true);
85 constructor.newInstance();
86 return result;
87 }
88
89
90
91
92
93
94
95
96 private static Field getClassDeclaredField(Class<?> clss, String fieldName) {
97 return Stream.<Class<?>>iterate(clss, Objects::nonNull, Class::getSuperclass)
98 .flatMap(cls -> Arrays.stream(cls.getDeclaredFields()))
99 .filter(field -> fieldName.equals(field.getName()))
100 .findFirst()
101 .map(field -> {
102 field.setAccessible(true);
103 return field;
104 })
105 .orElseThrow(() -> {
106 return new IllegalStateException(String.format(Locale.ROOT,
107 "Field '%s' not found in '%s'", fieldName, clss.getCanonicalName()));
108 });
109 }
110
111
112
113
114
115
116
117
118
119 private static Method getClassDeclaredMethod(Class<?> clss,
120 String methodName,
121 int parameters) {
122 final Stream<Method> methods = Stream.<Class<?>>iterate(clss, Class::getSuperclass)
123 .flatMap(cls -> Arrays.stream(cls.getDeclaredMethods()))
124 .filter(method -> {
125 return methodName.equals(method.getName());
126 });
127
128 final Supplier<String> exceptionMessage = () -> {
129 return String.format(Locale.ROOT, "Method '%s' with %d parameters not found in '%s'",
130 methodName, parameters, clss.getCanonicalName());
131 };
132
133 return getMatchingExecutable(methods, parameters, exceptionMessage);
134 }
135
136
137
138
139
140
141
142
143
144
145 private static <T extends java.lang.reflect.Executable> T getMatchingExecutable(
146 Stream<T> execs, int parameters, Supplier<String> exceptionMessage) {
147 return execs.filter(method -> {
148 return parameters == method.getParameterCount();
149 })
150 .findFirst()
151 .map(method -> {
152 method.setAccessible(true);
153 return method;
154 })
155 .orElseThrow(() -> {
156 return new IllegalStateException(exceptionMessage.get());
157 });
158 }
159
160
161
162
163
164
165
166
167
168
169 public static boolean isStatefulFieldClearedDuringBeginTree(AbstractCheck check,
170 DetailAST astToVisit,
171 String fieldName,
172 Predicate<Object> isClear) {
173 check.beginTree(astToVisit);
174 check.visitToken(astToVisit);
175 check.beginTree(null);
176 return isClear.test(getInternalState(check, fieldName, Object.class));
177 }
178
179
180
181
182
183
184
185
186
187
188
189 public static boolean isStatefulFieldClearedDuringLocalSetup(
190 TreeWalkerFilter filter, TreeWalkerAuditEvent event,
191 String fieldName, Predicate<Object> isClear) throws Exception {
192 filter.accept(event);
193 invokeMethod(filter, "finishLocalSetup");
194 final Field resultField = getClassDeclaredField(filter.getClass(), fieldName);
195 return isClear.test(resultField.get(filter));
196 }
197
198
199
200
201
202
203 public static PackageObjectFactory getPackageObjectFactory() throws CheckstyleException {
204 final ClassLoader cl = TestUtil.class.getClassLoader();
205 final Set<String> packageNames = PackageNamesLoader.getPackageNames(cl);
206 return new PackageObjectFactory(packageNames, cl);
207 }
208
209
210
211
212
213
214
215
216
217 public static Optional<DetailAST> findTokenInAstByPredicate(DetailAST root,
218 Predicate<DetailAST> predicate) {
219 DetailAST curNode = root;
220 while (!predicate.test(curNode)) {
221 DetailAST toVisit = curNode.getFirstChild();
222 while (curNode != null && toVisit == null) {
223 toVisit = curNode.getNextSibling();
224 if (toVisit == null) {
225 curNode = curNode.getParent();
226 }
227 }
228
229 if (curNode == toVisit || curNode == root.getParent()) {
230 curNode = null;
231 break;
232 }
233
234 curNode = toVisit;
235 }
236 return Optional.ofNullable(curNode);
237 }
238
239
240
241
242
243
244
245
246
247
248 public static int getJdkVersion() {
249 String version = System.getProperty("java.specification.version");
250 if (version.startsWith("1.")) {
251 version = version.substring(2);
252 }
253 return Integer.parseInt(version);
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267 public static int adjustFlushCountForOutputStreamClose(int flushCount) {
268 int result = flushCount;
269 if (getJdkVersion() >= 13) {
270 ++result;
271 }
272 return result;
273 }
274
275
276
277
278
279
280
281
282
283
284
285
286 public static <V> V getResultWithLimitedResources(Callable<V> callable) throws Exception {
287 final FutureTask<V> futureTask = new FutureTask<>(callable);
288 final Thread thread = new Thread(null, futureTask,
289 "LimitedStackSizeThread", MINIMAL_STACK_SIZE);
290 thread.start();
291 return futureTask.get(10, TimeUnit.SECONDS);
292 }
293
294
295
296
297
298
299
300
301
302 public static <T> T getInternalState(Object instance, String fieldName, Class<T> clazz) {
303 try {
304 final Field field = getClassDeclaredField(instance.getClass(), fieldName);
305 return clazz.cast(field.get(instance));
306 }
307 catch (ReflectiveOperationException exc) {
308 final String message = String.format(Locale.ROOT,
309 "Failed to get field '%s' for instance of class '%s'",
310 fieldName, instance.getClass().getSimpleName());
311 throw new IllegalStateException(message, exc);
312 }
313 }
314
315
316
317
318
319
320
321
322
323
324 public static Map<String, String> getInternalStateMap(Object instance, String fieldName) {
325 return getInternalState(instance, fieldName, Map.class);
326 }
327
328
329
330
331
332
333
334
335
336
337 public static Map<Integer, List<TextBlock>> getInternalStateMapIntegerList(
338 Object instance, String fieldName) {
339 return getInternalState(instance, fieldName, Map.class);
340 }
341
342
343
344
345
346
347
348
349
350
351 public static List<AuditListener> getInternalStateListAuditListener(
352 Object instance, String fieldName) {
353 return getInternalState(instance, fieldName, List.class);
354 }
355
356
357
358
359
360
361
362
363
364
365 public static List<Pattern> getInternalStateListPattern(
366 Object instance, String fieldName) {
367 return getInternalState(instance, fieldName, List.class);
368 }
369
370
371
372
373
374
375
376
377
378
379 public static List<Comparable<Object>> getInternalStateListComparable(
380 Object instance, String fieldName) {
381 return getInternalState(instance, fieldName, List.class);
382 }
383
384
385
386
387
388
389
390
391
392
393 public static Collection<Checks> getInternalStateCollectionChecks(
394 Object instance, String fieldName) {
395 return getInternalState(instance, fieldName, Collection.class);
396 }
397
398
399
400
401
402
403
404
405
406
407 public static Set<TreeWalkerFilter> getInternalStateSetTreeWalkerFilter(
408 Object instance, String fieldName) {
409 return getInternalState(instance, fieldName, Set.class);
410 }
411
412
413
414
415
416
417
418
419
420
421 public static <T> T getInternalStaticState(Class<?> clss, String fieldName, Class<T> clazz) {
422 try {
423 final Field field = getClassDeclaredField(clss, fieldName);
424 return clazz.cast(field.get(null));
425 }
426 catch (ReflectiveOperationException exc) {
427 final String message = String.format(Locale.ROOT,
428 "Failed to get static field '%s' for class '%s'",
429 fieldName, clss);
430 throw new IllegalStateException(message, exc);
431 }
432 }
433
434
435
436
437
438
439
440
441
442
443 public static Map<String, String> getInternalStaticStateMap(Class<?> clss, String fieldName) {
444 return getInternalStaticState(clss, fieldName, Map.class);
445 }
446
447
448
449
450
451
452
453
454
455
456 public static ThreadLocal<List<Object>> getInternalStaticStateThreadLocal(
457 Class<?> clss, String fieldName) {
458 return getInternalStaticState(clss, fieldName, ThreadLocal.class);
459 }
460
461
462
463
464
465
466
467
468
469
470 public static void setInternalState(Object instance, String fieldName, Object value) {
471 try {
472 final Field field = getClassDeclaredField(instance.getClass(), fieldName);
473 field.set(instance, value);
474 }
475 catch (ReflectiveOperationException exc) {
476 final String message = String.format(Locale.ROOT,
477 "Failed to set field '%s' for instance of class '%s'",
478 fieldName, instance.getClass().getSimpleName());
479 throw new IllegalStateException(message, exc);
480 }
481 }
482
483
484
485
486
487
488
489
490
491
492
493
494
495 public static <T> T invokeMethod(Object instance,
496 String methodToExecute, Object... arguments) throws ReflectiveOperationException {
497 final Class<?> clss = instance.getClass();
498 final Method method = getClassDeclaredMethod(clss, methodToExecute, arguments.length);
499 return (T) method.invoke(instance, arguments);
500 }
501
502
503
504
505
506
507
508
509
510
511
512
513
514 public static <T> T invokeStaticMethod(Class<?> clss,
515 String methodToExecute, Object... arguments) throws ReflectiveOperationException {
516 final Method method = getClassDeclaredMethod(clss, methodToExecute, arguments.length);
517 return (T) method.invoke(null, arguments);
518 }
519
520
521
522
523
524
525
526
527
528
529
530 @SuppressWarnings("unchecked")
531 public static <T> T instantiate(Class<T> clss, Object... arguments)
532 throws ReflectiveOperationException {
533
534 final Stream<Constructor<T>> ctors =
535 Arrays.stream(clss.getDeclaredConstructors()).map(Constructor.class::cast);
536
537 final Supplier<String> exceptionMessage = () -> {
538 return String.format(Locale.ROOT, "Constructor with %d parameters not found in '%s'",
539 arguments.length, clss.getCanonicalName());
540 };
541
542 final Constructor<T> constructor =
543 getMatchingExecutable(ctors, arguments.length, exceptionMessage);
544 constructor.setAccessible(true);
545
546 return constructor.newInstance(arguments);
547 }
548
549
550
551
552
553
554
555
556
557
558
559 public static <T> Class<T> getInnerClassType(Class<?> declaringClass, String name)
560 throws ClassNotFoundException {
561 return (Class<T>) Class.forName(declaringClass.getName() + "$" + name);
562 }
563
564
565
566
567
568
569
570
571
572 public static <T extends Throwable> T getExpectedThrowable(Class<T> expectedType,
573 Executable executable,
574 String message) {
575 return assertThrows(expectedType, executable, message);
576 }
577
578
579
580
581
582
583
584
585 public static <T extends Throwable> T getExpectedThrowable(Class<T> expectedType,
586 Executable executable) {
587 return assertThrows(expectedType, executable);
588 }
589
590 }