diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml
index 329c8036a7..4ceab06d20 100644
--- a/pmd-java/pom.xml
+++ b/pmd-java/pom.xml
@@ -39,6 +39,11 @@
+
+ kotlin-maven-plugin
+ org.jetbrains.kotlin
+
+
org.apache.maven.plugins
maven-antrun-plugin
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java11Test.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java11Test.java
deleted file mode 100644
index aeb7942d4b..0000000000
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/Java11Test.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.ast;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-
-import org.apache.commons.io.IOUtils;
-import org.junit.Assert;
-import org.junit.Test;
-
-import net.sourceforge.pmd.lang.ast.Node;
-import net.sourceforge.pmd.lang.java.ParserTstUtil;
-
-public class Java11Test {
- private static String loadSource(String name) {
- try {
- return IOUtils.toString(Java10Test.class.getResourceAsStream("jdkversiontests/java11/" + name),
- StandardCharsets.UTF_8);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Test
- public void testLocalVariableSyntaxForLambdaParametersWithJava10() {
- ASTCompilationUnit compilationUnit = ParserTstUtil.parseAndTypeResolveJava("10",
- loadSource("LocalVariableSyntaxForLambdaParameters.java"));
-
- List lambdas = compilationUnit.findDescendantsOfType(ASTLambdaExpression.class);
- Assert.assertEquals(4, lambdas.size());
-
- // (var x) -> String.valueOf(x);
- List formalParameters = lambdas.get(0).findDescendantsOfType(ASTFormalParameter.class);
- Assert.assertEquals(1, formalParameters.size());
- ASTType type = formalParameters.get(0).getFirstChildOfType(ASTType.class);
- assertEquals("var", type.getTypeImage());
- assertEquals(1, type.jjtGetNumChildren());
- ASTReferenceType referenceType = type.getFirstChildOfType(ASTReferenceType.class);
- assertNotNull(referenceType);
- assertEquals(1, referenceType.jjtGetNumChildren());
- ASTClassOrInterfaceType classType = referenceType.getFirstChildOfType(ASTClassOrInterfaceType.class);
- assertNotNull(classType);
- assertEquals("var", classType.getImage());
-
- // (var x, var y) -> x + y;
- formalParameters = lambdas.get(1).findDescendantsOfType(ASTFormalParameter.class);
- Assert.assertEquals(2, formalParameters.size());
- type = formalParameters.get(0).getFirstChildOfType(ASTType.class);
- assertEquals("var", type.getTypeImage());
- assertEquals(1, type.jjtGetNumChildren());
- referenceType = type.getFirstChildOfType(ASTReferenceType.class);
- assertNotNull(referenceType);
- assertEquals(1, referenceType.jjtGetNumChildren());
- classType = referenceType.getFirstChildOfType(ASTClassOrInterfaceType.class);
- assertNotNull(classType);
- assertEquals("var", classType.getImage());
- type = formalParameters.get(1).getFirstChildOfType(ASTType.class);
- assertEquals("var", type.getTypeImage());
- assertEquals(1, type.jjtGetNumChildren());
-
- // (@Nonnull var x) -> String.valueOf(x);
- formalParameters = lambdas.get(2).findDescendantsOfType(ASTFormalParameter.class);
- Assert.assertEquals(1, formalParameters.size());
- Node firstChild = formalParameters.get(0).jjtGetChild(0);
- Assert.assertTrue(firstChild instanceof ASTAnnotation);
- }
-
- @Test
- public void testLocalVariableSyntaxForLambdaParametersWithJava11() {
- ASTCompilationUnit compilationUnit = ParserTstUtil.parseAndTypeResolveJava("11",
- loadSource("LocalVariableSyntaxForLambdaParameters.java"));
-
- List lambdas = compilationUnit.findDescendantsOfType(ASTLambdaExpression.class);
- Assert.assertEquals(4, lambdas.size());
-
- // (var x) -> String.valueOf(x);
- List formalParameters = lambdas.get(0).findDescendantsOfType(ASTFormalParameter.class);
- Assert.assertEquals(1, formalParameters.size());
- Assert.assertNull(formalParameters.get(0).getTypeNode());
- Assert.assertTrue(formalParameters.get(0).isTypeInferred());
- }
-}
diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/Java11Test.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/Java11Test.kt
new file mode 100644
index 0000000000..11d36f4fa0
--- /dev/null
+++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/Java11Test.kt
@@ -0,0 +1,103 @@
+import io.kotlintest.should
+import io.kotlintest.shouldBe
+import net.sourceforge.pmd.lang.ast.test.matchNode
+import net.sourceforge.pmd.lang.java.ast.*
+
+import org.junit.Test
+
+class Java11Test {
+
+
+ @Test
+ fun testLocalVariableSyntaxForLambdaParametersWithJava10() {
+
+ val lambdas = listOf(
+ "(var x) -> String.valueOf(x)",
+ "(var x, var y) -> x + y",
+ "(@Nonnull var x) -> String.valueOf(x)"
+ ).map { parseExpression(it, javaVersion = "10") }
+
+ // (var x) -> String.valueOf(x)
+ lambdas[0] should matchNode {
+ child {
+ child {
+ child {
+ it.typeImage shouldBe "var"
+
+ child {
+ child {
+ it.image shouldBe "var"
+ }
+ }
+ }
+
+ child { }
+ }
+ }
+
+ unspecifiedChild()
+ }
+
+ // (var x, var y) -> x + y
+ lambdas[1] should matchNode {
+ child {
+ child {
+ child {
+ it.typeImage shouldBe "var"
+
+ child {
+ child {
+ it.image shouldBe "var"
+ }
+ }
+ }
+ child { }
+ }
+
+ child {
+ child {
+ it.typeImage shouldBe "var"
+
+ child {
+ child {
+ it.image shouldBe "var"
+ }
+ }
+ }
+ child { }
+
+ }
+ }
+
+ unspecifiedChild()
+ }
+
+ // (@Nonnull var x) -> String.valueOf(x)
+ lambdas[2] should matchNode {
+ child {
+ child {
+ child(ignoreChildren = true) {}
+ unspecifiedChildren(2)
+ }
+ }
+ unspecifiedChild()
+ }
+ }
+
+ @Test
+ fun testLocalVariableSyntaxForLambdaParametersWithJava11() {
+
+ val lambda: ASTLambdaExpression = parseExpression("(var x) -> String.valueOf(x)", javaVersion = "11")
+
+ lambda should matchNode {
+ child {
+ child {
+ it.isTypeInferred shouldBe true
+ child { }
+ }
+ }
+
+ unspecifiedChild()
+ }
+ }
+}
diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinParsingUtils.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinParsingUtils.kt
new file mode 100644
index 0000000000..4c8efcee98
--- /dev/null
+++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/KotlinParsingUtils.kt
@@ -0,0 +1,48 @@
+package net.sourceforge.pmd.lang.java.ast
+
+import net.sourceforge.pmd.lang.ast.Node
+import net.sourceforge.pmd.lang.java.ParserTstUtil
+
+
+const val defaultJavaVersion = "11"
+
+inline fun parseExpression(expr: String, javaVersion: String = defaultJavaVersion): N =
+ parseAstExpression(expr, javaVersion).getFirstDescendantOfType(N::class.java)
+
+fun parseAstExpression(expr: String, javaVersion: String = defaultJavaVersion): ASTExpression {
+
+ val source = """
+ class Foo {
+ {
+ Object o = $expr;
+ }
+ }
+ """.trimIndent()
+
+ val acu = ParserTstUtil.parseAndTypeResolveJava(javaVersion, source)
+
+ return acu.getFirstDescendantOfType(ASTVariableInitializer::class.java).jjtGetChild(0) as ASTExpression
+}
+
+
+inline fun parseStatement(stmt: String, javaVersion: String = defaultJavaVersion): N =
+ parseStatement(stmt, javaVersion).getFirstChildOfType(N::class.java)
+
+
+fun parseStatement(statement: String, javaVersion: String = defaultJavaVersion): ASTBlockStatement {
+
+ // place the param in a statement parsing context
+ val source = """
+ class Foo {
+ {
+ $statement
+ }
+ }
+ """.trimIndent()
+
+ val root = ParserTstUtil.parseAndTypeResolveJava(javaVersion, source)
+
+ return root.getFirstDescendantOfType(ASTBlockStatement::class.java)
+}
+
+// also need e.g. parseDeclaration
diff --git a/pmd-test/pom.xml b/pmd-test/pom.xml
index 873e8a371b..6a3b78228d 100644
--- a/pmd-test/pom.xml
+++ b/pmd-test/pom.xml
@@ -38,5 +38,68 @@
1.10.19
test
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib
+ ${kotlin.version}
+ compile
+
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib-jdk8
+ ${kotlin.version}
+ compile
+
+
+
+ org.jetbrains.kotlin
+ kotlin-test-junit
+ ${kotlin.version}
+ compile
+
+
+
+ io.kotlintest
+ kotlintest-runner-junit5
+ 3.1.8
+ compile
+
+
+
+
+ net.sourceforge.pmd
+ pmd-java
+ 6.6.0
+ test
+
+
+
+
+
+
+
+ kotlin-maven-plugin
+ org.jetbrains.kotlin
+ ${kotlin.version}
+
+
+ kotlin-compile
+
+ compile
+
+ process-sources
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.basedir}/src/main/java
+
+
+
+
+
+
+
diff --git a/pmd-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/AstMatcherDsl.kt b/pmd-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/AstMatcherDsl.kt
new file mode 100644
index 0000000000..52a3596ccf
--- /dev/null
+++ b/pmd-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/AstMatcherDsl.kt
@@ -0,0 +1,222 @@
+package net.sourceforge.pmd.lang.ast.test
+
+import arrow.legacy.disjunctionTry
+import io.kotlintest.Matcher
+import io.kotlintest.Result
+import net.sourceforge.pmd.lang.ast.Node
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+
+/**
+ * Wraps a node, providing easy access to [it]. Additional matching
+ * methods are provided to match children.
+ *
+ * @property it Wrapped node
+ * @param Type of the node
+ */
+class NWrapper private constructor(val it: N, private val childMatchersAreIgnored: Boolean) {
+
+ /** Index to which the next child matcher will apply. */
+ private var nextChildMatcherIdx = 0
+
+ private fun shiftChild(num: Int = 1): Node {
+
+ checkChildExists(nextChildMatcherIdx)
+
+ val ret = it.jjtGetChild(nextChildMatcherIdx)
+
+ nextChildMatcherIdx += num
+ return ret
+ }
+
+
+ private fun checkChildExists(childIdx: Int) =
+ assertTrue("Node has fewer children than expected, child #$childIdx doesn't exist") {
+ childIdx < it.numChildren
+ }
+
+
+ /**
+ * Specify that the next [num] children will only be tested for existence,
+ * but not for type, or anything else.
+ */
+ fun unspecifiedChildren(num: Int) {
+ shiftChild(num)
+ // Checks that the last child mentioned exists
+ checkChildExists(nextChildMatcherIdx - 1)
+ }
+
+
+ /**
+ * Specify that the next child will only be tested for existence,
+ * but not for type, or anything else.
+ */
+ fun unspecifiedChild() = unspecifiedChildren(1)
+
+
+ /**
+ * Specify that the next child will be tested against the assertions
+ * defined by the lambda.
+ *
+ * This method asserts that the child exists, and that it is of the
+ * required type [M]. The lambda is then executed on it. Subsequent
+ * calls to this method at the same tree level will test the next
+ * children.
+ *
+ * @param ignoreChildren If true, calls to [child] in the [nodeSpec] are ignored.
+ * The number of children of the child is not asserted either.
+ * @param nodeSpec Sequence of assertions to carry out on the child node
+ *
+ * @param M Expected type of the child
+ */
+ inline fun child(ignoreChildren: Boolean = false, noinline nodeSpec: NWrapper.() -> Unit) =
+ childImpl(ignoreChildren, M::class.java, nodeSpec)
+
+
+ @PublishedApi
+ internal fun childImpl(ignoreChildren: Boolean, childType: Class, nodeSpec: NWrapper.() -> Unit) {
+ if (!childMatchersAreIgnored) executeWrapper(childType, shiftChild(), ignoreChildren, nodeSpec)
+ }
+
+
+ override fun toString(): String {
+ return "NWrapper<${it.xPathNodeName}>"
+ }
+
+
+ companion object {
+
+ internal val Node.numChildren: Int
+ get() = this.jjtGetNumChildren()
+
+
+ private val Class.nodeName
+ get() =
+ if (simpleName.startsWith("AST", ignoreCase = false))
+ simpleName.substring("AST".length)
+ else simpleName
+
+ /**
+ * Execute wrapper assertions on a node.
+ *
+ * @param childType Expected type of [toWrap]
+ * @param toWrap Node on which to execute the assertions
+ * @param ignoreChildrenMatchers Ignore the children matchers in [spec]
+ * @param spec Assertions to carry out on [toWrap]
+ *
+ * @throws AssertionError If some assertions fail
+ */
+ @PublishedApi
+ internal fun executeWrapper(childType: Class, toWrap: Node, ignoreChildrenMatchers: Boolean, spec: NWrapper.() -> Unit) {
+
+ assertTrue("Expected node to have type ${childType.nodeName}, actual ${toWrap.javaClass.nodeName}") {
+ childType.isInstance(toWrap)
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ val wrapper = NWrapper(toWrap as M, ignoreChildrenMatchers)
+
+ wrapper.spec()
+
+ assertFalse("<${childType.nodeName}>: Wrong number of children, expected ${wrapper.nextChildMatcherIdx}, actual ${wrapper.it.numChildren}") {
+ !ignoreChildrenMatchers && wrapper.nextChildMatcherIdx != wrapper.it.numChildren
+ }
+ }
+ }
+}
+
+
+/**
+ * Matcher for a node, using [NWrapper] to specify a subtree against which
+ * the tested node will be tested.
+ *
+ * Use it with [io.kotlintest.should], e.g. `nodeshould matchNode {}`.
+ *
+ * @param N Expected type of the node
+ *
+ * @param ignoreChildren If true, calls to [NWrapper.child] in the [nodeSpec] are ignored.
+ * The number of children of the child is not asserted either.
+ *
+ * @param nodeSpec Sequence of assertions to carry out on the node, which can be referred to by [NWrapper.it].
+ * Assertions may onsist 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].
+ *
+ * ### Samples
+ *
+ * node should matchNode {
+ *
+ * // nesting matchers allow to specify a whole subtree
+ * child {
+ *
+ * // This would fail if the first child of the ForStatement wasn't a ForInit
+ * child {
+ * child {
+ *
+ * // If the parameter ignoreChildren is set to true, the number of children is not asserted
+ * // Calls to "child" in the block are completely ignored
+ * // The only checks carried out here are the type test and the assertions of the block
+ * child(ignoreChildren = true) {
+ *
+ * // In a "child" block, the tested node can be referred to as "it"
+ * // Here, its static type is ASTType, so we can inspect properties
+ * // of the node and make assertions
+ *
+ * it.typeImage shouldBe "int"
+ * it.type shouldNotBe null
+ * }
+ *
+ * // We don't care about that node, we only care that there is "some" node
+ * unspecifiedChild()
+ * }
+ * }
+ *
+ * // The subtree is ignored, but we check a ForUpdate is present at this child position
+ * child(ignoreChildren = true) {}
+ *
+ * // Here, ignoreChildren is not specified and takes its default value of false.
+ * // The lambda has no "child" calls and the node will be asserted to have no children
+ * child {}
+ * }
+ * }
+ */
+inline fun matchNode(ignoreChildren: Boolean = false, noinline nodeSpec: NWrapper.() -> Unit) = object : Matcher {
+ override fun test(value: Node): Result {
+ val matchRes = disjunctionTry {
+ NWrapper.executeWrapper(N::class.java, value, ignoreChildren, nodeSpec)
+ }
+
+ val didMatch = matchRes.isRight()
+
+
+ // Output when the node should have matched and did not
+ //
+ val failureMessage: String = matchRes.fold({
+ // Here the node failed
+ it.message ?: "The node did not match the pattern (no cause specified)"
+ }, {
+ // The node matched, which was expected
+ "SHOULD NOT BE OUTPUT"
+ })
+
+ val negatedMessage = matchRes.fold({
+ // the node didn't match, which was expected
+ "SHOULD NOT BE OUTPUT"
+ }, {
+ "The node should not have matched this pattern"
+ })
+
+
+ return Result(didMatch, failureMessage, negatedMessage)
+ }
+}
+
+// This one preserves the stack trace
+// It's still hard to read because of the inlines, and possibly only IntelliJ knows how to do that
+// I'll try to get kotlintest to preserve the original stack trace
+
+//inline fun Node.shouldMatchNode(ignoreChildren: Boolean = false, noinline nodeSpec: NWrapper.() -> Unit) {
+// NWrapper.executeWrapper(M::class.java, this, ignoreChildren, nodeSpec)
+//}
\ No newline at end of file
diff --git a/pmd-test/src/test/kotlin/net/sourceforge/pmd/lang/ast/test/DslTest.kt b/pmd-test/src/test/kotlin/net/sourceforge/pmd/lang/ast/test/DslTest.kt
new file mode 100644
index 0000000000..39833f4d86
--- /dev/null
+++ b/pmd-test/src/test/kotlin/net/sourceforge/pmd/lang/ast/test/DslTest.kt
@@ -0,0 +1,107 @@
+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 {}
+ }
+
+ test("Matcher with ignoreChildren should not check the number of children") {
+
+ parseStatement("int i = 0;") should matchNode(ignoreChildren = true) {}
+ }
+
+ failureTest("Incorrect node type should cause failure",
+ messageContains = setOf("Expression", "actual LocalVariableDeclaration")) {
+ parseStatement("int i = 0;") should matchNode(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 {
+ child(ignoreChildren = true) {}
+ // There's a VarDeclarator
+ }
+ }
+
+
+ test("Unspecified children should shift the next child matchers") {
+ parseStatement("int i = 0;") should matchNode {
+ unspecifiedChild()
+ child(ignoreChildren = true) {}
+ }
+ }
+
+ test("Unspecified children should count in total number of children") {
+ parseStatement("int i = 0;") should matchNode {
+ 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 {
+ unspecifiedChildren(3)
+ }
+ }
+
+ failureTest("Assertions are always executed in order",
+ messageContains = setOf("PrimitiveType")) {
+
+ parseStatement("int[] i = 0;") should matchNode {
+
+ child {
+
+ // Here we check that the child type check fails before the assertion
+ child {}
+
+ it.typeImage shouldBe "bratwurst"
+
+ }
+
+ unspecifiedChild()
+ }
+ }
+
+ failureTest("Assertions are always executed in order #2",
+ messageContains = setOf("bratwurst")) {
+
+ parseStatement("int[] i = 0;") should matchNode {
+
+ child {
+
+ it.typeImage shouldBe "bratwurst"
+
+ child {}
+
+ }
+
+ unspecifiedChild()
+ }
+ }
+
+ failureTest("Leaf nodes should assert that they have no children",
+ messageContains = setOf("number", "children", "expected 0")) {
+
+ parseStatement("int[] i = 0;") should matchNode {
+
+ child {} // This should fail
+ unspecifiedChild()
+ }
+ }
+
+
+})
+
+
+
diff --git a/pmd-test/src/test/kotlin/net/sourceforge/pmd/lang/ast/test/ParseUtils.kt b/pmd-test/src/test/kotlin/net/sourceforge/pmd/lang/ast/test/ParseUtils.kt
new file mode 100644
index 0000000000..d11554543a
--- /dev/null
+++ b/pmd-test/src/test/kotlin/net/sourceforge/pmd/lang/ast/test/ParseUtils.kt
@@ -0,0 +1,41 @@
+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
+
+}
+
diff --git a/pmd-test/src/test/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt b/pmd-test/src/test/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt
new file mode 100644
index 0000000000..f821080efa
--- /dev/null
+++ b/pmd-test/src/test/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt
@@ -0,0 +1,30 @@
+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 = emptySet(),
+ param: io.kotlintest.TestContext.() -> kotlin.Unit) {
+
+ this.expectFailure(testName, messageContains, param)
+}
+
+inline fun AbstractFunSpec.expectFailure(testName: String,
+ messageContains: Set = emptySet(),
+ noinline param: io.kotlintest.TestContext.() -> kotlin.Unit) {
+ test(testName) {
+ val exception = shouldThrow {
+ this.param() // this is the test context here
+ }
+
+ for (substr in messageContains) exception.message.shouldContainIgnoringCase(substr)
+
+ }
+}
diff --git a/pom.xml b/pom.xml
index 57df22d03d..0d067a58db 100644
--- a/pom.xml
+++ b/pom.xml
@@ -260,6 +260,14 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code
1.${java.version}
1.${java.version}
+ 1.8
+ 1.8
+
+
+ ${maven.compiler.test.target}
+ 1.2.61
+
+
5.0
2.22.0
3.0.0
@@ -275,6 +283,7 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code
-Xmx512m -Dfile.encoding=${project.build.sourceEncoding}
1.2
+
@@ -329,6 +338,30 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code
maven-clean-plugin
3.1.0
+
+
+
+
+ kotlin-maven-plugin
+ org.jetbrains.kotlin
+ ${kotlin.version}
+
+
+ kotlin-test-compile
+
+ test-compile
+
+ process-test-sources
+
+
+ ${project.basedir}/src/test/kotlin
+ ${project.basedir}/src/test/java
+
+
+
+
+
+
org.apache.maven.plugins
maven-compiler-plugin
@@ -336,6 +369,36 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code
${java.version}
+
+
+
+ default-compile
+ none
+
+
+
+ default-testCompile
+ none
+
+
+ java-compile
+ compile
+
+ compile
+
+
+
+ java-test-compile
+ test-compile
+
+ testCompile
+
+
+
+ ${maven.compiler.test.target}
+
+
+
org.apache.maven.plugins
@@ -369,7 +432,27 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code
once
alphabetical
+ ${project.basedir}/src/test/java
+ ${project.basedir}/src/test/kotlin
+
+
+
+
+
+
+ org.junit.platform
+ junit-platform-surefire-provider
+ 1.2.0
+
+
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ 5.3.0-M1
+
+
org.codehaus.mojo
@@ -853,6 +936,39 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code
system-rules
1.8.0
+
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib
+ ${kotlin.version}
+ test
+
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib-jdk8
+ ${kotlin.version}
+ test
+
+
+
+ org.jetbrains.kotlin
+ kotlin-test-junit
+ ${kotlin.version}
+ test
+
+
+
+ io.kotlintest
+ kotlintest-runner-junit5
+ 3.1.8
+ test
+
+