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;
21
22 import static com.google.common.truth.Truth.assertWithMessage;
23
24 import java.lang.reflect.Field;
25 import java.lang.reflect.Modifier;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.BitSet;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.Map;
34 import java.util.Map.Entry;
35 import java.util.Properties;
36 import java.util.Set;
37 import java.util.TreeMap;
38 import java.util.stream.Collectors;
39 import java.util.stream.Stream;
40
41 import org.junit.jupiter.api.Test;
42
43 import com.google.common.base.Splitter;
44 import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
45 import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
46 import com.puppycrawl.tools.checkstyle.Definitions;
47 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
48 import com.puppycrawl.tools.checkstyle.GlobalStatefulCheck;
49 import com.puppycrawl.tools.checkstyle.ModuleFactory;
50 import com.puppycrawl.tools.checkstyle.StatelessCheck;
51 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
52 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
53 import com.puppycrawl.tools.checkstyle.api.Configuration;
54 import com.puppycrawl.tools.checkstyle.checks.imports.ImportControlCheck;
55 import com.puppycrawl.tools.checkstyle.internal.utils.CheckUtil;
56 import com.puppycrawl.tools.checkstyle.internal.utils.ConfigurationUtil;
57 import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
58 import com.puppycrawl.tools.checkstyle.internal.utils.XdocUtil;
59 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
60 import com.puppycrawl.tools.checkstyle.utils.ModuleReflectionUtil;
61 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
62
63 public class AllChecksTest extends AbstractModuleTestSupport {
64
65 private static final Locale[] ALL_LOCALES = {
66 Locale.CHINESE,
67 Locale.ENGLISH,
68 Locale.of("es"),
69 Locale.of("fi"),
70 Locale.FRENCH,
71 Locale.GERMAN,
72 Locale.JAPANESE,
73 Locale.of("pt"),
74 Locale.of("ru"),
75 Locale.of("tr"),
76 };
77
78 private static final Map<String, Set<String>> CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE =
79 new HashMap<>();
80 private static final Map<String, Set<String>> GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE =
81 new HashMap<>();
82
83
84
85
86
87 private static final Set<String> MODULES_NOT_IN_CHECKSTYLE_CONFIG = Set.of(
88 "GoogleNonConstantFieldName"
89 );
90
91 private static final Set<String> INTERNAL_MODULES;
92
93 static {
94
95
96 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("NoWhitespaceBefore", Stream.of(
97
98 "GENERIC_START", "GENERIC_END").collect(Collectors.toUnmodifiableSet()));
99 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("AbbreviationAsWordInName", Stream.of(
100
101 "ENUM_CONSTANT_DEF").collect(Collectors.toUnmodifiableSet()));
102 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("FinalLocalVariable", Stream.of(
103
104
105 "PARAMETER_DEF").collect(Collectors.toUnmodifiableSet()));
106
107 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("IllegalToken",
108 Stream.of("LITERAL_SUPER", "LITERAL_ASSERT", "ENUM_CONSTANT_DEF",
109 "TYPE_PARAMETERS", "TYPE_UPPER_BOUNDS", "NUM_DOUBLE", "LITERAL_SWITCH",
110 "ANNOTATIONS", "LITERAL_SHORT", "LITERAL_PROTECTED", "FOR_CONDITION",
111 "FOR_INIT", "LITERAL_LONG", "MINUS", "OBJBLOCK", "LITERAL_NULL",
112 "ANNOTATION", "LITERAL_TRUE", "COMMENT_CONTENT", "LITERAL_CHAR",
113 "PARAMETER_DEF", "POST_DEC", "ANNOTATION_FIELD_DEF", "BLOCK_COMMENT_END",
114 "TYPE", "LITERAL_INT", "BSR", "ENUM", "ANNOTATION_MEMBER_VALUE_PAIR",
115 "TYPECAST", "LITERAL_SYNCHRONIZED", "PLUS_ASSIGN", "DOT", "LPAREN",
116 "LITERAL_IF", "LITERAL_CATCH", "BAND", "INTERFACE_DEF", "LOR", "BNOT",
117 "METHOD_CALL", "AT", "ELLIPSIS", "ARRAY_INIT", "FOR_EACH_CLAUSE",
118 "LITERAL_THROWS", "CHAR_LITERAL", "CASE_GROUP", "POST_INC", "SEMI",
119 "LITERAL_FINALLY", "ASSIGN", "RESOURCE_SPECIFICATION", "STATIC_IMPORT",
120 "GENERIC_START", "IMPORT", "SL", "VARIABLE_DEF", "LITERAL_DOUBLE",
121 "RCURLY", "RESOURCE", "SR", "COMMA", "BAND_ASSIGN", "METHOD_DEF",
122 "LITERAL_VOID", "NUM_LONG", "LITERAL_TRANSIENT", "LITERAL_THIS", "LCURLY",
123 "MINUS_ASSIGN", "TYPE_LOWER_BOUNDS", "TYPE_ARGUMENT", "LITERAL_CLASS",
124 "INSTANCE_INIT", "DIV", "STAR", "UNARY_MINUS", "FOR_ITERATOR", "NOT_EQUAL",
125 "LE", "LITERAL_INTERFACE", "LITERAL_FLOAT", "LITERAL_INSTANCEOF",
126 "BOR_ASSIGN", "LT", "SL_ASSIGN", "ELIST", "ANNOTATION_ARRAY_INIT",
127 "MODIFIERS", "LITERAL_BREAK", "EXTENDS_CLAUSE", "TYPE_PARAMETER",
128 "LITERAL_DEFAULT", "STATIC_INIT", "BSR_ASSIGN", "TYPE_EXTENSION_AND",
129 "BOR", "LITERAL_PRIVATE", "LITERAL_THROW", "LITERAL_BYTE", "BXOR",
130 "WILDCARD_TYPE", "FINAL", "PARAMETERS", "RPAREN", "SR_ASSIGN",
131 "UNARY_PLUS", "EMPTY_STAT", "LITERAL_STATIC", "LITERAL_CONTINUE",
132 "STAR_ASSIGN", "LAMBDA", "RBRACK", "BXOR_ASSIGN", "CTOR_CALL",
133 "LITERAL_FALSE", "DO_WHILE", "LITERAL_PUBLIC", "LITERAL_WHILE", "PLUS",
134 "INC", "CTOR_DEF", "GENERIC_END", "DIV_ASSIGN", "SLIST", "LNOT", "LAND",
135 "LITERAL_ELSE", "ABSTRACT", "STRICTFP", "QUESTION", "LITERAL_NEW",
136 "LITERAL_RETURN", "SINGLE_LINE_COMMENT", "INDEX_OP", "EXPR",
137 "BLOCK_COMMENT_BEGIN", "PACKAGE_DEF", "IMPLEMENTS_CLAUSE", "NUM_FLOAT",
138 "LITERAL_DO", "EOF", "GE", "RESOURCES", "MOD", "DEC", "EQUAL",
139 "LITERAL_BOOLEAN", "CLASS_DEF", "COLON", "LITERAL_TRY", "ENUM_DEF", "GT",
140 "NUM_INT", "ANNOTATION_DEF", "METHOD_REF", "TYPE_ARGUMENTS",
141 "DOUBLE_COLON", "IDENT", "MOD_ASSIGN", "LITERAL_FOR", "SUPER_CTOR_CALL",
142 "STRING_LITERAL", "ARRAY_DECLARATOR", "LITERAL_CASE",
143 "PATTERN_VARIABLE_DEF", "RECORD_DEF", "LITERAL_RECORD",
144 "RECORD_COMPONENTS", "RECORD_COMPONENT_DEF", "COMPACT_CTOR_DEF",
145 "TEXT_BLOCK_LITERAL_BEGIN", "TEXT_BLOCK_CONTENT", "TEXT_BLOCK_LITERAL_END",
146 "LITERAL_YIELD", "SWITCH_RULE")
147 .collect(Collectors.toUnmodifiableSet()));
148
149 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("IllegalTokenText",
150 Stream.of("NUM_DOUBLE", "NUM_FLOAT", "NUM_INT", "NUM_LONG", "IDENT",
151 "COMMENT_CONTENT", "STRING_LITERAL", "CHAR_LITERAL", "TEXT_BLOCK_CONTENT")
152 .collect(Collectors.toUnmodifiableSet()));
153
154 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("WriteTag",
155 Stream.of("ENUM_CONSTANT_DEF", "METHOD_DEF", "CTOR_DEF",
156 "ANNOTATION_FIELD_DEF", "RECORD_DEF", "COMPACT_CTOR_DEF")
157 .collect(Collectors.toUnmodifiableSet()));
158
159
160 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("AnnotationLocation",
161 Stream.of("CLASS_DEF", "CTOR_DEF", "ENUM_DEF", "INTERFACE_DEF",
162 "METHOD_DEF", "VARIABLE_DEF",
163 "RECORD_DEF", "COMPACT_CTOR_DEF")
164 .collect(Collectors.toUnmodifiableSet()));
165 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("NoLineWrap", Stream.of(
166
167
168 "METHOD_DEF", "CTOR_DEF", "COMPACT_CTOR_DEF",
169
170
171 "CLASS_DEF", "ENUM_DEF", "INTERFACE_DEF", "RECORD_DEF")
172 .collect(Collectors.toUnmodifiableSet()));
173 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("NoWhitespaceAfter", Stream.of(
174
175 "TYPECAST", "LITERAL_SYNCHRONIZED").collect(Collectors.toUnmodifiableSet()));
176 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("SeparatorWrap", Stream.of(
177
178
179 "LPAREN", "RPAREN").collect(Collectors.toUnmodifiableSet()));
180 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("NeedBraces", Stream.of(
181
182
183 "LITERAL_DEFAULT", "LITERAL_CASE").collect(Collectors.toUnmodifiableSet()));
184 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("FinalParameters", Stream.of(
185
186 "FOR_EACH_CLAUSE", "LITERAL_CATCH", "PATTERN_VARIABLE_DEF")
187 .collect(Collectors.toUnmodifiableSet()));
188 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("WhitespaceAround", Stream.of(
189
190 "ARRAY_INIT",
191 "ELLIPSIS",
192
193 "WILDCARD_TYPE", "GENERIC_END", "GENERIC_START")
194 .collect(Collectors.toUnmodifiableSet()));
195
196
197 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("AnnotationLocation", Stream.of(
198
199
200 "ANNOTATION_DEF", "ANNOTATION_FIELD_DEF", "ENUM_CONSTANT_DEF", "PACKAGE_DEF")
201 .collect(Collectors.toUnmodifiableSet()));
202 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("AbbreviationAsWordInName", Stream.of(
203
204 "ENUM_CONSTANT_DEF").collect(Collectors.toUnmodifiableSet()));
205 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("NoLineWrap", Stream.of(
206
207
208 "METHOD_DEF", "CTOR_DEF", "CLASS_DEF", "ENUM_DEF", "INTERFACE_DEF", "RECORD_DEF",
209 "COMPACT_CTOR_DEF")
210 .collect(Collectors.toUnmodifiableSet()));
211 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("SeparatorWrap", Stream.of(
212
213
214 "RBRACK",
215
216
217 "AT",
218
219
220 "SEMI",
221
222
223 "LPAREN", "RPAREN").collect(Collectors.toUnmodifiableSet()));
224 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("NeedBraces", Stream.of(
225
226 "LAMBDA", "LITERAL_DEFAULT", "LITERAL_CASE")
227 .collect(Collectors.toUnmodifiableSet()));
228 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("EmptyBlock", Stream.of(
229
230 "LITERAL_DEFAULT", "LITERAL_CASE",
231
232 "LITERAL_CATCH",
233
234 "ARRAY_INIT",
235
236
237 "INSTANCE_INIT", "LITERAL_DO", "LITERAL_FOR", "LITERAL_SYNCHRONIZED",
238 "LITERAL_WHILE", "STATIC_INIT").collect(Collectors.toUnmodifiableSet()));
239 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("WhitespaceAround", Stream.of(
240
241 "ARRAY_INIT",
242
243 "ELLIPSIS",
244
245 "GENERIC_START", "GENERIC_END", "WILDCARD_TYPE")
246 .collect(Collectors.toUnmodifiableSet()));
247 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("WhitespaceAfter", Stream.of(
248
249
250 "LITERAL_IF", "LITERAL_ELSE", "LITERAL_RETURN", "LITERAL_WHILE",
251 "LITERAL_DO", "LITERAL_FOR", "LITERAL_FINALLY", "DO_WHILE",
252 "LITERAL_SWITCH", "LITERAL_SYNCHRONIZED", "LITERAL_TRY", "LITERAL_CATCH",
253 "LAMBDA", "LITERAL_WHEN")
254 .collect(Collectors.toUnmodifiableSet()));
255 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("IllegalTokenText", Stream.of(
256
257 "NUM_DOUBLE", "NUM_FLOAT", "NUM_INT", "NUM_LONG",
258
259 "IDENT",
260
261 "COMMENT_CONTENT"
262 )
263 .collect(Collectors.toUnmodifiableSet()));
264 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("OperatorWrap", Stream.of(
265
266
267 "DIV_ASSIGN", "BOR_ASSIGN", "SL_ASSIGN", "ASSIGN", "BSR_ASSIGN", "BAND_ASSIGN",
268 "PLUS_ASSIGN", "MINUS_ASSIGN", "SR_ASSIGN", "STAR_ASSIGN", "BXOR_ASSIGN",
269 "MOD_ASSIGN",
270
271
272 "COLON").collect(Collectors.toUnmodifiableSet()));
273 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("NoWhitespaceBefore", Stream.of(
274
275 "GENERIC_START", "GENERIC_END",
276
277
278 "ELLIPSIS").collect(Collectors.toUnmodifiableSet()));
279 INTERNAL_MODULES = Definitions.INTERNAL_MODULES.stream()
280 .map(moduleName -> {
281 final List<String> packageTokens = Splitter
282 .on(".").splitToList(moduleName);
283 return packageTokens.getLast();
284 })
285 .collect(Collectors.toUnmodifiableSet());
286 }
287
288 @Override
289 public String getPackageLocation() {
290 return "com/puppycrawl/tools/checkstyle/internal/allchecks";
291 }
292
293 @Test
294 public void testAllModulesWithDefaultConfiguration() throws Exception {
295 final String inputFilePath = getPath("InputAllChecksDefaultConfig.java");
296 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
297
298 for (Class<?> module : CheckUtil.getCheckstyleModules()) {
299 if (ModuleReflectionUtil.isRootModule(module)) {
300 continue;
301 }
302
303 final DefaultConfiguration moduleConfig = createModuleConfig(module);
304 if (module.equals(ImportControlCheck.class)) {
305
306
307 moduleConfig.addProperty("file", getPath(
308 "InputAllChecksImportControl.xml"));
309 }
310 verify(moduleConfig, inputFilePath, expected);
311 }
312 }
313
314 @Test
315 public void testDefaultTokensAreSubsetOfAcceptableTokens() throws Exception {
316 for (Class<?> check : CheckUtil.getCheckstyleChecks()) {
317 if (AbstractCheck.class.isAssignableFrom(check)) {
318 final AbstractCheck testedCheck = (AbstractCheck) TestUtil.instantiate(check);
319 final int[] defaultTokens = testedCheck.getDefaultTokens();
320 final int[] acceptableTokens = testedCheck.getAcceptableTokens();
321
322 assertWithMessage("%s's default tokens must be a subset of acceptable tokens.",
323 check.getName())
324 .that(isSubset(defaultTokens, acceptableTokens))
325 .isTrue();
326 }
327 }
328 }
329
330 @Test
331 public void testRequiredTokensAreSubsetOfAcceptableTokens() throws Exception {
332 for (Class<?> check : CheckUtil.getCheckstyleChecks()) {
333 if (AbstractCheck.class.isAssignableFrom(check)) {
334 final AbstractCheck testedCheck = (AbstractCheck) TestUtil.instantiate(check);
335 final int[] requiredTokens = testedCheck.getRequiredTokens();
336 final int[] acceptableTokens = testedCheck.getAcceptableTokens();
337
338 assertWithMessage("%s's required tokens must be a subset of acceptable tokens.",
339 check.getName())
340 .that(isSubset(requiredTokens, acceptableTokens))
341 .isTrue();
342 }
343 }
344 }
345
346 @Test
347 public void testRequiredTokensAreSubsetOfDefaultTokens() throws Exception {
348 for (Class<?> check : CheckUtil.getCheckstyleChecks()) {
349 if (AbstractCheck.class.isAssignableFrom(check)) {
350 final AbstractCheck testedCheck = (AbstractCheck) TestUtil.instantiate(check);
351 final int[] defaultTokens = testedCheck.getDefaultTokens();
352 final int[] requiredTokens = testedCheck.getRequiredTokens();
353
354 assertWithMessage("%s's required tokens must be a subset of default tokens.",
355 check.getName())
356 .that(isSubset(requiredTokens, defaultTokens))
357 .isTrue();
358 }
359 }
360 }
361
362 @Test
363 public void testAllModulesHaveMultiThreadAnnotation() throws Exception {
364 for (Class<?> module : CheckUtil.getCheckstyleModules()) {
365 if (ModuleReflectionUtil.isRootModule(module)
366 || ModuleReflectionUtil.isFilterModule(module)
367 || ModuleReflectionUtil.isFileFilterModule(module)
368 || ModuleReflectionUtil.isTreeWalkerFilterModule(module)) {
369 continue;
370 }
371
372 assertWithMessage("module '%s' must contain a multi-thread annotation",
373 module.getSimpleName())
374 .that(module.isAnnotationPresent(GlobalStatefulCheck.class)
375 || module.isAnnotationPresent(FileStatefulCheck.class)
376 || module.isAnnotationPresent(StatelessCheck.class))
377 .isTrue();
378 }
379 }
380
381 @Test
382 public void testAllModulesAreReferencedInConfigFile() throws Exception {
383 final Set<String> modulesReferencedInConfig = CheckUtil.getConfigCheckStyleModules();
384 final Set<String> moduleNames = CheckUtil.getSimpleNames(CheckUtil.getCheckstyleModules());
385
386 moduleNames.removeAll(INTERNAL_MODULES);
387 moduleNames.removeAll(MODULES_NOT_IN_CHECKSTYLE_CONFIG);
388 moduleNames.stream().filter(check -> !modulesReferencedInConfig.contains(check))
389 .forEach(check -> {
390 final String errorMessage = String.format(Locale.ROOT,
391 "%s is not referenced in checkstyle-checks.xml", check);
392 assertWithMessage(errorMessage).fail();
393 });
394 }
395
396 @Test
397 public void testAllCheckTokensAreReferencedInCheckstyleConfigFile() throws Exception {
398 final Configuration configuration = ConfigurationUtil
399 .loadConfiguration("config/checkstyle-checks.xml");
400
401 validateAllCheckTokensAreReferencedInConfigFile("checkstyle", configuration,
402 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE, false);
403 }
404
405 @Test
406 public void testAllCheckTokensAreReferencedInGoogleConfigFile() throws Exception {
407 final Configuration configuration = ConfigurationUtil
408 .loadConfiguration("src/main/resources/google_checks.xml");
409
410 validateAllCheckTokensAreReferencedInConfigFile("google", configuration,
411 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE, true);
412 }
413
414 private static void validateAllCheckTokensAreReferencedInConfigFile(String configName,
415 Configuration configuration, Map<String, Set<String>> tokensToIgnore,
416 boolean defaultTokensMustBeExplicit) throws Exception {
417 final ModuleFactory moduleFactory = TestUtil.getPackageObjectFactory();
418 final Set<Configuration> configChecks = ConfigurationUtil.getChecks(configuration);
419
420 final Map<String, Set<String>> configCheckTokens = new HashMap<>();
421 final Map<String, Set<String>> checkTokens = new HashMap<>();
422
423 for (Configuration checkConfig : configChecks) {
424 final String checkName = checkConfig.getName();
425 final Object instance;
426
427 try {
428 instance = moduleFactory.createModule(checkName);
429 }
430 catch (CheckstyleException exc) {
431 throw new CheckstyleException("Couldn't find check: " + checkName, exc);
432 }
433 final AbstractCheck check;
434 if (instance instanceof AbstractCheck abstractCheck
435 && !isAllTokensAcceptable(abstractCheck)) {
436 check = abstractCheck;
437 }
438 else {
439
440 continue;
441 }
442
443 Set<String> configTokens = configCheckTokens.get(checkName);
444
445 if (configTokens == null) {
446 configTokens = new HashSet<>();
447
448 configCheckTokens.put(checkName, configTokens);
449
450
451 final Set<String> overrideTokens = tokensToIgnore.get(checkName);
452
453 if (overrideTokens != null) {
454 configTokens.addAll(overrideTokens);
455 }
456
457 configTokens.addAll(CheckUtil.getTokenNameSet(check.getRequiredTokens()));
458 checkTokens.put(checkName,
459 CheckUtil.getTokenNameSet(check.getAcceptableTokens()));
460 }
461
462 try {
463 configTokens.addAll(Arrays.asList(checkConfig.getProperty("tokens").trim()
464 .split(",\\s*")));
465 }
466 catch (CheckstyleException exc) {
467
468 if (defaultTokensMustBeExplicit) {
469 validateDefaultTokens(checkConfig, check, configTokens);
470 }
471 else {
472 configTokens.addAll(CheckUtil.getTokenNameSet(check.getDefaultTokens()));
473 }
474 }
475 }
476 for (Entry<String, Set<String>> entry : checkTokens.entrySet()) {
477 final Set<String> actual = configCheckTokens.get(entry.getKey());
478 assertWithMessage(
479 "'%s' should have all acceptable tokens "
480 + "from check in %s config "
481 + "or specify an override to ignore the specific tokens",
482 entry.getKey(), configName)
483 .that(actual)
484 .isEqualTo(entry.getValue());
485 }
486 }
487
488 private static boolean isAllTokensAcceptable(AbstractCheck check) {
489 return Arrays.equals(check.getAcceptableTokens(), TokenUtil.getAllTokenIds());
490 }
491
492 private static void validateDefaultTokens(Configuration checkConfig, AbstractCheck check,
493 Set<String> configTokens) {
494
495 final BitSet defaultTokensSet = TokenUtil.asBitSet(check.getDefaultTokens());
496 final BitSet requiredTokensSet = TokenUtil.asBitSet(check.getRequiredTokens());
497
498 if (defaultTokensSet.equals(requiredTokensSet)) {
499 configTokens.addAll(
500 CheckUtil.getTokenNameSet(check.getDefaultTokens()));
501 }
502 else {
503 assertWithMessage("All default tokens should be used in config for %s",
504 checkConfig.getName()).fail();
505 }
506 }
507
508 @Test
509 public void testAllCheckstyleModulesHaveXdocDocumentation() throws Exception {
510 final Set<String> checkstyleModulesNames = CheckUtil.getSimpleNames(CheckUtil
511 .getCheckstyleModules());
512 final Set<String> modulesNamesWhichHaveXdocs = XdocUtil.getModulesNamesWhichHaveXdoc();
513
514
515 checkstyleModulesNames.remove("TreeWalker");
516 checkstyleModulesNames.remove("Checker");
517
518 checkstyleModulesNames.removeAll(INTERNAL_MODULES);
519 checkstyleModulesNames.stream()
520 .filter(moduleName -> !modulesNamesWhichHaveXdocs.contains(moduleName))
521 .forEach(moduleName -> {
522 final String missingModuleMessage = String.format(Locale.ROOT,
523 "Module %s does not have xdoc documentation.",
524 moduleName);
525 assertWithMessage(missingModuleMessage).fail();
526 });
527 }
528
529 @Test
530 public void testAllCheckstyleModulesInCheckstyleConfig() throws Exception {
531 final Set<String> configChecks = CheckUtil.getConfigCheckStyleModules();
532 final Set<String> moduleNames = CheckUtil.getSimpleNames(CheckUtil.getCheckstyleModules());
533 moduleNames.removeAll(INTERNAL_MODULES);
534 moduleNames.removeAll(MODULES_NOT_IN_CHECKSTYLE_CONFIG);
535 for (String moduleName : moduleNames) {
536 assertWithMessage("checkstyle-checks.xml is missing module: %s", moduleName)
537 .that(configChecks)
538 .contains(moduleName);
539 }
540 }
541
542 @Test
543 public void testAllCheckstyleChecksHaveMessage() throws Exception {
544 for (Class<?> module : CheckUtil.getCheckstyleChecks()) {
545 final String name = module.getSimpleName();
546 final Set<Field> messages = CheckUtil.getCheckMessages(module, false);
547
548
549 if ("SuppressWarningsHolder".equals(name)) {
550 assertWithMessage("%s should not have any 'MSG_*' fields for error messages", name)
551 .that(messages)
552 .isEmpty();
553 }
554 else {
555 assertWithMessage(
556 "%s should have at least one 'MSG_*' field for error messages", name)
557 .that(messages)
558 .isNotEmpty();
559 }
560 }
561 }
562
563 @Test
564 public void testAllCheckstyleMessages() throws Exception {
565 final Map<String, List<String>> usedMessages = new TreeMap<>();
566
567
568 for (Class<?> module : CheckUtil.getCheckstyleModules()) {
569 for (Field message : CheckUtil.getCheckMessages(module, true)) {
570 assertWithMessage("%s.%s should be 'public static final'", module.getSimpleName(),
571 message.getName())
572 .that(message.getModifiers())
573 .isEqualTo(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
574
575
576 message.trySetAccessible();
577
578 if (!INTERNAL_MODULES.contains(module.getSimpleName())) {
579 verifyCheckstyleMessage(usedMessages, module, message);
580 }
581 }
582 }
583
584
585 for (Entry<String, List<String>> entry : usedMessages.entrySet()) {
586 final Properties pr = new Properties();
587 pr.load(AllChecksTest.class.getResourceAsStream(
588 "/" + entry.getKey().replace('.', '/') + "/messages.properties"));
589
590 for (Object key : pr.keySet()) {
591
592 if ("translation.wrongLanguageCode".equals(key)) {
593 continue;
594 }
595
596 assertWithMessage("property '%s' isn't used by any check in package '%s'", key,
597 entry.getKey())
598 .that(entry.getValue())
599 .contains(key.toString());
600 }
601 }
602 }
603
604 private static void verifyCheckstyleMessage(Map<String, List<String>> usedMessages,
605 Class<?> module, Field message) throws Exception {
606 final String messageString = message.get(null).toString();
607 final String packageName = module.getPackage().getName();
608 final List<String> packageMessages =
609 usedMessages.computeIfAbsent(packageName, key -> new ArrayList<>());
610
611 packageMessages.add(messageString);
612
613 for (Locale locale : ALL_LOCALES) {
614 String result = null;
615
616 try {
617 result = CheckUtil.getCheckMessage(module, locale, messageString);
618 }
619
620 catch (Exception exc) {
621 assertWithMessage("%s with the message '%s' in locale '%s' failed with: %s - %s",
622 module.getSimpleName(), messageString, locale.getLanguage(),
623 exc.getClass().getSimpleName(), exc.getMessage()).fail();
624 }
625
626 assertWithMessage("%s should have text for the message '%s' in locale %s'",
627 module.getSimpleName(), messageString, locale.getLanguage())
628 .that(result)
629 .isNotNull();
630 assertWithMessage("%s should have non-empty text for the message '%s' in locale '%s'",
631 module.getSimpleName(), messageString, locale.getLanguage())
632 .that(result.trim())
633 .isNotEmpty();
634 assertWithMessage(
635 "%s should have non-TODO text for the message '%s' in locale %s'",
636 module.getSimpleName(), messageString, locale.getLanguage())
637 .that(!"todo.match".equals(messageString)
638 && result.trim().startsWith("TODO"))
639 .isFalse();
640 }
641 }
642
643
644
645
646
647
648
649
650 private static boolean isSubset(int[] array, int... arrayToCheckIn) {
651 Arrays.sort(arrayToCheckIn);
652 boolean result = true;
653 for (final int element : array) {
654 if (Arrays.binarySearch(arrayToCheckIn, element) < 0) {
655 result = false;
656 break;
657 }
658 }
659 return result;
660 }
661
662 }