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;
21
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertTrue;
24
25 import java.io.ByteArrayInputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.InputStreamReader;
30 import java.io.LineNumberReader;
31 import java.nio.charset.StandardCharsets;
32 import java.text.MessageFormat;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Locale;
39 import java.util.Map;
40 import java.util.ResourceBundle;
41 import java.util.stream.Collectors;
42
43 import com.google.common.collect.MapDifference;
44 import com.google.common.collect.Maps;
45 import com.puppycrawl.tools.checkstyle.api.Configuration;
46 import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
47 import com.puppycrawl.tools.checkstyle.internal.utils.BriefUtLogger;
48 import com.puppycrawl.tools.checkstyle.utils.ModuleReflectionUtil;
49
50 public abstract class AbstractModuleTestSupport extends AbstractPathTestSupport {
51
52
53
54
55 public enum ModuleCreationOption {
56
57
58
59
60
61 IN_TREEWALKER,
62
63
64
65
66 IN_CHECKER,
67
68 }
69
70 private static final String ROOT_MODULE_NAME = "root";
71
72 private final ByteArrayOutputStream stream = new ByteArrayOutputStream();
73
74
75
76
77
78 public ByteArrayOutputStream getStream() {
79 return stream;
80 }
81
82
83
84
85
86 public final BriefUtLogger getBriefUtLogger() {
87 return new BriefUtLogger(stream);
88 }
89
90 protected static DefaultConfiguration createModuleConfig(Class<?> clazz) {
91 return new DefaultConfiguration(clazz.getName());
92 }
93
94
95
96
97
98
99
100 public final Checker createChecker(Configuration moduleConfig)
101 throws Exception {
102 ModuleCreationOption moduleCreationOption = ModuleCreationOption.IN_CHECKER;
103
104 final String moduleName = moduleConfig.getName();
105 if (!ROOT_MODULE_NAME.equals(moduleName)) {
106 try {
107 final Class<?> moduleClass = Class.forName(moduleName);
108 if (ModuleReflectionUtil.isCheckstyleTreeWalkerCheck(moduleClass)
109 || ModuleReflectionUtil.isTreeWalkerFilterModule(moduleClass)) {
110 moduleCreationOption = ModuleCreationOption.IN_TREEWALKER;
111 }
112 }
113 catch (ClassNotFoundException ignore) {
114
115 }
116 }
117
118 return createChecker(moduleConfig, moduleCreationOption);
119 }
120
121
122
123
124
125
126
127
128
129 public final Checker createChecker(Configuration moduleConfig,
130 ModuleCreationOption moduleCreationOption)
131 throws Exception {
132 final Checker checker = new Checker();
133 checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
134
135 if (moduleCreationOption == ModuleCreationOption.IN_TREEWALKER) {
136 final Configuration dc = createTreeWalkerConfig(moduleConfig);
137 checker.configure(dc);
138 }
139 else if (ROOT_MODULE_NAME.equals(moduleConfig.getName())) {
140 checker.configure(moduleConfig);
141 }
142 else {
143 final Configuration dc = createRootConfig(moduleConfig);
144 checker.configure(dc);
145 }
146 checker.addListener(new BriefUtLogger(stream));
147 return checker;
148 }
149
150
151
152
153
154
155
156
157 protected static DefaultConfiguration createTreeWalkerConfig(Configuration config) {
158 final DefaultConfiguration dc =
159 new DefaultConfiguration("configuration");
160 final DefaultConfiguration twConf = createModuleConfig(TreeWalker.class);
161
162 dc.addAttribute("charset", StandardCharsets.UTF_8.name());
163 dc.addChild(twConf);
164 twConf.addChild(config);
165 return dc;
166 }
167
168
169
170
171
172
173 protected static DefaultConfiguration createRootConfig(Configuration config) {
174 final DefaultConfiguration dc = new DefaultConfiguration(ROOT_MODULE_NAME);
175 if (config != null) {
176 dc.addChild(config);
177 }
178 return dc;
179 }
180
181
182
183
184
185
186
187
188
189
190 protected final String getNonCompilablePath(String filename) throws IOException {
191 return new File("src/test/resources-noncompilable/" + getPackageLocation() + "/"
192 + filename).getCanonicalPath();
193 }
194
195
196
197
198
199
200
201
202
203 protected final String getUriString(String filename) {
204 return new File("src/test/resources/" + getPackageLocation() + "/" + filename).toURI()
205 .toString();
206 }
207
208
209
210
211
212
213
214
215
216
217
218 protected final void verify(Configuration aConfig, String fileName, String... expected)
219 throws Exception {
220 verify(createChecker(aConfig), fileName, fileName, expected);
221 }
222
223
224
225
226
227
228
229
230
231
232
233
234 protected void verify(Checker checker, String fileName, String... expected)
235 throws Exception {
236 verify(checker, fileName, fileName, expected);
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251 protected final void verify(Checker checker,
252 String processedFilename,
253 String messageFileName,
254 String... expected)
255 throws Exception {
256 verify(checker,
257 new File[] {new File(processedFilename)},
258 messageFileName, expected);
259 }
260
261
262
263
264
265
266
267
268
269
270 protected void verify(Checker checker,
271 File[] processedFiles,
272 String messageFileName,
273 String... expected)
274 throws Exception {
275 stream.flush();
276 stream.reset();
277 final List<File> theFiles = new ArrayList<>();
278 Collections.addAll(theFiles, processedFiles);
279 final int errs = checker.process(theFiles);
280
281
282 try (ByteArrayInputStream inputStream =
283 new ByteArrayInputStream(stream.toByteArray());
284 LineNumberReader lnr = new LineNumberReader(
285 new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
286 final List<String> actuals = lnr.lines().limit(expected.length)
287 .sorted().collect(Collectors.toList());
288 Arrays.sort(expected);
289
290 for (int i = 0; i < expected.length; i++) {
291 final String expectedResult = messageFileName + ":" + expected[i];
292 assertEquals("error message " + i, expectedResult, actuals.get(i));
293 }
294
295 assertEquals("unexpected output: " + lnr.readLine(),
296 expected.length, errs);
297 }
298
299 checker.destroy();
300 }
301
302
303
304
305
306
307
308
309 protected final void verify(Checker checker,
310 File[] processedFiles,
311 Map<String, List<String>> expectedViolations)
312 throws Exception {
313 stream.flush();
314 stream.reset();
315 final List<File> theFiles = new ArrayList<>();
316 Collections.addAll(theFiles, processedFiles);
317 final int errs = checker.process(theFiles);
318
319
320 final Map<String, List<String>> actualViolations = getActualViolations(errs);
321 final Map<String, List<String>> realExpectedViolations =
322 Maps.filterValues(expectedViolations, input -> !input.isEmpty());
323 final MapDifference<String, List<String>> violationDifferences =
324 Maps.difference(realExpectedViolations, actualViolations);
325
326 final Map<String, List<String>> missingViolations =
327 violationDifferences.entriesOnlyOnLeft();
328 final Map<String, List<String>> unexpectedViolations =
329 violationDifferences.entriesOnlyOnRight();
330 final Map<String, MapDifference.ValueDifference<List<String>>> differingViolations =
331 violationDifferences.entriesDiffering();
332
333 final StringBuilder message = new StringBuilder(256);
334 if (!missingViolations.isEmpty()) {
335 message.append("missing violations: ").append(missingViolations);
336 }
337 if (!unexpectedViolations.isEmpty()) {
338 if (message.length() > 0) {
339 message.append('\n');
340 }
341 message.append("unexpected violations: ").append(unexpectedViolations);
342 }
343 if (!differingViolations.isEmpty()) {
344 if (message.length() > 0) {
345 message.append('\n');
346 }
347 message.append("differing violations: ").append(differingViolations);
348 }
349
350 assertTrue(message.toString(),
351 missingViolations.isEmpty()
352 && unexpectedViolations.isEmpty()
353 && differingViolations.isEmpty());
354
355 checker.destroy();
356 }
357
358 private Map<String, List<String>> getActualViolations(int errorCount) throws IOException {
359
360 try (ByteArrayInputStream inputStream =
361 new ByteArrayInputStream(stream.toByteArray());
362 LineNumberReader lnr = new LineNumberReader(
363 new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
364 final Map<String, List<String>> actualViolations = new HashMap<>();
365 for (String line = lnr.readLine(); line != null && lnr.getLineNumber() <= errorCount;
366 line = lnr.readLine()) {
367
368
369 final String[] actualViolation = line.split("(?<=.{2}):", 2);
370 final String actualViolationFileName = actualViolation[0];
371 final String actualViolationMessage = actualViolation[1];
372
373 List<String> actualViolationsPerFile =
374 actualViolations.get(actualViolationFileName);
375 if (actualViolationsPerFile == null) {
376 actualViolationsPerFile = new ArrayList<>();
377 actualViolations.put(actualViolationFileName, actualViolationsPerFile);
378 }
379 actualViolationsPerFile.add(actualViolationMessage);
380 }
381
382 return actualViolations;
383 }
384 }
385
386
387
388
389
390
391
392
393
394 protected final String getCheckMessage(String messageKey, Object... arguments) {
395 return internalGetCheckMessage(getMessageBundle(), messageKey, arguments);
396 }
397
398
399
400
401
402
403
404
405
406
407 protected static String getCheckMessage(
408 Class<?> clazz, String messageKey, Object... arguments) {
409 return internalGetCheckMessage(getMessageBundle(clazz.getName()), messageKey, arguments);
410 }
411
412
413
414
415
416
417
418
419
420
421 private static String internalGetCheckMessage(
422 String messageBundle, String messageKey, Object... arguments) {
423 final ResourceBundle resourceBundle = ResourceBundle.getBundle(
424 messageBundle,
425 Locale.getDefault(),
426 Thread.currentThread().getContextClassLoader(),
427 new LocalizedMessage.Utf8Control());
428 final String pattern = resourceBundle.getString(messageKey);
429 final MessageFormat formatter = new MessageFormat(pattern, Locale.ROOT);
430 return formatter.format(arguments);
431 }
432
433 private String getMessageBundle() {
434 final String className = getClass().getName();
435 return getMessageBundle(className);
436 }
437
438 private static String getMessageBundle(String className) {
439 final String messageBundle;
440 final String messages = "messages";
441 final int endIndex = className.lastIndexOf('.');
442 if (endIndex < 0) {
443 messageBundle = messages;
444 }
445 else {
446 final String packageName = className.substring(0, endIndex);
447 messageBundle = packageName + "." + messages;
448 }
449 return messageBundle;
450 }
451
452 }