[test] Migrate PmdRuleTst to JUnit5 dynamic tests

This commit is contained in:
Andreas Dangel
2022-04-29 16:49:25 +02:00
parent 5c3afd881f
commit ac3f152dcd
3 changed files with 93 additions and 16 deletions

View File

@ -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<DynamicTest> ruleTests() {
final List<Rule> rules = new ArrayList<>(getRules());
rules.sort(Comparator.comparing(Rule::getName));
final List<TestDescriptor> 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();
}
}

View File

@ -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<Integer> 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<Integer> determineLineNumbers(String testXml) {
List<Integer> 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("<test-code", index)) {
tests.add(lineNumber);
}
}
index++;
}
return tests;
}
/**
@ -398,10 +425,14 @@ public abstract class RuleTst {
}
}
private TestDescriptor[] parseTests(Rule rule, Document doc) {
private TestDescriptor[] parseTests(Rule rule, Document doc, String testXmlFileName, List<Integer> 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;
}

View File

@ -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;
}
}