From e013645d879e6896e53988b5082e1fbb44920074 Mon Sep 17 00:00:00 2001 From: Sergey Rostov Date: Wed, 3 Apr 2019 13:43:32 +0300 Subject: [PATCH] Gradle, test: allTests task, same class and method names for jvm, js and native #KT-30691 Fixed #KT-31448 Fixed --- .../testing/TCServiceMessagesClient.kt | 337 ++++++++++++------ .../gradle/plugin/KotlinTargetConfigurator.kt | 8 +- .../KotlinJsCompilationTestsConfigurator.kt | 13 +- .../js/internal/NodeJsStackTraceParser.kt | 137 +++++++ .../targets/js/tasks/KotlinNodeJsTestTask.kt | 14 +- .../targets/js/testing/karma/KotlinKarma.kt | 100 ++++++ .../targets/js/testing/mocha/KotlinMocha.kt | 64 ++++ .../testing/nodejs/KotlinNodeJsTestRunner.kt | 68 ++++ .../jvm/KotlinJvmTargetConfigurator.kt | 2 - .../targets/jvm/tasks/KotlinJvmTestTask.kt | 42 +++ .../native/KotlinNativeTargetConfigurator.kt | 10 +- .../native/tasks/KotlinNativeTestTask.kt | 18 +- .../gradle/tasks/AggregateTestReport.kt | 175 +++++++++ .../kotlin/gradle/tasks/KotlinTestTask.kt | 31 +- .../kotlin/gradle/testing/conventions.kt | 36 ++ .../kotlin/gradle/testing/testsRegistry.kt | 51 +++ .../internal/testing/tcsmc/AppendLeaf.kt | 116 ------ .../internal/testing/tcsmc/AppendRoot.kt | 58 --- .../gradle/internal/testing/tcsmc/Complex.kt | 336 +++++++++-------- .../internal/testing/tcsmc/ReplaceRoot.kt | 66 ---- .../gradle/internal/testing/tcsmc/SkipRoot.kt | 30 -- .../tcsmc/TCServiceMessagesClientTest.kt | 7 +- 22 files changed, 1107 insertions(+), 612 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/internal/NodeJsStackTraceParser.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/nodejs/KotlinNodeJsTestRunner.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/tasks/KotlinJvmTestTask.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/AggregateTestReport.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/conventions.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/testsRegistry.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendLeaf.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendRoot.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/ReplaceRoot.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/SkipRoot.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesClient.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesClient.kt index 4601f6338d1..730bb9df609 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesClient.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesClient.kt @@ -1,3 +1,8 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + package org.jetbrains.kotlin.gradle.internal.testing import jetbrains.buildServer.messages.serviceMessages.* @@ -16,34 +21,25 @@ import java.lang.System.currentTimeMillis as currentTimeMillis1 data class TCServiceMessagesClientSettings( val rootNodeName: String, - val nameOfRootSuiteToAppend: String? = null, - val nameOfRootSuiteToReplace: String? = null, - val nameOfLeafTestToAppend: String? = null, - val skipRoots: Boolean = false, - val treatFailedTestOutputAsStacktrace: Boolean = false -) { - init { - if (skipRoots) { - check(nameOfRootSuiteToReplace == null) { "nameOfRootSuiteToReplace makes no sense when skipRoots is set" } - check(nameOfRootSuiteToAppend == null) { "nameOfRootSuiteToAppend cannot work with skipRoots" } - } - } -} + val testNameSuffix: String? = null, + val prependSuiteName: Boolean = false, + val treatFailedTestOutputAsStacktrace: Boolean = false, + val stackTraceParser: (String) -> ParsedStackTrace? = { null }, + val ignoreOutOfRootNodes: Boolean = false +) internal class TCServiceMessagesClient( private val results: TestResultProcessor, val settings: TCServiceMessagesClientSettings, val log: Logger ) : ServiceMessageParserCallback { + lateinit var rootOperationId: Any + inline fun root(operation: OperationIdentifier, actions: () -> Unit) { - RootNode(operation.id).open { root -> - if (settings.nameOfRootSuiteToAppend != null) { - SuiteNode(root, settings.nameOfRootSuiteToAppend).open { - actions() - } - } else { - actions() - } + rootOperationId = operation.id + + RootNode(operation.id).open { + actions() } } @@ -55,7 +51,7 @@ internal class TCServiceMessagesClient( log.kotlinDebug { "TCSM: $message" } when (message) { - is TestSuiteStarted -> open(message.ts, SuiteNode(leaf, hookSuiteName(leaf, message.suiteName))) + is TestSuiteStarted -> open(message.ts, SuiteNode(requireLeafGroup(), message.suiteName)) is TestStarted -> beginTest(message.ts, message.testName) is TestStdOut -> requireLeafTest().output(StdOut, message.stdOut) is TestStdErr -> requireLeafTest().output(StdErr, message.stdErr) @@ -64,21 +60,17 @@ internal class TCServiceMessagesClient( is TestIgnored -> { if (message.attributes["suite"] == "true") { // non standard property for dealing with ignored test suites without visiting all inner tests - SuiteNode(requireLeaf(), message.testName).open(message.ts) { message.ts } + SuiteNode(requireLeafGroup(), message.testName).open(message.ts) { message.ts } } else { beginTest(message.ts, message.testName, isIgnored = true) endTest(message.ts, message.testName) } } - is TestSuiteFinished -> close(message.ts, hookSuiteName(leaf?.parent, message.suiteName)) + is TestSuiteFinished -> close(message.ts, message.suiteName) else -> Unit } } - private fun hookSuiteName(parent: Node?, originalName: String) = - if (parent?.parent == null && settings.nameOfRootSuiteToReplace != null) settings.nameOfRootSuiteToReplace - else originalName - override fun regularText(text: String) { log.kotlinDebug { "TCSM stdout captured: $text" } val actualText = text + "\n" @@ -93,40 +85,29 @@ internal class TCServiceMessagesClient( private fun beginTest(ts: Long, testName: String, isIgnored: Boolean = false) { val parent = requireLeafGroup() + parent.requireReportingNode() - val parsedName = ParsedTestName(testName, parent.localId) - - if (settings.nameOfLeafTestToAppend != null) { - val group = open(ts, SuiteNode(parent, parsedName.methodName)) - open( - ts, TestNode( - group, parsedName.className, parsedName.classDisplayName, parsedName.methodName, - displayName = "${parsedName.methodName}.${settings.nameOfLeafTestToAppend}", - localId = "$testName.${settings.nameOfLeafTestToAppend}", - ignored = isIgnored - ) - ) - } else { - open( - ts, TestNode( - parent, parsedName.className, parsedName.classDisplayName, parsedName.methodName, - displayName = parsedName.methodName, - localId = testName, - ignored = isIgnored - ) - ) + val finalTestName = testName.let { + if (settings.prependSuiteName) "${parent.fullNameWithoutRoot}.$it" + else it } + + val parsedName = ParsedTestName(finalTestName, parent.localId) + val fullTestName = if (settings.testNameSuffix == null) parsedName.methodName + else "${parsedName.methodName}[${settings.testNameSuffix}]" + + open( + ts, TestNode( + parent, parsedName.className, parsedName.classDisplayName, parsedName.methodName, + displayName = fullTestName, + localId = testName, + ignored = isIgnored + ) + ) } private fun endTest(ts: Long, testName: String) { - val parsedName = ParsedTestName(testName, leaf!!.parent!!.localId) - - if (settings.nameOfLeafTestToAppend != null) { - close(ts, "$testName.${settings.nameOfLeafTestToAppend}") - close(ts, parsedName.methodName) - } else { - close(ts, testName) - } + close(ts, testName) } private fun TestNode.failure( @@ -135,7 +116,9 @@ internal class TCServiceMessagesClient( hasFailures = true val stacktrace = buildString { - append(message.stacktrace) + if (message.stacktrace != null) { + append(message.stacktrace) + } if (settings.treatFailedTestOutputAsStacktrace) { append(output) @@ -143,18 +126,49 @@ internal class TCServiceMessagesClient( } } - results.failure(descriptor.id, KotlinTestFailure(message.failureMessage, stacktrace)) + val parsedStackTrace = settings.stackTraceParser(stacktrace) + + results.failure( + descriptor.id, + KotlinTestFailure( + (parsedStackTrace?.message ?: message.failureMessage)?.let { extractExceptionClassName(it) } + ?: "Unknown", + message.failureMessage, + stacktrace, + patchStackTrace(this, parsedStackTrace?.stackTrace), + message.expected, + message.actual + ) + ) } + private fun extractExceptionClassName(message: String): String = + message.substringBefore(':').trim() + + /** + * Required for org.gradle.api.internal.tasks.testing.logging.ShortExceptionFormatter.printException + * In JS Stacktraces we have short class name, while filter using FQN + * So, let replace short class name with FQN for current test + */ + private fun patchStackTrace(node: TestNode, stackTrace: List?): List? = + stackTrace?.map { + if (it.className == node.classDisplayName) StackTraceElement( + node.className, + it.methodName, + it.fileName, + it.lineNumber + ) else it + } + private fun TestNode.output( destination: TestOutputEvent.Destination, text: String ) { if (settings.treatFailedTestOutputAsStacktrace) { output.append(text) + } else { + results.output(descriptor.id, DefaultTestOutputEvent(destination, text)) } - - results.output(descriptor.id, DefaultTestOutputEvent(destination, text)) } private inline fun NodeType.open(contents: (NodeType) -> Unit) = open(System.currentTimeMillis()) { @@ -162,7 +176,6 @@ internal class TCServiceMessagesClient( System.currentTimeMillis() } - private inline fun NodeType.open(tsStart: Long, contents: (NodeType) -> Long) { val child = open(tsStart, this@open) val tsEnd = contents(child) @@ -172,30 +185,30 @@ internal class TCServiceMessagesClient( private fun open(ts: Long, new: NodeType): NodeType = new.also { log.kotlinDebug { "Test node opened: $it" } - if (!it.isSkippedRoot) { - results.started(it.descriptor, TestStartEvent(ts, it.reportingParent?.descriptor?.id)) - } + it.markStarted(ts) push(it) } private fun close(ts: Long, assertLocalId: String?) = pop().also { if (assertLocalId != null) { + if (it.localId != assertLocalId && settings.ignoreOutOfRootNodes && it.parent == null) { + push(it) + return it + } + check(it.localId == assertLocalId) { - "Bad TCSM: unexpected node to close: ${it.localId}, stack: ${ - collectParents().joinToString("") { item -> "\n - ${item.localId}" } + "Bad TCSM: unexpected node to close `$assertLocalId`, expected `${it.localId}`, stack: ${ + leaf.collectParents().joinToString("") { item -> "\n - ${item.localId}" } }\n" } } log.kotlinDebug { "Test node closed: $it" } - - if (!it.isSkippedRoot) { - results.completed(it.descriptor.id, TestCompleteEvent(ts, it.resultType)) - } + it.markCompleted(ts) } - private fun collectParents(): MutableList { - var i = leaf + private fun Node?.collectParents(): MutableList { + var i = this val items = mutableListOf() while (i != null) { items.add(i) @@ -204,6 +217,7 @@ internal class TCServiceMessagesClient( return items } + class ParsedTestName(testName: String, parentName: String) { val hasClassName: Boolean val className: String @@ -226,27 +240,36 @@ internal class TCServiceMessagesClient( } } + enum class NodeState { + created, started, completed + } /** - * Node of tests tree + * Node of tests tree. + * */ abstract inner class Node( var parent: Node? = null, val localId: String ) { - val reportingParent: Node? - get() = when { - parent == null -> null - parent!!.isSkippedRoot -> parent!!.reportingParent - else -> parent + val id: String = if (parent != null) "${parent!!.id}/$localId" else localId + + open val cleanName: String + get() = localId + + abstract val descriptor: TestDescriptorInternal? + + var state: NodeState = NodeState.created + + var reportingParent: GroupNode? = null + get() { + checkReportingNodeCreated() + return field } - val isSkippedRoot: Boolean - get() = settings.skipRoots && parent != null && parent!!.parent == null - - val id: String = if (parent != null) "${reportingParent?.descriptor?.id}.$localId" else localId - - abstract val descriptor: TestDescriptorInternal + private fun checkReportingNodeCreated() { + check(descriptor != null) + } var hasFailures: Boolean = false set(value) { @@ -279,26 +302,126 @@ internal class TCServiceMessagesClient( else -> SKIPPED } - override fun toString(): String = descriptor.toString() - } + override fun toString(): String = id - inner class RootNode(val ownerBuildOperationId: Any) : Node(null, settings.rootNodeName) { - override val descriptor = - object : DefaultTestSuiteDescriptor(settings.rootNodeName, localId) { - override fun getOwnerBuildOperationId(): Any? = this@RootNode.ownerBuildOperationId + abstract fun markStarted(ts: Long) + abstract fun markCompleted(ts: Long) + + fun checkState(state: NodeState) { + check(this.state == state) { + "$this should be in state $state" } + } + + protected fun reportStarted(ts: Long) { + checkState(NodeState.created) + reportingParent?.checkState(NodeState.started) + + results.started(descriptor!!, TestStartEvent(ts, reportingParent?.descriptor?.id)) + + state = NodeState.started + } + + protected fun reportCompleted(ts: Long) { + checkState(NodeState.started) + reportingParent?.checkState(NodeState.started) + + results.completed(descriptor!!.id, TestCompleteEvent(ts, resultType)) + + state = NodeState.completed + } } - inner class SuiteNode(parent: Node? = null, name: String) : Node(parent, name) { - override val descriptor = object : DefaultTestSuiteDescriptor(id, name) { - override fun getParent(): TestDescriptorInternal? = this@SuiteNode.parent?.descriptor + abstract inner class GroupNode(parent: Node?, localId: String) : Node(parent, localId) { + val fullNameWithoutRoot: String + get() = collectParents().dropLast(1) + .reversed() + .map { it.localId } + .filter { it.isNotBlank() } + .joinToString(".") { it } + + abstract fun requireReportingNode(): TestDescriptorInternal + } + + inner class RootNode(val ownerBuildOperationId: Any) : GroupNode(null, settings.rootNodeName) { + override val descriptor: TestDescriptorInternal = object : DefaultTestSuiteDescriptor(settings.rootNodeName, localId) { + override fun getOwnerBuildOperationId(): Any? = this@RootNode.ownerBuildOperationId + override fun getParent(): TestDescriptorInternal? = null + } + + override fun requireReportingNode(): TestDescriptorInternal = descriptor + + override fun markStarted(ts: Long) { + reportStarted(ts) + } + + override fun markCompleted(ts: Long) { + reportCompleted(ts) + } + } + + fun cleanName(parent: GroupNode, name: String): String { + // Some test reporters may report test suite in name (Kotlin/Native) + val parentName = parent.fullNameWithoutRoot + return name.removePrefix("$parentName.") + } + + inner class SuiteNode(parent: GroupNode, name: String) : GroupNode(parent, name) { + override val cleanName = cleanName(parent, name) + + private var shouldReportComplete = false + + override var descriptor: TestDescriptorInternal? = null + private set + + override fun requireReportingNode(): TestDescriptorInternal = descriptor ?: createReportingNode() + + /** + * Called when first test in suite started + */ + private fun createReportingNode(): TestDescriptorInternal { + val parents = collectParents() + val fullName = parents.reversed() + .map { it.cleanName } + .filter { it.isNotBlank() } + .joinToString(".") + + val reportingParent = parents.last() as RootNode + this.reportingParent = reportingParent + + descriptor = object : DefaultTestSuiteDescriptor(id, fullName) { + override fun getDisplayName(): String = fullNameWithoutRoot + override fun getOwnerBuildOperationId(): Any? = rootOperationId + override fun getParent(): TestDescriptorInternal = reportingParent.descriptor + } + + shouldReportComplete = true + + check(startedTs != 0L) + reportStarted(startedTs) + + return descriptor!! + } + + private var startedTs: Long = 0 + + override fun markStarted(ts: Long) { + check(descriptor == null) + startedTs = ts + } + + override fun markCompleted(ts: Long) { + if (shouldReportComplete) { + check(descriptor != null) + reportCompleted(ts) + } } } inner class TestNode( - parent: Node, - className: String, - classDisplayName: String, + parent: GroupNode, + val className: String, + val classDisplayName: String, methodName: String, displayName: String, localId: String, @@ -306,8 +429,20 @@ internal class TCServiceMessagesClient( ) : Node(parent, localId) { val output by lazy { StringBuilder() } - override val descriptor = object : DefaultTestDescriptor(id, className, methodName, classDisplayName, displayName) { - override fun getParent(): TestDescriptorInternal? = this@TestNode.parent?.descriptor + private val parentDescriptor = (this@TestNode.parent as GroupNode).requireReportingNode() + + override val descriptor: TestDescriptorInternal = + object : DefaultTestDescriptor(id, className, methodName, classDisplayName, displayName) { + override fun getOwnerBuildOperationId(): Any? = rootOperationId + override fun getParent(): TestDescriptorInternal = parentDescriptor + } + + override fun markStarted(ts: Long) { + reportStarted(ts) + } + + override fun markCompleted(ts: Long) { + reportCompleted(ts) } init { @@ -332,8 +467,8 @@ internal class TCServiceMessagesClient( } private fun requireLeaf() = leaf ?: error("test out of group") - private fun requireLeafGroup() = requireLeaf().also { - check(it !is TestNode) { "previous test `$it` not finished" } + private fun requireLeafGroup(): GroupNode = requireLeaf().let { + it as? GroupNode ?: error("previous test `$it` not finished") } private fun requireLeafTest() = leaf as? TestNode diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt index 75664049051..623175e9d04 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt @@ -25,6 +25,8 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.language.jvm.tasks.ProcessResources import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.mpp.* +import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTestTask +import org.jetbrains.kotlin.gradle.testing.registerTestTask import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import java.util.concurrent.Callable @@ -363,7 +365,11 @@ abstract class KotlinTargetConfigurator ?: return // Otherwise, there is no runtime classpath - target.project.tasks.create(lowerCamelCaseName(target.disambiguationClassifier, testTaskNameSuffix), Test::class.java).apply { + target.project.tasks.create(lowerCamelCaseName(target.disambiguationClassifier, testTaskNameSuffix), KotlinJvmTestTask::class.java).apply { + registerTestTask(this) + + targetName = target.disambiguationClassifier + project.afterEvaluate { // use afterEvaluate to override the JavaPlugin defaults for Test tasks conventionMapping.map("testClassesDirs") { testCompilation.output.classesDirs } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationTestsConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationTestsConfigurator.kt index eecac8032ac..c460d6d1908 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationTestsConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsCompilationTestsConfigurator.kt @@ -1,20 +1,17 @@ package org.jetbrains.kotlin.gradle.targets.js -import org.gradle.api.Project import org.gradle.api.file.FileCollection -import org.gradle.api.internal.plugins.DslObject import org.gradle.language.base.plugins.LifecycleBasePlugin -import org.gradle.testing.base.plugins.TestingBasePlugin import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsSetupTask import org.jetbrains.kotlin.gradle.targets.js.tasks.* import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile -import org.jetbrains.kotlin.gradle.tasks.KotlinTestTask import org.jetbrains.kotlin.gradle.tasks.registerTask +import org.jetbrains.kotlin.gradle.testing.configureConventions +import org.jetbrains.kotlin.gradle.testing.registerTestTask import org.jetbrains.kotlin.utils.addIfNotNull -import java.io.File internal class KotlinJsCompilationTestsConfigurator( val compilation: KotlinCompilationToRunnableFiles<*> @@ -88,6 +85,7 @@ internal class KotlinJsCompilationTestsConfigurator( if (disambiguationClassifier != null) { testJs.targetName = disambiguationClassifier + testJs.showTestTargetName = true } testJs.nodeJsProcessOptions.workingDir = project.projectDir @@ -99,12 +97,11 @@ internal class KotlinJsCompilationTestsConfigurator( ) testJs.nodeModulesToLoad = setOf(compileTestKotlin2Js.outputFile.name) - KotlinTestTask.configureConventions(testJs) + testJs.configureConventions() + registerTestTask(testJs) } project.afterEvaluate { - project.tasks.maybeCreate(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(testTask.getTaskOrProvider()) - // defer nodeJs executable setup, as nodejs project settings may change during configuration testTask.configure { val nodeJsSetupTask = projectWithNodeJsPlugin.tasks.findByName(NodeJsSetupTask.NAME) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/internal/NodeJsStackTraceParser.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/internal/NodeJsStackTraceParser.kt new file mode 100644 index 00000000000..dc8eb3ab490 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/internal/NodeJsStackTraceParser.kt @@ -0,0 +1,137 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.targets.js.internal + +import org.jetbrains.kotlin.gradle.internal.testing.ParsedStackTrace +import java.lang.StringBuilder + +data class NodeJsStackTrace( + val message: String?, + val stackTrace: List? +) { + fun toJvm() = ParsedStackTrace( + message, + stackTrace?.map { it.toJvmStackTraceElement() } + ) + + + override fun toString(): String { + return "NodeJsStackTrace(\nmessage=\"$message\",\nstacktrace=[\n${stackTrace?.joinToString("\n")}\n])" + } +} + +data class NodeJsStackTraceElement( + val className: String? = null, + val methodName: String? = null, + val fileName: String? = null, + val lineNumber: Int = -1, + val colNumber: Int = -1 +) { + fun toJvmStackTraceElement() = StackTraceElement( + className ?: "", + methodName ?: "", + fileName, + lineNumber + ) +} + +fun parseNodeJsStackTraceAsJvm(stackTrace: String): ParsedStackTrace? = + parseNodeJsStackTrace(stackTrace).toJvm() + +fun parseNodeJsStackTrace(stackTrace: String): NodeJsStackTrace { + val message = StringBuilder() + var firstLines = true + val stack = mutableListOf() + + // see examples at NodeJsStackTraceParserKtTest + stackTrace.lines().forEach { + val srcLine = it.trim() + + var className: String? = null + var methodName: String? = null + var fileName: String? = null + var lineNumber: Int? = null + var colNumber: Int? = null + + fun parsePos(fileAndPos: String) { + val fileAndPosComponents = fileAndPos.split(":") + fileName = fileAndPosComponents[0] + if (fileAndPosComponents.size > 1) lineNumber = fileAndPosComponents[1].toIntOrNull() + if (fileAndPosComponents.size > 2) colNumber = fileAndPosComponents[2].toIntOrNull() + } + + if (srcLine.startsWith("at ")) { + firstLines = false + val line = srcLine.removePrefix("at ") + + val classAndMethod: String? = if (line.endsWith(")") && "(" in line) { + val fileAndPos = line.substringAfterLast("(").removeSuffix(")") + parsePos(fileAndPos) + line.substringBeforeLast("(") + } else if (line.contains(":")) { + parsePos(line) + null + } else line + + if (classAndMethod != null) { + if ("." in classAndMethod) { + methodName = filterMethodName(classAndMethod.substringAfterLast(".")).trim() + className = classAndMethod.substringBeforeLast(".").trim() + } else { + methodName = classAndMethod.trim() + } + + if (methodName.endsWith("]") && "[as " in methodName) { + methodName = methodName.substringAfterLast("[as ").removeSuffix("]").trim() + } + + methodName = filterMethodName(methodName) + + if (methodName.isBlank()) methodName = null + + if (methodName != null) { + if (methodName.endsWith("_init")) { + className = methodName.removeSuffix("_init") + methodName = "init" + } + } + + if (className != null) { + className = filterClassName(className) + } + } + + stack.add( + NodeJsStackTraceElement( + className, + methodName, + fileName, + lineNumber ?: -1, + colNumber ?: -1 + ) + ) + } else { + if (firstLines) { + message.appendln(it) + } + } + } + + return NodeJsStackTrace( + message.toString().trim().let { if (it.isEmpty()) null else it }, + if (stack.isEmpty()) null else stack + ) +} + +fun filterClassName(className: String): String = + className.replace('$', '.') + +private fun filterMethodName(name: String): String = + if ("_" in name) { + val suffix = name.substringAfterLast("_") + if (suffix.endsWith("$") || suffix.toIntOrNull() != null) name.substringBeforeLast("_") + else name + } else name \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestTask.kt index 92f752b4e1b..475f8bed56c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/tasks/KotlinNodeJsTestTask.kt @@ -71,15 +71,11 @@ open class KotlinNodeJsTestTask : KotlinTestTask() { ignoredTestSuites.cli ) - val clientSettings = when (testsGrouping) { - TestsGrouping.none -> TCServiceMessagesClientSettings(rootNodeName = name, skipRoots = true) - TestsGrouping.root -> TCServiceMessagesClientSettings(rootNodeName = name, nameOfRootSuiteToReplace = targetName) - TestsGrouping.leaf -> TCServiceMessagesClientSettings( - rootNodeName = name, - skipRoots = true, - nameOfLeafTestToAppend = targetName - ) - } + val clientSettings = TCServiceMessagesClientSettings( + name, + testNameSuffix = if (showTestTargetName) targetName else null, + prepandSuiteName = true + ) return TCServiceMessagesTestExecutionSpec( extendedForkOptions, diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt new file mode 100644 index 00000000000..2de32b350f2 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.targets.js.testing.karma + +import com.google.gson.GsonBuilder +import org.gradle.process.ProcessForkOptions +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec +import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies +import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject +import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest +import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTestFramework + +class KotlinKarma : KotlinJsTestFramework { + override fun configure(dependenciesHolder: HasKotlinDependencies) { + dependenciesHolder.dependencies { + runtimeOnly(npm("karma", "4.0.1")) + runtimeOnly(npm("karma-chrome-launcher", "2.2.0")) + runtimeOnly(npm("puppeteer", "1.14.0")) + runtimeOnly(npm("karma-source-map-support", "1.4.0")) + runtimeOnly(npm("karma-browserify", "6.0.0")) + runtimeOnly(npm("browserify", "16.2.3")) + runtimeOnly(npm("karma-mocha", "1.3.0")) + runtimeOnly(npm("mocha", "6.1.2")) + runtimeOnly(npm("karma-teamcity-reporter", "1.1.0")) + } + } + + override fun createTestExecutionSpec( + task: KotlinJsTest, + forkOptions: ProcessForkOptions, + nodeJsArgs: MutableList + ): TCServiceMessagesTestExecutionSpec { + val clientSettings = TCServiceMessagesClientSettings( + task.name, + testNameSuffix = task.targetName, + prependSuiteName = true, + stackTraceParser = ::parseNodeJsStackTraceAsJvm, + ignoreOutOfRootNodes = true + ) + + val npmProjectLayout = NpmProject[task.project] + + val files = task.nodeModulesToLoad.map { + npmProjectLayout.nodeModulesDir.resolve(it).also { file -> + check(file.isFile) { "Cannot find ${file.canonicalPath}" } + }.canonicalPath + }.let { + GsonBuilder().create().toJson(it) + } + + val karmaConfJs = npmProjectLayout.nodeWorkDir.resolve("karma.conf.js") + karmaConfJs.printWriter().use { + //language=JavaScript + it.println( + """ + process.env.CHROME_BIN = require('puppeteer').executablePath(); + + module.exports = (config) => { + config.set({ + basePath: 'node_modules', + files: $files, + frameworks: ['browserify', 'source-map-support', 'mocha'], + browsers: ['ChromeHeadless'], + reporters: ['teamcity'], + singleRun: true, + preprocessors: { + '*.js': [ 'browserify' ] + }, + browserify: { + debug: true + } + }); + } + """.trimIndent() + ) + } + + val nodeModules = listOf("karma/bin/karma") + + val args = nodeJsArgs + + nodeModules.map { + npmProjectLayout.nodeModulesDir.resolve(it).also { file -> + check(file.isFile) { "Cannot find ${file.canonicalPath}" } + }.canonicalPath + } + + listOf("start", karmaConfJs.absolutePath) + + return TCServiceMessagesTestExecutionSpec( + forkOptions, + args, + false, + clientSettings + ) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt new file mode 100644 index 00000000000..eb58bed8513 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.targets.js.testing.mocha + +import org.gradle.process.ProcessForkOptions +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec +import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies +import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject +import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest +import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTestFramework + +class KotlinMocha : KotlinJsTestFramework { + override fun configure(dependenciesHolder: HasKotlinDependencies) { + dependenciesHolder.dependencies { + runtimeOnly(kotlin("test-nodejs-runner")) + runtimeOnly(npm("mocha", "6.1.2")) + runtimeOnly(npm("mocha-teamcity-reporter", ">=2.0.0")) + } + } + + override fun createTestExecutionSpec( + task: KotlinJsTest, + forkOptions: ProcessForkOptions, + nodeJsArgs: MutableList + ): TCServiceMessagesTestExecutionSpec { + val clientSettings = TCServiceMessagesClientSettings( + task.name, + testNameSuffix = task.targetName, + prependSuiteName = true, + stackTraceParser = ::parseNodeJsStackTraceAsJvm, + ignoreOutOfRootNodes = true + ) + + val nodeModules = listOf( + ".bin/mocha", + task.nodeModulesToLoad.single() + ) + + val npmProjectLayout = NpmProject[task.project] + + val args = nodeJsArgs + + nodeModules.map { + npmProjectLayout.nodeModulesDir.resolve(it).also { file -> + check(file.isFile) { "Cannot find ${file.canonicalPath}" } + }.canonicalPath + } + + listOf( + "-r", "kotlin-nodejs-source-map-support.js", + "--reporter", "mocha-teamcity-reporter" + ) + + return TCServiceMessagesTestExecutionSpec( + forkOptions, + args, + false, + clientSettings + ) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/nodejs/KotlinNodeJsTestRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/nodejs/KotlinNodeJsTestRunner.kt new file mode 100644 index 00000000000..217d924ab8e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/nodejs/KotlinNodeJsTestRunner.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.targets.js.testing.nodejs + +import org.gradle.api.tasks.Input +import org.gradle.process.ProcessForkOptions +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings +import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec +import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies +import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject +import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTestFramework +import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest +import org.jetbrains.kotlin.gradle.testing.IgnoredTestSuites + +class KotlinNodeJsTestRunner : KotlinJsTestFramework { + @Input + var ignoredTestSuites: IgnoredTestSuites = IgnoredTestSuites.showWithContents + + override fun configure(dependenciesHolder: HasKotlinDependencies) { + dependenciesHolder.dependencies { + runtimeOnly(kotlin("test-nodejs-runner")) + } + } + + override fun createTestExecutionSpec( + task: KotlinJsTest, + forkOptions: ProcessForkOptions, + nodeJsArgs: MutableList + ): TCServiceMessagesTestExecutionSpec { + val cliArgs = KotlinNodeJsTestRunnerCliArgs( + task.nodeModulesToLoad.toList(), + task.includePatterns, + task.excludePatterns, + ignoredTestSuites.cli + ) + + val clientSettings = TCServiceMessagesClientSettings( + task.name, + testNameSuffix = task.targetName, + prependSuiteName = true, + stackTraceParser = ::parseNodeJsStackTraceAsJvm + ) + + val testRuntimeNodeModules = listOf( + "kotlin-test-nodejs-runner/kotlin-test-nodejs-runner.js", + "kotlin-test-nodejs-runner/kotlin-nodejs-source-map-support.js" + ) + + val npmProjectLayout = NpmProject[task.project] + + val args = nodeJsArgs + + testRuntimeNodeModules.map { + npmProjectLayout.getModuleEntryPath(it) + } + + cliArgs.toList() + + return TCServiceMessagesTestExecutionSpec( + forkOptions, + args, + true, + clientSettings + ) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetConfigurator.kt index 9c55a8d40ed..ec4a75da3ed 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetConfigurator.kt @@ -5,11 +5,9 @@ package org.jetbrains.kotlin.gradle.targets.jvm -import org.jetbrains.kotlin.gradle.plugin.Kotlin2JsSourceSetProcessor import org.jetbrains.kotlin.gradle.plugin.Kotlin2JvmSourceSetProcessor import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/tasks/KotlinJvmTestTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/tasks/KotlinJvmTestTask.kt new file mode 100644 index 00000000000..c0ac686cae1 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/tasks/KotlinJvmTestTask.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.targets.jvm.tasks + +import org.gradle.api.internal.tasks.testing.* +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.testing.Test + +open class KotlinJvmTestTask : Test() { + @Input + @Optional + var targetName: String? = null + + override fun createTestExecuter(): TestExecuter = + if (targetName != null) Executor( + super.createTestExecuter(), + targetName!! + ) + else super.createTestExecuter() + + class Executor( + private val delegate: TestExecuter, + private val targetName: String + ) : TestExecuter by delegate { + override fun execute(testExecutionSpec: JvmTestExecutionSpec, testResultProcessor: TestResultProcessor) { + delegate.execute(testExecutionSpec, object : TestResultProcessor by testResultProcessor { + override fun started(test: TestDescriptorInternal, event: TestStartEvent) { + val myTest = object : TestDescriptorInternal by test { + override fun getDisplayName(): String = "${test.displayName}[$targetName]" + override fun getClassName(): String? = test.className?.replace('$', '.') + override fun getClassDisplayName(): String? = test.classDisplayName?.replace('$', '.') + } + testResultProcessor.started(myTest, event) + } + }) + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt index 0a1c8b7b60f..edf903df030 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt @@ -14,19 +14,18 @@ import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE import org.gradle.api.internal.artifacts.ArtifactAttributes import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet -import org.gradle.api.internal.plugins.DslObject import org.gradle.api.plugins.BasePlugin -import org.gradle.api.reporting.ReportingExtension import org.gradle.api.tasks.Copy import org.gradle.api.tasks.Exec import org.gradle.language.base.plugins.LifecycleBasePlugin -import org.gradle.testing.base.plugins.TestingBasePlugin import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTestTask import org.jetbrains.kotlin.gradle.tasks.* +import org.jetbrains.kotlin.gradle.testing.configureConventions +import org.jetbrains.kotlin.gradle.testing.registerTestTask import java.io.File import java.util.* @@ -135,6 +134,8 @@ open class KotlinNativeTargetConfigurator( group = LifecycleBasePlugin.VERIFICATION_GROUP description = "Executes Kotlin/Native unit tests for target ${binary.target.name}." + targetName = binary.compilation.target.targetName + showTestTargetName = true enabled = binary.target.konanTarget.isCurrentHost @@ -143,8 +144,9 @@ open class KotlinNativeTargetConfigurator( onlyIf { binary.outputFile.exists() } dependsOn(binary.linkTaskName) + configureConventions() - KotlinTestTask.configureConventions(this) + registerTestTask(this) } } else { tasks.create(taskName, Exec::class.java).apply { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTestTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTestTask.kt index 6313a272c74..b0cc9cbb0e7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTestTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTestTask.kt @@ -13,11 +13,11 @@ import org.gradle.process.ProcessForkOptions import org.gradle.process.internal.DefaultProcessForkOptions import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec -import org.jetbrains.kotlin.gradle.tasks.KotlinTestTask -import org.jetbrains.kotlin.gradle.testing.TestsGrouping +import org.jetbrains.kotlin.gradle.targets.native.internal.parseKotlinNativeStackTraceAsJvm +import org.jetbrains.kotlin.gradle.tasks.KotlinTest import java.io.File -open class KotlinNativeTestTask : KotlinTestTask() { +open class KotlinNativeTest : KotlinTest() { @Suppress("LeakingThis") @Internal val processOptions: ProcessForkOptions = DefaultProcessForkOptions(fileResolver) @@ -45,11 +45,13 @@ open class KotlinNativeTestTask : KotlinTestTask() { val extendedForkOptions = DefaultProcessForkOptions(fileResolver) processOptions.copyTo(extendedForkOptions) - val clientSettings = when (testsGrouping) { - TestsGrouping.none -> TCServiceMessagesClientSettings(rootNodeName = name) - TestsGrouping.root -> TCServiceMessagesClientSettings(rootNodeName = name, nameOfRootSuiteToAppend = targetName) - TestsGrouping.leaf -> TCServiceMessagesClientSettings(rootNodeName = name, nameOfLeafTestToAppend = targetName) - }.copy(treatFailedTestOutputAsStacktrace = true) + val clientSettings = TCServiceMessagesClientSettings( + name, + testNameSuffix = targetName, + prependSuiteName = targetName != null, + treatFailedTestOutputAsStacktrace = true, + stackTraceParser = ::parseKotlinNativeStackTraceAsJvm + ) val cliArgs = CliArgs("TEAMCITY", includePatterns, excludePatterns) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/AggregateTestReport.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/AggregateTestReport.kt new file mode 100644 index 00000000000..96bfd386671 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/AggregateTestReport.kt @@ -0,0 +1,175 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.file.FileCollection +import org.gradle.api.internal.CollectionCallbackActionDecorator +import org.gradle.api.internal.file.UnionFileCollection +import org.gradle.api.internal.tasks.testing.DefaultTestTaskReports +import org.gradle.api.internal.tasks.testing.junit.result.* +import org.gradle.api.internal.tasks.testing.report.DefaultTestReport +import org.gradle.api.tasks.* +import org.gradle.api.tasks.testing.* +import org.gradle.internal.concurrent.CompositeStoppable.stoppable +import org.gradle.internal.logging.ConsoleRenderer +import org.gradle.internal.operations.BuildOperationExecutor +import org.gradle.internal.reflect.Instantiator +import org.gradle.internal.remote.internal.inet.InetAddressFactory +import org.jetbrains.kotlin.gradle.utils.injected +import java.util.* +import javax.inject.Inject + +@Suppress("UnstableApiUsage") +open class AggregateTestReport : DefaultTask() { + @Internal + val testTasks = mutableListOf() + + /** + * Returns the set of binary test results to include in the report. + */ + @Suppress("MemberVisibilityCanBePrivate") + val testResultDirs: FileCollection + @PathSensitive(PathSensitivity.NONE) + @InputFiles + @SkipWhenEmpty + get() { + val dirs = UnionFileCollection() + testTasks.forEach { + dirs.addToUnion(project.files(it.binResultsDir).builtBy(it)) + } + return dirs + } + + @Input + var ignoreFailures: Boolean = false + + @Nested + val reports: TestTaskReports + + protected open val instantiator: Instantiator + @Inject get() = injected + + protected open val collectionCallbackActionDecorator: CollectionCallbackActionDecorator + @Inject get() = injected + + protected open val buildOperationExecutor: BuildOperationExecutor + @Inject get() = injected + + protected open val inetAddressFactory: InetAddressFactory + @Inject get() = injected + + init { + @Suppress("LeakingThis") + reports = instantiator.newInstance(DefaultTestTaskReports::class.java, this, collectionCallbackActionDecorator) + reports.junitXml.isEnabled = true + reports.html.isEnabled = true + } + + private var hasFailedTests = false + + private val failedTestsListener = object : TestListener { + override fun beforeTest(testDescriptor: TestDescriptor) { + } + + override fun afterSuite(suite: TestDescriptor, result: TestResult) { + } + + override fun beforeSuite(suite: TestDescriptor) { + } + + override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { + if (result.failedTestCount > 0) { + hasFailedTests = true + } + } + } + + private val intellijReporterListener = object : TestListener { + override fun beforeTest(testDescriptor: TestDescriptor) { + } + + override fun afterSuite(suite: TestDescriptor, result: TestResult) { + } + + override fun beforeSuite(suite: TestDescriptor) { + } + + override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { + if (result.failedTestCount > 0) { + hasFailedTests = true + } + } + } + + internal var checkFailedTests: Boolean = false + + fun registerTestTask(task: AbstractTestTask) { + testTasks.add(task) + task.addTestListener(failedTestsListener) + } + + @TaskAction + fun generateReport() { + val resultsProvider = createAggregateProvider() + try { + if (resultsProvider.isHasResults) { + val junitXml = reports.junitXml + if (junitXml.isEnabled) { + Binary2JUnitXmlReportGenerator( + junitXml.destination, + resultsProvider, + when { + junitXml.isOutputPerTestCase -> TestOutputAssociation.WITH_TESTCASE + else -> TestOutputAssociation.WITH_SUITE + }, + buildOperationExecutor, + inetAddressFactory.hostname + ).generate() + } + + val html = reports.html + if (html.isEnabled) { + val testReport = DefaultTestReport(buildOperationExecutor) + testReport.generateReport(resultsProvider, html.destination) + } + } else { + logger.info("{} - no binary test results found in dirs: {}.", path, testResultDirs.files) + didWork = false + } + } finally { + stoppable(resultsProvider).stop() + } + + if (checkFailedTests && hasFailedTests) { + val message = StringBuilder("There were failing tests.") + + if (reports.html.isEnabled) { + val reportUrl = ConsoleRenderer().asClickableFileUrl(reports.html.destination.resolve("index.html")) + message.append(" See the report at: $reportUrl") + } + + if (ignoreFailures) { + logger.warn(message.toString()) + } else { + throw GradleException(message.toString()) + } + } + } + + private fun createAggregateProvider(): TestResultsProvider { + val resultsProviders = LinkedList() + try { + val resultDirs = testResultDirs + return if (resultDirs.files.size == 1) BinaryResultBackedTestResultsProvider(resultDirs.singleFile) + else AggregateTestResultsProvider(resultDirs.mapTo(resultsProviders) { BinaryResultBackedTestResultsProvider(it) }) + } catch (e: RuntimeException) { + stoppable(resultsProviders).stop() + throw e + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinTestTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinTestTask.kt index 18657b08842..7ba009b404a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinTestTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinTestTask.kt @@ -5,27 +5,20 @@ package org.jetbrains.kotlin.gradle.tasks -import org.gradle.api.Project import org.gradle.api.internal.file.FileResolver -import org.gradle.api.internal.plugins.DslObject import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter -import org.gradle.api.reporting.ReportingExtension import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.testing.AbstractTestTask import org.gradle.process.internal.ExecHandleFactory -import org.gradle.testing.base.plugins.TestingBasePlugin import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutor -import org.jetbrains.kotlin.gradle.testing.TestsGrouping import org.jetbrains.kotlin.gradle.utils.injected -import java.io.File import javax.inject.Inject abstract class KotlinTestTask : AbstractTestTask() { @Input - var testsGrouping: TestsGrouping = - TestsGrouping.root + var showTestTargetName: Boolean = false @Input @Optional @@ -60,26 +53,4 @@ abstract class KotlinTestTask : AbstractTestTask() { execHandleFactory, buildOperationExecutor ) - - companion object { - @Suppress("UnstableApiUsage") - private val Project.testResults: File - get() = project.buildDir.resolve(TestingBasePlugin.TEST_RESULTS_DIR_NAME) - - private val Project.reportsDir: File - get() = project.extensions.getByType(ReportingExtension::class.java).baseDir - - @Suppress("UnstableApiUsage") - private val Project.testReports: File - get() = reportsDir.resolve(TestingBasePlugin.TESTS_DIR_NAME) - - internal fun configureConventions(task: KotlinTestTask) { - val htmlReport = DslObject(task.reports.html) - val xmlReport = DslObject(task.reports.junitXml) - - xmlReport.conventionMapping.map("destination") { task.project.testResults.resolve(task.name) } - htmlReport.conventionMapping.map("destination") { task.project.testReports.resolve(task.name) } - task.conventionMapping.map("binResultsDir") { task.project.testResults.resolve(task.name + "/binary") } - } - } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/conventions.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/conventions.kt new file mode 100644 index 00000000000..aeec6b33334 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/conventions.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.testing + +import org.gradle.api.Project +import org.gradle.api.internal.plugins.DslObject +import org.gradle.api.reporting.ReportingExtension +import org.gradle.api.tasks.testing.TestTaskReports +import org.gradle.testing.base.plugins.TestingBasePlugin +import org.jetbrains.kotlin.gradle.tasks.KotlinTestTask +import java.io.File + +@Suppress("UnstableApiUsage") +internal val Project.testResultsDir: File + get() = project.buildDir.resolve(TestingBasePlugin.TEST_RESULTS_DIR_NAME) +internal val Project.reportsDir: File + get() = project.extensions.getByType(ReportingExtension::class.java).baseDir +@Suppress("UnstableApiUsage") +internal val Project.testReportsDir: File + get() = reportsDir.resolve(TestingBasePlugin.TESTS_DIR_NAME) + +internal fun KotlinTestTask.configureConventions() { + reports.configureConventions(project, name) + conventionMapping.map("binResultsDir") { project.testResultsDir.resolve("$name/binary") } +} + +internal fun TestTaskReports.configureConventions(project: Project, name: String) { + val htmlReport = DslObject(html) + val xmlReport = DslObject(junitXml) + + xmlReport.conventionMapping.map("destination") { project.testResultsDir.resolve(name) } + htmlReport.conventionMapping.map("destination") { project.testReportsDir.resolve(name) } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/testsRegistry.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/testsRegistry.kt new file mode 100644 index 00000000000..118754a0543 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/testsRegistry.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.testing + +import org.gradle.api.Project +import org.gradle.api.tasks.testing.AbstractTestTask +import org.gradle.language.base.plugins.LifecycleBasePlugin +import org.jetbrains.kotlin.gradle.plugin.TaskHolder +import org.jetbrains.kotlin.gradle.tasks.AggregateTestReport +import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask + +internal val Project.allTestsTask: TaskHolder + get() = locateOrRegisterTask("allTests") { aggregate -> + tasks.maybeCreate(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(aggregate) + + aggregate.reports.configureConventions(project, "all") + + aggregate.onlyIf { + aggregate.testTasks.size > 1 + } + } + +@Suppress("UnstableApiUsage") +internal fun registerTestTask(task: AbstractTestTask) { + val project = task.project + project.tasks.maybeCreate(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(task) + + val allTests = project.allTestsTask.doGetTask() + allTests.dependsOn(task) + allTests.registerTestTask(task) + + project.gradle.taskGraph.whenReady { + if (it.hasTask(allTests)) { + // when [allTestsTask] task enabled + // let all tests be executed even on failed tests + // let all failed test be reported by [allTestsTask]: + // - disable all reporting in test tasks + // - enable [checkFailedTests] on [allTestsTask] + + task.ignoreFailures = true + task.reports.html.isEnabled = false + task.reports.junitXml.isEnabled = false + + allTests.checkFailedTests = true + allTests.ignoreFailures = false + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendLeaf.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendLeaf.kt deleted file mode 100644 index e2aa4de4f86..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendLeaf.kt +++ /dev/null @@ -1,116 +0,0 @@ -package org.jetbrains.kotlin.gradle.internal.testing.tcsmc - -import jetbrains.buildServer.messages.serviceMessages.TestFinished -import jetbrains.buildServer.messages.serviceMessages.TestStarted -import jetbrains.buildServer.messages.serviceMessages.TestSuiteFinished -import jetbrains.buildServer.messages.serviceMessages.TestSuiteStarted -import org.junit.Test - -class AppendLeaf : TCServiceMessagesClientTest() { - @Test - fun appendLeafWithFqn() { - skipRoots = true - nameOfLeafTestToAppend = "js" - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE myMethod // root.myMethod - STARTED TEST displayName: myMethod.js, classDisplayName: MyTest, className: my.company.product.MyTest, name: myMethod // root.myMethod.my.company.product.MyTest.myMethod.js - COMPLETED SUCCESS // root.myMethod.my.company.product.MyTest.myMethod.js - COMPLETED SUCCESS // root.myMethod -COMPLETED SUCCESS // root - """) { - serviceMessage(TestSuiteStarted("")) - serviceMessage(TestStarted("my.company.product.MyTest.myMethod", false, null)) - serviceMessage(TestFinished("my.company.product.MyTest.myMethod", 1)) - serviceMessage(TestSuiteFinished("")) - } - } - - @Test - fun appendLeafWithClassName() { - skipRoots = true - nameOfLeafTestToAppend = "js" - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE myMethod // root.myMethod - STARTED TEST displayName: myMethod.js, classDisplayName: MyTest, className: MyTest, name: myMethod // root.myMethod.MyTest.myMethod.js - COMPLETED SUCCESS // root.myMethod.MyTest.myMethod.js - COMPLETED SUCCESS // root.myMethod -COMPLETED SUCCESS // root - """) { - serviceMessage(TestSuiteStarted("")) - serviceMessage(TestStarted("MyTest.myMethod", false, null)) - serviceMessage(TestFinished("MyTest.myMethod", 1)) - serviceMessage(TestSuiteFinished("")) - } - } - - @Test - fun replaceRootWithSuite() { - nameOfRootSuiteToReplace = "js" - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE js // root.js - STARTED SUITE MyTest // root.js.MyTest - STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: MyTest, name: myMethod // root.js.MyTest.myMethod - COMPLETED SUCCESS // root.js.MyTest.myMethod - COMPLETED SUCCESS // root.js.MyTest - COMPLETED SUCCESS // root.js -COMPLETED SUCCESS // root - """) { - serviceMessage(TestSuiteStarted("")) - serviceMessage(TestSuiteStarted("MyTest")) - serviceMessage(TestStarted("myMethod", false, null)) - serviceMessage(TestFinished("myMethod", 1)) - serviceMessage(TestSuiteFinished("MyTest")) - serviceMessage(TestSuiteFinished("")) - } - } - - @Test - fun appendLeaf() { - skipRoots = true - nameOfLeafTestToAppend = "js" - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE myMethod // root.myMethod - STARTED TEST displayName: myMethod.js, classDisplayName: , className: , name: myMethod // root.myMethod.myMethod.js - COMPLETED SUCCESS // root.myMethod.myMethod.js - COMPLETED SUCCESS // root.myMethod -COMPLETED SUCCESS // root - """) { - serviceMessage(TestSuiteStarted("")) - serviceMessage(TestStarted("myMethod", false, null)) - serviceMessage(TestFinished("myMethod", 1)) - serviceMessage(TestSuiteFinished("")) - } - } - - @Test - fun appendLeafWithSuite() { - skipRoots = true - nameOfLeafTestToAppend = "js" - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE MyTest // root.MyTest - STARTED SUITE myMethod // root.MyTest.myMethod - STARTED TEST displayName: myMethod.js, classDisplayName: MyTest, className: MyTest, name: myMethod // root.MyTest.myMethod.myMethod.js - COMPLETED SUCCESS // root.MyTest.myMethod.myMethod.js - COMPLETED SUCCESS // root.MyTest.myMethod - COMPLETED SUCCESS // root.MyTest -COMPLETED SUCCESS // root - """) { - serviceMessage(TestSuiteStarted("")) - serviceMessage(TestSuiteStarted("MyTest")) - serviceMessage(TestStarted("myMethod", false, null)) - serviceMessage(TestFinished("myMethod", 1)) - serviceMessage(TestSuiteFinished("MyTest")) - serviceMessage(TestSuiteFinished("")) - } - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendRoot.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendRoot.kt deleted file mode 100644 index a0e7bf25140..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/AppendRoot.kt +++ /dev/null @@ -1,58 +0,0 @@ -package org.jetbrains.kotlin.gradle.internal.testing.tcsmc - -import jetbrains.buildServer.messages.serviceMessages.TestFinished -import jetbrains.buildServer.messages.serviceMessages.TestStarted -import org.junit.Test - -class AppendRoot : TCServiceMessagesClientTest() { - @Test - fun testWithFqn() { - nameOfRootSuiteToAppend = "ios" - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE ios // root.ios - STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: my.company.product.MyTest, name: myMethod // root.ios.my.company.product.MyTest.myMethod - COMPLETED SUCCESS // root.ios.my.company.product.MyTest.myMethod - COMPLETED SUCCESS // root.ios -COMPLETED SUCCESS // root - """) { - serviceMessage(TestStarted("my.company.product.MyTest.myMethod", false, null)) - serviceMessage(TestFinished("my.company.product.MyTest.myMethod", 1)) - } - } - - @Test - fun testWithClassName() { - nameOfRootSuiteToAppend = "ios" - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE ios // root.ios - STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: MyTest, name: myMethod // root.ios.MyTest.myMethod - COMPLETED SUCCESS // root.ios.MyTest.myMethod - COMPLETED SUCCESS // root.ios -COMPLETED SUCCESS // root - """) { - serviceMessage(TestStarted("MyTest.myMethod", false, null)) - serviceMessage(TestFinished("MyTest.myMethod", 1)) - } - } - - @Test - fun testRoot() { - nameOfRootSuiteToAppend = "ios" - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE ios // root.ios - STARTED TEST displayName: myMethod, classDisplayName: ios, className: ios, name: myMethod // root.ios.myMethod - COMPLETED SUCCESS // root.ios.myMethod - COMPLETED SUCCESS // root.ios -COMPLETED SUCCESS // root - """) { - serviceMessage(TestStarted("myMethod", false, null)) - serviceMessage(TestFinished("myMethod", 1)) - } - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/Complex.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/Complex.kt index 80072909051..70f0dc4dd0b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/Complex.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/Complex.kt @@ -1,201 +1,189 @@ package org.jetbrains.kotlin.gradle.internal.testing.tcsmc import jetbrains.buildServer.messages.serviceMessages.* -import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClient import org.junit.Test class Complex : TCServiceMessagesClientTest() { @Test fun testComplexJs() { - nameOfRootSuiteToReplace = "js" + rootNodeName = "jsTest" assertEvents( """ -STARTED SUITE root // root - STARTED SUITE js // root.js - STARTED SUITE MyTest // root.js.MyTest - STARTED TEST displayName: myTest1, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest1 // root.js.MyTest.my.company.product.MyTest.myTest1 - COMPLETED SUCCESS // root.js.MyTest.my.company.product.MyTest.myTest1 - STARTED TEST displayName: myTest2, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest2 // root.js.MyTest.my.company.product.MyTest.myTest2 - FAILURE null // root.js.MyTest.my.company.product.MyTest.myTest2 - COMPLETED FAILURE // root.js.MyTest.my.company.product.MyTest.myTest2 - STARTED TEST displayName: myTest3, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest3 // root.js.MyTest.my.company.product.MyTest.myTest3 - COMPLETED SKIPPED // root.js.MyTest.my.company.product.MyTest.myTest3 - STARTED SUITE MyTestNested // root.js.MyTest.MyTestNested - STARTED TEST displayName: myTest4, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest4 // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest4 - COMPLETED SUCCESS // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest4 - STARTED TEST displayName: myTest5, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest5 // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 - FAILURE null // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 - COMPLETED FAILURE // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 - STARTED TEST displayName: myTest6, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest6 // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest6 - COMPLETED SKIPPED // root.js.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest6 - STARTED SUITE MyTestNestedNested // root.js.MyTest.MyTestNested.MyTestNestedNested - STARTED TEST displayName: myTest7, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest7 // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest7 - COMPLETED SUCCESS // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest7 - STARTED TEST displayName: myTest8, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest8 // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 - FAILURE null // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 - COMPLETED FAILURE // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 - STARTED TEST displayName: myTest9, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest9 // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest9 - COMPLETED SKIPPED // root.js.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest9 - COMPLETED FAILURE // root.js.MyTest.MyTestNested.MyTestNestedNested - COMPLETED FAILURE // root.js.MyTest.MyTestNested - COMPLETED FAILURE // root.js.MyTest - STARTED SUITE MyTest2 // root.js.MyTest2 - STARTED TEST displayName: myTest10, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest10 // root.js.MyTest2.my.company.product.MyTest2.myTest10 - COMPLETED SUCCESS // root.js.MyTest2.my.company.product.MyTest2.myTest10 - STARTED TEST displayName: myTest11, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest11 // root.js.MyTest2.my.company.product.MyTest2.myTest11 - FAILURE null // root.js.MyTest2.my.company.product.MyTest2.myTest11 - COMPLETED FAILURE // root.js.MyTest2.my.company.product.MyTest2.myTest11 - STARTED TEST displayName: myTest12, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest12 // root.js.MyTest2.my.company.product.MyTest2.myTest12 - COMPLETED SKIPPED // root.js.MyTest2.my.company.product.MyTest2.myTest12 - STARTED SUITE MyTest2Nested // root.js.MyTest2.MyTest2Nested - STARTED TEST displayName: myTest13, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest13 // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest13 - COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest13 - STARTED TEST displayName: myTest14, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest14 // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest14 - COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest14 - STARTED TEST displayName: myTest15, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest15 // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest15 - COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest15 - STARTED SUITE MyTest2NestedNested // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested - STARTED TEST displayName: myTest16, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest16 // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest16 - COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest16 - STARTED TEST displayName: myTest17, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest17 // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest17 - COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest17 - STARTED TEST displayName: myTest18, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest18 // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest18 - COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest18 - COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested.MyTest2NestedNested - COMPLETED SKIPPED // root.js.MyTest2.MyTest2Nested - COMPLETED FAILURE // root.js.MyTest2 - COMPLETED FAILURE // root.js -COMPLETED FAILURE // root - """ +STARTED SUITE jsTest // jsTest + STARTED SUITE jsTest.my.company.product.MyTest // jsTest//my/company/product/MyTest + STARTED TEST displayName: myTest1, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest1 // jsTest//my/company/product/MyTest/my.company.product.MyTest.myTest1 + COMPLETED SUCCESS // jsTest//my/company/product/MyTest/my.company.product.MyTest.myTest1 + STARTED TEST displayName: myTest2, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest2 // jsTest//my/company/product/MyTest/my.company.product.MyTest.myTest2 + FAILURE null // jsTest//my/company/product/MyTest/my.company.product.MyTest.myTest2 + COMPLETED FAILURE // jsTest//my/company/product/MyTest/my.company.product.MyTest.myTest2 + STARTED TEST displayName: myTest3, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest3 // jsTest//my/company/product/MyTest/my.company.product.MyTest.myTest3 + COMPLETED SKIPPED // jsTest//my/company/product/MyTest/my.company.product.MyTest.myTest3 + STARTED SUITE jsTest.my.company.product.MyTest.MyTestNested // jsTest//my/company/product/MyTest/MyTestNested + STARTED TEST displayName: myTest4, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest4 // jsTest//my/company/product/MyTest/MyTestNested/my.company.product.MyTest.MyTestNested.myTest4 + COMPLETED SUCCESS // jsTest//my/company/product/MyTest/MyTestNested/my.company.product.MyTest.MyTestNested.myTest4 + STARTED TEST displayName: myTest5, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest5 // jsTest//my/company/product/MyTest/MyTestNested/my.company.product.MyTest.MyTestNested.myTest5 + FAILURE null // jsTest//my/company/product/MyTest/MyTestNested/my.company.product.MyTest.MyTestNested.myTest5 + COMPLETED FAILURE // jsTest//my/company/product/MyTest/MyTestNested/my.company.product.MyTest.MyTestNested.myTest5 + STARTED TEST displayName: myTest6, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest6 // jsTest//my/company/product/MyTest/MyTestNested/my.company.product.MyTest.MyTestNested.myTest6 + COMPLETED SKIPPED // jsTest//my/company/product/MyTest/MyTestNested/my.company.product.MyTest.MyTestNested.myTest6 + STARTED SUITE jsTest.my.company.product.MyTest.MyTestNested.MyTestNestedNested // jsTest//my/company/product/MyTest/MyTestNested/MyTestNestedNested + STARTED TEST displayName: myTest7, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest7 // jsTest//my/company/product/MyTest/MyTestNested/MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest7 + COMPLETED SUCCESS // jsTest//my/company/product/MyTest/MyTestNested/MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest7 + STARTED TEST displayName: myTest8, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest8 // jsTest//my/company/product/MyTest/MyTestNested/MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest8 + FAILURE null // jsTest//my/company/product/MyTest/MyTestNested/MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest8 + COMPLETED FAILURE // jsTest//my/company/product/MyTest/MyTestNested/MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest8 + STARTED TEST displayName: myTest9, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest9 // jsTest//my/company/product/MyTest/MyTestNested/MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest9 + COMPLETED SKIPPED // jsTest//my/company/product/MyTest/MyTestNested/MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest9 + COMPLETED FAILURE // jsTest//my/company/product/MyTest/MyTestNested/MyTestNestedNested + COMPLETED FAILURE // jsTest//my/company/product/MyTest/MyTestNested + COMPLETED FAILURE // jsTest//my/company/product/MyTest + STARTED SUITE jsTest.my.company.product.MyTest2 // jsTest//my/company/product/MyTest2 + STARTED TEST displayName: myTest10, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest10 // jsTest//my/company/product/MyTest2/my.company.product.MyTest2.myTest10 + COMPLETED SUCCESS // jsTest//my/company/product/MyTest2/my.company.product.MyTest2.myTest10 + STARTED TEST displayName: myTest11, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest11 // jsTest//my/company/product/MyTest2/my.company.product.MyTest2.myTest11 + FAILURE null // jsTest//my/company/product/MyTest2/my.company.product.MyTest2.myTest11 + COMPLETED FAILURE // jsTest//my/company/product/MyTest2/my.company.product.MyTest2.myTest11 + STARTED TEST displayName: myTest12, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest12 // jsTest//my/company/product/MyTest2/my.company.product.MyTest2.myTest12 + COMPLETED SKIPPED // jsTest//my/company/product/MyTest2/my.company.product.MyTest2.myTest12 + STARTED SUITE jsTest.my.company.product.MyTest2.MyTest2Nested // jsTest//my/company/product/MyTest2/MyTest2Nested + STARTED TEST displayName: myTest13, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest13 // jsTest//my/company/product/MyTest2/MyTest2Nested/my.company.product.MyTest2.MyTestNested.myTest13 + COMPLETED SKIPPED // jsTest//my/company/product/MyTest2/MyTest2Nested/my.company.product.MyTest2.MyTestNested.myTest13 + STARTED TEST displayName: myTest14, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest14 // jsTest//my/company/product/MyTest2/MyTest2Nested/my.company.product.MyTest2.MyTestNested.myTest14 + COMPLETED SKIPPED // jsTest//my/company/product/MyTest2/MyTest2Nested/my.company.product.MyTest2.MyTestNested.myTest14 + STARTED TEST displayName: myTest15, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest15 // jsTest//my/company/product/MyTest2/MyTest2Nested/my.company.product.MyTest2.MyTestNested.myTest15 + COMPLETED SKIPPED // jsTest//my/company/product/MyTest2/MyTest2Nested/my.company.product.MyTest2.MyTestNested.myTest15 + STARTED SUITE jsTest.my.company.product.MyTest2.MyTest2Nested.MyTest2NestedNested // jsTest//my/company/product/MyTest2/MyTest2Nested/MyTest2NestedNested + STARTED TEST displayName: myTest16, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest16 // jsTest//my/company/product/MyTest2/MyTest2Nested/MyTest2NestedNested/my.company.product.MyTest2.MyTest2NestedNested.myTest16 + COMPLETED SKIPPED // jsTest//my/company/product/MyTest2/MyTest2Nested/MyTest2NestedNested/my.company.product.MyTest2.MyTest2NestedNested.myTest16 + STARTED TEST displayName: myTest17, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest17 // jsTest//my/company/product/MyTest2/MyTest2Nested/MyTest2NestedNested/my.company.product.MyTest2.MyTest2NestedNested.myTest17 + COMPLETED SKIPPED // jsTest//my/company/product/MyTest2/MyTest2Nested/MyTest2NestedNested/my.company.product.MyTest2.MyTest2NestedNested.myTest17 + STARTED TEST displayName: myTest18, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest18 // jsTest//my/company/product/MyTest2/MyTest2Nested/MyTest2NestedNested/my.company.product.MyTest2.MyTest2NestedNested.myTest18 + COMPLETED SKIPPED // jsTest//my/company/product/MyTest2/MyTest2Nested/MyTest2NestedNested/my.company.product.MyTest2.MyTest2NestedNested.myTest18 + COMPLETED SKIPPED // jsTest//my/company/product/MyTest2/MyTest2Nested/MyTest2NestedNested + COMPLETED SKIPPED // jsTest//my/company/product/MyTest2/MyTest2Nested + COMPLETED FAILURE // jsTest//my/company/product/MyTest2 +COMPLETED FAILURE // jsTest + """.trimIndent() ) { serviceMessage(TestSuiteStarted("")) - complexFixture() + serviceMessage(TestSuiteStarted("my")) + serviceMessage(TestSuiteStarted("company")) + serviceMessage(TestSuiteStarted("product")) + + /**/serviceMessage(TestSuiteStarted("MyTest")) + /**/serviceMessage(TestStarted("my.company.product.MyTest.myTest1", false, null)) + /**/serviceMessage(TestFinished("my.company.product.MyTest.myTest1", 1)) + /**/serviceMessage(TestStarted("my.company.product.MyTest.myTest2", false, null)) + /**/serviceMessage(TestFailed("my.company.product.MyTest.myTest2", null)) + /**/serviceMessage(TestFinished("my.company.product.MyTest.myTest2", 1)) + /**/serviceMessage(TestIgnored("my.company.product.MyTest.myTest3", "")) + /**/serviceMessage(TestSuiteStarted("MyTestNested")) + /**//**/serviceMessage(TestStarted("my.company.product.MyTest.MyTestNested.myTest4", false, null)) + /**//**/serviceMessage(TestFinished("my.company.product.MyTest.MyTestNested.myTest4", 1)) + /**//**/serviceMessage(TestStarted("my.company.product.MyTest.MyTestNested.myTest5", false, null)) + /**//**/serviceMessage(TestFailed("my.company.product.MyTest.MyTestNested.myTest5", null)) + /**//**/serviceMessage(TestFinished("my.company.product.MyTest.MyTestNested.myTest5", 1)) + /**//**/serviceMessage(TestIgnored("my.company.product.MyTest.MyTestNested.myTest6", "")) + /**//**/serviceMessage(TestSuiteStarted("MyTestNestedNested")) + /**//**//**/serviceMessage(TestStarted("my.company.product.MyTest.MyTestNestedNested.myTest7", false, null)) + /**//**//**/serviceMessage(TestFinished("my.company.product.MyTest.MyTestNestedNested.myTest7", 1)) + /**//**//**/serviceMessage(TestStarted("my.company.product.MyTest.MyTestNestedNested.myTest8", false, null)) + /**//**//**/serviceMessage(TestFailed("my.company.product.MyTest.MyTestNestedNested.myTest8", null)) + /**//**//**/serviceMessage(TestFinished("my.company.product.MyTest.MyTestNestedNested.myTest8", 1)) + /**//**//**/serviceMessage(TestIgnored("my.company.product.MyTest.MyTestNestedNested.myTest9", "")) + /**//**/serviceMessage(TestSuiteFinished("MyTestNestedNested")) + /**/serviceMessage(TestSuiteFinished("MyTestNested")) + serviceMessage(TestSuiteFinished("MyTest")) + + serviceMessage(TestSuiteStarted("MyTest2")) + /**/serviceMessage(TestStarted("my.company.product.MyTest2.myTest10", false, null)) + /**/serviceMessage(TestFinished("my.company.product.MyTest2.myTest10", 1)) + /**/serviceMessage(TestStarted("my.company.product.MyTest2.myTest11", false, null)) + /**/serviceMessage(TestFailed("my.company.product.MyTest2.myTest11", null)) + /**/serviceMessage(TestFinished("my.company.product.MyTest2.myTest11", 1)) + /**/serviceMessage(TestIgnored("my.company.product.MyTest2.myTest12", "")) + /**/serviceMessage(TestSuiteStarted("MyTest2Nested")) + /**//**/serviceMessage(TestIgnored("my.company.product.MyTest2.MyTestNested.myTest13", "")) + /**//**/serviceMessage(TestIgnored("my.company.product.MyTest2.MyTestNested.myTest14", "")) + /**//**/serviceMessage(TestIgnored("my.company.product.MyTest2.MyTestNested.myTest15", "")) + /**//**/serviceMessage(TestSuiteStarted("MyTest2NestedNested")) + /**//**//**/serviceMessage(TestIgnored("my.company.product.MyTest2.MyTest2NestedNested.myTest16", "")) + /**//**//**/serviceMessage(TestIgnored("my.company.product.MyTest2.MyTest2NestedNested.myTest17", "")) + /**//**//**/serviceMessage(TestIgnored("my.company.product.MyTest2.MyTest2NestedNested.myTest18", "")) + /**//**/serviceMessage(TestSuiteFinished("MyTest2NestedNested")) + /**/serviceMessage(TestSuiteFinished("MyTest2Nested")) + serviceMessage(TestSuiteFinished("MyTest2")) + + serviceMessage(TestSuiteFinished("product")) + serviceMessage(TestSuiteFinished("company")) + serviceMessage(TestSuiteFinished("my")) serviceMessage(TestSuiteFinished("")) } } @Test fun testComplexIos() { - nameOfRootSuiteToAppend = "ios" + rootNodeName = "iosTest" assertEvents( """ -STARTED SUITE root // root - STARTED SUITE ios // root.ios - STARTED SUITE MyTest // root.ios.MyTest - STARTED TEST displayName: myTest1, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest1 // root.ios.MyTest.my.company.product.MyTest.myTest1 - COMPLETED SUCCESS // root.ios.MyTest.my.company.product.MyTest.myTest1 - STARTED TEST displayName: myTest2, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest2 // root.ios.MyTest.my.company.product.MyTest.myTest2 - FAILURE null // root.ios.MyTest.my.company.product.MyTest.myTest2 - COMPLETED FAILURE // root.ios.MyTest.my.company.product.MyTest.myTest2 - STARTED TEST displayName: myTest3, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest3 // root.ios.MyTest.my.company.product.MyTest.myTest3 - COMPLETED SKIPPED // root.ios.MyTest.my.company.product.MyTest.myTest3 - STARTED SUITE MyTestNested // root.ios.MyTest.MyTestNested - STARTED TEST displayName: myTest4, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest4 // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest4 - COMPLETED SUCCESS // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest4 - STARTED TEST displayName: myTest5, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest5 // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 - FAILURE null // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 - COMPLETED FAILURE // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest5 - STARTED TEST displayName: myTest6, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest6 // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest6 - COMPLETED SKIPPED // root.ios.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.myTest6 - STARTED SUITE MyTestNestedNested // root.ios.MyTest.MyTestNested.MyTestNestedNested - STARTED TEST displayName: myTest7, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest7 // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest7 - COMPLETED SUCCESS // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest7 - STARTED TEST displayName: myTest8, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest8 // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 - FAILURE null // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 - COMPLETED FAILURE // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest8 - STARTED TEST displayName: myTest9, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest9 // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest9 - COMPLETED SKIPPED // root.ios.MyTest.MyTestNested.MyTestNestedNested.my.company.product.MyTest.MyTestNestedNested.myTest9 - COMPLETED FAILURE // root.ios.MyTest.MyTestNested.MyTestNestedNested - COMPLETED FAILURE // root.ios.MyTest.MyTestNested - COMPLETED FAILURE // root.ios.MyTest - STARTED SUITE MyTest2 // root.ios.MyTest2 - STARTED TEST displayName: myTest10, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest10 // root.ios.MyTest2.my.company.product.MyTest2.myTest10 - COMPLETED SUCCESS // root.ios.MyTest2.my.company.product.MyTest2.myTest10 - STARTED TEST displayName: myTest11, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest11 // root.ios.MyTest2.my.company.product.MyTest2.myTest11 - FAILURE null // root.ios.MyTest2.my.company.product.MyTest2.myTest11 - COMPLETED FAILURE // root.ios.MyTest2.my.company.product.MyTest2.myTest11 - STARTED TEST displayName: myTest12, classDisplayName: MyTest2, className: my.company.product.MyTest2, name: myTest12 // root.ios.MyTest2.my.company.product.MyTest2.myTest12 - COMPLETED SKIPPED // root.ios.MyTest2.my.company.product.MyTest2.myTest12 - STARTED SUITE MyTest2Nested // root.ios.MyTest2.MyTest2Nested - STARTED TEST displayName: myTest13, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest13 // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest13 - COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest13 - STARTED TEST displayName: myTest14, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest14 // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest14 - COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest14 - STARTED TEST displayName: myTest15, classDisplayName: MyTestNested, className: my.company.product.MyTest2.MyTestNested, name: myTest15 // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest15 - COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.my.company.product.MyTest2.MyTestNested.myTest15 - STARTED SUITE MyTest2NestedNested // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested - STARTED TEST displayName: myTest16, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest16 // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest16 - COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest16 - STARTED TEST displayName: myTest17, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest17 // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest17 - COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest17 - STARTED TEST displayName: myTest18, classDisplayName: MyTest2NestedNested, className: my.company.product.MyTest2.MyTest2NestedNested, name: myTest18 // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest18 - COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested.my.company.product.MyTest2.MyTest2NestedNested.myTest18 - COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested.MyTest2NestedNested - COMPLETED SKIPPED // root.ios.MyTest2.MyTest2Nested - COMPLETED FAILURE // root.ios.MyTest2 - COMPLETED FAILURE // root.ios -COMPLETED FAILURE // root - """ +STARTED SUITE iosTest // iosTest + STARTED SUITE iosTest.my.company.product.MyTest // iosTest.my.company.product.MyTest + STARTED TEST displayName: myTest1, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest1 // iosTest/my.company.product.MyTest/my.company.product.MyTest.myTest1 + COMPLETED SUCCESS // iosTest/my.company.product.MyTest/my.company.product.MyTest.myTest1 + STARTED TEST displayName: myTest2, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest2 // iosTest/my.company.product.MyTest/my.company.product.MyTest.myTest2 + FAILURE null // iosTest/my.company.product.MyTest/my.company.product.MyTest.myTest2 + COMPLETED FAILURE // iosTest/my.company.product.MyTest/my.company.product.MyTest.myTest2 + STARTED TEST displayName: myTest3, classDisplayName: MyTest, className: my.company.product.MyTest, name: myTest3 // iosTest/my.company.product.MyTest/my.company.product.MyTest.myTest3 + COMPLETED SKIPPED // iosTest/my.company.product.MyTest/my.company.product.MyTest.myTest3 + STARTED SUITE iosTest.my.company.product.MyTest.MyTestNested // iosTest/my.company.product.MyTest.MyTestNested + STARTED TEST displayName: myTest4, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest4 // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.myTest4 + COMPLETED SUCCESS // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.myTest4 + STARTED TEST displayName: myTest5, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest5 // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.myTest5 + FAILURE null // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.myTest5 + COMPLETED FAILURE // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.myTest5 + STARTED TEST displayName: myTest6, classDisplayName: MyTestNested, className: my.company.product.MyTest.MyTestNested, name: myTest6 // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.myTest6 + COMPLETED SKIPPED // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.myTest6 + STARTED SUITE iosTest.my.company.product.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.MyTestNestedNested // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.MyTestNestedNested + STARTED TEST displayName: myTest7, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest7 // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest7 + COMPLETED SUCCESS // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest7 + STARTED TEST displayName: myTest8, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest8 // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest8 + FAILURE null // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest8 + COMPLETED FAILURE // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest8 + STARTED TEST displayName: myTest9, classDisplayName: MyTestNestedNested, className: my.company.product.MyTest.MyTestNestedNested, name: myTest9 // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest9 + COMPLETED SKIPPED // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested/my.company.product.MyTest.MyTestNested.MyTestNestedNested/my.company.product.MyTest.MyTestNestedNested.myTest9 + COMPLETED FAILURE // iosTest/my.company.product.MyTest/my.company.product.MyTest.MyTestNested.my.company.product.MyTest.MyTestNested.MyTestNestedNested + COMPLETED FAILURE // iosTest/my.company.product.MyTest.MyTestNested + COMPLETED FAILURE // iosTest.my.company.product.MyTest +COMPLETED FAILURE // iosTest + """.trimIndent() ) { - complexFixture() + serviceMessage(TestSuiteStarted("my.company.product.MyTest")) + /**/serviceMessage(TestStarted("my.company.product.MyTest.myTest1", false, null)) + /**/serviceMessage(TestFinished("my.company.product.MyTest.myTest1", 1)) + /**/serviceMessage(TestStarted("my.company.product.MyTest.myTest2", false, null)) + /**/serviceMessage(TestFailed("my.company.product.MyTest.myTest2", null)) + /**/serviceMessage(TestFinished("my.company.product.MyTest.myTest2", 1)) + /**/serviceMessage(TestIgnored("my.company.product.MyTest.myTest3", "")) + /**/serviceMessage(TestSuiteStarted("my.company.product.MyTest.MyTestNested")) + /**//**/serviceMessage(TestStarted("my.company.product.MyTest.MyTestNested.myTest4", false, null)) + /**//**/serviceMessage(TestFinished("my.company.product.MyTest.MyTestNested.myTest4", 1)) + /**//**/serviceMessage(TestStarted("my.company.product.MyTest.MyTestNested.myTest5", false, null)) + /**//**/serviceMessage(TestFailed("my.company.product.MyTest.MyTestNested.myTest5", null)) + /**//**/serviceMessage(TestFinished("my.company.product.MyTest.MyTestNested.myTest5", 1)) + /**//**/serviceMessage(TestIgnored("my.company.product.MyTest.MyTestNested.myTest6", "")) + /**//**/serviceMessage(TestSuiteStarted("my.company.product.MyTest.MyTestNested.MyTestNestedNested")) + /**//**//**/serviceMessage(TestStarted("my.company.product.MyTest.MyTestNestedNested.myTest7", false, null)) + /**//**//**/serviceMessage(TestFinished("my.company.product.MyTest.MyTestNestedNested.myTest7", 1)) + /**//**//**/serviceMessage(TestStarted("my.company.product.MyTest.MyTestNestedNested.myTest8", false, null)) + /**//**//**/serviceMessage(TestFailed("my.company.product.MyTest.MyTestNestedNested.myTest8", null)) + /**//**//**/serviceMessage(TestFinished("my.company.product.MyTest.MyTestNestedNested.myTest8", 1)) + /**//**//**/serviceMessage(TestIgnored("my.company.product.MyTest.MyTestNestedNested.myTest9", "")) + /**//**/serviceMessage(TestSuiteFinished("my.company.product.MyTest.MyTestNested.MyTestNestedNested")) + /**/serviceMessage(TestSuiteFinished("my.company.product.MyTest.MyTestNested")) + serviceMessage(TestSuiteFinished("my.company.product.MyTest")) } } - private fun TCServiceMessagesClient.complexFixture() { - serviceMessage(TestSuiteStarted("MyTest")) - - serviceMessage(TestStarted("my.company.product.MyTest.myTest1", false, null)) - serviceMessage(TestFinished("my.company.product.MyTest.myTest1", 1)) - serviceMessage(TestStarted("my.company.product.MyTest.myTest2", false, null)) - serviceMessage(TestFailed("my.company.product.MyTest.myTest2", null)) - serviceMessage(TestFinished("my.company.product.MyTest.myTest2", 1)) - serviceMessage(TestIgnored("my.company.product.MyTest.myTest3", "")) - - serviceMessage(TestSuiteStarted("MyTestNested")) - serviceMessage(TestStarted("my.company.product.MyTest.MyTestNested.myTest4", false, null)) - serviceMessage(TestFinished("my.company.product.MyTest.MyTestNested.myTest4", 1)) - serviceMessage(TestStarted("my.company.product.MyTest.MyTestNested.myTest5", false, null)) - serviceMessage(TestFailed("my.company.product.MyTest.MyTestNested.myTest5", null)) - serviceMessage(TestFinished("my.company.product.MyTest.MyTestNested.myTest5", 1)) - serviceMessage(TestIgnored("my.company.product.MyTest.MyTestNested.myTest6", "")) - - serviceMessage(TestSuiteStarted("MyTestNestedNested")) - serviceMessage(TestStarted("my.company.product.MyTest.MyTestNestedNested.myTest7", false, null)) - serviceMessage(TestFinished("my.company.product.MyTest.MyTestNestedNested.myTest7", 1)) - serviceMessage(TestStarted("my.company.product.MyTest.MyTestNestedNested.myTest8", false, null)) - serviceMessage(TestFailed("my.company.product.MyTest.MyTestNestedNested.myTest8", null)) - serviceMessage(TestFinished("my.company.product.MyTest.MyTestNestedNested.myTest8", 1)) - serviceMessage(TestIgnored("my.company.product.MyTest.MyTestNestedNested.myTest9", "")) - serviceMessage(TestSuiteFinished("MyTestNestedNested")) - - serviceMessage(TestSuiteFinished("MyTestNested")) - - serviceMessage(TestSuiteFinished("MyTest")) - - serviceMessage(TestSuiteStarted("MyTest2")) - - serviceMessage(TestStarted("my.company.product.MyTest2.myTest10", false, null)) - serviceMessage(TestFinished("my.company.product.MyTest2.myTest10", 1)) - serviceMessage(TestStarted("my.company.product.MyTest2.myTest11", false, null)) - serviceMessage(TestFailed("my.company.product.MyTest2.myTest11", null)) - serviceMessage(TestFinished("my.company.product.MyTest2.myTest11", 1)) - serviceMessage(TestIgnored("my.company.product.MyTest2.myTest12", "")) - - serviceMessage(TestSuiteStarted("MyTest2Nested")) - serviceMessage(TestIgnored("my.company.product.MyTest2.MyTestNested.myTest13", "")) - serviceMessage(TestIgnored("my.company.product.MyTest2.MyTestNested.myTest14", "")) - serviceMessage(TestIgnored("my.company.product.MyTest2.MyTestNested.myTest15", "")) - - serviceMessage(TestSuiteStarted("MyTest2NestedNested")) - serviceMessage(TestIgnored("my.company.product.MyTest2.MyTest2NestedNested.myTest16", "")) - serviceMessage(TestIgnored("my.company.product.MyTest2.MyTest2NestedNested.myTest17", "")) - serviceMessage(TestIgnored("my.company.product.MyTest2.MyTest2NestedNested.myTest18", "")) - serviceMessage(TestSuiteFinished("MyTest2NestedNested")) - - serviceMessage(TestSuiteFinished("MyTest2Nested")) - - serviceMessage(TestSuiteFinished("MyTest2")) - } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/ReplaceRoot.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/ReplaceRoot.kt deleted file mode 100644 index 2b7362f0731..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/ReplaceRoot.kt +++ /dev/null @@ -1,66 +0,0 @@ -package org.jetbrains.kotlin.gradle.internal.testing.tcsmc - -import jetbrains.buildServer.messages.serviceMessages.TestFinished -import jetbrains.buildServer.messages.serviceMessages.TestStarted -import jetbrains.buildServer.messages.serviceMessages.TestSuiteFinished -import jetbrains.buildServer.messages.serviceMessages.TestSuiteStarted -import org.junit.Test - -class ReplaceRoot : TCServiceMessagesClientTest() { - @Test - fun replaceRootWithFqn() { - nameOfRootSuiteToReplace = "js" - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE js // root.js - STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: my.company.product.MyTest, name: myMethod // root.js.my.company.product.MyTest.myMethod - COMPLETED SUCCESS // root.js.my.company.product.MyTest.myMethod - COMPLETED SUCCESS // root.js -COMPLETED SUCCESS // root - """) { - serviceMessage(TestSuiteStarted("")) - serviceMessage(TestStarted("my.company.product.MyTest.myMethod", false, null)) - serviceMessage(TestFinished("my.company.product.MyTest.myMethod", 1)) - serviceMessage(TestSuiteFinished("")) - } - } - - @Test - fun replaceRootWithClassName() { - nameOfRootSuiteToReplace = "js" - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE js // root.js - STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: MyTest, name: myMethod // root.js.MyTest.myMethod - COMPLETED SUCCESS // root.js.MyTest.myMethod - COMPLETED SUCCESS // root.js -COMPLETED SUCCESS // root - """) { - serviceMessage(TestSuiteStarted("")) - serviceMessage(TestStarted("MyTest.myMethod", false, null)) - serviceMessage(TestFinished("MyTest.myMethod", 1)) - serviceMessage(TestSuiteFinished("")) - } - } - - @Test - fun replaceRoot() { - nameOfRootSuiteToReplace = "js" - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE js // root.js - STARTED TEST displayName: myMethod, classDisplayName: js, className: js, name: myMethod // root.js.myMethod - COMPLETED SUCCESS // root.js.myMethod - COMPLETED SUCCESS // root.js -COMPLETED SUCCESS // root - """) { - serviceMessage(TestSuiteStarted("")) - serviceMessage(TestStarted("myMethod", false, null)) - serviceMessage(TestFinished("myMethod", 1)) - serviceMessage(TestSuiteFinished("")) - } - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/SkipRoot.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/SkipRoot.kt deleted file mode 100644 index 2ad631cea44..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/SkipRoot.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.jetbrains.kotlin.gradle.internal.testing.tcsmc - -import jetbrains.buildServer.messages.serviceMessages.TestFinished -import jetbrains.buildServer.messages.serviceMessages.TestStarted -import jetbrains.buildServer.messages.serviceMessages.TestSuiteFinished -import jetbrains.buildServer.messages.serviceMessages.TestSuiteStarted -import org.junit.Test - -class SkipRoot: TCServiceMessagesClientTest() { - @Test - fun testSkipRoot() { - skipRoots = true - - assertEvents(""" -STARTED SUITE root // root - STARTED SUITE MyTest // root.MyTest - STARTED TEST displayName: myMethod, classDisplayName: MyTest, className: MyTest, name: myMethod // root.MyTest.myMethod - COMPLETED SUCCESS // root.MyTest.myMethod - COMPLETED SUCCESS // root.MyTest -COMPLETED SUCCESS // root - """) { - serviceMessage(TestSuiteStarted("")) - serviceMessage(TestSuiteStarted("MyTest")) - serviceMessage(TestStarted("myMethod", false, null)) - serviceMessage(TestFinished("myMethod", 1)) - serviceMessage(TestSuiteFinished("MyTest")) - serviceMessage(TestSuiteFinished("")) - } - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TCServiceMessagesClientTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TCServiceMessagesClientTest.kt index e61b2a765d6..bc4d4ea1a83 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TCServiceMessagesClientTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/internal/testing/tcsmc/TCServiceMessagesClientTest.kt @@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory import kotlin.test.assertEquals open class TCServiceMessagesClientTest { + protected var rootNodeName: String = "root" protected var nameOfRootSuiteToAppend: String? = null protected var nameOfRootSuiteToReplace: String? = null protected var nameOfLeafTestToAppend: String? = null @@ -34,11 +35,7 @@ open class TCServiceMessagesClientTest { return TCServiceMessagesClient( results, TCServiceMessagesClientSettings( - "root", - nameOfRootSuiteToAppend, - nameOfRootSuiteToReplace, - nameOfLeafTestToAppend, - skipRoots, + rootNodeName, treatFailedTestOutputAsStacktrace ), LoggerFactory.getLogger("test")