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