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