forked from checkstyle/checkstyle
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue checkstyle#4592: Added AbstractModuleTestSupport and GenericWhi…
…tespaceCheckTest, ParenPadCheckTest, FileTabCharacterCheckTest, PackageAnnotationCheckTest now extend from it
- Loading branch information
Showing
6 changed files
with
346 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
327 changes: 327 additions & 0 deletions
327
src/test/java/com/puppycrawl/tools/checkstyle/AbstractModuleTestSupport.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,327 @@ | ||
//////////////////////////////////////////////////////////////////////////////// | ||
// checkstyle: Checks Java source code for adherence to a set of rules. | ||
// Copyright (C) 2001-2017 the original author or authors. | ||
// | ||
// This library is free software; you can redistribute it and/or | ||
// modify it under the terms of the GNU Lesser General Public | ||
// License as published by the Free Software Foundation; either | ||
// version 2.1 of the License, or (at your option) any later version. | ||
// | ||
// This library is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
// Lesser General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU Lesser General Public | ||
// License along with this library; if not, write to the Free Software | ||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
//////////////////////////////////////////////////////////////////////////////// | ||
|
||
package com.puppycrawl.tools.checkstyle; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertTrue; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.io.LineNumberReader; | ||
import java.nio.charset.StandardCharsets; | ||
import java.text.MessageFormat; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Locale; | ||
import java.util.Map; | ||
import java.util.ResourceBundle; | ||
import java.util.stream.Collectors; | ||
|
||
import com.google.common.collect.MapDifference; | ||
import com.google.common.collect.Maps; | ||
import com.puppycrawl.tools.checkstyle.api.Configuration; | ||
import com.puppycrawl.tools.checkstyle.api.LocalizedMessage; | ||
|
||
public abstract class AbstractModuleTestSupport extends AbstractPathTestSupport { | ||
|
||
protected final ByteArrayOutputStream stream = new ByteArrayOutputStream(); | ||
|
||
protected static DefaultConfiguration createCheckConfig(Class<?> clazz) { | ||
return new DefaultConfiguration(clazz.getName()); | ||
} | ||
|
||
/** | ||
* Creates {@link Checker} instance based on the given {@link Configuration} instance. | ||
* @param checkConfig {@link Configuration} instance. | ||
* @return {@link Checker} instance based on the given {@link Configuration} instance. | ||
* @throws Exception if an exception occurs during checker configuration. | ||
*/ | ||
public Checker createChecker(Configuration checkConfig) | ||
throws Exception { | ||
final DefaultConfiguration dc = createCheckerConfig(checkConfig); | ||
final Checker checker = new Checker(); | ||
checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); | ||
checker.configure(dc); | ||
checker.addListener(new BriefUtLogger(stream)); | ||
return checker; | ||
} | ||
|
||
/** | ||
* Creates {@link DefaultConfiguration} for the {@link Checker} | ||
* based on the given {@link Configuration} instance. | ||
* @param config {@link Configuration} instance. | ||
* @return {@link DefaultConfiguration} for the {@link Checker} | ||
* based on the given {@link Configuration} instance. | ||
*/ | ||
protected DefaultConfiguration createCheckerConfig(Configuration config) { | ||
final DefaultConfiguration dc = | ||
new DefaultConfiguration("configuration"); | ||
final DefaultConfiguration twConf = createCheckConfig(TreeWalker.class); | ||
// make sure that the tests always run with this charset | ||
dc.addAttribute("charset", "UTF-8"); | ||
dc.addChild(twConf); | ||
twConf.addChild(config); | ||
return dc; | ||
} | ||
|
||
/** | ||
* Returns canonical path for the file with the given file name. | ||
* The path is formed base on the non-compilable resources location. | ||
* This implementation uses 'src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/' | ||
* as a non-compilable resource location. | ||
* @param filename file name. | ||
* @return canonical path for the file with the given file name. | ||
* @throws IOException if I/O exception occurs while forming the path. | ||
*/ | ||
protected final String getNonCompilablePath(String filename) throws IOException { | ||
return new File("src/test/resources-noncompilable/" + getPackageLocation() + "/" | ||
+ filename).getCanonicalPath(); | ||
} | ||
|
||
/** | ||
* Performs verification of the file with the given file name. Uses specified configuration. | ||
* Expected messages are represented by the array of strings. | ||
* This implementation uses overloaded | ||
* {@link BaseCheckTestSupport#verify(Checker, File[], String, String...)} method inside. | ||
* @param aConfig configuration. | ||
* @param fileName file name to verify. | ||
* @param expected an array of expected messages. | ||
* @throws Exception if exception occurs during verification process. | ||
*/ | ||
protected void verify(Configuration aConfig, String fileName, String... expected) | ||
throws Exception { | ||
verify(createChecker(aConfig), fileName, fileName, expected); | ||
} | ||
|
||
/** | ||
* Performs verification of the file with the given file name. | ||
* Uses provided {@link Checker} instance. | ||
* Expected messages are represented by the array of strings. | ||
* This implementation uses overloaded | ||
* {@link BaseCheckTestSupport#verify(Checker, String, String, String...)} method inside. | ||
* @param checker {@link Checker} instance. | ||
* @param fileName file name to verify. | ||
* @param expected an array of expected messages. | ||
* @throws Exception if exception occurs during verification process. | ||
*/ | ||
protected void verify(Checker checker, String fileName, String... expected) | ||
throws Exception { | ||
verify(checker, fileName, fileName, expected); | ||
} | ||
|
||
/** | ||
* Performs verification of the file with the given file name. | ||
* Uses provided {@link Checker} instance. | ||
* Expected messages are represented by the array of strings. | ||
* This implementation uses overloaded | ||
* {@link BaseCheckTestSupport#verify(Checker, File[], String, String...)} method inside. | ||
* @param checker {@link Checker} instance. | ||
* @param processedFilename file name to verify. | ||
* @param messageFileName message file name. | ||
* @param expected an array of expected messages. | ||
* @throws Exception if exception occurs during verification process. | ||
*/ | ||
protected void verify(Checker checker, | ||
String processedFilename, | ||
String messageFileName, | ||
String... expected) | ||
throws Exception { | ||
verify(checker, | ||
new File[] {new File(processedFilename)}, | ||
messageFileName, expected); | ||
} | ||
|
||
/** | ||
* We keep two verify methods with separate logic only for convenience of debugging | ||
* We have minimum amount of multi-file test cases | ||
*/ | ||
protected void verify(Checker checker, | ||
File[] processedFiles, | ||
String messageFileName, | ||
String... expected) | ||
throws Exception { | ||
stream.flush(); | ||
final List<File> theFiles = new ArrayList<>(); | ||
Collections.addAll(theFiles, processedFiles); | ||
final int errs = checker.process(theFiles); | ||
|
||
// process each of the lines | ||
final ByteArrayInputStream inputStream = | ||
new ByteArrayInputStream(stream.toByteArray()); | ||
try (LineNumberReader lnr = new LineNumberReader( | ||
new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { | ||
|
||
final List<String> actuals = lnr.lines().limit(expected.length) | ||
.sorted().collect(Collectors.toList()); | ||
Arrays.sort(expected); | ||
|
||
for (int i = 0; i < expected.length; i++) { | ||
final String expectedResult = messageFileName + ":" + expected[i]; | ||
assertEquals("error message " + i, expectedResult, actuals.get(i)); | ||
} | ||
|
||
assertEquals("unexpected output: " + lnr.readLine(), | ||
expected.length, errs); | ||
} | ||
|
||
checker.destroy(); | ||
} | ||
|
||
/** | ||
* Performs verification of the given files. | ||
* @param checker {@link Checker} instance | ||
* @param processedFiles files to process. | ||
* @param expectedViolations a map of expected violations per files. | ||
* @throws Exception if exception occurs during verification process. | ||
*/ | ||
protected void verify(Checker checker, | ||
File[] processedFiles, | ||
Map<String, List<String>> expectedViolations) | ||
throws Exception { | ||
stream.flush(); | ||
final List<File> theFiles = new ArrayList<>(); | ||
Collections.addAll(theFiles, processedFiles); | ||
final int errs = checker.process(theFiles); | ||
|
||
// process each of the lines | ||
final Map<String, List<String>> actualViolations = getActualViolations(errs); | ||
final Map<String, List<String>> realExpectedViolations = | ||
Maps.filterValues(expectedViolations, input -> !input.isEmpty()); | ||
final MapDifference<String, List<String>> violationDifferences = | ||
Maps.difference(realExpectedViolations, actualViolations); | ||
|
||
final Map<String, List<String>> missingViolations = | ||
violationDifferences.entriesOnlyOnLeft(); | ||
final Map<String, List<String>> unexpectedViolations = | ||
violationDifferences.entriesOnlyOnRight(); | ||
final Map<String, MapDifference.ValueDifference<List<String>>> differingViolations = | ||
violationDifferences.entriesDiffering(); | ||
|
||
final StringBuilder message = new StringBuilder(); | ||
if (!missingViolations.isEmpty()) { | ||
message.append("missing violations: ").append(missingViolations); | ||
} | ||
if (!unexpectedViolations.isEmpty()) { | ||
if (message.length() > 0) { | ||
message.append('\n'); | ||
} | ||
message.append("unexpected violations: ").append(unexpectedViolations); | ||
} | ||
if (!differingViolations.isEmpty()) { | ||
if (message.length() > 0) { | ||
message.append('\n'); | ||
} | ||
message.append("differing violations: ").append(differingViolations); | ||
} | ||
|
||
assertTrue(message.toString(), | ||
missingViolations.isEmpty() | ||
&& unexpectedViolations.isEmpty() | ||
&& differingViolations.isEmpty()); | ||
|
||
checker.destroy(); | ||
} | ||
|
||
private Map<String, List<String>> getActualViolations(int errorCount) throws IOException { | ||
// process each of the lines | ||
final ByteArrayInputStream inputStream = | ||
new ByteArrayInputStream(stream.toByteArray()); | ||
|
||
try (LineNumberReader lnr = new LineNumberReader( | ||
new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { | ||
|
||
final Map<String, List<String>> actualViolations = new HashMap<>(); | ||
for (String line = lnr.readLine(); line != null && lnr.getLineNumber() <= errorCount; | ||
line = lnr.readLine()) { | ||
// have at least 2 characters before the splitting colon, | ||
// to not split after the drive letter on windows | ||
final String[] actualViolation = line.split("(?<=.{2}):", 2); | ||
final String actualViolationFileName = actualViolation[0]; | ||
final String actualViolationMessage = actualViolation[1]; | ||
|
||
List<String> actualViolationsPerFile = | ||
actualViolations.get(actualViolationFileName); | ||
if (actualViolationsPerFile == null) { | ||
actualViolationsPerFile = new ArrayList<>(); | ||
actualViolations.put(actualViolationFileName, actualViolationsPerFile); | ||
} | ||
actualViolationsPerFile.add(actualViolationMessage); | ||
} | ||
|
||
return actualViolations; | ||
} | ||
} | ||
|
||
/** | ||
* Gets the check message 'as is' from appropriate 'messages.properties' | ||
* file. | ||
* | ||
* @param messageKey the key of message in 'messages.properties' file. | ||
* @param arguments the arguments of message in 'messages.properties' file. | ||
*/ | ||
protected String getCheckMessage(String messageKey, Object... arguments) { | ||
return internalGetCheckMessage(getMessageBundle(), messageKey, arguments); | ||
} | ||
|
||
/** | ||
* Gets the check message 'as is' from appropriate 'messages.properties' | ||
* file. | ||
* | ||
* @param messageBundle the bundle name. | ||
* @param messageKey the key of message in 'messages.properties' file. | ||
* @param arguments the arguments of message in 'messages.properties' file. | ||
*/ | ||
private static String internalGetCheckMessage( | ||
String messageBundle, String messageKey, Object... arguments) { | ||
final ResourceBundle resourceBundle = ResourceBundle.getBundle( | ||
messageBundle, | ||
Locale.getDefault(), | ||
Thread.currentThread().getContextClassLoader(), | ||
new LocalizedMessage.Utf8Control()); | ||
final String pattern = resourceBundle.getString(messageKey); | ||
final MessageFormat formatter = new MessageFormat(pattern, Locale.ROOT); | ||
return formatter.format(arguments); | ||
} | ||
|
||
private String getMessageBundle() { | ||
final String className = getClass().getName(); | ||
return getMessageBundle(className); | ||
} | ||
|
||
private static String getMessageBundle(String className) { | ||
final String messageBundle; | ||
final String messages = "messages"; | ||
final int endIndex = className.lastIndexOf('.'); | ||
if (endIndex < 0) { | ||
messageBundle = messages; | ||
} | ||
else { | ||
final String packageName = className.substring(0, endIndex); | ||
messageBundle = packageName + "." + messages; | ||
} | ||
return messageBundle; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.