Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@
package com.puppycrawl.tools.checkstyle.filters;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
Expand All @@ -42,12 +43,33 @@
import com.puppycrawl.tools.checkstyle.PropertiesExpander;
import com.puppycrawl.tools.checkstyle.api.Configuration;
import com.puppycrawl.tools.checkstyle.api.RootModule;
import com.puppycrawl.tools.checkstyle.bdd.InlineConfigParser;
import com.puppycrawl.tools.checkstyle.bdd.TestInputViolation;
import com.puppycrawl.tools.checkstyle.internal.utils.BriefUtLogger;

/**
* Base class for patch filter evaluation tests.
*
* <p>Each test bundle declares its expected violations in one of two ways:</p>
* <ul>
* <li>Inline {@code // violation 'message'} comments in the input files, parsed by the main
* library's {@link InlineConfigParser#getViolationsFromInputFile(String)} (preferred). The
* column is not asserted, matching the inline-violation convention shared with checkstyle:
* these filters operate on lines, not columns.</li>
* <li>A legacy {@code expected.txt} file listing {@code file:line:column: message}
* entries.</li>
* </ul>
*
* <p>The presence of {@code expected.txt} selects the mode, so bundles can be migrated to inline
* comments one at a time by adding the comments and deleting {@code expected.txt}.</p>
*/
abstract class AbstractPatchFilterEvaluationTest extends AbstractModuleTestSupport {

private static final String CONTEXT_CONFIG_PATTERN = "(default|zero)ContextConfig.xml";

private static final FilenameFilter INPUT_FILE_FILTER =
(dir, name) -> name.endsWith(".java") || name.endsWith(".properties");

protected abstract String getPatchFileLocation();

protected void testByConfig(String configPath)
Expand All @@ -63,7 +85,7 @@ protected void testByConfig(String configPath)

final String path = getPath(inputFile);
final int errorCounter = processFiles(rootModule, path);
assertResults(configPath, path, errorCounter, stream);
assertResults(path, errorCounter, stream);
}

private static RootModule createRootModule(Configuration config) throws Exception {
Expand All @@ -77,36 +99,101 @@ private static RootModule createRootModule(Configuration config) throws Exceptio
}

private static int processFiles(RootModule rootModule, String path) throws Exception {
final File file = new File(path);
final File[] files = file.listFiles((dir, name) -> {
return name.endsWith(".java") || name.endsWith(".properties");
});
return rootModule.process(listInputFiles(path));
}

private static List<File> listInputFiles(String path) throws IOException {
final File[] files = new File(path).listFiles(INPUT_FILE_FILTER);
if (files == null) {
throw new IOException("there is no java file in this directory.");
}

final List<File> theFiles = Arrays.asList(files);
final List<File> theFiles = new ArrayList<>(Arrays.asList(files));
Collections.sort(theFiles);
return rootModule.process(theFiles);
return theFiles;
}

private void assertResults(String configPath, String path, int errorCounter,
ByteArrayOutputStream stream) throws IOException {
private void assertResults(String path, int errorCounter, ByteArrayOutputStream stream)
throws Exception {
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(stream.toByteArray());
LineNumberReader lnr = new LineNumberReader(
new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
final String expectedFile = configPath.replaceFirst(CONTEXT_CONFIG_PATTERN,
"expected.txt");
final Path expectedFilePath = Paths.get(getPath(expectedFile));
final List<String> expected = Files.readAllLines(expectedFilePath);
final List<String> actuals = lnr.lines().toList();

for (int index = 0; index < expected.size(); index++) {
final String expectedResult = path + File.separator + expected.get(index);
assertEquals(expectedResult, actuals.get(index),
"error message " + index + ". Expected file: " + expectedFilePath);
final File expectedFile = new File(path, "expected.txt");
if (expectedFile.exists()) {
assertLegacyResults(expectedFile, path, errorCounter, actuals);
}
else {
assertInlineResults(path, errorCounter, actuals);
}
assertEquals(expected.size(), errorCounter, "unexpected output: " + lnr.readLine());
}
}

private static void assertLegacyResults(File expectedFile, String path, int errorCounter,
List<String> actuals) throws IOException {
final List<String> expected = Files.readAllLines(expectedFile.toPath());
for (int index = 0; index < expected.size(); index++) {
final String expectedResult = path + File.separator + expected.get(index);
assertEquals(expectedResult, actuals.get(index),
"error message " + index + ". Expected file: " + expectedFile);
}
assertEquals(expected.size(), errorCounter, "unexpected output");
}

/**
* Verifies the actual output against inline {@code // violation 'message'} comments in the
* bundle's input files. Violations are parsed with the main library's
* {@link InlineConfigParser#getViolationsFromInputFile(String)} and checked per file, then a
* total count guards against any reported violation that is not attributed to a file.
*
* @param path the directory containing the bundle's input files
* @param errorCounter number of violations reported by checkstyle
* @param actuals actual output lines
* @throws Exception if an input file cannot be parsed
*/
private static void assertInlineResults(String path, int errorCounter, List<String> actuals)
throws Exception {
int expectedTotal = 0;
for (File file : listInputFiles(path)) {
final List<TestInputViolation> violations =
InlineConfigParser.getViolationsFromInputFile(file.getPath());
final String prefix = file.getPath() + ":";
final List<String> actualViolations = actuals.stream()
.filter(line -> line.startsWith(prefix))
.map(line -> line.substring(prefix.length()))
.toList();
verifyViolations(file.getPath(), violations, actualViolations);
expectedTotal += violations.size();
}
assertEquals(expectedTotal, errorCounter,
"number of violations does not match inline comments");
}

/**
* Replicates {@code AbstractModuleTestSupport.verifyViolations}, which is not visible here:
* the reported violation lines must equal the commented lines, then each violation must match
* its {@link TestInputViolation#toRegex()}. The column is not asserted, matching the
* inline-violation convention shared with checkstyle: these filters operate on lines.
*
* @param file file path, for assertion messages
* @param testInputViolations expected violations parsed from inline comments
* @param actualViolations actual violations for the file, as {@code line:column: message}
*/
private static void verifyViolations(String file,
List<TestInputViolation> testInputViolations,
List<String> actualViolations) {
final List<Integer> actualViolationLines = actualViolations.stream()
.map(violation -> violation.substring(0, violation.indexOf(':')))
.map(Integer::valueOf)
.toList();
final List<Integer> expectedViolationLines = testInputViolations.stream()
.map(TestInputViolation::getLineNo)
.toList();
assertEquals(expectedViolationLines, actualViolationLines,
"Violation lines for " + file + " differ.");
for (int index = 0; index < actualViolations.size(); index++) {
assertTrue(actualViolations.get(index).matches(
testInputViolations.get(index).toRegex()),
"Actual and expected violations differ for " + file);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class Test {

// Long line ----------------------------------------------------------------
// Long line ----------------------------------------------------------------
// Contains a tab -> <- //warn
// Contains a tab -> <- //warn // filtered violation 'Line contains a tab character.'


}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package Checker.FileTabCharacter;

public class Test2 {
// Contains a tab -> <- //warn // violation without filter
// Contains a tab -> <- //warn // violation 'Line contains a tab character.'
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ index d57b91c..41cc056 100644
// Long line ----------------------------------------------------------------
// Long line ----------------------------------------------------------------
- // Contains a tab -> <- //warns
// Contains a tab -> <- //warn
// Contains a tab -> <- //warn // filtered violation 'Line contains a tab character.'


diff --git a/Test2.java b/Test2.java
Expand All @@ -18,5 +18,5 @@ index 6055de3..b4ea012 100644
package Checker.FileTabCharacter;

public class Test2 {
+ // Contains a tab -> <- //warn // violation without filter
+ // Contains a tab -> <- //warn // violation 'Line contains a tab character.'
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class Test {

// Long line ----------------------------------------------------------------
// Long line ----------------------------------------------------------------
// Contains a tab -> <- //warn
// Contains a tab -> <- //warn // filtered violation 'Line contains a tab character.'


}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package Checker.FileTabCharacter;

public class Test2 {
// Contains a tab -> <- //warn // violation without filter
// Contains a tab -> <- //warn // violation 'Line contains a tab character.'
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ index d57b91c..41cc056 100644
// Long line ----------------------------------------------------------------
// Long line ----------------------------------------------------------------
- // Contains a tab -> <- //warns
// Contains a tab -> <- //warn
// Contains a tab -> <- //warn // filtered violation 'Line contains a tab character.'


diff --git a/Test2.java b/Test2.java
Expand All @@ -18,5 +18,5 @@ index 6055de3..b4ea012 100644
package Checker.FileTabCharacter;

public class Test2 {
+ // Contains a tab -> <- //warn // violation without filter
+ // Contains a tab -> <- //warn // violation 'Line contains a tab character.'
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ public class Test {



// Contains a tab -> <- //warns // violation without filter
// Contains a tab -> <- //warns // violation 'Line contains a tab character.'
// Long line ----------------------------------------------------------------
// Long line ----------------------------------------------------------------
// Contains a tab -> <- //warns
// Contains a tab -> <- //warns
// Contains a tab -> <- //warns // filtered violation 'Line contains a tab character.'
// Contains a tab -> <- //warns // filtered violation 'Line contains a tab character.'


}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ index 63f6e7c..8e0f63c 100644



+ // Contains a tab -> <- //warns // violation without filter
+ // Contains a tab -> <- //warns // violation 'Line contains a tab character.'
// Long line ----------------------------------------------------------------
// Long line ----------------------------------------------------------------
// Contains a tab -> <- //warns
// Contains a tab -> <- //warns // filtered violation 'Line contains a tab character.'

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ index 63f6e7c..8e0f63c 100644
--- a/Test.java
+++ b/Test.java
@@ -9,0 +10 @@ public class Test {
+ // Contains a tab -> <- //warns // violation without filter
+ // Contains a tab -> <- //warns // violation 'Line contains a tab character.'
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ public class Test {



// Contains a tab -> <- //warns // violation without filter
// Contains a tab -> <- //warns // violation 'Line contains a tab character.'
// Long line ----------------------------------------------------------------
// Long line ----------------------------------------------------------------
// Contains a tab -> <- //warns
// Contains a tab -> <- //warns
// Contains a tab -> <- //warns // filtered violation 'Line contains a tab character.'
// Contains a tab -> <- //warns // filtered violation 'Line contains a tab character.'


}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ index 63f6e7c..8e0f63c 100644



+ // Contains a tab -> <- //warns // violation without filter
+ // Contains a tab -> <- //warns // violation 'Line contains a tab character.'
// Long line ----------------------------------------------------------------
// Long line ----------------------------------------------------------------
// Contains a tab -> <- //warns
// Contains a tab -> <- //warns // filtered violation 'Line contains a tab character.'

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ index 63f6e7c..8e0f63c 100644
--- a/Test.java
+++ b/Test.java
@@ -9,0 +10 @@ public class Test {
+ // Contains a tab -> <- //warns // violation without filter
+ // Contains a tab -> <- //warns // violation 'Line contains a tab character.'