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", "PATTERN_VARIABLE_DEF")
177 .collect(Collectors.toUnmodifiableSet()));
178 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE.put("WhitespaceAround", Stream.of(
179
180 "ARRAY_INIT",
181 "ELLIPSIS",
182
183 "WILDCARD_TYPE", "GENERIC_END", "GENERIC_START")
184 .collect(Collectors.toUnmodifiableSet()));
185
186
187 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("AnnotationLocation", Stream.of(
188
189
190 "ANNOTATION_DEF", "ANNOTATION_FIELD_DEF", "ENUM_CONSTANT_DEF", "PACKAGE_DEF")
191 .collect(Collectors.toUnmodifiableSet()));
192 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("AbbreviationAsWordInName", Stream.of(
193
194 "ENUM_CONSTANT_DEF").collect(Collectors.toUnmodifiableSet()));
195 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("NoLineWrap", Stream.of(
196
197
198 "METHOD_DEF", "CTOR_DEF", "CLASS_DEF", "ENUM_DEF", "INTERFACE_DEF", "RECORD_DEF",
199 "COMPACT_CTOR_DEF")
200 .collect(Collectors.toUnmodifiableSet()));
201 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("SeparatorWrap", Stream.of(
202
203
204 "RBRACK",
205
206
207 "AT",
208
209
210 "SEMI",
211
212
213 "LPAREN", "RPAREN").collect(Collectors.toUnmodifiableSet()));
214 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("NeedBraces", Stream.of(
215
216 "LAMBDA", "LITERAL_DEFAULT", "LITERAL_CASE")
217 .collect(Collectors.toUnmodifiableSet()));
218 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("EmptyBlock", Stream.of(
219
220 "LITERAL_DEFAULT", "LITERAL_CASE",
221
222 "LITERAL_CATCH",
223
224 "ARRAY_INIT",
225
226
227 "INSTANCE_INIT", "LITERAL_DO", "LITERAL_FOR", "LITERAL_SYNCHRONIZED",
228 "LITERAL_WHILE", "STATIC_INIT").collect(Collectors.toUnmodifiableSet()));
229 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("WhitespaceAround", Stream.of(
230
231 "ARRAY_INIT",
232
233 "ELLIPSIS",
234
235 "GENERIC_START", "GENERIC_END", "WILDCARD_TYPE")
236 .collect(Collectors.toUnmodifiableSet()));
237 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("IllegalTokenText", Stream.of(
238
239 "NUM_DOUBLE", "NUM_FLOAT", "NUM_INT", "NUM_LONG",
240
241 "IDENT",
242
243 "COMMENT_CONTENT"
244 )
245 .collect(Collectors.toUnmodifiableSet()));
246 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("OperatorWrap", Stream.of(
247
248
249 "DIV_ASSIGN", "BOR_ASSIGN", "SL_ASSIGN", "ASSIGN", "BSR_ASSIGN", "BAND_ASSIGN",
250 "PLUS_ASSIGN", "MINUS_ASSIGN", "SR_ASSIGN", "STAR_ASSIGN", "BXOR_ASSIGN",
251 "MOD_ASSIGN",
252
253
254 "COLON").collect(Collectors.toUnmodifiableSet()));
255 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE.put("NoWhitespaceBefore", Stream.of(
256
257 "GENERIC_START", "GENERIC_END",
258
259
260 "ELLIPSIS").collect(Collectors.toUnmodifiableSet()));
261 INTERNAL_MODULES = Definitions.INTERNAL_MODULES.stream()
262 .map(moduleName -> {
263 final String[] packageTokens = moduleName.split("\\.");
264 return packageTokens[packageTokens.length - 1];
265 })
266 .collect(Collectors.toUnmodifiableSet());
267 }
268
269 @Override
270 protected String getPackageLocation() {
271 return "com/puppycrawl/tools/checkstyle/internal/allchecks";
272 }
273
274 @Test
275 public void testAllModulesWithDefaultConfiguration() throws Exception {
276 final String inputFilePath = getPath("InputAllChecksDefaultConfig.java");
277 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
278
279 for (Class<?> module : CheckUtil.getCheckstyleModules()) {
280 if (ModuleReflectionUtil.isRootModule(module)) {
281 continue;
282 }
283
284 final DefaultConfiguration moduleConfig = createModuleConfig(module);
285 if (module.equals(ImportControlCheck.class)) {
286
287
288 moduleConfig.addProperty("file", getPath(
289 "InputAllChecksImportControl.xml"));
290 }
291 verify(moduleConfig, inputFilePath, expected);
292 }
293 }
294
295 @Test
296 public void testDefaultTokensAreSubsetOfAcceptableTokens() throws Exception {
297 for (Class<?> check : CheckUtil.getCheckstyleChecks()) {
298 if (AbstractCheck.class.isAssignableFrom(check)) {
299 final AbstractCheck testedCheck = (AbstractCheck) check.getDeclaredConstructor()
300 .newInstance();
301 final int[] defaultTokens = testedCheck.getDefaultTokens();
302 final int[] acceptableTokens = testedCheck.getAcceptableTokens();
303
304 assertWithMessage("%s's default tokens must be a subset of acceptable tokens.",
305 check.getName())
306 .that(isSubset(defaultTokens, acceptableTokens))
307 .isTrue();
308 }
309 }
310 }
311
312 @Test
313 public void testRequiredTokensAreSubsetOfAcceptableTokens() throws Exception {
314 for (Class<?> check : CheckUtil.getCheckstyleChecks()) {
315 if (AbstractCheck.class.isAssignableFrom(check)) {
316 final AbstractCheck testedCheck = (AbstractCheck) check.getDeclaredConstructor()
317 .newInstance();
318 final int[] requiredTokens = testedCheck.getRequiredTokens();
319 final int[] acceptableTokens = testedCheck.getAcceptableTokens();
320
321 assertWithMessage("%s's required tokens must be a subset of acceptable tokens.",
322 check.getName())
323 .that(isSubset(requiredTokens, acceptableTokens))
324 .isTrue();
325 }
326 }
327 }
328
329 @Test
330 public void testRequiredTokensAreSubsetOfDefaultTokens() throws Exception {
331 for (Class<?> check : CheckUtil.getCheckstyleChecks()) {
332 if (AbstractCheck.class.isAssignableFrom(check)) {
333 final AbstractCheck testedCheck = (AbstractCheck) check.getDeclaredConstructor()
334 .newInstance();
335 final int[] defaultTokens = testedCheck.getDefaultTokens();
336 final int[] requiredTokens = testedCheck.getRequiredTokens();
337
338 assertWithMessage("%s's required tokens must be a subset of default tokens.",
339 check.getName())
340 .that(isSubset(requiredTokens, defaultTokens))
341 .isTrue();
342 }
343 }
344 }
345
346 @Test
347 public void testAllModulesHaveMultiThreadAnnotation() throws Exception {
348 for (Class<?> module : CheckUtil.getCheckstyleModules()) {
349 if (ModuleReflectionUtil.isRootModule(module)
350 || ModuleReflectionUtil.isFilterModule(module)
351 || ModuleReflectionUtil.isFileFilterModule(module)
352 || ModuleReflectionUtil.isTreeWalkerFilterModule(module)) {
353 continue;
354 }
355
356 assertWithMessage("module '" + module.getSimpleName()
357 + "' must contain a multi-thread annotation")
358 .that(module.isAnnotationPresent(GlobalStatefulCheck.class)
359 || module.isAnnotationPresent(FileStatefulCheck.class)
360 || module.isAnnotationPresent(StatelessCheck.class))
361 .isTrue();
362 }
363 }
364
365 @Test
366 public void testAllModulesAreReferencedInConfigFile() throws Exception {
367 final Set<String> modulesReferencedInConfig = CheckUtil.getConfigCheckStyleModules();
368 final Set<String> moduleNames = CheckUtil.getSimpleNames(CheckUtil.getCheckstyleModules());
369
370 moduleNames.removeAll(INTERNAL_MODULES);
371 moduleNames.stream().filter(check -> !modulesReferencedInConfig.contains(check))
372 .forEach(check -> {
373 final String errorMessage = String.format(Locale.ROOT,
374 "%s is not referenced in checkstyle-checks.xml", check);
375 assertWithMessage(errorMessage).fail();
376 });
377 }
378
379 @Test
380 public void testAllCheckTokensAreReferencedInCheckstyleConfigFile() throws Exception {
381 final Configuration configuration = ConfigurationUtil
382 .loadConfiguration("config/checkstyle-checks.xml");
383
384 validateAllCheckTokensAreReferencedInConfigFile("checkstyle", configuration,
385 CHECKSTYLE_TOKENS_IN_CONFIG_TO_IGNORE, false);
386 }
387
388 @Test
389 public void testAllCheckTokensAreReferencedInGoogleConfigFile() throws Exception {
390 final Configuration configuration = ConfigurationUtil
391 .loadConfiguration("src/main/resources/google_checks.xml");
392
393 validateAllCheckTokensAreReferencedInConfigFile("google", configuration,
394 GOOGLE_TOKENS_IN_CONFIG_TO_IGNORE, true);
395 }
396
397 private static void validateAllCheckTokensAreReferencedInConfigFile(String configName,
398 Configuration configuration, Map<String, Set<String>> tokensToIgnore,
399 boolean defaultTokensMustBeExplicit) throws Exception {
400 final ModuleFactory moduleFactory = TestUtil.getPackageObjectFactory();
401 final Set<Configuration> configChecks = ConfigurationUtil.getChecks(configuration);
402
403 final Map<String, Set<String>> configCheckTokens = new HashMap<>();
404 final Map<String, Set<String>> checkTokens = new HashMap<>();
405
406 for (Configuration checkConfig : configChecks) {
407 final String checkName = checkConfig.getName();
408 final Object instance;
409
410 try {
411 instance = moduleFactory.createModule(checkName);
412 }
413 catch (CheckstyleException exc) {
414 throw new CheckstyleException("Couldn't find check: " + checkName, exc);
415 }
416 final AbstractCheck check;
417 if (instance instanceof AbstractCheck
418 && !isAllTokensAcceptable((AbstractCheck) instance)) {
419 check = (AbstractCheck) instance;
420 }
421 else {
422
423 continue;
424 }
425
426 Set<String> configTokens = configCheckTokens.get(checkName);
427
428 if (configTokens == null) {
429 configTokens = new HashSet<>();
430
431 configCheckTokens.put(checkName, configTokens);
432
433
434 final Set<String> overrideTokens = tokensToIgnore.get(checkName);
435
436 if (overrideTokens != null) {
437 configTokens.addAll(overrideTokens);
438 }
439
440 configTokens.addAll(CheckUtil.getTokenNameSet(check.getRequiredTokens()));
441 checkTokens.put(checkName,
442 CheckUtil.getTokenNameSet(check.getAcceptableTokens()));
443 }
444
445 try {
446 configTokens.addAll(Arrays.asList(checkConfig.getProperty("tokens").trim()
447 .split(",\\s*")));
448 }
449 catch (CheckstyleException exc) {
450
451 if (defaultTokensMustBeExplicit) {
452 validateDefaultTokens(checkConfig, check, configTokens);
453 }
454 else {
455 configTokens.addAll(CheckUtil.getTokenNameSet(check.getDefaultTokens()));
456 }
457 }
458 }
459 for (Entry<String, Set<String>> entry : checkTokens.entrySet()) {
460 final Set<String> actual = configCheckTokens.get(entry.getKey());
461 assertWithMessage("'" + entry.getKey()
462 + "' should have all acceptable tokens from check in " + configName
463 + " config or specify an override to ignore the specific tokens")
464 .that(actual)
465 .isEqualTo(entry.getValue());
466 }
467 }
468
469 private static boolean isAllTokensAcceptable(AbstractCheck check) {
470 return Arrays.equals(check.getAcceptableTokens(), TokenUtil.getAllTokenIds());
471 }
472
473 private static void validateDefaultTokens(Configuration checkConfig, AbstractCheck check,
474 Set<String> configTokens) {
475
476 final BitSet defaultTokensSet = TokenUtil.asBitSet(check.getDefaultTokens());
477 final BitSet requiredTokensSet = TokenUtil.asBitSet(check.getRequiredTokens());
478
479 if (defaultTokensSet.equals(requiredTokensSet)) {
480 configTokens.addAll(
481 CheckUtil.getTokenNameSet(check.getDefaultTokens()));
482 }
483 else {
484 assertWithMessage("All default tokens should be used in config for "
485 + checkConfig.getName()).fail();
486 }
487 }
488
489 @Test
490 public void testAllCheckstyleModulesHaveXdocDocumentation() throws Exception {
491 final Set<String> checkstyleModulesNames = CheckUtil.getSimpleNames(CheckUtil
492 .getCheckstyleModules());
493 final Set<String> modulesNamesWhichHaveXdocs = XdocUtil.getModulesNamesWhichHaveXdoc();
494
495
496 checkstyleModulesNames.remove("TreeWalker");
497 checkstyleModulesNames.remove("Checker");
498
499 checkstyleModulesNames.removeAll(INTERNAL_MODULES);
500 checkstyleModulesNames.stream()
501 .filter(moduleName -> !modulesNamesWhichHaveXdocs.contains(moduleName))
502 .forEach(moduleName -> {
503 final String missingModuleMessage = String.format(Locale.ROOT,
504 "Module %s does not have xdoc documentation.",
505 moduleName);
506 assertWithMessage(missingModuleMessage).fail();
507 });
508 }
509
510 @Test
511 public void testAllCheckstyleModulesInCheckstyleConfig() throws Exception {
512 final Set<String> configChecks = CheckUtil.getConfigCheckStyleModules();
513 final Set<String> moduleNames = CheckUtil.getSimpleNames(CheckUtil.getCheckstyleModules());
514 moduleNames.removeAll(INTERNAL_MODULES);
515 for (String moduleName : moduleNames) {
516 assertWithMessage("checkstyle-checks.xml is missing module: " + moduleName)
517 .that(configChecks)
518 .contains(moduleName);
519 }
520 }
521
522 @Test
523 public void testAllCheckstyleChecksHaveMessage() throws Exception {
524 for (Class<?> module : CheckUtil.getCheckstyleChecks()) {
525 final String name = module.getSimpleName();
526 final Set<Field> messages = CheckUtil.getCheckMessages(module, false);
527
528
529 if ("SuppressWarningsHolder".equals(name)) {
530 assertWithMessage(name + " should not have any 'MSG_*' fields for error messages")
531 .that(messages)
532 .isEmpty();
533 }
534 else {
535 assertWithMessage(
536 name + " should have at least one 'MSG_*' field for error messages")
537 .that(messages)
538 .isNotEmpty();
539 }
540 }
541 }
542
543 @Test
544 public void testAllCheckstyleMessages() throws Exception {
545 final Map<String, List<String>> usedMessages = new TreeMap<>();
546
547
548 for (Class<?> module : CheckUtil.getCheckstyleModules()) {
549 for (Field message : CheckUtil.getCheckMessages(module, true)) {
550 assertWithMessage(module.getSimpleName() + "." + message.getName()
551 + " should be 'public static final'")
552 .that(message.getModifiers())
553 .isEqualTo(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
554
555
556 message.trySetAccessible();
557
558 if (!INTERNAL_MODULES.contains(module.getSimpleName())) {
559 verifyCheckstyleMessage(usedMessages, module, message);
560 }
561 }
562 }
563
564
565 for (Entry<String, List<String>> entry : usedMessages.entrySet()) {
566 final Properties pr = new Properties();
567 pr.load(AllChecksTest.class.getResourceAsStream(
568 "/" + entry.getKey().replace('.', '/') + "/messages.properties"));
569
570 for (Object key : pr.keySet()) {
571
572 if ("translation.wrongLanguageCode".equals(key)) {
573 continue;
574 }
575
576 assertWithMessage("property '" + key + "' isn't used by any check in package '"
577 + entry.getKey() + "'")
578 .that(entry.getValue())
579 .contains(key.toString());
580 }
581 }
582 }
583
584 private static void verifyCheckstyleMessage(Map<String, List<String>> usedMessages,
585 Class<?> module, Field message) throws Exception {
586 final String messageString = message.get(null).toString();
587 final String packageName = module.getPackage().getName();
588 final List<String> packageMessages =
589 usedMessages.computeIfAbsent(packageName, key -> new ArrayList<>());
590
591 packageMessages.add(messageString);
592
593 for (Locale locale : ALL_LOCALES) {
594 String result = null;
595
596 try {
597 result = CheckUtil.getCheckMessage(module, locale, messageString);
598 }
599
600 catch (Exception exc) {
601 assertWithMessage(module.getSimpleName() + " with the message '" + messageString
602 + "' in locale '" + locale.getLanguage() + "' failed with: "
603 + exc.getClass().getSimpleName() + " - " + exc.getMessage()).fail();
604 }
605
606 assertWithMessage(module.getSimpleName() + " should have text for the message '"
607 + messageString + "' in locale " + locale.getLanguage() + "'")
608 .that(result)
609 .isNotNull();
610 assertWithMessage("%s should have non-empty text for the message '%s' in locale '%s'",
611 module.getSimpleName(), messageString, locale.getLanguage())
612 .that(result.trim())
613 .isNotEmpty();
614 assertWithMessage(
615 module.getSimpleName() + " should have non-TODO text for the message '"
616 + messageString + "' in locale " + locale.getLanguage() + "'")
617 .that(!"todo.match".equals(messageString)
618 && result.trim().startsWith("TODO"))
619 .isFalse();
620 }
621 }
622
623
624
625
626
627
628
629
630 private static boolean isSubset(int[] array, int... arrayToCheckIn) {
631 Arrays.sort(arrayToCheckIn);
632 boolean result = true;
633 for (final int element : array) {
634 if (Arrays.binarySearch(arrayToCheckIn, element) < 0) {
635 result = false;
636 break;
637 }
638 }
639 return result;
640 }
641
642 }