View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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.Paths;
26  import java.util.ArrayList;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Locale;
30  import java.util.Map;
31  
32  import org.junit.Assert;
33  import org.junit.Test;
34  
35  /**
36   * AllTestsTest.
37   * @noinspection ClassIndependentOfModule
38   */
39  public class AllTestsTest {
40      @Test
41      public void testAllInputsHaveTest() throws Exception {
42          final Map<String, List<String>> allTests = new HashMap<>();
43  
44          Files.walk(Paths.get("src/test/java"))
45              .forEach(filePath -> {
46                  grabAllTests(allTests, filePath.toFile());
47              });
48  
49          Assert.assertTrue("found tests", !allTests.keySet().isEmpty());
50  
51          Files.walk(Paths.get("src/test/resources"))
52              .forEach(filePath -> {
53                  verifyInputFile(allTests, filePath.toFile());
54              });
55          Files.walk(Paths.get("src/test/resources-noncompilable"))
56              .forEach(filePath -> {
57                  verifyInputFile(allTests, filePath.toFile());
58              });
59      }
60  
61      @Test
62      public void testAllTestsHaveProductionCode() throws Exception {
63          final Map<String, List<String>> allTests = new HashMap<>();
64  
65          Files.walk(Paths.get("src/main/java"))
66              .forEach(filePath -> {
67                  grabAllFiles(allTests, filePath.toFile());
68              });
69  
70          Assert.assertTrue("found tests", !allTests.keySet().isEmpty());
71  
72          Files.walk(Paths.get("src/test/java"))
73              .forEach(filePath -> {
74                  verifyHasProductionFile(allTests, filePath.toFile());
75              });
76      }
77  
78      private static void grabAllTests(Map<String, List<String>> allTests, File file) {
79          if (file.isFile() && file.getName().endsWith("Test.java")) {
80              String path;
81  
82              try {
83                  path = getSimplePath(file.getCanonicalPath()).replace("CheckTest.java", "")
84                          .replace("Test.java", "");
85              }
86              catch (IOException ex) {
87                  throw new IllegalStateException(ex);
88              }
89  
90              // override for 'AbstractCheck' naming
91              if (path.endsWith(File.separator + "Abstract")) {
92                  path += "Check";
93              }
94  
95              final int slash = path.lastIndexOf(File.separatorChar);
96              final String packge = path.substring(0, slash);
97  
98              List<String> classes = allTests.get(packge);
99  
100             if (classes == null) {
101                 classes = new ArrayList<>();
102 
103                 allTests.put(packge, classes);
104             }
105 
106             classes.add(path.substring(slash + 1));
107         }
108     }
109 
110     private static void grabAllFiles(Map<String, List<String>> allTests, File file) {
111         if (file.isFile()) {
112             final String path;
113 
114             try {
115                 path = getSimplePath(file.getCanonicalPath());
116             }
117             catch (IOException ex) {
118                 throw new IllegalStateException(ex);
119             }
120 
121             final int slash = path.lastIndexOf(File.separatorChar);
122             final String packge = path.substring(0, slash);
123 
124             List<String> classes = allTests.get(packge);
125 
126             if (classes == null) {
127                 classes = new ArrayList<>();
128 
129                 allTests.put(packge, classes);
130             }
131 
132             classes.add(path.substring(slash + 1));
133         }
134     }
135 
136     private static void verifyInputFile(Map<String, List<String>> allTests, File file) {
137         if (file.isFile()) {
138             final String path;
139 
140             try {
141                 path = getSimplePath(file.getCanonicalPath());
142             }
143             catch (IOException ex) {
144                 throw new IllegalStateException(ex);
145             }
146 
147             // until https://github.com/checkstyle/checkstyle/issues/5105
148             if (!path.contains(File.separatorChar + "grammars" + File.separatorChar)
149                     && !path.contains(File.separatorChar + "foo" + File.separatorChar)
150                     && !path.contains(File.separatorChar + "bar" + File.separatorChar)) {
151                 String fileName = file.getName();
152                 final boolean skipFileNaming = shouldSkipInputFileNameCheck(path, fileName);
153 
154                 if (!skipFileNaming) {
155                     Assert.assertTrue("Resource must start with 'Input' or 'Expected': " + path,
156                             fileName.startsWith("Input") || fileName.startsWith("Expected"));
157 
158                     if (fileName.startsWith("Input")) {
159                         fileName = fileName.substring(5);
160                     }
161                     else {
162                         fileName = fileName.substring(8);
163                     }
164 
165                     final int period = fileName.lastIndexOf('.');
166 
167                     if (period > 0) {
168                         fileName = fileName.substring(0, period);
169                     }
170                 }
171 
172                 verifyInputFile(allTests, skipFileNaming, path, fileName);
173             }
174         }
175     }
176 
177     private static void verifyInputFile(Map<String, List<String>> allTests, boolean skipFileNaming,
178             String path, String fileName) {
179         List<String> classes;
180         int slash = path.lastIndexOf(File.separatorChar);
181         String packge = path.substring(0, slash);
182         boolean found = false;
183 
184         for (int depth = 0; depth < 4; depth++) {
185             // -@cs[MoveVariableInsideIf] assignment value is modified later so it can't be
186             // moved
187             final String folderPath = packge;
188             slash = packge.lastIndexOf(File.separatorChar);
189             packge = path.substring(0, slash);
190             classes = allTests.get(packge);
191 
192             if (classes != null
193                     && checkInputMatchCorrectFileStructure(classes, folderPath, skipFileNaming,
194                             fileName)) {
195                 found = true;
196                 break;
197             }
198         }
199 
200         Assert.assertTrue("Resource must be named after a Test like 'InputMyCustomCase.java' "
201                 + "and be in the sub-package of the test like 'mycustom' "
202                 + "for test 'MyCustomCheckTest': " + path, found);
203     }
204 
205     private static void verifyHasProductionFile(Map<String, List<String>> allTests, File file) {
206         if (file.isFile()) {
207             final String fileName = file.getName().replace("Test.java", ".java");
208 
209             if (!fileName.endsWith("TestSupport.java")
210                     // tests external utility XPathEvaluator
211                     && !"XpathMapper.java".equals(fileName)) {
212                 final String path;
213 
214                 try {
215                     path = getSimplePath(file.getCanonicalPath());
216                 }
217                 catch (IOException ex) {
218                     throw new IllegalStateException(ex);
219                 }
220 
221                 if (!path.contains(File.separatorChar + "grammars" + File.separatorChar)
222                         && !path.contains(File.separatorChar + "internal" + File.separatorChar)) {
223                     final int slash = path.lastIndexOf(File.separatorChar);
224                     final String packge = path.substring(0, slash);
225                     final List<String> classes = allTests.get(packge);
226 
227                     Assert.assertTrue("Test must be named after a production class "
228                             + "and must be in the same package of the production class: " + path,
229                             classes != null && classes.contains(fileName));
230                 }
231             }
232         }
233     }
234 
235     private static boolean checkInputMatchCorrectFileStructure(List<String> classes,
236             String folderPath, boolean skipFileNaming, String fileName) {
237         boolean result = false;
238 
239         for (String clss : classes) {
240             if (folderPath.endsWith(File.separatorChar + clss.toLowerCase(Locale.ENGLISH))
241                     && (skipFileNaming || fileName.startsWith(clss))) {
242                 result = true;
243                 break;
244             }
245         }
246 
247         return result;
248     }
249 
250     private static boolean shouldSkipInputFileNameCheck(String path, String fileName) {
251         return "package-info.java".equals(fileName)
252                 || "package.html".equals(fileName)
253                 // special directory for files that can't be renamed or are secondary inputs
254                 || path.contains(File.separatorChar + "inputs" + File.separatorChar)
255                 // all inputs must start with 'messages'
256                 || path.contains(File.separatorChar + "translation" + File.separatorChar);
257     }
258 
259     private static String getSimplePath(String path) {
260         return path.substring(path.lastIndexOf("com" + File.separator + "puppycrawl"));
261     }
262 }