1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.google.checkstyle.test.base;
21
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertTrue;
24
25 import java.io.BufferedReader;
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.InputStreamReader;
31 import java.io.LineNumberReader;
32 import java.nio.charset.StandardCharsets;
33 import java.nio.file.Files;
34 import java.nio.file.Paths;
35 import java.text.MessageFormat;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.Locale;
40 import java.util.Map;
41 import java.util.Properties;
42 import java.util.Set;
43 import java.util.regex.Pattern;
44
45 import com.puppycrawl.tools.checkstyle.Checker;
46 import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
47 import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
48 import com.puppycrawl.tools.checkstyle.PropertiesExpander;
49 import com.puppycrawl.tools.checkstyle.TreeWalker;
50 import com.puppycrawl.tools.checkstyle.api.AbstractViolationReporter;
51 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
52 import com.puppycrawl.tools.checkstyle.api.Configuration;
53 import com.puppycrawl.tools.checkstyle.internal.utils.BriefUtLogger;
54 import com.puppycrawl.tools.checkstyle.internal.utils.CheckUtil;
55 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
56 import com.puppycrawl.tools.checkstyle.utils.ModuleReflectionUtil;
57
58 public abstract class AbstractModuleTestSupport extends AbstractPathTestSupport {
59
60
61
62
63 public enum ModuleCreationOption {
64
65
66
67
68
69 IN_TREEWALKER,
70
71
72
73
74 IN_CHECKER,
75
76 }
77
78 private static final String ROOT_MODULE_NAME = "root";
79
80 private static final Pattern WARN_PATTERN = CommonUtil
81 .createPattern(".*[ ]*//[ ]*warn[ ]*|/[*]\\s?warn\\s?[*]/");
82
83 private static final String XML_NAME = "/google_checks.xml";
84
85 private static final Configuration CONFIGURATION;
86
87 private static final Set<Class<?>> CHECKSTYLE_MODULES;
88
89 private final ByteArrayOutputStream stream = new ByteArrayOutputStream();
90
91 static {
92 try {
93 CONFIGURATION = ConfigurationLoader.loadConfiguration(XML_NAME,
94 new PropertiesExpander(System.getProperties()));
95 }
96 catch (CheckstyleException ex) {
97 throw new IllegalStateException(ex);
98 }
99 try {
100 CHECKSTYLE_MODULES = CheckUtil.getCheckstyleModules();
101 }
102 catch (IOException ex) {
103 throw new IllegalStateException(ex);
104 }
105 }
106
107
108
109
110
111 protected final BriefUtLogger getBriefUtLogger() {
112 return new BriefUtLogger(stream);
113 }
114
115
116
117
118
119
120
121
122
123
124 protected final String getNonCompilablePath(String filename) throws IOException {
125 return new File("src/it/resources-noncompilable/" + getPackageLocation() + "/"
126 + filename).getCanonicalPath();
127 }
128
129
130
131
132
133
134 protected static DefaultConfiguration createModuleConfig(Class<?> clazz) {
135 return new DefaultConfiguration(clazz.getSimpleName());
136 }
137
138
139
140
141
142
143
144 protected final Checker createChecker(Configuration moduleConfig)
145 throws Exception {
146 final String name = moduleConfig.getName();
147 ModuleCreationOption moduleCreationOption = ModuleCreationOption.IN_CHECKER;
148
149 for (Class<?> moduleClass : CHECKSTYLE_MODULES) {
150 if (moduleClass.getSimpleName().equals(name)
151 || moduleClass.getSimpleName().equals(name + "Check")) {
152 if (ModuleReflectionUtil.isCheckstyleTreeWalkerCheck(moduleClass)
153 || ModuleReflectionUtil.isTreeWalkerFilterModule(moduleClass)) {
154 moduleCreationOption = ModuleCreationOption.IN_TREEWALKER;
155 }
156 break;
157 }
158 }
159
160 return createChecker(moduleConfig, moduleCreationOption);
161 }
162
163
164
165
166
167
168
169
170
171 protected final Checker createChecker(Configuration moduleConfig,
172 ModuleCreationOption moduleCreationOption)
173 throws Exception {
174 final Configuration dc;
175
176 if (moduleCreationOption == ModuleCreationOption.IN_TREEWALKER) {
177 dc = createTreeWalkerConfig(moduleConfig);
178 }
179 else if (ROOT_MODULE_NAME.equals(moduleConfig.getName())) {
180 dc = moduleConfig;
181 }
182 else {
183 dc = createRootConfig(moduleConfig);
184 }
185
186 final Checker checker = new Checker();
187
188
189 final Locale locale = Locale.ENGLISH;
190 checker.setLocaleCountry(locale.getCountry());
191 checker.setLocaleLanguage(locale.getLanguage());
192 checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
193 checker.configure(dc);
194 checker.addListener(getBriefUtLogger());
195 return checker;
196 }
197
198
199
200
201
202
203
204 protected static DefaultConfiguration createTreeWalkerConfig(Configuration config) {
205 final DefaultConfiguration dc =
206 new DefaultConfiguration("configuration");
207 final DefaultConfiguration twConf = createModuleConfig(TreeWalker.class);
208
209 dc.addAttribute("charset", "iso-8859-1");
210 dc.addChild(twConf);
211 twConf.addChild(config);
212 return dc;
213 }
214
215
216
217
218
219
220 protected static DefaultConfiguration createRootConfig(Configuration config) {
221 final DefaultConfiguration dc = new DefaultConfiguration(ROOT_MODULE_NAME);
222 dc.addChild(config);
223 return dc;
224 }
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239 protected final void verify(Configuration config, String fileName, String[] expected,
240 Integer... warnsExpected) throws Exception {
241 verify(createChecker(config),
242 new File[] {new File(fileName)},
243 fileName, expected, warnsExpected);
244 }
245
246
247
248
249
250
251
252
253
254
255 protected final void verify(Checker checker,
256 File[] processedFiles,
257 String messageFileName,
258 String[] expected,
259 Integer... warnsExpected)
260 throws Exception {
261 stream.flush();
262 stream.reset();
263 final List<File> theFiles = new ArrayList<>();
264 Collections.addAll(theFiles, processedFiles);
265 final List<Integer> theWarnings = new ArrayList<>();
266 Collections.addAll(theWarnings, warnsExpected);
267 final int errs = checker.process(theFiles);
268
269
270 try (ByteArrayInputStream inputStream =
271 new ByteArrayInputStream(stream.toByteArray());
272 LineNumberReader lnr = new LineNumberReader(
273 new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
274 int previousLineNumber = 0;
275 for (int i = 0; i < expected.length; i++) {
276 final String expectedResult = messageFileName + ":" + expected[i];
277 final String actual = lnr.readLine();
278 assertEquals("error message " + i, expectedResult, actual);
279
280 String parseInt = removeDeviceFromPathOnWindows(actual);
281 parseInt = parseInt.substring(parseInt.indexOf(':') + 1);
282 parseInt = parseInt.substring(0, parseInt.indexOf(':'));
283 final int lineNumber = Integer.parseInt(parseInt);
284 assertTrue("input file is expected to have a warning comment on line number "
285 + lineNumber, previousLineNumber == lineNumber
286 || theWarnings.remove((Integer) lineNumber));
287 previousLineNumber = lineNumber;
288 }
289
290 assertEquals("unexpected output: " + lnr.readLine(),
291 expected.length, errs);
292 assertEquals("unexpected warnings " + theWarnings, 0, theWarnings.size());
293 }
294
295 checker.destroy();
296 }
297
298
299
300
301
302
303
304
305
306
307
308 protected static String getCheckMessage(Class<? extends AbstractViolationReporter> aClass,
309 String messageKey, Object... arguments) throws IOException {
310 final Properties pr = new Properties();
311 pr.load(aClass.getResourceAsStream("messages.properties"));
312 final MessageFormat formatter = new MessageFormat(pr.getProperty(messageKey),
313 Locale.ROOT);
314 return formatter.format(arguments);
315 }
316
317
318
319
320
321
322
323
324 protected static String getCheckMessage(Map<String, String> messages, String messageKey,
325 Object... arguments) {
326 String checkMessage = null;
327 for (Map.Entry<String, String> entry : messages.entrySet()) {
328 if (messageKey.equals(entry.getKey())) {
329 final MessageFormat formatter = new MessageFormat(entry.getValue(), Locale.ROOT);
330 checkMessage = formatter.format(arguments);
331 break;
332 }
333 }
334 return checkMessage;
335 }
336
337
338
339
340
341
342
343 protected static Configuration getModuleConfig(String moduleName) {
344 return getModuleConfig(moduleName, null);
345 }
346
347
348
349
350
351
352
353
354 protected static Configuration getModuleConfig(String moduleName, String moduleId) {
355 final Configuration result;
356 final List<Configuration> configs = getModuleConfigs(moduleName);
357 if (configs.size() == 1) {
358 result = configs.get(0);
359 }
360 else if (moduleId == null) {
361 throw new IllegalStateException("multiple instances of the same Module are detected");
362 }
363 else {
364 result = configs.stream().filter(conf -> {
365 try {
366 return conf.getAttribute("id").equals(moduleId);
367 }
368 catch (CheckstyleException ex) {
369 throw new IllegalStateException("problem to get ID attribute from " + conf, ex);
370 }
371 })
372 .findFirst().orElseGet(null);
373 }
374
375 return result;
376 }
377
378
379
380
381
382
383 protected static List<Configuration> getModuleConfigs(String moduleName) {
384 final List<Configuration> result = new ArrayList<>();
385 for (Configuration currentConfig : CONFIGURATION.getChildren()) {
386 if ("TreeWalker".equals(currentConfig.getName())) {
387 for (Configuration moduleConfig : currentConfig.getChildren()) {
388 if (moduleName.equals(moduleConfig.getName())) {
389 result.add(moduleConfig);
390 }
391 }
392 }
393 else if (moduleName.equals(currentConfig.getName())) {
394 result.add(currentConfig);
395 }
396 }
397 return result;
398 }
399
400 private static String removeDeviceFromPathOnWindows(String path) {
401 String fixedPath = path;
402 final String os = System.getProperty("os.name", "Unix");
403 if (os.startsWith("Windows")) {
404 fixedPath = path.substring(path.indexOf(':') + 1);
405 }
406 return fixedPath;
407 }
408
409
410
411
412
413
414
415
416 protected Integer[] getLinesWithWarn(String fileName) throws IOException {
417 final List<Integer> result = new ArrayList<>();
418 try (BufferedReader br = Files.newBufferedReader(
419 Paths.get(fileName), StandardCharsets.UTF_8)) {
420 int lineNumber = 1;
421 while (true) {
422 final String line = br.readLine();
423 if (line == null) {
424 break;
425 }
426 if (WARN_PATTERN.matcher(line).find()) {
427 result.add(lineNumber);
428 }
429 lineNumber++;
430 }
431 }
432 return result.toArray(new Integer[0]);
433 }
434
435 }