diff --git a/Gemfile.lock b/Gemfile.lock index b41e9645e9..0a16d16858 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -43,11 +43,11 @@ GEM faraday-em_http (1.0.0) faraday-em_synchrony (1.0.0) faraday-excon (1.1.0) - faraday-http-cache (2.2.0) + faraday-http-cache (2.4.0) faraday (>= 0.8) faraday-httpclient (1.0.1) - faraday-multipart (1.0.3) - multipart-post (>= 1.2, < 3) + faraday-multipart (1.0.4) + multipart-post (~> 2) faraday-net_http (1.0.1) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) @@ -65,15 +65,15 @@ GEM liquid (5.3.0) logger-colors (1.0.0) mini_portile2 (2.8.0) - multipart-post (2.1.1) + multipart-post (2.2.3) nap (1.1.0) no_proxy_fix (0.1.2) - nokogiri (1.13.6) + nokogiri (1.13.7) mini_portile2 (~> 2.8.0) racc (~> 1.4) - octokit (4.22.0) - faraday (>= 0.9) - sawyer (~> 0.8.0, >= 0.5.3) + octokit (4.25.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) open4 (1.3.4) pmdtester (1.5.1) differ (~> 0.1) @@ -87,20 +87,20 @@ GEM racc (1.6.0) rchardet (1.8.0) rexml (3.2.5) - rouge (3.28.0) + rouge (3.29.0) ruby2_keywords (0.0.5) - rufus-scheduler (3.8.1) + rufus-scheduler (3.8.2) fugit (~> 1.1, >= 1.1.6) safe_yaml (1.0.5) - sawyer (0.8.2) + sawyer (0.9.2) addressable (>= 2.3.5) - faraday (> 0.8, < 2.0) + faraday (>= 0.17.3, < 3) slop (4.9.2) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - tzinfo (2.0.4) + tzinfo (2.0.5) concurrent-ruby (~> 1.0) - unicode-display_width (2.1.0) + unicode-display_width (2.2.0) PLATFORMS ruby diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index af044ad8d5..f85828b039 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.5) + activesupport (6.0.5.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -14,7 +14,7 @@ GEM execjs coffee-script-source (1.11.1) colorator (1.1.0) - commonmarker (0.23.4) + commonmarker (0.23.5) concurrent-ruby (1.1.10) dnsruby (1.61.9) simpleidn (~> 0.1) @@ -25,33 +25,14 @@ GEM ffi (>= 1.15.0) eventmachine (1.2.7) execjs (2.8.1) - faraday (1.10.0) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0) - faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - faraday-retry (~> 1.0) + faraday (2.3.0) + faraday-net_http (~> 2.0) ruby2_keywords (>= 0.0.4) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-multipart (1.0.3) - multipart-post (>= 1.2, < 3) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday-retry (1.0.3) + faraday-net_http (2.0.3) ffi (1.15.5) forwardable-extended (2.6.0) gemoji (3.0.1) - github-pages (226) + github-pages (227) github-pages-health-check (= 1.17.9) jekyll (= 3.9.2) jekyll-avatar (= 0.7.0) @@ -93,7 +74,7 @@ GEM liquid (= 4.0.3) mercenary (~> 0.3) minima (= 2.5.1) - nokogiri (>= 1.13.4, < 2.0) + nokogiri (>= 1.13.6, < 2.0) rouge (= 3.26.0) terminal-table (~> 1.4) github-pages-health-check (1.17.9) @@ -102,7 +83,7 @@ GEM octokit (~> 4.0) public_suffix (>= 3.0, < 5.0) typhoeus (~> 1.3) - html-pipeline (2.14.1) + html-pipeline (2.14.2) activesupport (>= 2) nokogiri (>= 1.4) http_parser.rb (0.8.0) @@ -230,14 +211,13 @@ GEM jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.15.0) - multipart-post (2.1.1) - nokogiri (1.13.6) + minitest (5.16.2) + nokogiri (1.13.7) mini_portile2 (~> 2.8.0) racc (~> 1.4) - octokit (4.22.0) - faraday (>= 0.9) - sawyer (~> 0.8.0, >= 0.5.3) + octokit (4.25.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (4.0.7) @@ -255,9 +235,9 @@ GEM sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - sawyer (0.8.2) + sawyer (0.9.2) addressable (>= 2.3.5) - faraday (> 0.8, < 2.0) + faraday (>= 0.17.3, < 3) simpleidn (0.2.1) unf (~> 0.1.4) terminal-table (1.8.0) @@ -265,13 +245,13 @@ GEM thread_safe (0.3.6) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (1.2.9) + tzinfo (1.2.10) thread_safe (~> 0.1) unf (0.1.4) unf_ext - unf_ext (0.0.8.1) + unf_ext (0.0.8.2) unicode-display_width (1.8.0) - zeitwerk (2.5.4) + zeitwerk (2.6.0) PLATFORMS ruby diff --git a/docs/_plugins/jdoc_namespace_tag.rb b/docs/_plugins/jdoc_namespace_tag.rb index f6de2f0c49..f17070f01c 100644 --- a/docs/_plugins/jdoc_namespace_tag.rb +++ b/docs/_plugins/jdoc_namespace_tag.rb @@ -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 diff --git a/docs/pages/pmd/userdocs/extending/testing.md b/docs/pages/pmd/userdocs/extending/testing.md index f33c74175b..7464dfd4af 100644 --- a/docs/pages/pmd/userdocs/extending/testing.md +++ b/docs/pages/pmd/userdocs/extending/testing.md @@ -135,14 +135,18 @@ between different test cases. The `` 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. diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 6bf79e2b2a..654fa64779 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -59,6 +59,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 @@ -70,8 +71,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) @@ -82,6 +87,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 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/jaxen/xml/RegexpAcceptance.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/jaxen/xml/RegexpAcceptance.xml deleted file mode 100644 index a42ecbdfa8..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/jaxen/xml/RegexpAcceptance.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - 1 - - - - - 0 - - - - - 1 - - - \ No newline at end of file diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml index f1cdcecd88..ecdd106b5d 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml @@ -150,7 +150,8 @@ public class TournamentTest extends TestCase { ]]> - + + JUnit 4 test detection without proper auxclasspath 1 7 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/CyclomaticComplexity.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/CyclomaticComplexity.xml index cf0423eb7b..bce355d839 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/CyclomaticComplexity.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/design/xml/CyclomaticComplexity.xml @@ -149,7 +149,7 @@ public class Test { - + #985 Suppressed methods shouldn't affect avg CyclomaticComplexity 2 0 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml index fac409a04b..faeb1f6d6a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloseResource.xml @@ -298,7 +298,7 @@ public class Foo { ]]> - + invoke an external method that close the resource: bug 2920057 closeStatement,closeStatement,closeResultSet,closeConnexion java.sql.Connection,java.sql.Statement,java.sql.ResultSet,java.sql.PreparedStatement @@ -329,7 +329,7 @@ public class StructureFactory { ]]> - + 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 closeStatement,closeStatement,closeResultSet,closeConnexion java.sql.Connection,java.sql.Statement,java.sql.ResultSet,java.sql.PreparedStatement @@ -364,7 +364,7 @@ public class StructureFactory { ]]> - + #1011 CloseResource Rule ignores Constructors 1 - + #1011 CloseResource Rule ignores Constructors - closed in finally 0 - + #1011 CloseResource Rule ignores Constructors - not a problem - instance variable 0 - + #1029 No instance level check in the close resource rule 0 codeFragments = parseCodeFragments(err, root); @@ -51,7 +55,7 @@ class BaseTestParserImpl { RuleTestDescriptor descriptor = new RuleTestDescriptor(i, rule.deepCopy()); try (PmdXmlReporter errScope = err.newScope()) { - parseSingleTest(testCodes.get(i), descriptor, codeFragments, usedFragments, errScope); + parseSingleTest(testCodes.get(i), descriptor, codeFragments, usedFragments, positionedXmlDoc.getPositioner(), errScope); if (!errScope.hasError()) { result.addTest(descriptor); } @@ -85,6 +89,7 @@ class BaseTestParserImpl { RuleTestDescriptor descriptor, Map fragments, Set usedFragments, + XmlPositioner xmlPositioner, PmdXmlReporter err) { { String description = getSingleChildText(testCode, "description", true, err); @@ -102,6 +107,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); @@ -118,6 +130,9 @@ class BaseTestParserImpl { if (lversion != null) { descriptor.setLanguageVersion(lversion); } + + XmlPosition startPosition = xmlPositioner.startPositionOf(testCode); + descriptor.setLineNumber(startPosition.getLine()); } private void parseExpectedProblems(Element testCode, RuleTestDescriptor descriptor, PmdXmlReporter err) { diff --git a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/RuleTestCollection.java b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/RuleTestCollection.java index 8ce61f6c70..0814e984cc 100644 --- a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/RuleTestCollection.java +++ b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/RuleTestCollection.java @@ -18,6 +18,7 @@ import java.util.Objects; public class RuleTestCollection { private final List tests = new ArrayList<>(); + private String absoluteUriToTestXmlFile; public void addTest(RuleTestDescriptor descriptor) { tests.add(Objects.requireNonNull(descriptor)); @@ -28,4 +29,24 @@ 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; + } + + public String getAbsoluteUriToTestXmlFile() { + return absoluteUriToTestXmlFile; + } + + public void setAbsoluteUriToTestXmlFile(String absoluteUriToTestXmlFile) { + this.absoluteUriToTestXmlFile = absoluteUriToTestXmlFile; + } } diff --git a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/RuleTestDescriptor.java b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/RuleTestDescriptor.java index 6d45540b1b..01baf8f00c 100644 --- a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/RuleTestDescriptor.java +++ b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/RuleTestDescriptor.java @@ -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(); @@ -25,6 +26,7 @@ public class RuleTestDescriptor { private int expectedProblems; private List expectedLineNumbers; private List expectedMessages; + private int lineNumber; public RuleTestDescriptor(int index, Rule rule) { this.index = index; @@ -106,4 +108,20 @@ public class RuleTestDescriptor { public List getExpectedMessages() { return expectedMessages; } + + public boolean isFocused() { + return focused; + } + + public void setFocused(boolean focused) { + this.focused = focused; + } + + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } } diff --git a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/TestSchemaParser.java b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/TestSchemaParser.java index 3c4ff8464d..4d1688c940 100644 --- a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/TestSchemaParser.java +++ b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/TestSchemaParser.java @@ -64,7 +64,7 @@ public class TestSchemaParser { PositionedXmlDoc doc = ooxml.parse(newDocumentBuilder(), inputSource); try (PmdXmlReporterImpl err = new PmdXmlReporterImpl(ooxml, doc.getPositioner())) { - RuleTestCollection collection = version.getParserImpl().parseDocument(rule, doc.getDocument(), err); + RuleTestCollection collection = version.getParserImpl().parseDocument(rule, doc, err); if (err.hasError()) { // todo maybe add a way not to throw here throw new IllegalStateException("Errors were encountered while parsing XML tests"); diff --git a/pmd-test-schema/src/main/resources/net/sourceforge/pmd/test/schema/rule-tests_1_0_0.xsd b/pmd-test-schema/src/main/resources/net/sourceforge/pmd/test/schema/rule-tests_1_0_0.xsd index 12d39b7976..6d8b1d2ac5 100644 --- a/pmd-test-schema/src/main/resources/net/sourceforge/pmd/test/schema/rule-tests_1_0_0.xsd +++ b/pmd-test-schema/src/main/resources/net/sourceforge/pmd/test/schema/rule-tests_1_0_0.xsd @@ -58,7 +58,13 @@ - + + + + This attribute is deprecated, it is assumed true and ignored. + + + @@ -67,7 +73,13 @@ - + + + + This attribute is deprecated, it is assumed true and ignored. + + + @@ -76,6 +88,18 @@ + + + + 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. + + + diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTestRunner.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTestRunner.java index 8ac071c85b..5e08bf3d3b 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTestRunner.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTestRunner.java @@ -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; import java.util.concurrent.ConcurrentMap; @@ -27,6 +25,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. It supports Before and After methods as well as @@ -67,13 +67,20 @@ public class RuleTestRunner extends ParentRunner { @Override protected List getChildren() { - final List rules = new ArrayList<>(instance.getRules()); + List rules = new ArrayList<>(instance.getRules()); rules.sort(Comparator.comparing(Rule::getName)); - final List tests = new LinkedList<>(); - for (final Rule r : rules) { - final TestDescriptor[] ruleTests = instance.extractTestsFromXml(r); - Collections.addAll(tests, ruleTests); + List tests = new ArrayList<>(); + for (Rule r : rules) { + RuleTestCollection ruleTests = instance.parseTestCollection(r); + RuleTestDescriptor focused = ruleTests.getFocusedTestOrNull(); + for (RuleTestDescriptor t : ruleTests.getTests()) { + TestDescriptor td = new TestDescriptor(t, ruleTests.getAbsoluteUriToTestXmlFile()); + if (focused != null && !focused.equals(t)) { + td.setRegressionTest(false); // disable it + } + tests.add(td); + } } return tests; 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 b545ee8dc8..b9247aa1e7 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 @@ -10,12 +10,10 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; -import java.nio.charset.StandardCharsets; 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.Map; import java.util.stream.Collectors; @@ -33,6 +31,7 @@ import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetLoadException; import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.processor.AbstractPMDProcessor; @@ -40,8 +39,8 @@ import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.renderers.TextRenderer; import net.sourceforge.pmd.reporting.GlobalAnalysisListener; import net.sourceforge.pmd.test.schema.RuleTestCollection; +import net.sourceforge.pmd.test.schema.RuleTestDescriptor; import net.sourceforge.pmd.test.schema.TestSchemaParser; -import net.sourceforge.pmd.util.IOUtil; /** * Advanced methods for test cases @@ -73,6 +72,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 { @@ -96,12 +97,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, Object> oldProperties = rule.getPropertiesByPropertyDescriptor(); try { @@ -229,37 +231,33 @@ public abstract class RuleTst { } private Report processUsingStringReader(TestDescriptor test, Rule rule) { - return runTestFromString(test.getCode(), rule, test.getLanguageVersion(), test.isUseAuxClasspath()); + return runTestFromString(test.getCode(), rule, test.getLanguageVersion()); } - public Report runTestFromString(String code, Rule rule, LanguageVersion languageVersion, boolean isUseAuxClasspath) { + /** + * Run the rule on the given code and put the violations in the report. + */ + @InternalApi + @Deprecated + public Report runTestFromString(String code, Rule rule, LanguageVersion languageVersion) { + return runTestFromString(code, rule, languageVersion, true); + } + + @InternalApi + @Deprecated + public Report runTestFromString(String code, Rule rule, LanguageVersion languageVersion, + boolean isUseAuxClasspath) { try { PMDConfiguration configuration = new PMDConfiguration(); configuration.setIgnoreIncrementalAnalysis(true); configuration.setDefaultLanguageVersion(languageVersion); configuration.setThreads(1); - - if (isUseAuxClasspath) { - // configure the "auxclasspath" option for unit testing - // we share a single classloader so that pmd-java doesn't create - // a new TypeSystem for every test. This problem will go - // away when languages have a lifecycle. - configuration.setClassLoader(classpathClassLoader); - } 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 + // we share a single classloader so that pmd-java doesn't create + // a new TypeSystem for every test. This problem will go + // away when languages have a lifecycle. + configuration.setClassLoader(classpathClassLoader); try (GlobalReportBuilderListener reportBuilder = new GlobalReportBuilderListener(); // Add a listener that throws when an error occurs: @@ -283,10 +281,18 @@ public abstract class RuleTst { } } + @InternalApi + @Deprecated + public Report runTestFromString(TestDescriptor test, Rule rule) { + return runTestFromString(test.getCode(), rule, test.getLanguageVersion(), test.isUseAuxClasspath()); + } + /** * 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())) { @@ -304,12 +310,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/"); } @@ -319,55 +339,21 @@ 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, testsFileName, baseDirectory); } private TestDescriptor[] toLegacyArray(RuleTestCollection collection, String testsFileName, String baseDirectory) { - String testXmlFileName = baseDirectory + testsFileName + ".xml"; - List lineNumbersForTests; - try (InputStream inputStream = getClass().getResourceAsStream(testXmlFileName)) { - String testXml = IOUtil.readToString(inputStream, StandardCharsets.UTF_8); - lineNumbersForTests = determineLineNumbers(testXml); - } catch (Exception e) { - throw new RuntimeException("Couldn't parse " + testXmlFileName + ", due to: " + e, e); - } - - if (lineNumbersForTests.size() != collection.getTests().size()) { - throw new IllegalStateException("Test to line number mapping doesn't work!"); - } - - String absoluteUriToTestXmlFile = new File(".").getAbsoluteFile().toURI() + "/src/test/resources/" - + this.getClass().getPackage().getName().replaceAll("\\.", "/") - + "/" + testXmlFileName; - - TestDescriptor[] result = new TestDescriptor[collection.getTests().size()]; for (int i = 0; i < collection.getTests().size(); i++) { - result[i] = new TestDescriptor(collection.getTests().get(i), absoluteUriToTestXmlFile, lineNumbersForTests.get(i)); + result[i] = new TestDescriptor(collection.getTests().get(i), collection.getAbsoluteUriToTestXmlFile()); } return result; } - 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(" 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); + List tests = new ArrayList<>(); + for (Rule r : rules) { + RuleTestCollection ruleTests = parseTestCollection(r); + RuleTestDescriptor focused = ruleTests.getFocusedTestOrNull(); + for (RuleTestDescriptor t : ruleTests.getTests()) { + TestDescriptor td = new TestDescriptor(t, ruleTests.getAbsoluteUriToTestXmlFile()); + if (focused != null && !focused.equals(t)) { + td.setRegressionTest(false); // disable it + } + tests.add(td); + } } return tests.stream().map(this::toDynamicTest).collect(Collectors.toList()); 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 7657d5c11f..3982199f89 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 @@ -17,8 +17,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; @@ -54,7 +57,7 @@ public class TestDescriptor { } // for compatibility - TestDescriptor(RuleTestDescriptor td, String absoluteUriToTestXmlFile, int lineNumber) { + TestDescriptor(RuleTestDescriptor td, String absoluteUriToTestXmlFile) { this.rule = td.getRule(); this.code = td.getCode(); this.description = td.getDescription(); @@ -66,7 +69,7 @@ public class TestDescriptor { this.properties = td.getProperties(); this.languageVersion = td.getLanguageVersion(); this.numberInDocument = td.getIndex(); - this.setTestSourceUri(absoluteUriToTestXmlFile, lineNumber); + this.setTestSourceUri(absoluteUriToTestXmlFile, td.getLineNumber()); } public int getNumberInDocument() { diff --git a/pmd-test/src/main/resources/rule-tests_1_0_0.xsd b/pmd-test/src/main/resources/rule-tests_1_0_0.xsd index 3a44281895..bc398088d3 100644 --- a/pmd-test/src/main/resources/rule-tests_1_0_0.xsd +++ b/pmd-test/src/main/resources/rule-tests_1_0_0.xsd @@ -58,7 +58,13 @@ - + + + + This attribute is deprecated, it is assumed true and ignored. + + + @@ -67,7 +73,13 @@ - + + + + This attribute is deprecated, it is assumed true and ignored. + + + @@ -76,6 +88,18 @@ + + + + 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. + + + diff --git a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java index e00dee9987..6fad772396 100644 --- a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java +++ b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java @@ -30,6 +30,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