Split pmd-test to isolate kotlin dependencies

This commit is contained in:
Clément Fournier
2018-09-24 15:01:29 +02:00
parent 76b4b6ee7a
commit 387e794116
11 changed files with 201 additions and 154 deletions

View File

@ -144,6 +144,13 @@
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<!-- TEST DEPENDENCIES -->
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-lang-test</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>net.sourceforge.pmd</groupId> <groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-test</artifactId> <artifactId>pmd-test</artifactId>

View File

@ -1,4 +1,3 @@
package net.sourceforge.pmd.lang.java.ast package net.sourceforge.pmd.lang.java.ast
import io.kotlintest.Matcher import io.kotlintest.Matcher
@ -146,7 +145,7 @@ data class ParserTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest,
*/ */
inline fun <reified N : Node> matchExpr(ignoreChildren: Boolean = false, inline fun <reified N : Node> matchExpr(ignoreChildren: Boolean = false,
noinline nodeSpec: NWrapper<N>.() -> Unit): Matcher<String> = noinline nodeSpec: NWrapper<N>.() -> Unit): Matcher<String> =
makeMatcher(ExpressionParsingCtx(), ignoreChildren, nodeSpec) makeMatcher(ExpressionParsingCtx(this), ignoreChildren, nodeSpec)
/** /**
* Returns a String matcher that parses the node using [parseStatement] with * Returns a String matcher that parses the node using [parseStatement] with
@ -154,7 +153,7 @@ data class ParserTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest,
*/ */
inline fun <reified N : Node> matchStmt(ignoreChildren: Boolean = false, inline fun <reified N : Node> matchStmt(ignoreChildren: Boolean = false,
noinline nodeSpec: NWrapper<N>.() -> Unit) = noinline nodeSpec: NWrapper<N>.() -> Unit) =
makeMatcher(StatementParsingCtx(), ignoreChildren, nodeSpec) makeMatcher(StatementParsingCtx(this), ignoreChildren, nodeSpec)
/** /**
@ -163,8 +162,7 @@ data class ParserTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest,
*/ */
inline fun <reified N : Node> matchType(ignoreChildren: Boolean = false, inline fun <reified N : Node> matchType(ignoreChildren: Boolean = false,
noinline nodeSpec: NWrapper<N>.() -> Unit) = noinline nodeSpec: NWrapper<N>.() -> Unit) =
makeMatcher(TypeParsingCtx(), ignoreChildren, nodeSpec) makeMatcher(TypeParsingCtx(this), ignoreChildren, nodeSpec)
/** /**
@ -180,98 +178,100 @@ data class ParserTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest,
} }
fun parseAstExpression(expr: String): ASTExpression = ExpressionParsingCtx().parseNode(expr) fun parseAstExpression(expr: String): ASTExpression = ExpressionParsingCtx(this).parseNode(expr)
fun parseAstStatement(statement: String): ASTBlockStatement = StatementParsingCtx().parseNode(statement) fun parseAstStatement(statement: String): ASTBlockStatement = StatementParsingCtx(this).parseNode(statement)
fun parseAstType(type: String): ASTType = TypeParsingCtx().parseNode(type) fun parseAstType(type: String): ASTType = TypeParsingCtx(this).parseNode(type)
inline fun <reified N : Node> parseExpression(expr: String): N = ExpressionParsingCtx().parseAndFind(expr) inline fun <reified N : Node> parseExpression(expr: String): N = ExpressionParsingCtx(this).parseAndFind(expr)
// don't forget the semicolon // don't forget the semicolon
inline fun <reified N : Node> parseStatement(stmt: String): N = StatementParsingCtx().parseAndFind(stmt) inline fun <reified N : Node> parseStatement(stmt: String): N = StatementParsingCtx(this).parseAndFind(stmt)
inline fun <reified N : Node> parseType(type: String): N = TypeParsingCtx().parseAndFind(type) inline fun <reified N : Node> parseType(type: String): N = TypeParsingCtx(this).parseAndFind(type)
companion object {
/**
* Finds the first descendant of type [N] of [this] node which is /**
* accessible in a straight line. The descendant must be accessible * Finds the first descendant of type [N] of [this] node which is
* from the [this] on a path where each node has a single child. * accessible in a straight line. The descendant must be accessible
* * from the [this] on a path where each node has a single child.
* If one node has another child, the search is aborted and the method *
* returns null. * If one node has another child, the search is aborted and the method
*/ * returns null.
fun <N : Node> Node.findFirstNodeOnStraightLine(klass: Class<N>): N? { */
return when { fun <N : Node> Node.findFirstNodeOnStraightLine(klass: Class<N>): N? {
klass.isInstance(this) -> { return when {
@Suppress("UNCHECKED_CAST") klass.isInstance(this) -> {
val n = this as N @Suppress("UNCHECKED_CAST")
n val n = this as N
n
}
this.numChildren == 1 -> getChild(0).findFirstNodeOnStraightLine(klass)
else -> null
} }
this.numChildren == 1 -> getChild(0).findFirstNodeOnStraightLine(klass)
else -> null
}
}
/**
* Describes a kind of node that can be found commonly in the same contexts.
* This type defines some machinery to parse a string to this kind of node
* without much ado by placing it in a specific parsing context.
*/
abstract inner class NodeParsingCtx<T : Node>(val constructName: String) {
abstract fun getTemplate(construct: String): String
abstract fun retrieveNode(acu: ASTCompilationUnit): T
/**
* Parse the string in the context described by this object. The parsed node is usually
* the child of the returned [T] node. Note that [parseAndFind] can save you some keystrokes
* because it finds a descendant of the wanted type.
*
* @param construct The construct to parse
*
* @return A [T] whose child is the given statement
*
* @throws ParseException If the argument is no valid construct of this kind (mind the language version)
*/
fun parseNode(construct: String): T {
val root = ParserTstUtil.parseAndTypeResolveJava(javaVersion.pmdName, getTemplate(construct))
return retrieveNode(root)
} }
/** /**
* Parse the string the context described by this object, and finds the first descendant of type [N]. * Describes a kind of node that can be found commonly in the same contexts.
* The descendant is searched for by [findFirstNodeOnStraightLine], to prevent accidental * This type defines some machinery to parse a string to this kind of node
* mis-selection of a node. In such a case, a [NoSuchElementException] is thrown, and you * without much ado by placing it in a specific parsing context.
* should fix your test case.
*
* @param construct The construct to parse
* @param N The type of node to find
*
* @return The first descendant of type [N] found in the parsed expression
*
* @throws NoSuchElementException If no node of type [N] is found by [findFirstNodeOnStraightLine]
* @throws ParseException If the argument is no valid construct of this kind
*
*/ */
inline fun <reified N : Node> parseAndFind(construct: String): N = abstract class NodeParsingCtx<T : Node>(val constructName: String, protected val ctx: ParserTestCtx) {
parseNode(construct).findFirstNodeOnStraightLine(N::class.java)
?: throw NoSuchElementException("No node of type ${N::class.java.simpleName} in the given $constructName:\n\t$construct")
} abstract fun getTemplate(construct: String): String
inner class ExpressionParsingCtx : NodeParsingCtx<ASTExpression>("expression") { abstract fun retrieveNode(acu: ASTCompilationUnit): T
override fun getTemplate(construct: String): String = /**
* Parse the string in the context described by this object. The parsed node is usually
* the child of the returned [T] node. Note that [parseAndFind] can save you some keystrokes
* because it finds a descendant of the wanted type.
*
* @param construct The construct to parse
*
* @return A [T] whose child is the given statement
*
* @throws ParseException If the argument is no valid construct of this kind (mind the language version)
*/
fun parseNode(construct: String): T {
val root = ParserTstUtil.parseAndTypeResolveJava(ctx.javaVersion.pmdName, getTemplate(construct))
return retrieveNode(root)
}
/**
* Parse the string the context described by this object, and finds the first descendant of type [N].
* The descendant is searched for by [findFirstNodeOnStraightLine], to prevent accidental
* mis-selection of a node. In such a case, a [NoSuchElementException] is thrown, and you
* should fix your test case.
*
* @param construct The construct to parse
* @param N The type of node to find
*
* @return The first descendant of type [N] found in the parsed expression
*
* @throws NoSuchElementException If no node of type [N] is found by [findFirstNodeOnStraightLine]
* @throws ParseException If the argument is no valid construct of this kind
*
*/
inline fun <reified N : Node> parseAndFind(construct: String): N =
parseNode(construct).findFirstNodeOnStraightLine(N::class.java)
?: throw NoSuchElementException("No node of type ${N::class.java.simpleName} in the given $constructName:\n\t$construct")
}
class ExpressionParsingCtx(ctx: ParserTestCtx) : NodeParsingCtx<ASTExpression>("expression", ctx) {
override fun getTemplate(construct: String): String =
""" """
${imports.joinToString(separator = "\n")} ${ctx.imports.joinToString(separator = "\n")}
class Foo { class Foo {
{ {
Object o = $construct; Object o = $construct;
@ -280,14 +280,14 @@ data class ParserTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest,
""".trimIndent() """.trimIndent()
override fun retrieveNode(acu: ASTCompilationUnit): ASTExpression = acu.getFirstDescendantOfType(ASTVariableInitializer::class.java).getChild(0) as ASTExpression override fun retrieveNode(acu: ASTCompilationUnit): ASTExpression = acu.getFirstDescendantOfType(ASTVariableInitializer::class.java).getChild(0) as ASTExpression
} }
inner class StatementParsingCtx : NodeParsingCtx<ASTBlockStatement>("statement") { class StatementParsingCtx(ctx: ParserTestCtx) : NodeParsingCtx<ASTBlockStatement>("statement", ctx) {
override fun getTemplate(construct: String): String = override fun getTemplate(construct: String): String =
""" """
${imports.joinToString(separator = "\n")} ${ctx.imports.joinToString(separator = "\n")}
class Foo { class Foo {
{ {
$construct $construct
@ -296,21 +296,21 @@ data class ParserTestCtx(val javaVersion: JavaVersion = JavaVersion.Latest,
""".trimIndent() """.trimIndent()
override fun retrieveNode(acu: ASTCompilationUnit): ASTBlockStatement = acu.getFirstDescendantOfType(ASTBlockStatement::class.java) override fun retrieveNode(acu: ASTCompilationUnit): ASTBlockStatement = acu.getFirstDescendantOfType(ASTBlockStatement::class.java)
} }
inner class TypeParsingCtx : NodeParsingCtx<ASTType>("type") { class TypeParsingCtx(ctx: ParserTestCtx) : NodeParsingCtx<ASTType>("type", ctx) {
override fun getTemplate(construct: String): String = override fun getTemplate(construct: String): String =
""" """
${imports.joinToString(separator = "\n")} ${ctx.imports.joinToString(separator = "\n")}
class Foo { class Foo {
$construct foo; $construct foo;
} }
""".trimIndent() """.trimIndent()
override fun retrieveNode(acu: ASTCompilationUnit): ASTType = acu.getFirstDescendantOfType(ASTType::class.java) override fun retrieveNode(acu: ASTCompilationUnit): ASTType = acu.getFirstDescendantOfType(ASTType::class.java)
}
} }
} }

94
pmd-lang-test/pom.xml Normal file
View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>pmd-lang-test</artifactId>
<name>PMD language module testing utilities</name>
<description>
Module containing utilities to test language implementations,
including parsers and ASTs. This module uses Kotlin.
</description>
<parent>
<artifactId>pmd</artifactId>
<groupId>net.sourceforge.pmd</groupId>
<version>6.8.0-SNAPSHOT</version>
</parent>
<build>
<plugins>
<!-- The kotlin plugin has to run before the java plugin-->
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>kotlin-compile</id>
<goals>
<goal>compile</goal>
</goals>
<phase>process-sources</phase>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/main/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-core</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.kotlintest</groupId>
<artifactId>kotlintest-runner-junit5</artifactId>
<version>3.1.8</version>
<scope>compile</scope>
</dependency>
<!-- Use pmd-java for tests -->
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-java</artifactId>
<version>6.7.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -38,68 +38,5 @@
<version>1.10.19</version> <version>1.10.19</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.kotlintest</groupId>
<artifactId>kotlintest-runner-junit5</artifactId>
<version>3.1.8</version>
<scope>compile</scope>
</dependency>
<!-- Use pmd-java for tests -->
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-java</artifactId>
<version>6.6.0</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build>
<plugins>
<!-- The kotlin plugin has to run before the java plugin-->
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>kotlin-compile</id>
<goals>
<goal>compile</goal>
</goals>
<phase>process-sources</phase>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/main/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

View File

@ -111,7 +111,7 @@ public class ASTTreeCell extends TreeCell<Node> {
for (int i = 0; i < node.jjtGetNumChildren(); i++) { for (int i = 0; i < node.jjtGetNumChildren(); i++) {
Node child = node.jjtGetChild(i); Node child = node.jjtGetChild(i);
if (value == child) { if (value.equals(child)) {
// The array contains name of corresponding properties // The array contains name of corresponding properties
childrenProps[i] = prop.getName(); childrenProps[i] = prop.getName();
break; break;

View File

@ -939,6 +939,14 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code
<!-- TEST DEPENDENCIES --> <!-- TEST DEPENDENCIES -->
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd-lang-test</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!-- Kotlin --> <!-- Kotlin -->
<dependency> <dependency>
@ -1161,5 +1169,6 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code
<module>pmd-java8</module> <module>pmd-java8</module>
<module>pmd-ui</module> <module>pmd-ui</module>
<module>pmd-doc</module> <module>pmd-doc</module>
<module>pmd-lang-test</module>
</modules> </modules>
</project> </project>