Merge pull request #4058 from oowekyala:pmd6-focused-test-attr

[test-schema] Add focused attr to the test schema #4058
This commit is contained in:
Andreas Dangel
2022-07-23 19:15:20 +02:00
18 changed files with 209 additions and 100 deletions

View File

@ -102,7 +102,7 @@ class JDocNamespaceDeclaration < Liquid::Tag
RESERVED_NSPACES = ['apex', 'core', 'cpp', 'cs', 'dart', 'dist', 'doc', 'fortran', 'go', 'groovy', 'java',
'javascript', 'jsp',
'kotlin', 'lua', 'matlab', 'objectivec', 'perl', 'php', 'plsql', 'python', 'ruby', 'scala', 'swift',
'test', 'ui',
'test', 'test-schema', 'ui',
'modelica', 'visualforce', 'vm', 'xml'].flat_map {|m| [m, "pmd-" + m]}
def self.make_base_namespaces

View File

@ -135,14 +135,18 @@ between different test cases.
The `<test-code>` elements understands the following optional attributes:
* **reinitializeRule**: By default, it's `true`, so each test case starts with a fresh instantiated rule. Set it
to `false` to reproduce cases, where the previous run has influences.
* **disabled**: By default, it's `false`. Set it to `true`, to ignore and skip a test case.
* **disabled**: By default, it's `false`. Set ti to `true`, to ignore and skip a test case.
* **focused**: By default, it's `false`. Set it to `true`, to ignore all other test cases.
* **useAuxClasspath**: By default, it's `true`. Set it to `false` to reproduce issues which only
* **useAuxClasspath**: _deprecated since PMD 6.48.0: assumed true, has no effect anymore._
By default, it's `true`. Set it to `false` to reproduce issues which only
appear without type resolution.
* **reinitializeRule**: _deprecated since PMD 6.48.0: assumed true, has no effect anymore._
By default, it's `true`, so each test case starts with a fresh instantiated rule. Set it
to `false` to reproduce cases, where the previous run has influences.
* **regressionTest**: _deprecated since PMD 6.48.0: Use `disabled` instead. Note: It has the opposite boolean
semantic._ By default, it's `true`. Set it to `false`, to ignore and skip a test case.

View File

@ -54,6 +54,7 @@ Being based on a proper Antlr grammar, CPD can:
* java-performance
* [#3625](https://github.com/pmd/pmd/issues/3625): \[java] AddEmptyString - false negative with empty var
* test
* [#3302](https://github.com/pmd/pmd/pull/3302): \[test] Improve xml test schema
* [#3758](https://github.com/pmd/pmd/issues/3758): \[test] Move pmd-test to java 8
* [#3976](https://github.com/pmd/pmd/pull/3976): \[test] Extract xml schema module
@ -65,8 +66,12 @@ Being based on a proper Antlr grammar, CPD can:
this module for testing your own custom rules, you'll need to make sure to use at least Java 8.
* The new module "pmd-test-schema" contains now the XSD schema and the code to parse the rule test XML files. The
schema has been extracted in order to easily share it with other tools like the Rule Designer or IDE plugins.
* The attribute `isRegressionTest` is deprecated and the new attribute `disabled` should be used instead for
defining whether a rule test should be skipped or not.
* Test schema changes:
* The attribute `isRegressionTest` of `test-code` is deprecated. The new
attribute `disabled` should be used instead for defining whether a rule test should be skipped or not.
* The attributes `reinitializeRule` and `useAuxClasspath` of `test-code` are deprecated and assumed true.
They will not be replaced.
* The new attribute `focused` of `test-code` allows disabling all tests except the focused one temporarily.
* More information about the rule test framework can be found in the documentation:
[Testing your rules](pmd_userdocs_extending_testing.html)
@ -77,6 +82,8 @@ Being based on a proper Antlr grammar, CPD can:
but it is no longer supported with Java 19 Preview.
* The interface {% jdoc core::cpd.renderer.CPDRenderer %} is deprecated. For custom CPD renderers
the new interface {% jdoc core::cpd.renderer.CPDReportRenderer %} should be used.
* The class {% jdoc test::testframework.TestDescriptor %} is deprecated, replaced with {% jdoc test-schema::testframework.RuleTestDescriptor %}.
* Many methods of {% jdoc test::testframework.RuleTst %} have been deprecated as internal API.
#### Experimental APIs

View File

@ -4,33 +4,64 @@
package net.sourceforge.pmd.jaxen;
import static org.junit.Assert.assertEquals;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.PmdAnalysis;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.java.JavaLanguageModule;
import net.sourceforge.pmd.lang.rule.XPathRule;
import net.sourceforge.pmd.testframework.SimpleAggregatorTst;
import net.sourceforge.pmd.testframework.TestDescriptor;
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
public class RegexpAcceptanceTest extends SimpleAggregatorTst {
public class RegexpAcceptanceTest {
private static final String XPATH = "//ClassOrInterfaceDeclaration[matches(@Image, 'F?o')]";
@Override
protected void setUp() {
// not registering any rule
private Rule rule;
@Before
public void setUp() {
rule = new XPathRule(XPathVersion.XPATH_1_0, XPATH);
rule.setLanguage(LanguageRegistry.getLanguage(JavaLanguageModule.NAME));
rule.setMessage("F?o matched");
}
@Test
public void testSimple() {
Rule r = new XPathRule(XPATH);
r.setLanguage(LanguageRegistry.getLanguage(JavaLanguageModule.NAME));
r.setMessage("");
TestDescriptor[] testDescriptors = extractTestsFromXml(r, "RegexpAcceptance");
for (TestDescriptor testDescriptor : testDescriptors) {
testDescriptor.setReinitializeRule(false);
public void shouldMatchFoo() {
List<RuleViolation> violations = eval("public class Foo {}");
assertEquals(1, violations.size());
}
@Test
public void shouldNotMatchBar() {
List<RuleViolation> violations = eval("public class Bar {}");
assertEquals(0, violations.size());
}
@Test
public void shouldMatchFlo() {
List<RuleViolation> violations = eval("public class Flo {}");
assertEquals(1, violations.size());
}
private List<RuleViolation> eval(String code) {
PMDConfiguration config = new PMDConfiguration();
config.setIgnoreIncrementalAnalysis(true);
try (PmdAnalysis pmd = PmdAnalysis.create(config)) {
pmd.addRuleSet(RuleSet.forSingleRule(rule));
pmd.files().addSourceFile(code, "RegexpAcceptanceTest.java");
Report report = pmd.performAnalysisAndCollectReport();
return report.getViolations();
}
runTests(testDescriptors);
}
}

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test-data
xmlns="http://pmd.sourceforge.net/rule-tests"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
<test-code>
<description><![CDATA[
XPath should match Foo
]]></description>
<expected-problems>1</expected-problems>
<code><![CDATA[
public class Foo {}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
XPath should not match Bar
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Bar {}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
XPath should match Flo
]]></description>
<expected-problems>1</expected-problems>
<code><![CDATA[
public class Flo {}
]]></code>
</test-code>
</test-data>

View File

@ -178,7 +178,8 @@ public class TournamentTest extends TestCase {
]]></code>
</test-code>
<test-code useAuxClasspath="false">
<!-- it's not possible anymore to test without auxclasspath. junit4 is always on the test auxclasspath -->
<test-code disabled="true">
<description>JUnit 4 test detection without proper auxclasspath</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>8</expected-linenumbers>

View File

@ -149,7 +149,7 @@ public class Test {
<code-ref id="constructor-violation"/>
</test-code>
<test-code reinitializeRule="true">
<test-code>
<description>#985 Suppressed methods shouldn't affect avg CyclomaticComplexity</description>
<rule-property name="methodReportLevel">2</rule-property>
<expected-problems>0</expected-problems>

View File

@ -164,7 +164,7 @@ public class Test {
<code-ref id="constructor-violation"/>
</test-code>
<test-code reinitializeRule="true">
<test-code>
<description>#985 Suppressed methods shouldn't affect avg CyclomaticComplexity</description>
<rule-property name="reportLevel">2</rule-property>
<expected-problems>0</expected-problems>

View File

@ -249,7 +249,7 @@ public class Foo {
]]></code>
</test-code>
<test-code reinitializeRule="true">
<test-code>
<description>invoke an external method that close the resource: bug 2920057</description>
<rule-property name="closeTargets">closeStatement,closeStatement,closeResultSet,closeConnexion</rule-property>
<rule-property name="types">java.sql.Connection,java.sql.Statement,java.sql.ResultSet,java.sql.PreparedStatement</rule-property>
@ -280,7 +280,7 @@ public class StructureFactory {
]]></code>
</test-code>
<test-code reinitializeRule="true">
<test-code>
<description>invoke an external method that closes the resource, but one is not the right method and an another is not the right variable: see bug 2920057</description>
<rule-property name="closeTargets">closeStatement,closeStatement,closeResultSet,closeConnexion</rule-property>
<rule-property name="types">java.sql.Connection,java.sql.Statement,java.sql.ResultSet,java.sql.PreparedStatement</rule-property>
@ -312,7 +312,7 @@ public class StructureFactory {
]]></code>
</test-code>
<test-code reinitializeRule="true">
<test-code>
<description>#1011 CloseResource Rule ignores Constructors</description>
<expected-problems>1</expected-problems>
<code><![CDATA[
@ -326,7 +326,7 @@ public class Test {
]]></code>
</test-code>
<test-code reinitializeRule="true">
<test-code>
<description>#1011 CloseResource Rule ignores Constructors - closed in finally</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
@ -345,7 +345,7 @@ public class Test {
]]></code>
</test-code>
<test-code reinitializeRule="true">
<test-code>
<description>#1011 CloseResource Rule ignores Constructors - not a problem - instance variable</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
@ -360,7 +360,7 @@ public class Test {
]]></code>
</test-code>
<test-code reinitializeRule="true">
<test-code>
<description>#1029 No instance level check in the close resource rule</description>
<expected-problems>0</expected-problems>
<code><![CDATA[

View File

@ -100,6 +100,13 @@ class BaseTestParserImpl {
descriptor.setDisabled(disabled);
boolean focused = parseBoolAttribute(testCode, "focused", false, err,
"Attribute focused is used, do not forget to remove it when checking in sources");
descriptor.setFocused(focused);
Properties properties = parseRuleProperties(testCode, descriptor.getRule(), err);
descriptor.getProperties().putAll(properties);

View File

@ -28,4 +28,17 @@ public class RuleTestCollection {
return Collections.unmodifiableList(tests);
}
/**
* Returns the last test of the collection which is focused.
*/
public RuleTestDescriptor getFocusedTestOrNull() {
RuleTestDescriptor focused = null;
for (RuleTestDescriptor test : tests) {
if (test.isFocused()) {
focused = test;
}
}
return focused;
}
}

View File

@ -16,6 +16,7 @@ import net.sourceforge.pmd.lang.LanguageVersion;
public class RuleTestDescriptor {
private boolean disabled;
private boolean focused;
private String description;
private LanguageVersion languageVersion;
private final Properties properties = new Properties();
@ -106,4 +107,12 @@ public class RuleTestDescriptor {
public List<String> getExpectedMessages() {
return expectedMessages;
}
public boolean isFocused() {
return focused;
}
public void setFocused(boolean focused) {
this.focused = focused;
}
}

View File

@ -58,7 +58,13 @@
</annotation>
</element>
</sequence>
<attribute name="reinitializeRule" type="boolean" default="true"/>
<attribute name="reinitializeRule" type="boolean" default="true">
<annotation>
<documentation>
This attribute is deprecated, it is assumed true and ignored.
</documentation>
</annotation>
</attribute>
<attribute name="regressionTest" type="boolean" default="true">
<annotation>
<documentation>
@ -67,7 +73,13 @@
</documentation>
</annotation>
</attribute>
<attribute name="useAuxClasspath" type="boolean" default="true"/>
<attribute name="useAuxClasspath" type="boolean" default="true">
<annotation>
<documentation>
This attribute is deprecated, it is assumed true and ignored.
</documentation>
</annotation>
</attribute>
<attribute name="disabled" type="boolean" default="false">
<annotation>
@ -76,6 +88,18 @@
</documentation>
</annotation>
</attribute>
<attribute name="focused" type="boolean" default="false">
<annotation>
<documentation>
If true, only this test will be executed, and all others will be disabled.
If several tests in the same file are focused, then the last one wins, in
document order.
This attribute is provided as a way for developers to temporarily focus on a single test.
Test files with a focused test should not be checked in. For this reason,
using this attribute produces a warning.
</documentation>
</annotation>
</attribute>
</complexType>
<complexType name="codeFragmentType">

View File

@ -5,9 +5,7 @@
package net.sourceforge.pmd.testframework;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@ -26,6 +24,8 @@ import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.test.schema.RuleTestCollection;
import net.sourceforge.pmd.test.schema.RuleTestDescriptor;
/**
* A JUnit Runner, that executes all declared rule tests in the class.
@ -64,18 +64,18 @@ public class RuleTestRunner extends ParentRunner<TestDescriptor> {
@Override
protected List<TestDescriptor> getChildren() {
List<Rule> rules = new ArrayList<>(instance.getRules());
Collections.sort(rules, new Comparator<Rule>() {
@Override
public int compare(Rule o1, Rule o2) {
return o1.getName().compareTo(o2.getName());
}
});
rules.sort(Comparator.comparing(Rule::getName));
List<TestDescriptor> tests = new LinkedList<>();
List<TestDescriptor> tests = new ArrayList<>();
for (Rule r : rules) {
TestDescriptor[] ruleTests = instance.extractTestsFromXml(r);
for (TestDescriptor t : ruleTests) {
tests.add(t);
RuleTestCollection ruleTests = instance.parseTestCollection(r);
RuleTestDescriptor focused = ruleTests.getFocusedTestOrNull();
for (RuleTestDescriptor t : ruleTests.getTests()) {
TestDescriptor td = new TestDescriptor(t);
if (focused != null && !focused.equals(t)) {
td.setRegressionTest(false); // disable it
}
tests.add(td);
}
}

View File

@ -30,6 +30,7 @@ import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.RulesetsFactoryUtils;
import net.sourceforge.pmd.SourceCodeProcessor;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.renderers.TextRenderer;
@ -51,6 +52,8 @@ public abstract class RuleTst {
/**
* Find a rule in a certain ruleset by name
*
* todo make this static
*/
public Rule findRule(String ruleSet, String ruleName) {
try {
@ -73,12 +76,13 @@ public abstract class RuleTst {
* violations.
*/
@SuppressWarnings("unchecked")
@InternalApi
@Deprecated
public void runTest(TestDescriptor test) {
Rule rule = test.getRule();
if (test.getReinitializeRule()) {
rule = reinitializeRule(rule);
}
// always reinitialize the rule, regardless of test.getReinitializeRule() (#3976 / #3302)
rule = reinitializeRule(rule);
Map<PropertyDescriptor<?>, Object> oldProperties = rule.getPropertiesByPropertyDescriptor();
try {
@ -218,34 +222,23 @@ public abstract class RuleTst {
/**
* Run the rule on the given code and put the violations in the report.
*/
@InternalApi
@Deprecated
public void runTestFromString(String code, Rule rule, Report report, LanguageVersion languageVersion) {
runTestFromString(code, rule, report, languageVersion, true);
}
@InternalApi
@Deprecated
public void runTestFromString(String code, Rule rule, Report report, LanguageVersion languageVersion,
boolean isUseAuxClasspath) {
try {
PMDConfiguration configuration = new PMDConfiguration();
configuration.setDefaultLanguageVersion(languageVersion);
configuration.setIgnoreIncrementalAnalysis(true);
if (isUseAuxClasspath) {
// configure the "auxclasspath" option for unit testing
configuration.prependAuxClasspath(".");
} else {
// simple class loader, that doesn't delegate to parent.
// this allows us in the tests to simulate PMD run without
// auxclasspath, not even the classes from the test dependencies
// will be found.
configuration.setClassLoader(new ClassLoader() {
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.startsWith("java.") || name.startsWith("javax.")) {
return super.loadClass(name, resolve);
}
throw new ClassNotFoundException(name);
}
});
}
// regardless of isUseAuxClasspath the auxclasspath is always used (#3976 / #3302)
// configure the "auxclasspath" option for unit testing
configuration.prependAuxClasspath(".");
RuleContext ctx = new RuleContext();
ctx.setReport(report);
ctx.setSourceCodeFile(new File("n/a"));
@ -259,6 +252,8 @@ public abstract class RuleTst {
}
}
@InternalApi
@Deprecated
public void runTestFromString(TestDescriptor test, Rule rule, Report report) {
runTestFromString(test.getCode(), rule, report, test.getLanguageVersion(), test.isUseAuxClasspath());
}
@ -267,6 +262,8 @@ public abstract class RuleTst {
* getResourceAsStream tries to find the XML file in weird locations if the
* ruleName includes the package, so we strip it here.
*/
@InternalApi
@Deprecated
protected String getCleanRuleName(Rule rule) {
String fullClassName = rule.getClass().getName();
if (fullClassName.equals(rule.getName())) {
@ -284,12 +281,26 @@ public abstract class RuleTst {
* ./xml/RuleName.xml relative to the test class. The format is defined in
* test-data.xsd.
*/
@InternalApi
@Deprecated
public TestDescriptor[] extractTestsFromXml(Rule rule) {
String testsFileName = getCleanRuleName(rule);
return extractTestsFromXml(rule, testsFileName);
}
/**
* Extract a set of tests from an XML file. The file should be
* ./xml/RuleName.xml relative to the test class. The format is defined in
* rule-tests_1_0_0.xsd in pmd-test-schema.
*/
RuleTestCollection parseTestCollection(Rule rule) {
String testsFileName = getCleanRuleName(rule);
return parseTestXml(rule, testsFileName, "xml/");
}
@InternalApi
@Deprecated
public TestDescriptor[] extractTestsFromXml(Rule rule, String testsFileName) {
return extractTestsFromXml(rule, testsFileName, "xml/");
}
@ -299,6 +310,8 @@ public abstract class RuleTst {
* should be ./xml/[testsFileName].xml relative to the test class. The
* format is defined in test-data.xsd.
*/
@InternalApi
@Deprecated
public TestDescriptor[] extractTestsFromXml(Rule rule, String testsFileName, String baseDirectory) {
RuleTestCollection collection = parseTestXml(rule, testsFileName, baseDirectory);
return toLegacyArray(collection);
@ -355,6 +368,8 @@ public abstract class RuleTst {
/**
* Run a set of tests of a certain sourceType.
*/
@InternalApi
@Deprecated
public void runTests(TestDescriptor[] tests) {
for (int i = 0; i < tests.length; i++) {
runTest(tests[i]);

View File

@ -16,8 +16,11 @@ import net.sourceforge.pmd.test.schema.RuleTestDescriptor;
/**
* Stores the information required to run a complete test.
*
* @deprecated Use {@link RuleTestDescriptor} instead
*/
@Ignore("this is not a unit test")
@Deprecated
public class TestDescriptor {
private Rule rule;
private Properties properties;

View File

@ -58,7 +58,13 @@
</annotation>
</element>
</sequence>
<attribute name="reinitializeRule" type="boolean" default="true"/>
<attribute name="reinitializeRule" type="boolean" default="true">
<annotation>
<documentation>
This attribute is deprecated, it is assumed true and ignored.
</documentation>
</annotation>
</attribute>
<attribute name="regressionTest" type="boolean" default="true">
<annotation>
<documentation>
@ -67,7 +73,13 @@
</documentation>
</annotation>
</attribute>
<attribute name="useAuxClasspath" type="boolean" default="true"/>
<attribute name="useAuxClasspath" type="boolean" default="true">
<annotation>
<documentation>
This attribute is deprecated, it is assumed true and ignored.
</documentation>
</annotation>
</attribute>
<attribute name="disabled" type="boolean" default="false">
<annotation>
@ -76,6 +88,18 @@
</documentation>
</annotation>
</attribute>
<attribute name="focused" type="boolean" default="false">
<annotation>
<documentation>
If true, only this test will be executed, and all others will be disabled.
If several tests in the same file are focused, then the last one wins, in
document order.
This attribute is provided as a way for developers to temporarily focus on a single test.
Test files with a focused test should not be checked in. For this reason,
using this attribute produces a warning.
</documentation>
</annotation>
</attribute>
</complexType>
<complexType name="codeFragmentType">

View File

@ -36,6 +36,10 @@ public class RuleTstTest {
private Rule rule = mock(Rule.class);
private RuleTst ruleTester = new RuleTst() {
@Override
public Rule findRule(String ruleSet, String ruleName) {
return rule;
}
};
@Test