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.source} + ${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 + +