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 java.io.File;
23 import java.io.IOException;
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.function.Consumer;
33 import java.util.stream.Stream;
34
35 import org.junit.Assert;
36 import org.junit.Test;
37
38
39
40
41
42 public class AllTestsTest {
43
44 @Test
45 public void testAllInputsHaveTest() throws Exception {
46 final Map<String, List<String>> allTests = new HashMap<>();
47
48 walk(Paths.get("src/test/java"), filePath -> {
49 grabAllTests(allTests, filePath.toFile());
50 });
51
52 Assert.assertTrue("found tests", !allTests.keySet().isEmpty());
53
54 walk(Paths.get("src/test/resources/com/puppycrawl"), filePath -> {
55 verifyInputFile(allTests, filePath.toFile());
56 });
57 walk(Paths.get("src/test/resources-noncompilable/com/puppycrawl"), filePath -> {
58 verifyInputFile(allTests, filePath.toFile());
59 });
60 }
61
62 @Test
63 public void testAllTestsHaveProductionCode() throws Exception {
64 final Map<String, List<String>> allTests = new HashMap<>();
65
66 walk(Paths.get("src/main/java"), filePath -> {
67 grabAllFiles(allTests, filePath.toFile());
68 });
69
70 Assert.assertTrue("found tests", !allTests.keySet().isEmpty());
71
72 walk(Paths.get("src/test/java"), filePath -> {
73 verifyHasProductionFile(allTests, filePath.toFile());
74 });
75 }
76
77 private static void walk(Path path, Consumer<Path> action) throws IOException {
78 try (Stream<Path> walk = Files.walk(path)) {
79 walk.forEach(action);
80 }
81 }
82
83 private static void grabAllTests(Map<String, List<String>> allTests, File file) {
84 if (file.isFile() && file.getName().endsWith("Test.java")) {
85 String path;
86
87 try {
88 path = getSimplePath(file.getCanonicalPath()).replace("CheckTest.java", "")
89 .replace("Test.java", "");
90 }
91 catch (IOException ex) {
92 throw new IllegalStateException(ex);
93 }
94
95
96 if (path.endsWith(File.separator + "Abstract")) {
97 path += "Check";
98 }
99
100 final int slash = path.lastIndexOf(File.separatorChar);
101 final String packge = path.substring(0, slash);
102
103 List<String> classes = allTests.get(packge);
104
105 if (classes == null) {
106 classes = new ArrayList<>();
107
108 allTests.put(packge, classes);
109 }
110
111 classes.add(path.substring(slash + 1));
112 }
113 }
114
115 private static void grabAllFiles(Map<String, List<String>> allTests, File file) {
116 if (file.isFile()) {
117 final String path;
118
119 try {
120 path = getSimplePath(file.getCanonicalPath());
121 }
122 catch (IOException ex) {
123 throw new IllegalStateException(ex);
124 }
125
126 final int slash = path.lastIndexOf(File.separatorChar);
127 final String packge = path.substring(0, slash);
128
129 List<String> classes = allTests.get(packge);
130
131 if (classes == null) {
132 classes = new ArrayList<>();
133
134 allTests.put(packge, classes);
135 }
136
137 classes.add(path.substring(slash + 1));
138 }
139 }
140
141 private static void verifyInputFile(Map<String, List<String>> allTests, File file) {
142 if (file.isFile()) {
143 final String path;
144
145 try {
146 path = getSimplePath(file.getCanonicalPath());
147 }
148 catch (IOException ex) {
149 throw new IllegalStateException(ex);
150 }
151
152
153 if (!path.contains(File.separatorChar + "grammar" + File.separatorChar)
154 && !path.contains(File.separatorChar + "foo" + File.separatorChar)
155 && !path.contains(File.separatorChar + "bar" + File.separatorChar)) {
156 String fileName = file.getName();
157 final boolean skipFileNaming = shouldSkipInputFileNameCheck(path, fileName);
158
159 if (!skipFileNaming) {
160 Assert.assertTrue("Resource must start with 'Input' or 'Expected': " + path,
161 fileName.startsWith("Input") || fileName.startsWith("Expected"));
162
163 if (fileName.startsWith("Input")) {
164 fileName = fileName.substring(5);
165 }
166 else {
167 fileName = fileName.substring(8);
168 }
169
170 final int period = fileName.lastIndexOf('.');
171
172 if (period > 0) {
173 fileName = fileName.substring(0, period);
174 }
175 }
176
177 verifyInputFile(allTests, skipFileNaming, path, fileName);
178 }
179 }
180 }
181
182 private static void verifyInputFile(Map<String, List<String>> allTests, boolean skipFileNaming,
183 String path, String fileName) {
184 List<String> classes;
185 int slash = path.lastIndexOf(File.separatorChar);
186 String packge = path.substring(0, slash);
187 boolean found = false;
188
189 for (int depth = 0; depth < 4; depth++) {
190
191
192 final String folderPath = packge;
193 slash = packge.lastIndexOf(File.separatorChar);
194 packge = path.substring(0, slash);
195 classes = allTests.get(packge);
196
197 if (classes != null
198 && checkInputMatchCorrectFileStructure(classes, folderPath, skipFileNaming,
199 fileName)) {
200 found = true;
201 break;
202 }
203 }
204
205 Assert.assertTrue("Resource must be named after a Test like 'InputMyCustomCase.java' "
206 + "and be in the sub-package of the test like 'mycustom' "
207 + "for test 'MyCustomCheckTest': " + path, found);
208 }
209
210 private static void verifyHasProductionFile(Map<String, List<String>> allTests, File file) {
211 if (file.isFile()) {
212 final String fileName = file.getName().replace("Test.java", ".java");
213
214 if (!fileName.endsWith("TestSupport.java")
215
216 && !"XpathMapper.java".equals(fileName)) {
217 final String path;
218
219 try {
220 path = getSimplePath(file.getCanonicalPath());
221 }
222 catch (IOException ex) {
223 throw new IllegalStateException(ex);
224 }
225
226 if (!path.contains(File.separatorChar + "grammar" + File.separatorChar)
227 && !path.contains(File.separatorChar + "internal" + File.separatorChar)) {
228 final int slash = path.lastIndexOf(File.separatorChar);
229 final String packge = path.substring(0, slash);
230 final List<String> classes = allTests.get(packge);
231
232 Assert.assertTrue("Test must be named after a production class "
233 + "and must be in the same package of the production class: " + path,
234 classes != null && classes.contains(fileName));
235 }
236 }
237 }
238 }
239
240 private static boolean checkInputMatchCorrectFileStructure(List<String> classes,
241 String folderPath, boolean skipFileNaming, String fileName) {
242 boolean result = false;
243
244 for (String clss : classes) {
245 if (folderPath.endsWith(File.separatorChar + clss.toLowerCase(Locale.ENGLISH))
246 && (skipFileNaming || fileName.startsWith(clss))) {
247 result = true;
248 break;
249 }
250 }
251
252 return result;
253 }
254
255 private static boolean shouldSkipInputFileNameCheck(String path, String fileName) {
256 return "package-info.java".equals(fileName)
257 || "package.html".equals(fileName)
258
259 || path.contains(File.separatorChar + "inputs" + File.separatorChar)
260
261 || path.contains(File.separatorChar + "translation" + File.separatorChar);
262 }
263
264 private static String getSimplePath(String path) {
265 return path.substring(path.lastIndexOf("com" + File.separator + "puppycrawl"));
266 }
267
268 }