Merge branch 'master' into 7.0.x
This commit is contained in:
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -1,6 +1,8 @@
|
||||
<!-- Please, prefix the report title with the language it applies to within brackets, such as [java] or [apex].
|
||||
If not specific to a language, you can use [core]. -->
|
||||
|
||||
<!-- NB: issues about the rule designer should be opened at https://github.com/pmd/pmd-designer/issues -->
|
||||
|
||||
**Affects PMD Version:**
|
||||
|
||||
**Rule:**
|
||||
|
@ -3,6 +3,9 @@
|
||||
First off, thanks for taking the time to contribute!
|
||||
|
||||
|
||||
| NB: the rule designer is developed over at [pmd/pmd-designer](https://github.com/pmd/pmd-designer). Please refer to the specific [contributor documentation](https://github.com/pmd/pmd-designer#contributing) if your issue, feature request or PR touches the designer. |
|
||||
| --- |
|
||||
|
||||
## Pull requests
|
||||
|
||||
* Please create your pull request against the `master` branch. We will rebase/merge it to the maintenance
|
||||
|
@ -22,6 +22,10 @@ Our latest source of PMD can be found on [GitHub](https://github.com/pmd/pmd). F
|
||||
* [How to build PMD](BUILDING.md)
|
||||
* [How to contribute to PMD](CONTRIBUTING.md)
|
||||
|
||||
The rule designer is developed over at [pmd/pmd-designer](https://github.com/pmd/pmd-designer).
|
||||
Please see [its README](https://github.com/pmd/pmd-designer#contributing) for
|
||||
developer documentation.
|
||||
|
||||
## News and Website
|
||||
|
||||
More information can be found on our [Website](https://pmd.github.io) and on [SourceForge](https://sourceforge.net/projects/pmd/).
|
||||
|
@ -80,6 +80,9 @@ echo " the new release based on the release notes"
|
||||
echo
|
||||
echo "* Update **../pmd.github.io/_config.yml** to mention the new release"
|
||||
echo
|
||||
echo "* Update property `pmd-designer.version` in **pom.xml** to reference the latest pmd-designer release"
|
||||
echo " See <https://search.maven.org/search?q=g:net.sourceforge.pmd%20AND%20a:pmd-ui&core=gav> for the available releases."
|
||||
echo
|
||||
echo "Press enter to continue..."
|
||||
read
|
||||
|
||||
|
@ -33,8 +33,10 @@ The date and the version must be updated in `docs/_config.yml`, e.g.
|
||||
pmd:
|
||||
version: 6.0.0
|
||||
date: 2017-12-15
|
||||
release_type: minor
|
||||
```
|
||||
|
||||
The release type could be one of "bugfix", "minor", or "major".
|
||||
|
||||
The release notes usual mention any new rules that have been added since the last release.
|
||||
Please double check the file `pmd-core/src/main/resources/rulesets/releases/<version>.xml`, so
|
||||
@ -43,6 +45,10 @@ that all new rules are listed.
|
||||
We maintain a documentation for the [next major release](pmd_next_major_development.html). Copy the API
|
||||
changes from the current release notes to this document: `docs/pages/next_major_development.md`.
|
||||
|
||||
The designer lives at [pmd/pmd-designer](https://github.com/pmd/pmd-designer).
|
||||
Update property `pmd-designer.version` in **pom.xml** to reference the latest pmd-designer release.
|
||||
See <https://search.maven.org/search?q=g:net.sourceforge.pmd%20AND%20a:pmd-ui&core=gav> for the available releases.
|
||||
|
||||
Check in all (version) changes to branch master or any other branch, from which the release takes place:
|
||||
|
||||
$ git commit -a -m "Prepare pmd release <version>"
|
||||
|
@ -58,6 +58,7 @@ folder: pmd/rules
|
||||
* [UseCollectionIsEmpty](pmd_rules_java_bestpractices.html#usecollectionisempty): The isEmpty() method on java.util.Collection is provided to determine if a collection has any ele...
|
||||
* [UseTryWithResources](pmd_rules_java_bestpractices.html#usetrywithresources): Java 7 introduced the try-with-resources statement. This statement ensures that each resource is ...
|
||||
* [UseVarargs](pmd_rules_java_bestpractices.html#usevarargs): Java 5 introduced the varargs parameter declaration for methods and constructors. This syntactic...
|
||||
* [WhileLoopWithLiteralBoolean](pmd_rules_java_bestpractices.html#whileloopwithliteralboolean): 'do {} while (true);' requires reading the end of the statement before it isapparent that it loop...
|
||||
|
||||
## Code Style
|
||||
|
||||
|
@ -5,7 +5,7 @@ permalink: pmd_rules_java_bestpractices.html
|
||||
folder: pmd/rules/java
|
||||
sidebaractiveurl: /pmd_rules_java.html
|
||||
editmepath: ../pmd-java/src/main/resources/category/java/bestpractices.xml
|
||||
keywords: Best Practices, AbstractClassWithoutAbstractMethod, AccessorClassGeneration, AccessorMethodGeneration, ArrayIsStoredDirectly, AvoidPrintStackTrace, AvoidReassigningLoopVariables, AvoidReassigningParameters, AvoidStringBufferField, AvoidUsingHardCodedIP, CheckResultSet, ConstantsInInterface, DefaultLabelNotLastInSwitchStmt, ForLoopCanBeForeach, ForLoopVariableCount, GuardLogStatement, JUnit4SuitesShouldUseSuiteAnnotation, JUnit4TestShouldUseAfterAnnotation, JUnit4TestShouldUseBeforeAnnotation, JUnit4TestShouldUseTestAnnotation, JUnitAssertionsShouldIncludeMessage, JUnitTestContainsTooManyAsserts, JUnitTestsShouldIncludeAssert, JUnitUseExpected, LooseCoupling, MethodReturnsInternalArray, MissingOverride, OneDeclarationPerLine, PositionLiteralsFirstInCaseInsensitiveComparisons, PositionLiteralsFirstInComparisons, PreserveStackTrace, ReplaceEnumerationWithIterator, ReplaceHashtableWithMap, ReplaceVectorWithList, SwitchStmtsShouldHaveDefault, SystemPrintln, UnusedFormalParameter, UnusedImports, UnusedLocalVariable, UnusedPrivateField, UnusedPrivateMethod, UseAssertEqualsInsteadOfAssertTrue, UseAssertNullInsteadOfAssertTrue, UseAssertSameInsteadOfAssertTrue, UseAssertTrueInsteadOfAssertEquals, UseCollectionIsEmpty, UseTryWithResources, UseVarargs
|
||||
keywords: Best Practices, AbstractClassWithoutAbstractMethod, AccessorClassGeneration, AccessorMethodGeneration, ArrayIsStoredDirectly, AvoidPrintStackTrace, AvoidReassigningLoopVariables, AvoidReassigningParameters, AvoidStringBufferField, AvoidUsingHardCodedIP, CheckResultSet, ConstantsInInterface, DefaultLabelNotLastInSwitchStmt, ForLoopCanBeForeach, ForLoopVariableCount, GuardLogStatement, JUnit4SuitesShouldUseSuiteAnnotation, JUnit4TestShouldUseAfterAnnotation, JUnit4TestShouldUseBeforeAnnotation, JUnit4TestShouldUseTestAnnotation, JUnitAssertionsShouldIncludeMessage, JUnitTestContainsTooManyAsserts, JUnitTestsShouldIncludeAssert, JUnitUseExpected, LooseCoupling, MethodReturnsInternalArray, MissingOverride, OneDeclarationPerLine, PositionLiteralsFirstInCaseInsensitiveComparisons, PositionLiteralsFirstInComparisons, PreserveStackTrace, ReplaceEnumerationWithIterator, ReplaceHashtableWithMap, ReplaceVectorWithList, SwitchStmtsShouldHaveDefault, SystemPrintln, UnusedFormalParameter, UnusedImports, UnusedLocalVariable, UnusedPrivateField, UnusedPrivateMethod, UseAssertEqualsInsteadOfAssertTrue, UseAssertNullInsteadOfAssertTrue, UseAssertSameInsteadOfAssertTrue, UseAssertTrueInsteadOfAssertEquals, UseCollectionIsEmpty, UseTryWithResources, UseVarargs, WhileLoopWithLiteralBoolean
|
||||
language: Java
|
||||
---
|
||||
<!-- DO NOT EDIT THIS FILE. This file is generated from file ../pmd-java/src/main/resources/category/java/bestpractices.xml. -->
|
||||
@ -1750,3 +1750,41 @@ public class Foo {
|
||||
<rule ref="category/java/bestpractices.xml/UseVarargs" />
|
||||
```
|
||||
|
||||
## WhileLoopWithLiteralBoolean
|
||||
|
||||
**Since:** PMD 6.13.0
|
||||
|
||||
**Priority:** Medium (3)
|
||||
|
||||
`do {} while (true);` requires reading the end of the statement before it is
|
||||
apparent that it loops forever, whereas `while (true) {}` is easier to understand.
|
||||
|
||||
`do {} while (false);` is redundant, and if an inner variable scope is required,
|
||||
a block `{}` is sufficient.
|
||||
|
||||
`while (false) {}` will never execute the block and can be removed in its entirety.
|
||||
|
||||
**This rule is defined by the following XPath expression:**
|
||||
``` xpath
|
||||
//DoStatement[Expression/PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral] |
|
||||
//WhileStatement[Expression/PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral[@True = false()]]
|
||||
```
|
||||
|
||||
**Example(s):**
|
||||
|
||||
``` java
|
||||
public class Example {
|
||||
{
|
||||
while (true) { } // allowed
|
||||
while (false) { } // disallowed
|
||||
do { } while (true); // disallowed
|
||||
do { } while (false); // disallowed
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Use this rule by referencing it:**
|
||||
``` xml
|
||||
<rule ref="category/java/bestpractices.xml/WhileLoopWithLiteralBoolean" />
|
||||
```
|
||||
|
||||
|
@ -1265,8 +1265,8 @@ should be annotated with @Test and @Ignore.
|
||||
or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')]
|
||||
]
|
||||
[not(Annotation)]
|
||||
[MethodDeclaration[(@Public = 'true' or @PackagePrivate = 'true') and @Static = 'false' and
|
||||
ResultType[@Void = 'true'] and
|
||||
[MethodDeclaration[(@Public = true() or @PackagePrivate = true()) and @Static = false() and
|
||||
ResultType[@Void = true()] and
|
||||
MethodDeclarator/FormalParameters[@ParameterCount = 0]
|
||||
]
|
||||
]
|
||||
|
@ -31,6 +31,12 @@ references only rules, that are most likely to apply everywhere.
|
||||
|
||||
Any feedback would be greatly appreciated.
|
||||
|
||||
#### PMD Designer
|
||||
|
||||
The rule designer's codebase has been moved out of the main repository and
|
||||
will be developed at [pmd/pmd-designer](https://github.com/pmd/pmd-designer)
|
||||
from now on. The maven coordinates will stay the same for the time being.
|
||||
The designer will still be shipped with PMD's binaries.
|
||||
|
||||
#### New Rules
|
||||
|
||||
@ -42,6 +48,10 @@ Any feedback would be greatly appreciated.
|
||||
methods in test classes, which are not annotated with `@Test`. These methods might be test cases where
|
||||
the annotation has been forgotten. Because of that those test cases are never executed.
|
||||
|
||||
* The new Java rule {% rule "java/bestpractices/WhileLoopWithLiteralBoolean" %} (`java-bestpractices`) finds
|
||||
Do-While-Loops and While-Loops that can be simplified since they use simply `true` or `false` as their
|
||||
loop condition.
|
||||
|
||||
### Fixed Issues
|
||||
|
||||
### API Changes
|
||||
@ -53,10 +63,14 @@ Any feedback would be greatly appreciated.
|
||||
Properties "cc_categories", "cc_remediation_points_multiplier", "cc_block_highlighting" will also be removed.
|
||||
See [#1702](https://github.com/pmd/pmd/pull/1702) for more.
|
||||
|
||||
* The Apex ruleset `rulesets/apex/ruleset.xml` has been deprecated and will be removed in 7.0.0. Please use the new
|
||||
quickstart ruleset `rulesets/apex/quickstart.xml` instead.
|
||||
|
||||
### External Contributions
|
||||
|
||||
* [#1704](https://github.com/pmd/pmd/pull/1704): \[java] Added AvoidUncheckedExceptionsInSignatures Rule - [Bhanu Prakash Pamidi](https://github.com/pamidi99)
|
||||
* [#1706](https://github.com/pmd/pmd/pull/1706): \[java] Add DetachedTestCase rule - [David Burström](https://github.com/davidburstromspotify)
|
||||
* [#1709](https://github.com/pmd/pmd/pull/1709): \[java] Detect while loops with literal booleans conditions - [David Burström](https://github.com/davidburstromspotify)
|
||||
|
||||
{% endtocmaker %}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
This ruleset contains links to rules that are new in PMD v6.13.0
|
||||
</description>
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/WhileLoopWithLiteralBoolean"/>
|
||||
<rule ref="category/java/design.xml/AvoidUncheckedExceptionsInSignatures"/>
|
||||
<rule ref="category/java/errorprone.xml/DetachedTestCase"/>
|
||||
|
||||
|
@ -242,7 +242,7 @@
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
<artifactId>pmd-ui</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<version>${pmd-designer.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
@ -1633,4 +1633,41 @@ public class Foo {
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="WhileLoopWithLiteralBoolean"
|
||||
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||
language="java"
|
||||
since="6.13.0"
|
||||
message="The loop can be simplified."
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#whileloopwithliteralboolean">
|
||||
<description>
|
||||
`do {} while (true);` requires reading the end of the statement before it is
|
||||
apparent that it loops forever, whereas `while (true) {}` is easier to understand.
|
||||
|
||||
`do {} while (false);` is redundant, and if an inner variable scope is required,
|
||||
a block `{}` is sufficient.
|
||||
|
||||
`while (false) {}` will never execute the block and can be removed in its entirety.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
//DoStatement[Expression/PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral] |
|
||||
//WhileStatement[Expression/PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral[@True = false()]]
|
||||
</value>
|
||||
</property>
|
||||
<property name="version" value="2.0" />
|
||||
</properties>
|
||||
<example>
|
||||
public class Example {
|
||||
{
|
||||
while (true) { } // allowed
|
||||
while (false) { } // disallowed
|
||||
do { } while (true); // disallowed
|
||||
do { } while (false); // disallowed
|
||||
}
|
||||
}
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
</ruleset>
|
||||
|
@ -1144,13 +1144,14 @@ should be annotated with @Test and @Ignore.
|
||||
or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')]
|
||||
]
|
||||
[not(Annotation)]
|
||||
[MethodDeclaration[(@Public = 'true' or @PackagePrivate = 'true') and @Static = 'false' and
|
||||
ResultType[@Void = 'true'] and
|
||||
[MethodDeclaration[(@Public = true() or @PackagePrivate = true()) and @Static = false() and
|
||||
ResultType[@Void = true()] and
|
||||
MethodDeclarator/FormalParameters[@ParameterCount = 0]
|
||||
]
|
||||
]
|
||||
]]></value>
|
||||
</property>
|
||||
<property name="version" value="2.0" />
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
|
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.bestpractices;
|
||||
|
||||
import net.sourceforge.pmd.testframework.PmdRuleTst;
|
||||
|
||||
public class WhileLoopWithLiteralBooleanTest extends PmdRuleTst {
|
||||
// no additional unit tests
|
||||
}
|
@ -10,7 +10,7 @@ import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.Latest
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class ASTCatchStatementTest : FunSpec({
|
||||
class ASTCatchStatementTest : ParserTestSpec({
|
||||
|
||||
parserTest("Test crash on multicatch", javaVersions = Earliest..J1_6) {
|
||||
|
||||
@ -44,7 +44,7 @@ class ASTCatchStatementTest : FunSpec({
|
||||
child<ASTCatchStatement> {
|
||||
it.isMulticatchStatement shouldBe true
|
||||
|
||||
val types = childRet<ASTFormalParameter, List<ASTType>> {
|
||||
val types = fromChild<ASTFormalParameter, List<ASTType>> {
|
||||
val ioe = child<ASTType>(ignoreChildren = true) {
|
||||
it.type shouldBe IOException::class.java
|
||||
}
|
||||
@ -57,7 +57,7 @@ class ASTCatchStatementTest : FunSpec({
|
||||
it.image shouldBe "e"
|
||||
}
|
||||
|
||||
return@childRet listOf(ioe, aerr)
|
||||
listOf(ioe, aerr)
|
||||
}
|
||||
|
||||
it.caughtExceptionTypeNodes.shouldContainExactly(types)
|
||||
|
@ -1,14 +1,12 @@
|
||||
package net.sourceforge.pmd.lang.java.ast
|
||||
|
||||
import io.kotlintest.should
|
||||
import io.kotlintest.specs.FunSpec
|
||||
import net.sourceforge.pmd.lang.ast.test.shouldBe
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.Earliest
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.Latest
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaVersion.J1_8
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaVersion.J9
|
||||
|
||||
class ASTMethodDeclarationTest : FunSpec({
|
||||
class ASTMethodDeclarationTest : ParserTestSpec({
|
||||
|
||||
// notes about dsl:
|
||||
// * testGroup generates one test per "should" assertion that
|
||||
@ -16,7 +14,7 @@ class ASTMethodDeclarationTest : FunSpec({
|
||||
// (without explicitly giving them each a specific name)
|
||||
// * the it::isPublic syntax allows including the property name in the error message in case of failure
|
||||
|
||||
testGroup("Non-private interfaces members should be public", javaVersions = Earliest..Latest) {
|
||||
parserTest("Non-private interfaces members should be public", javaVersions = Earliest..Latest) {
|
||||
|
||||
genClassHeader = "interface Bar"
|
||||
|
||||
@ -55,7 +53,7 @@ class ASTMethodDeclarationTest : FunSpec({
|
||||
|
||||
}
|
||||
|
||||
testGroup("Non-default methods in interfaces should be abstract", javaVersions = J1_8..Latest) {
|
||||
parserTest("Non-default methods in interfaces should be abstract", javaVersions = J1_8..Latest) {
|
||||
|
||||
genClassHeader = "interface Bar"
|
||||
|
||||
|
@ -1,15 +1,13 @@
|
||||
|
||||
import io.kotlintest.should
|
||||
import io.kotlintest.shouldBe
|
||||
import io.kotlintest.specs.FunSpec
|
||||
import net.sourceforge.pmd.lang.java.ast.*
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaVersion.*
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaVersion.Companion.Latest
|
||||
|
||||
class Java11Test : FunSpec({
|
||||
class Java11Test : ParserTestSpec({
|
||||
|
||||
|
||||
parserTest("Test lambda parameter with var keyword", javaVersions = J1_8..J10) {
|
||||
parserTest("var keyword should be a normal type pre-java 11", javaVersions = J1_8..J10) {
|
||||
|
||||
"(var x) -> String.valueOf(x)" should matchExpr<ASTLambdaExpression> {
|
||||
child<ASTFormalParameters> {
|
||||
@ -76,7 +74,7 @@ class Java11Test : FunSpec({
|
||||
}
|
||||
}
|
||||
|
||||
parserTest("Test lambda parameter with var keyword", javaVersions = J11..Latest) {
|
||||
parserTest("var keyword should generate no type after java 11", javaVersions = J11..Latest) {
|
||||
|
||||
"(var x) -> String.valueOf(x)" should matchExpr<ASTLambdaExpression> {
|
||||
child<ASTFormalParameters> {
|
||||
|
@ -1,10 +1,7 @@
|
||||
package net.sourceforge.pmd.lang.java.ast
|
||||
|
||||
import io.kotlintest.Matcher
|
||||
import io.kotlintest.Result
|
||||
import io.kotlintest.matchers.string.shouldContain
|
||||
import io.kotlintest.shouldThrow
|
||||
import io.kotlintest.specs.AbstractFunSpec
|
||||
import net.sourceforge.pmd.lang.ast.Node
|
||||
import net.sourceforge.pmd.lang.ast.test.*
|
||||
import net.sourceforge.pmd.lang.java.ParserTstUtil
|
||||
@ -36,109 +33,9 @@ enum class JavaVersion : Comparable<JavaVersion> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify several tests at once for different java versions.
|
||||
* One test will be generated per version in [javaVersions].
|
||||
* Use [focusOn] to execute one test in isolation.
|
||||
*
|
||||
* @param name Name of the test. Will be postfixed by the specific
|
||||
* java version used to run it
|
||||
* @param javaVersions Language versions for which to generate tests
|
||||
* @param focusOn Sets the java version of the test to isolate
|
||||
* @param assertions Assertions and further configuration
|
||||
* to perform with the parsing context
|
||||
*/
|
||||
fun AbstractFunSpec.parserTest(name: String,
|
||||
javaVersions: List<JavaVersion>,
|
||||
focusOn: JavaVersion? = null,
|
||||
assertions: ParserTestCtx.() -> Unit) {
|
||||
|
||||
javaVersions.forEach {
|
||||
|
||||
val focus = if (focusOn != null && focusOn == it) "f:" else ""
|
||||
|
||||
test("$focus$name (Java ${it.pmdName})") {
|
||||
ParserTestCtx(it).assertions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a new test for a single java version. To execute the test in isolation,
|
||||
* prefix the name with `"f:"`.
|
||||
*
|
||||
* @param name Name of the test. Will be postfixed by the [javaVersion]
|
||||
* @param javaVersion Language version to use when parsing
|
||||
* @param assertions Assertions and further configuration
|
||||
* to perform with the parsing context
|
||||
*/
|
||||
fun AbstractFunSpec.parserTest(name: String,
|
||||
javaVersion: JavaVersion = JavaVersion.Latest,
|
||||
assertions: ParserTestCtx.() -> Unit) {
|
||||
parserTest(name, listOf(javaVersion), null, assertions)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines a group of tests that should be named similarly,
|
||||
* executed on several java versions. Calls to "should" in
|
||||
* the block are intercepted to create a new test, with the
|
||||
* given [name] as a common prefix.
|
||||
*
|
||||
* This is useful to make a batch of grammar specs for grammar
|
||||
* regression tests without bothering to find a name.
|
||||
*
|
||||
* @param name Common prefix for the test names
|
||||
* @param javaVersions Language versions for which to generate tests
|
||||
* @param spec Assertions. Each call to [io.kotlintest.should] on a string
|
||||
* receiver is replaced by a [GroupTestCtx.should], which creates a
|
||||
* new parser test.
|
||||
*/
|
||||
fun AbstractFunSpec.testGroup(name: String,
|
||||
javaVersions: List<JavaVersion>,
|
||||
spec: GroupTestCtx.() -> Unit) {
|
||||
javaVersions.forEach {
|
||||
testGroup(name, it, spec)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines a group of tests that should be named similarly.
|
||||
* Calls to "should" in the block are intercepted to create
|
||||
* a new test, with the given [name] as a common prefix.
|
||||
*
|
||||
* This is useful to make a batch of grammar specs for grammar
|
||||
* regression tests without bothering to find a name.
|
||||
*
|
||||
* @param name Common prefix for the test names
|
||||
* @param javaVersion Language versions to use when parsing
|
||||
* @param spec Assertions. Each call to [io.kotlintest.should] on a string
|
||||
* receiver is replaced by a [GroupTestCtx.should], which creates a
|
||||
* new parser test.
|
||||
*
|
||||
*/
|
||||
fun AbstractFunSpec.testGroup(name: String,
|
||||
javaVersion: JavaVersion = JavaVersion.Latest,
|
||||
spec: GroupTestCtx.() -> Unit) {
|
||||
GroupTestCtx(this, name, javaVersion).spec()
|
||||
}
|
||||
|
||||
class GroupTestCtx(private val funspec: AbstractFunSpec, private val groupName: String, javaVersion: JavaVersion) : ParserTestCtx(javaVersion) {
|
||||
|
||||
infix fun String.should(matcher: Matcher<String>) {
|
||||
funspec.parserTest("$groupName: '$this'", javaVersion = javaVersion) {
|
||||
this@should kotlintestShould matcher
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extensible environment to describe parse/match testing workflows in a concise way.
|
||||
* Can be used inside of a [io.kotlintest.specs.FunSpec] with [parserTest].
|
||||
* Can be used inside of a [ParserTestSpec] with [ParserTestSpec.parserTest].
|
||||
*
|
||||
* Parsing contexts allow to parse a string containing only the node you're interested
|
||||
* in instead of writing up a full class that the parser can handle. See [parseAstExpression],
|
||||
@ -186,11 +83,8 @@ open class ParserTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest,
|
||||
return types + otherImports.map { "import $it;" }
|
||||
}
|
||||
|
||||
inline fun <reified N : Node> makeMatcher(nodeParsingCtx: NodeParsingCtx<*>, ignoreChildren: Boolean, noinline nodeSpec: NWrapper<N>.() -> Unit): Matcher<String> =
|
||||
object : Matcher<String> {
|
||||
override fun test(value: String): Result =
|
||||
matchNode(ignoreChildren, nodeSpec).test(nodeParsingCtx.parseAndFind<N>(value))
|
||||
}
|
||||
inline fun <reified N : Node> makeMatcher(nodeParsingCtx: NodeParsingCtx<*>, ignoreChildren: Boolean, noinline nodeSpec: NodeSpec<N>)
|
||||
: Assertions<String> = { nodeParsingCtx.parseAndFind<N>(it).shouldMatchNode(ignoreChildren, nodeSpec) }
|
||||
|
||||
|
||||
/**
|
||||
@ -199,7 +93,7 @@ open class ParserTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest,
|
||||
*
|
||||
*/
|
||||
inline fun <reified N : Node> matchExpr(ignoreChildren: Boolean = false,
|
||||
noinline nodeSpec: NWrapper<N>.() -> Unit): Matcher<String> =
|
||||
noinline nodeSpec: NodeSpec<N>) =
|
||||
makeMatcher(ExpressionParsingCtx(this), ignoreChildren, nodeSpec)
|
||||
|
||||
/**
|
||||
@ -207,7 +101,7 @@ open class ParserTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest,
|
||||
* type param [N], then matches it against the [nodeSpec] using [matchNode].
|
||||
*/
|
||||
inline fun <reified N : Node> matchStmt(ignoreChildren: Boolean = false,
|
||||
noinline nodeSpec: NWrapper<N>.() -> Unit) =
|
||||
noinline nodeSpec: NodeSpec<N>) =
|
||||
makeMatcher(StatementParsingCtx(this), ignoreChildren, nodeSpec)
|
||||
|
||||
|
||||
@ -216,16 +110,15 @@ open class ParserTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest,
|
||||
* type param [N], then matches it against the [nodeSpec] using [matchNode].
|
||||
*/
|
||||
inline fun <reified N : Node> matchType(ignoreChildren: Boolean = false,
|
||||
noinline nodeSpec: NWrapper<N>.() -> Unit) =
|
||||
noinline nodeSpec: NodeSpec<N>) =
|
||||
makeMatcher(TypeParsingCtx(this), ignoreChildren, nodeSpec)
|
||||
|
||||
/**
|
||||
* Returns a String matcher that parses the node using [parseToplevelDeclaration] with
|
||||
* type param [N], then matches it against the [nodeSpec] using [matchNode].
|
||||
*/
|
||||
inline fun <reified N : ASTAnyTypeDeclaration> matchToplevelType(
|
||||
ignoreChildren: Boolean = false,
|
||||
noinline nodeSpec: NWrapper<N>.() -> Unit) =
|
||||
inline fun <reified N : ASTAnyTypeDeclaration> matchToplevelType(ignoreChildren: Boolean = false,
|
||||
noinline nodeSpec: NodeSpec<N>) =
|
||||
makeMatcher(TopLevelTypeDeclarationParsingCtx(this), ignoreChildren, nodeSpec)
|
||||
|
||||
/**
|
||||
@ -236,7 +129,7 @@ open class ParserTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest,
|
||||
*/
|
||||
inline fun <reified N : Node> matchDeclaration(
|
||||
ignoreChildren: Boolean = false,
|
||||
noinline nodeSpec: NWrapper<N>.() -> Unit) = makeMatcher(EnclosedDeclarationParsingCtx(this), ignoreChildren, nodeSpec)
|
||||
noinline nodeSpec: NodeSpec<N>) = makeMatcher(EnclosedDeclarationParsingCtx(this), ignoreChildren, nodeSpec)
|
||||
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,130 @@
|
||||
package net.sourceforge.pmd.lang.java.ast
|
||||
|
||||
import io.kotlintest.AbstractSpec
|
||||
import io.kotlintest.TestContext
|
||||
import io.kotlintest.TestType
|
||||
import io.kotlintest.specs.IntelliMarker
|
||||
import net.sourceforge.pmd.lang.ast.test.Assertions
|
||||
import io.kotlintest.should as kotlintestShould
|
||||
|
||||
/**
|
||||
* Base class for grammar tests that use the DSL. Tests are layered into
|
||||
* containers that make it easier to browse in the IDE. Layout is group name,
|
||||
* then java version, then test case. Test cases are "should" assertions matching
|
||||
* a string against a matcher defined in [ParserTestCtx], e.g. [ParserTestCtx.matchExpr].
|
||||
*
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
abstract class ParserTestSpec(body: ParserTestSpec.() -> Unit) : AbstractSpec(), IntelliMarker {
|
||||
|
||||
init {
|
||||
body()
|
||||
}
|
||||
|
||||
fun test(name: String, test: TestContext.() -> Unit) =
|
||||
addTestCase(name, test, defaultTestCaseConfig, TestType.Test)
|
||||
|
||||
/**
|
||||
* Defines a group of tests that should be named similarly,
|
||||
* with separate tests for separate versions.
|
||||
*
|
||||
* Calls to "should" in the block are intercepted to create
|
||||
* a new test.
|
||||
*
|
||||
* This is useful to make a batch of grammar specs for grammar
|
||||
* regression tests without bothering to find a name.
|
||||
*
|
||||
* @param name Name of the container test
|
||||
* @param spec Assertions. Each call to [io.kotlintest.should] on a string
|
||||
* receiver is replaced by a [GroupTestCtx.should], which creates a
|
||||
* new parser test.
|
||||
*
|
||||
*/
|
||||
fun parserTestGroup(name: String,
|
||||
spec: GroupTestCtx.() -> Unit) =
|
||||
addTestCase(name, { GroupTestCtx(this).spec() }, defaultTestCaseConfig, TestType.Container)
|
||||
|
||||
/**
|
||||
* Defines a group of tests that should be named similarly.
|
||||
* Calls to "should" in the block are intercepted to create
|
||||
* a new test, with the given [name] as a common prefix.
|
||||
*
|
||||
* This is useful to make a batch of grammar specs for grammar
|
||||
* regression tests without bothering to find a name.
|
||||
*
|
||||
* @param name Name of the container test
|
||||
* @param javaVersion Language versions to use when parsing
|
||||
* @param spec Assertions. Each call to [io.kotlintest.should] on a string
|
||||
* receiver is replaced by a [GroupTestCtx.should], which creates a
|
||||
* new parser test.
|
||||
*
|
||||
*/
|
||||
fun parserTest(name: String,
|
||||
javaVersion: JavaVersion = JavaVersion.Latest,
|
||||
spec: GroupTestCtx.VersionedTestCtx.() -> Unit) =
|
||||
parserTest(name, listOf(javaVersion), spec)
|
||||
|
||||
/**
|
||||
* Defines a group of tests that should be named similarly,
|
||||
* executed on several java versions. Calls to "should" in
|
||||
* the block are intercepted to create a new test, with the
|
||||
* given [name] as a common prefix.
|
||||
*
|
||||
* This is useful to make a batch of grammar specs for grammar
|
||||
* regression tests without bothering to find a name.
|
||||
*
|
||||
* @param name Name of the container test
|
||||
* @param javaVersions Language versions for which to generate tests
|
||||
* @param spec Assertions. Each call to [io.kotlintest.should] on a string
|
||||
* receiver is replaced by a [GroupTestCtx.should], which creates a
|
||||
* new parser test.
|
||||
*/
|
||||
fun parserTest(name: String,
|
||||
javaVersions: List<JavaVersion>,
|
||||
spec: GroupTestCtx.VersionedTestCtx.() -> Unit) =
|
||||
parserTestGroup(name) {
|
||||
onVersions(javaVersions) {
|
||||
spec()
|
||||
}
|
||||
}
|
||||
|
||||
private fun containedParserTestImpl(
|
||||
context: TestContext,
|
||||
name: String,
|
||||
javaVersion: JavaVersion,
|
||||
assertions: ParserTestCtx.() -> Unit) {
|
||||
|
||||
context.registerTestCase(
|
||||
name = name,
|
||||
spec = this,
|
||||
test = { ParserTestCtx(javaVersion).assertions() },
|
||||
config = defaultTestCaseConfig,
|
||||
type = TestType.Test
|
||||
)
|
||||
}
|
||||
|
||||
inner class GroupTestCtx(private val context: TestContext) {
|
||||
|
||||
fun onVersions(javaVersions: List<JavaVersion>, spec: VersionedTestCtx.() -> Unit) {
|
||||
javaVersions.forEach { javaVersion ->
|
||||
|
||||
context.registerTestCase(
|
||||
name = "Java ${javaVersion.pmdName}",
|
||||
spec = this@ParserTestSpec,
|
||||
test = { VersionedTestCtx(this, javaVersion).spec() },
|
||||
config = defaultTestCaseConfig,
|
||||
type = TestType.Container
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
inner class VersionedTestCtx(private val context: TestContext, javaVersion: JavaVersion) : ParserTestCtx(javaVersion) {
|
||||
|
||||
infix fun String.should(matcher: Assertions<String>) {
|
||||
containedParserTestImpl(context, "'$this'", javaVersion = javaVersion) {
|
||||
this@should kotlintestShould matcher
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package net.sourceforge.pmd.lang.java.ast
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.test.shouldMatch
|
||||
import net.sourceforge.pmd.lang.ast.test.shouldBe
|
||||
import java.util.*
|
||||
import kotlin.reflect.KCallable
|
||||
|
||||
infix fun <T, U : T> Optional<T>.shouldBePresent(any: U) {
|
||||
::isPresent shouldBe true
|
||||
::get shouldBe any
|
||||
}
|
||||
|
||||
fun Optional<*>.shouldBeEmpty() {
|
||||
::isPresent shouldBe false
|
||||
}
|
||||
|
||||
fun KCallable<Optional<*>>.shouldBeEmpty() = this shouldMatch {
|
||||
::isPresent shouldBe false
|
||||
}
|
||||
|
||||
infix fun <T, U : T> KCallable<Optional<T>>.shouldBePresent(any: U) = this shouldMatch {
|
||||
::isPresent shouldBe true
|
||||
::get shouldBe any
|
||||
}
|
||||
|
@ -1,12 +1,8 @@
|
||||
package net.sourceforge.pmd.lang.java.ast
|
||||
|
||||
import io.kotlintest.should
|
||||
import io.kotlintest.shouldBe
|
||||
import io.kotlintest.specs.FunSpec
|
||||
|
||||
// prototype using a junit syntax
|
||||
|
||||
class WildcardBoundsTest : FunSpec({
|
||||
class WildcardBoundsTest : ParserTestSpec({
|
||||
|
||||
parserTest("Simple grammar test") {
|
||||
|
||||
|
@ -0,0 +1,81 @@
|
||||
<?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>do while true</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>3</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
do {
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>do while false</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>3</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
do {
|
||||
} while (false);
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>do while call</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
do {
|
||||
} while (call(true));
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>while true</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
while (true) {
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>while false</description>
|
||||
<expected-problems>1</expected-problems>
|
||||
<expected-linenumbers>3</expected-linenumbers>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
while (false) {
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>while call false</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
class Foo {
|
||||
{
|
||||
while (call(false)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
</test-data>
|
@ -82,6 +82,13 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.oowekyala.treeutils</groupId>
|
||||
<artifactId>tree-matchers</artifactId>
|
||||
<version>2.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Use pmd-java for tests -->
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.pmd</groupId>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,47 @@
|
||||
package net.sourceforge.pmd.lang.ast.test
|
||||
|
||||
import com.github.oowekyala.treeutils.TreeLikeAdapter
|
||||
import com.github.oowekyala.treeutils.matchers.MatchingConfig
|
||||
import com.github.oowekyala.treeutils.matchers.TreeNodeWrapper
|
||||
import com.github.oowekyala.treeutils.matchers.baseShouldMatchSubtree
|
||||
import com.github.oowekyala.treeutils.printers.KotlintestBeanTreePrinter
|
||||
import net.sourceforge.pmd.lang.ast.Node
|
||||
|
||||
/** An adapter for [baseShouldMatchSubtree]. */
|
||||
object NodeTreeLikeAdapter : TreeLikeAdapter<Node> {
|
||||
override fun getChildren(node: Node): List<Node> = node.findChildrenOfType(Node::class.java)
|
||||
|
||||
override fun nodeName(type: Class<out Node>): String = type.simpleName.removePrefix("AST")
|
||||
}
|
||||
|
||||
/** A subtree matcher written in the DSL documented on [TreeNodeWrapper]. */
|
||||
typealias NodeSpec<N> = TreeNodeWrapper<Node, N>.() -> Unit
|
||||
|
||||
/** A function feedable to [io.kotlintest.should], which fails the test if an [AssertionError] is thrown. */
|
||||
typealias Assertions<M> = (M) -> Unit
|
||||
|
||||
/** A shorthand for [baseShouldMatchSubtree] providing the [NodeTreeLikeAdapter]. */
|
||||
inline fun <reified N : Node> Node?.shouldMatchNode(ignoreChildren: Boolean = false, noinline nodeSpec: NodeSpec<N>) {
|
||||
this.baseShouldMatchSubtree(MatchingConfig(adapter = NodeTreeLikeAdapter, errorPrinter = KotlintestBeanTreePrinter(NodeTreeLikeAdapter)), ignoreChildren, nodeSpec)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [an assertion function][Assertions] asserting that its parameter conforms to the given [NodeSpec].
|
||||
*
|
||||
* Use it with [io.kotlintest.should], e.g. `node should matchNode<ASTExpression> {}`.
|
||||
*
|
||||
* See also the samples on [TreeNodeWrapper].
|
||||
*
|
||||
* @param N Expected type of the node
|
||||
*
|
||||
* @param ignoreChildren If true, calls to [TreeNodeWrapper.child] in the [nodeSpec] are forbidden.
|
||||
* The number of children of the child is not asserted.
|
||||
*
|
||||
* @param nodeSpec Sequence of assertions to carry out on the node, which can be referred to by [TreeNodeWrapper.it].
|
||||
* Assertions may consist of [NWrapper.child] calls, which perform the same type of node
|
||||
* matching on a child of the tested node.
|
||||
*
|
||||
* @return A matcher for AST nodes, suitable for use by [io.kotlintest.should].
|
||||
*/
|
||||
inline fun <reified N : Node> matchNode(ignoreChildren: Boolean = false, noinline nodeSpec: NodeSpec<N>)
|
||||
: Assertions<Node?> = { it.shouldMatchNode(ignoreChildren, nodeSpec) }
|
@ -1,6 +1,6 @@
|
||||
package net.sourceforge.pmd.lang.ast.test
|
||||
|
||||
import io.kotlintest.Matcher
|
||||
import io.kotlintest.should
|
||||
import kotlin.reflect.KCallable
|
||||
import io.kotlintest.shouldBe as ktShouldBe
|
||||
|
||||
@ -47,4 +47,5 @@ private fun <N, V> assertWrapper(callable: KCallable<N>, right: V, asserter: (N,
|
||||
*/
|
||||
infix fun <N, V : N> KCallable<N>.shouldBe(expected: V?) = this.shouldEqual(expected)
|
||||
|
||||
infix fun <T> KCallable<T>.shouldBe(expected: Matcher<T>) = assertWrapper(this, expected) { n, v -> n ktShouldBe v }
|
||||
infix fun <T> KCallable<T>.shouldMatch(expected: T.() -> Unit) = assertWrapper(this, expected) { n, v -> n should v }
|
||||
|
||||
|
@ -1,138 +0,0 @@
|
||||
package net.sourceforge.pmd.lang.ast.test
|
||||
|
||||
import io.kotlintest.should
|
||||
import io.kotlintest.shouldBe
|
||||
import io.kotlintest.specs.FunSpec
|
||||
import net.sourceforge.pmd.lang.java.ast.*
|
||||
|
||||
|
||||
class DslTest : FunSpec({
|
||||
|
||||
failureTest("Empty matcher spec should check the number of children",
|
||||
messageContains = setOf("Wrong", "number", "children", "expected 0", "actual 2")) {
|
||||
|
||||
parseStatement("int i = 0;") should matchNode<ASTLocalVariableDeclaration> {}
|
||||
}
|
||||
|
||||
test("Matcher with ignoreChildren should not check the number of children") {
|
||||
|
||||
parseStatement("int i = 0;") should matchNode<ASTLocalVariableDeclaration>(ignoreChildren = true) {}
|
||||
}
|
||||
|
||||
failureTest("Incorrect node type should cause failure",
|
||||
messageContains = setOf("Expression", "actual LocalVariableDeclaration")) {
|
||||
parseStatement("int i = 0;") should matchNode<ASTExpression>(ignoreChildren = true) {}
|
||||
}
|
||||
|
||||
failureTest("Specifying any child in a pattern should cause the number of children to be checked",
|
||||
messageContains = setOf("number", "children", "expected 1", "actual 2")) {
|
||||
|
||||
parseStatement("int i = 0;") should matchNode<ASTLocalVariableDeclaration> {
|
||||
child<ASTType>(ignoreChildren = true) {}
|
||||
// There's a VarDeclarator
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test("Unspecified children should shift the next child matchers") {
|
||||
parseStatement("int i = 0;") should matchNode<ASTLocalVariableDeclaration> {
|
||||
unspecifiedChild()
|
||||
child<ASTVariableDeclarator>(ignoreChildren = true) {}
|
||||
}
|
||||
}
|
||||
|
||||
test("Unspecified children should count in total number of children") {
|
||||
parseStatement("int i = 0;") should matchNode<ASTLocalVariableDeclaration> {
|
||||
unspecifiedChildren(2)
|
||||
}
|
||||
}
|
||||
|
||||
failureTest("Unspecified children should be counted in the number of expected children",
|
||||
messageContains = setOf("#2 doesn't exist")) {
|
||||
|
||||
parseStatement("int i = 0;") should matchNode<ASTLocalVariableDeclaration> {
|
||||
unspecifiedChildren(3)
|
||||
}
|
||||
}
|
||||
|
||||
failureTest("Assertions are always executed in order",
|
||||
messageContains = setOf("PrimitiveType")) {
|
||||
|
||||
parseStatement("int[] i = 0;") should matchNode<ASTLocalVariableDeclaration> {
|
||||
|
||||
child<ASTType> {
|
||||
|
||||
// Here we check that the child type check fails before the assertion
|
||||
child<ASTPrimitiveType> {}
|
||||
|
||||
it.typeImage shouldBe "bratwurst"
|
||||
|
||||
}
|
||||
|
||||
unspecifiedChild()
|
||||
}
|
||||
}
|
||||
|
||||
failureTest("Assertions are always executed in order #2",
|
||||
messageContains = setOf("bratwurst")) {
|
||||
|
||||
parseStatement("int[] i = 0;") should matchNode<ASTLocalVariableDeclaration> {
|
||||
|
||||
child<ASTType> {
|
||||
|
||||
it.typeImage shouldBe "bratwurst"
|
||||
|
||||
child<ASTPrimitiveType> {}
|
||||
|
||||
}
|
||||
|
||||
unspecifiedChild()
|
||||
}
|
||||
}
|
||||
|
||||
failureTest("All assertions should have a node path",
|
||||
messageContains = setOf("At /LocalVariableDeclaration/Type:", "expected: \"bratwurst\"")) {
|
||||
|
||||
parseStatement("int[] i = 0;") should matchNode<ASTLocalVariableDeclaration> {
|
||||
|
||||
child<ASTType> {
|
||||
|
||||
// this fails
|
||||
it.typeImage shouldBe "bratwurst"
|
||||
|
||||
}
|
||||
|
||||
unspecifiedChild()
|
||||
}
|
||||
}
|
||||
|
||||
failureTest("Child assertions should have a node path",
|
||||
messageContains = setOf("At /LocalVariableDeclaration/Type:", "expected", "type", "LambdaExpression")) {
|
||||
|
||||
parseStatement("int[] i = 0;") should matchNode<ASTLocalVariableDeclaration> {
|
||||
|
||||
child<ASTType> {
|
||||
|
||||
// this fails
|
||||
child<ASTLambdaExpression> { }
|
||||
}
|
||||
|
||||
unspecifiedChild()
|
||||
}
|
||||
}
|
||||
|
||||
failureTest("Leaf nodes should assert that they have no children",
|
||||
messageContains = setOf("number", "children", "expected 0")) {
|
||||
|
||||
parseStatement("int[] i = 0;") should matchNode<ASTLocalVariableDeclaration> {
|
||||
|
||||
child<ASTType> {} // This should fail
|
||||
unspecifiedChild()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
package net.sourceforge.pmd.lang.ast.test
|
||||
|
||||
import net.sourceforge.pmd.lang.LanguageRegistry
|
||||
import net.sourceforge.pmd.lang.ast.Node
|
||||
import net.sourceforge.pmd.lang.java.JavaLanguageModule
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit
|
||||
import java.io.StringReader
|
||||
|
||||
|
||||
// These could be used directly by the pmd-java test module
|
||||
|
||||
fun parseStatement(statement: String): Node {
|
||||
|
||||
// place the param in a statement parsing context
|
||||
val source = """
|
||||
class Foo {
|
||||
{
|
||||
$statement
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val root = parseCompilationUnit(source)
|
||||
|
||||
return root.getFirstDescendantOfType(ASTBlockStatement::class.java).jjtGetChild(0)
|
||||
}
|
||||
|
||||
fun parseCompilationUnit(sourceCode: String): ASTCompilationUnit {
|
||||
|
||||
val languageVersionHandler = LanguageRegistry.getLanguage(JavaLanguageModule.NAME).defaultVersion.languageVersionHandler
|
||||
val rootNode = languageVersionHandler.getParser(languageVersionHandler.defaultParserOptions).parse(":test:", StringReader(sourceCode))
|
||||
languageVersionHandler.getQualifiedNameResolutionFacade(ClassLoader.getSystemClassLoader()).start(rootNode)
|
||||
languageVersionHandler.symbolFacade.start(rootNode)
|
||||
languageVersionHandler.dataFlowFacade.start(rootNode)
|
||||
languageVersionHandler.getTypeResolutionFacade(ClassLoader.getSystemClassLoader()).start(rootNode)
|
||||
languageVersionHandler.multifileFacade.start(rootNode)
|
||||
return rootNode as ASTCompilationUnit
|
||||
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
package net.sourceforge.pmd.lang.ast.test
|
||||
|
||||
import io.kotlintest.matchers.string.shouldContainIgnoringCase
|
||||
import io.kotlintest.shouldThrow
|
||||
import io.kotlintest.specs.AbstractFunSpec
|
||||
|
||||
|
||||
// Improve on the KotlinTest DSL for our specific needs
|
||||
// a testing DSL testing a testing DSL!
|
||||
|
||||
|
||||
fun AbstractFunSpec.failureTest(testName: String,
|
||||
messageContains: Set<String> = emptySet(),
|
||||
param: io.kotlintest.TestContext.() -> kotlin.Unit) {
|
||||
|
||||
this.expectFailure<AssertionError>(testName, messageContains, param)
|
||||
}
|
||||
|
||||
inline fun <reified T : Throwable> AbstractFunSpec.expectFailure(testName: String,
|
||||
messageContains: Set<String> = emptySet(),
|
||||
noinline param: io.kotlintest.TestContext.() -> kotlin.Unit) {
|
||||
test(testName) {
|
||||
val exception = shouldThrow<T> {
|
||||
this.param() // this is the test context here
|
||||
}
|
||||
|
||||
for (substr in messageContains) exception.message.shouldContainIgnoringCase(substr)
|
||||
|
||||
}
|
||||
}
|
3
pmd-ui/README.md
Normal file
3
pmd-ui/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
The rule designer's codebase lives at [pmd/pmd-designer](https://github.com/pmd/pmd-designer)
|
||||
from now on (March 2019).
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user