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.beans.PropertyDescriptor;
25 import java.io.File;
26 import java.io.IOException;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.stream.Collectors;
36 import java.util.stream.Stream;
37
38 import org.apache.commons.beanutils.PropertyUtils;
39 import org.junit.jupiter.api.Test;
40
41 import com.puppycrawl.tools.checkstyle.AbstractPathTestSupport;
42 import com.puppycrawl.tools.checkstyle.internal.utils.CheckUtil;
43 import com.puppycrawl.tools.checkstyle.internal.utils.XdocUtil;
44
45 public class XdocsExampleFileTest {
46
47 private static final Set<String> COMMON_PROPERTIES = Set.of(
48 "severity",
49 "id",
50 "fileExtensions",
51 "tabWidth",
52 "fileContents",
53 "tokens",
54 "javadocTokens",
55 "violateExecutionOnNonTightHtml"
56 );
57
58 @Test
59 public void testAllCheckPropertiesAreUsedInXdocsExamples() throws Exception {
60 final Map<String, Set<String>> usedPropertiesByCheck =
61 XdocUtil.extractUsedPropertiesFromXdocsExamples();
62 final List<String> failures = new ArrayList<>();
63
64 for (Class<?> checkClass : CheckUtil.getCheckstyleChecks()) {
65 final String checkSimpleName = checkClass.getSimpleName();
66
67 final Set<String> definedProperties = Arrays.stream(
68 PropertyUtils.getPropertyDescriptors(checkClass))
69 .filter(descriptor -> descriptor.getWriteMethod() != null)
70 .map(PropertyDescriptor::getName)
71 .filter(property -> !COMMON_PROPERTIES.contains(property))
72 .collect(Collectors.toUnmodifiableSet());
73
74 final Set<String> usedProperties =
75 usedPropertiesByCheck.getOrDefault(checkSimpleName, Collections.emptySet());
76
77 for (String property : definedProperties) {
78 if (!usedProperties.contains(property)) {
79 failures.add("Missing property in xdoc: '"
80 + property + "' of " + checkSimpleName);
81 }
82 }
83 }
84 if (!failures.isEmpty()) {
85 assertWithMessage("Xdocs are missing properties:\n" + String.join("\n", failures))
86 .fail();
87 }
88 }
89
90 @Test
91 public void testAllExampleFilesHaveCorrespondingTestMethods() throws Exception {
92 final Path examplesResources = Path.of("src/xdocs-examples/resources");
93 final Path examplesNonCompilable = Path.of("src/xdocs-examples/resources-noncompilable");
94 final Path examplesTestRoot = Path.of(
95 "src/xdocs-examples/java/com/puppycrawl/tools/checkstyle/checks");
96 final List<String> failures = new ArrayList<>();
97
98 try (Stream<Path> testFiles = Files.walk(examplesTestRoot)) {
99 testFiles
100 .filter(path -> path.toString().endsWith("ExamplesTest.java"))
101 .forEach(testFile -> {
102 try {
103 scanFile(testFile, examplesResources, examplesNonCompilable, failures);
104 }
105 catch (IOException exception) {
106 throw new IllegalStateException("Error processing: "
107 + testFile, exception);
108 }
109 });
110 }
111 if (!failures.isEmpty()) {
112 assertWithMessage("Example files are missing corresponding test methods:\n"
113 + String.join("\n", failures))
114 .fail();
115 }
116 }
117
118 private static void scanFile(Path testFile, Path examplesResources, Path examplesNonCompilable,
119 List<String> failures)
120 throws IOException {
121 final String testContent = Files.readString(testFile);
122
123 final String className = Path.of("src/xdocs-examples/java").toAbsolutePath()
124 .relativize(testFile.toAbsolutePath()).toString()
125 .replace(File.separator, ".")
126 .replaceFirst("\\.java$", "");
127
128 try {
129 final Class<?> testClass = Class.forName(className);
130 final AbstractPathTestSupport instance = (AbstractPathTestSupport) testClass
131 .getDeclaredConstructor().newInstance();
132 final String packageLocation = instance.getPackageLocation();
133
134 scanExampleDirectory(examplesResources.resolve(packageLocation),
135 testContent, testFile, failures);
136 scanExampleDirectory(examplesNonCompilable.resolve(packageLocation),
137 testContent, testFile, failures);
138 }
139 catch (ReflectiveOperationException exception) {
140 throw new IllegalStateException("Failed to instantiate " + className, exception);
141 }
142 }
143
144 private static void scanExampleDirectory(Path exampleDir, String testContent,
145 Path testFile, List<String> failures) throws IOException {
146 if (Files.exists(exampleDir) && Files.isDirectory(exampleDir)) {
147 try (Stream<Path> exampleFiles = Files.list(exampleDir)) {
148 exampleFiles
149 .filter(path -> {
150 final String fileName = path.getFileName()
151 .toString();
152 return fileName.matches("Example\\d+\\.java");
153 })
154 .forEach(exampleFile -> {
155 final String fileName = exampleFile.getFileName()
156 .toString();
157 if (!testContent.contains("\"" + fileName + "\"")) {
158 failures.add("Missing test for " + fileName + " in "
159 + testFile.getFileName());
160 }
161 });
162 }
163 }
164 }
165 }