diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PmdRuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PmdRuleTst.java index ff6b3d349b..6e07254698 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PmdRuleTst.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PmdRuleTst.java @@ -4,14 +4,19 @@ package net.sourceforge.pmd.testframework; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; import java.util.List; +import java.util.stream.Collectors; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; import net.sourceforge.pmd.Rule; -@RunWith(PMDTestRunner.class) public class PmdRuleTst extends RuleTst { @Override @@ -29,4 +34,33 @@ public class PmdRuleTst extends RuleTst { Rule rule = findRule(rulesetXml, getClass().getSimpleName().replaceFirst("Test$", "")); return Collections.singletonList(rule); } + + @TestFactory + Collection ruleTests() { + final List rules = new ArrayList<>(getRules()); + rules.sort(Comparator.comparing(Rule::getName)); + + final List tests = new LinkedList<>(); + for (final Rule r : rules) { + final TestDescriptor[] ruleTests = extractTestsFromXml(r); + Collections.addAll(tests, ruleTests); + } + + return tests.stream().map(this::toDynamicTest).collect(Collectors.toList()); + } + + private DynamicTest toDynamicTest(TestDescriptor testDescriptor) { + if (isIgnored(testDescriptor)) { + return DynamicTest.dynamicTest("[IGNORED] " + testDescriptor.getTestMethodName(), + testDescriptor.getTestSourceUri(), + () -> {}); + } + return DynamicTest.dynamicTest(testDescriptor.getTestMethodName(), + testDescriptor.getTestSourceUri(), + () -> runTest(testDescriptor)); + } + + private static boolean isIgnored(TestDescriptor testDescriptor) { + return TestDescriptor.inRegressionTestMode() && !testDescriptor.isRegressionTest(); + } } diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java index 4ff1e82f6d..c30970a94a 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java @@ -5,12 +5,13 @@ package net.sourceforge.pmd.testframework; import static net.sourceforge.pmd.util.CollectionUtil.listOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.StringReader; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -24,12 +25,15 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Assertions; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; @@ -117,14 +121,14 @@ public abstract class RuleTst { RuleSet parsedRset = new RuleSetLoader().warnDeprecated(false).loadFromResource(ruleSet); Rule rule = parsedRset.getRuleByName(ruleName); if (rule == null) { - fail("Rule " + ruleName + " not found in ruleset " + ruleSet); + Assertions.fail("Rule " + ruleName + " not found in ruleset " + ruleSet); } else { rule.setRuleSetName(ruleSet); } return rule; } catch (RuleSetLoadException e) { e.printStackTrace(); - fail("Couldn't find ruleset " + ruleSet); + Assertions.fail("Couldn't find ruleset " + ruleSet); return null; } } @@ -170,8 +174,8 @@ public abstract class RuleTst { if (test.getNumberOfProblemsExpected() != res) { printReport(test, report); } - assertEquals('"' + test.getDescription() + "\" resulted in wrong number of failures,", - test.getNumberOfProblemsExpected(), res); + Assertions.assertEquals(test.getNumberOfProblemsExpected(), res, + '"' + test.getDescription() + "\" resulted in wrong number of failures,"); assertMessages(report, test); assertLineNumbers(report, test); } finally { @@ -212,9 +216,8 @@ public abstract class RuleTst { if (!expectedMessages.get(index).equals(actual)) { printReport(test, report); } - assertEquals( - '"' + test.getDescription() + "\" produced wrong message on violation number " + (index + 1) + ".", - expectedMessages.get(index), actual); + Assertions.assertEquals(expectedMessages.get(index), actual, + '"' + test.getDescription() + "\" produced wrong message on violation number " + (index + 1) + "."); index++; } } @@ -237,8 +240,9 @@ public abstract class RuleTst { if (expected.get(index) != actual.intValue()) { printReport(test, report); } - assertEquals('"' + test.getDescription() + "\" violation on wrong line number: violation number " - + (index + 1) + ".", expected.get(index), actual); + Assertions.assertEquals(expected.get(index), actual, + '"' + test.getDescription() + "\" violation on wrong line number: violation number " + + (index + 1) + "."); index++; } } @@ -359,16 +363,39 @@ public abstract class RuleTst { String testXmlFileName = baseDirectory + testsFileName + ".xml"; Document doc; + List lineNumbersForTests; try (InputStream inputStream = getClass().getResourceAsStream(testXmlFileName)) { if (inputStream == null) { throw new RuntimeException("Couldn't find " + testXmlFileName); } - doc = documentBuilder.parse(inputStream); + String testXml = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + lineNumbersForTests = determineLineNumbers(testXml); + try (StringReader r = new StringReader(testXml)) { + doc = documentBuilder.parse(new InputSource(r)); + } } catch (FactoryConfigurationError | IOException | SAXException e) { throw new RuntimeException("Couldn't parse " + testXmlFileName + ", due to: " + e, e); } - return parseTests(rule, doc); + return parseTests(rule, doc, testXmlFileName, lineNumbersForTests); + } + + private List determineLineNumbers(String testXml) { + List tests = new ArrayList<>(); + int lineNumber = 1; + int index = 0; + while (index < testXml.length()) { + char c = testXml.charAt(index); + if (c == '\n') { + lineNumber++; + } else if (c == '<') { + if (testXml.startsWith(" lineNumbersForTests) { Element root = doc.getDocumentElement(); NodeList testCodes = root.getElementsByTagName("test-code"); + String absolutePathToTestXmlFile = new File(".").getAbsolutePath() + "/src/test/resources/" + + this.getClass().getPackage().getName().replaceAll("\\.", "/") + + "/" + testXmlFileName; + TestDescriptor[] tests = new TestDescriptor[testCodes.getLength()]; for (int i = 0; i < testCodes.getLength(); i++) { Element testCode = (Element) testCodes.item(i); @@ -506,6 +537,7 @@ public abstract class RuleTst { tests[i].setExpectedLineNumbers(expectedLineNumbers); tests[i].setProperties(properties); tests[i].setNumberInDocument(i + 1); + tests[i].setTestSourceUri(absolutePathToTestXmlFile, lineNumbersForTests.get(i)); } return tests; } diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/TestDescriptor.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/TestDescriptor.java index 9ac683cf50..961ecca90d 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/TestDescriptor.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/TestDescriptor.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.testframework; +import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Properties; @@ -32,6 +33,8 @@ public class TestDescriptor { private boolean useAuxClasspath = true; private int numberInDocument = -1; + private URI testSourceUri; + public TestDescriptor() { // Empty default descriptor added to please mvn surefire plugin } @@ -163,4 +166,12 @@ public class TestDescriptor { .replaceAll("[^\\w\\d_$]", "_") .replaceAll("\\s+", "_"); } + + public void setTestSourceUri(String absolutePath, int line) { + this.testSourceUri = URI.create("file:" + absolutePath + "?line=" + line); + } + + public URI getTestSourceUri() { + return testSourceUri; + } }