View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2018 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  
41      @Test
42      public void testAllInputsHaveTest() throws Exception {
43          final Map<String, List<String>> allTests = new HashMap<>();
44  
45          Files.walk(Paths.get("src/test/java"))
46              .forEach(filePath -> {
47                  grabAllTests(allTests, filePath.toFile());
48              });
49  
50          Assert.assertTrue("found tests", !allTests.keySet().isEmpty());
51  
52          Files.walk(Paths.get("src/test/resources/com/puppycrawl"))
53              .forEach(filePath -> {
54                  verifyInputFile(allTests, filePath.toFile());
55              });
56          Files.walk(Paths.get("src/test/resources-noncompilable/com/puppycrawl"))
57              .forEach(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          Files.walk(Paths.get("src/main/java"))
67              .forEach(filePath -> {
68                  grabAllFiles(allTests, filePath.toFile());
69              });
70  
71          Assert.assertTrue("found tests", !allTests.keySet().isEmpty());
72  
73          Files.walk(Paths.get("src/test/java"))
74              .forEach(filePath -> {
75                  verifyHasProductionFile(allTests, filePath.toFile());
76              });
77      }
78  
79      private static void grabAllTests(Map<String, List<String>> allTests, File file) {
80          if (file.isFile() && file.getName().endsWith("Test.java")) {
81              String path;
82  
83              try {
84                  path = getSimplePath(file.getCanonicalPath()).replace("CheckTest.java", "")
85                          .replace("Test.java", "");
86              }
87              catch (IOException ex) {
88                  throw new IllegalStateException(ex);
89              }
90  
91              // override for 'AbstractCheck' naming
92              if (path.endsWith(File.separator + "Abstract")) {
93                  path += "Check";
94              }
95  
96              final int slash = path.lastIndexOf(File.separatorChar);
97              final String packge = path.substring(0, slash);
98  
99              List<String> classes = allTests.get(packge);
100 
101             if (classes == null) {
102                 classes = new ArrayList<>();
103 
104                 allTests.put(packge, classes);
105             }
106 
107             classes.add(path.substring(slash + 1));
108         }
109     }
110 
111     private static void grabAllFiles(Map<String, List<String>> allTests, File file) {
112         if (file.isFile()) {
113             final String path;
114 
115             try {
116                 path = getSimplePath(file.getCanonicalPath());
117             }
118             catch (IOException ex) {
119                 throw new IllegalStateException(ex);
120             }
121 
122             final int slash = path.lastIndexOf(File.separatorChar);
123             final String packge = path.substring(0, slash);
124 
125             List<String> classes = allTests.get(packge);
126 
127             if (classes == null) {
128                 classes = new ArrayList<>();
129 
130                 allTests.put(packge, classes);
131             }
132 
133             classes.add(path.substring(slash + 1));
134         }
135     }
136 
137     private static void verifyInputFile(Map<String, List<String>> allTests, File file) {
138         if (file.isFile()) {
139             final String path;
140 
141             try {
142                 path = getSimplePath(file.getCanonicalPath());
143             }
144             catch (IOException ex) {
145                 throw new IllegalStateException(ex);
146             }
147 
148             // until https://github.com/checkstyle/checkstyle/issues/5105
149             if (!path.contains(File.separatorChar + "grammars" + File.separatorChar)
150                     && !path.contains(File.separatorChar + "foo" + File.separatorChar)
151                     && !path.contains(File.separatorChar + "bar" + File.separatorChar)) {
152                 String fileName = file.getName();
153                 final boolean skipFileNaming = shouldSkipInputFileNameCheck(path, fileName);
154 
155                 if (!skipFileNaming) {
156                     Assert.assertTrue("Resource must start with 'Input' or 'Expected': " + path,
157                             fileName.startsWith("Input") || fileName.startsWith("Expected"));
158 
159                     if (fileName.startsWith("Input")) {
160                         fileName = fileName.substring(5);
161                     }
162                     else {
163                         fileName = fileName.substring(8);
164                     }
165 
166                     final int period = fileName.lastIndexOf('.');
167 
168                     if (period > 0) {
169                         fileName = fileName.substring(0, period);
170                     }
171                 }
172 
173                 verifyInputFile(allTests, skipFileNaming, path, fileName);
174             }
175         }
176     }
177 
178     private static void verifyInputFile(Map<String, List<String>> allTests, boolean skipFileNaming,
179             String path, String fileName) {
180         List<String> classes;
181         int slash = path.lastIndexOf(File.separatorChar);
182         String packge = path.substring(0, slash);
183         boolean found = false;
184 
185         for (int depth = 0; depth < 4; depth++) {
186             // -@cs[MoveVariableInsideIf] assignment value is modified later so it can't be
187             // moved
188             final String folderPath = packge;
189             slash = packge.lastIndexOf(File.separatorChar);
190             packge = path.substring(0, slash);
191             classes = allTests.get(packge);
192 
193             if (classes != null
194                     && checkInputMatchCorrectFileStructure(classes, folderPath, skipFileNaming,
195                             fileName)) {
196                 found = true;
197                 break;
198             }
199         }
200 
201         Assert.assertTrue("Resource must be named after a Test like 'InputMyCustomCase.java' "
202                 + "and be in the sub-package of the test like 'mycustom' "
203                 + "for test 'MyCustomCheckTest': " + path, found);
204     }
205 
206     private static void verifyHasProductionFile(Map<String, List<String>> allTests, File file) {
207         if (file.isFile()) {
208             final String fileName = file.getName().replace("Test.java", ".java");
209 
210             if (!fileName.endsWith("TestSupport.java")
211                     // tests external utility XPathEvaluator
212                     && !"XpathMapper.java".equals(fileName)) {
213                 final String path;
214 
215                 try {
216                     path = getSimplePath(file.getCanonicalPath());
217                 }
218                 catch (IOException ex) {
219                     throw new IllegalStateException(ex);
220                 }
221 
222                 if (!path.contains(File.separatorChar + "grammars" + File.separatorChar)
223                         && !path.contains(File.separatorChar + "internal" + File.separatorChar)) {
224                     final int slash = path.lastIndexOf(File.separatorChar);
225                     final String packge = path.substring(0, slash);
226                     final List<String> classes = allTests.get(packge);
227 
228                     Assert.assertTrue("Test must be named after a production class "
229                             + "and must be in the same package of the production class: " + path,
230                             classes != null && classes.contains(fileName));
231                 }
232             }
233         }
234     }
235 
236     private static boolean checkInputMatchCorrectFileStructure(List<String> classes,
237             String folderPath, boolean skipFileNaming, String fileName) {
238         boolean result = false;
239 
240         for (String clss : classes) {
241             if (folderPath.endsWith(File.separatorChar + clss.toLowerCase(Locale.ENGLISH))
242                     && (skipFileNaming || fileName.startsWith(clss))) {
243                 result = true;
244                 break;
245             }
246         }
247 
248         return result;
249     }
250 
251     private static boolean shouldSkipInputFileNameCheck(String path, String fileName) {
252         return "package-info.java".equals(fileName)
253                 || "package.html".equals(fileName)
254                 // special directory for files that can't be renamed or are secondary inputs
255                 || path.contains(File.separatorChar + "inputs" + File.separatorChar)
256                 // all inputs must start with 'messages'
257                 || path.contains(File.separatorChar + "translation" + File.separatorChar);
258     }
259 
260     private static String getSimplePath(String path) {
261         return path.substring(path.lastIndexOf("com" + File.separator + "puppycrawl"));
262     }
263 
264 }