diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/KotlinTestTasksResolver.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/KotlinTestTasksResolver.kt new file mode 100644 index 00000000000..65b0e66922f --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/KotlinTestTasksResolver.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.idea.gradle.testing + +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.externalSystem.model.DataNode +import com.intellij.openapi.externalSystem.model.ProjectKeys +import com.intellij.openapi.externalSystem.model.project.ModuleData +import com.intellij.openapi.externalSystem.model.project.ProjectData +import com.intellij.openapi.externalSystem.model.task.TaskData +import com.intellij.openapi.externalSystem.util.Order +import com.intellij.openapi.util.registry.Registry +import com.intellij.util.Consumer +import org.gradle.tooling.model.idea.IdeaModule +import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel +import org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder +import org.jetbrains.kotlin.idea.configuration.getMppModel +import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension + +@Order(Int.MIN_VALUE) +open class KotlinTestTasksResolver : AbstractProjectResolverExtension() { + companion object { + private const val ENABLED_REGISTRY_KEY = "kotlin.gradle.testing.enabled" + } + + private val LOG by lazy { Logger.getInstance(KotlinTestTasksResolver::class.java) } + + override fun getToolingExtensionsClasses(): Set> { + return setOf(KotlinMPPGradleModelBuilder::class.java, Unit::class.java) + } + + override fun populateModuleTasks( + gradleModule: IdeaModule, + ideModule: DataNode, + ideProject: DataNode + ): MutableCollection { + if (!Registry.`is`(ENABLED_REGISTRY_KEY)) + return super.populateModuleTasks(gradleModule, ideModule, ideProject) + + val mppModel = resolverCtx.getMppModel(gradleModule) + ?: return super.populateModuleTasks(gradleModule, ideModule, ideProject) + + return postprocessTaskData(mppModel, ideModule, nextResolver.populateModuleTasks(gradleModule, ideModule, ideProject)) + } + + private fun postprocessTaskData( + mppModel: KotlinMPPGradleModel, + ideModule: DataNode, + originalTaskData: MutableCollection + ): MutableCollection { + val testTaskNames = mutableSetOf().apply { + mppModel.targets.forEach { target -> + target.testTasks.forEach { testTaskModel -> + add(testTaskModel.taskName) + } + } + } + + fun buildNewTaskDataMarkedAsTest(original: TaskData): TaskData = + TaskData(original.owner, original.name, original.linkedExternalProjectPath, original.description).apply { + group = original.group + type = original.type + isInherited = original.isInherited + + isTest = true + } + + val replacementMap: Map = mutableMapOf().apply { + originalTaskData.forEach { + if (it.name in testTaskNames && !it.isTest) { + put(it, buildNewTaskDataMarkedAsTest(it)) + } + } + } + + ideModule.children.filter { it.data in replacementMap }.forEach { it.clear(true) } + replacementMap.values.forEach { ideModule.createChild(ProjectKeys.TASK, it) } + + return originalTaskData.mapTo(arrayListOf()) { replacementMap[it] ?: it } + } + + override fun enhanceTaskProcessing(taskNames: MutableList, jvmAgentSetup: String?, initScriptConsumer: Consumer) { + if (!Registry.`is`(ENABLED_REGISTRY_KEY)) + return + + try { + val testLoggerScript = javaClass + .getResourceAsStream("/org/jetbrains/kotlin/idea/gradle/testing/KotlinMppTestLogger.groovy") + .bufferedReader() + .readText() + + initScriptConsumer.consume(testLoggerScript) + } catch (e: Exception) { + LOG.error(e) + } + } +} \ No newline at end of file diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/common/KotlinMultiplatformCommonTestClassGradleConfigurationProducer.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/common/KotlinMultiplatformCommonTestClassGradleConfigurationProducer.kt new file mode 100644 index 00000000000..7f246ae2e7e --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/common/KotlinMultiplatformCommonTestClassGradleConfigurationProducer.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.idea.gradle.testing.common + +import com.intellij.openapi.module.Module +import org.jetbrains.kotlin.idea.run.AbstractKotlinMultiplatformTestClassGradleConfigurationProducer +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.isCommon + +class KotlinMultiplatformCommonTestClassGradleConfigurationProducer : AbstractKotlinMultiplatformTestClassGradleConfigurationProducer() { + override fun isApplicable(module: Module, platform: TargetPlatform) = platform.isCommon() +} \ No newline at end of file diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/common/KotlinMultiplatformCommonTestMethodGradleConfigurationProducer.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/common/KotlinMultiplatformCommonTestMethodGradleConfigurationProducer.kt new file mode 100644 index 00000000000..b2405f5e5bd --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/common/KotlinMultiplatformCommonTestMethodGradleConfigurationProducer.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.idea.gradle.testing.common + +import com.intellij.openapi.module.Module +import org.jetbrains.kotlin.idea.run.AbstractKotlinMultiplatformTestMethodGradleConfigurationProducer +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.isCommon + +class KotlinMultiplatformCommonTestMethodGradleConfigurationProducer : AbstractKotlinMultiplatformTestMethodGradleConfigurationProducer() { + override fun isApplicable(module: Module, platform: TargetPlatform) = platform.isCommon() +} \ No newline at end of file diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/js/KotlinMultiplatformJsTestClassGradleConfigurationProducer.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/js/KotlinMultiplatformJsTestClassGradleConfigurationProducer.kt new file mode 100644 index 00000000000..d80cb3e386b --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/js/KotlinMultiplatformJsTestClassGradleConfigurationProducer.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.idea.gradle.testing.js + +import com.intellij.execution.actions.ConfigurationContext +import com.intellij.openapi.module.Module +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationDataProvider +import org.jetbrains.kotlin.idea.run.AbstractKotlinMultiplatformTestClassGradleConfigurationProducer +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.js.isJs + +class KotlinMultiplatformJsTestClassGradleConfigurationProducer + : AbstractKotlinMultiplatformTestClassGradleConfigurationProducer(), KotlinJSRunConfigurationDataProvider +{ + override val isForTests: Boolean get() = true + override fun isApplicable(module: Module, platform: TargetPlatform) = platform.isJs() + + override fun getConfigurationData(context: ConfigurationContext) = Unit + override val hasTestFramework get() = true +} diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/js/KotlinMultiplatformJsTestMethodGradleConfigurationProducer.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/js/KotlinMultiplatformJsTestMethodGradleConfigurationProducer.kt new file mode 100644 index 00000000000..55227c37c54 --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/js/KotlinMultiplatformJsTestMethodGradleConfigurationProducer.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.idea.gradle.testing.js + +import com.intellij.execution.actions.ConfigurationContext +import com.intellij.openapi.module.Module +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationDataProvider +import org.jetbrains.kotlin.idea.run.AbstractKotlinMultiplatformTestMethodGradleConfigurationProducer +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.js.isJs + +class KotlinMultiplatformJsTestMethodGradleConfigurationProducer + : AbstractKotlinMultiplatformTestMethodGradleConfigurationProducer(), KotlinJSRunConfigurationDataProvider +{ + override val isForTests: Boolean get() = true + override fun isApplicable(module: Module, platform: TargetPlatform) = platform.isJs() + + override fun getConfigurationData(context: ConfigurationContext) = Unit +} \ No newline at end of file diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/native/KotlinMultiplatformNativeTestClassGradleConfigurationProducer.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/native/KotlinMultiplatformNativeTestClassGradleConfigurationProducer.kt new file mode 100644 index 00000000000..81497d5dde1 --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/native/KotlinMultiplatformNativeTestClassGradleConfigurationProducer.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.idea.gradle.testing.native + +import com.intellij.openapi.module.Module +import org.jetbrains.kotlin.ide.konan.KotlinNativeRunConfigurationProvider +import org.jetbrains.kotlin.idea.run.AbstractKotlinMultiplatformTestClassGradleConfigurationProducer +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.konan.isNative + +class KotlinMultiplatformNativeTestClassGradleConfigurationProducer + : AbstractKotlinMultiplatformTestClassGradleConfigurationProducer(), KotlinNativeRunConfigurationProvider +{ + override val isForTests: Boolean get() = true + override fun isApplicable(module: Module, platform: TargetPlatform) = platform.isNative() +} diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/native/KotlinMultiplatformNativeTestMethodGradleConfigurationProducer.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/native/KotlinMultiplatformNativeTestMethodGradleConfigurationProducer.kt new file mode 100644 index 00000000000..1a979993232 --- /dev/null +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/gradle/testing/native/KotlinMultiplatformNativeTestMethodGradleConfigurationProducer.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.idea.gradle.testing.native + +import com.intellij.openapi.module.Module +import org.jetbrains.kotlin.ide.konan.KotlinNativeRunConfigurationProvider +import org.jetbrains.kotlin.idea.run.AbstractKotlinMultiplatformTestMethodGradleConfigurationProducer +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.konan.isNative + +class KotlinMultiplatformNativeTestMethodGradleConfigurationProducer + : AbstractKotlinMultiplatformTestMethodGradleConfigurationProducer(), KotlinNativeRunConfigurationProvider +{ + override val isForTests: Boolean get() = true + override fun isApplicable(module: Module, platform: TargetPlatform) = platform.isNative() +} \ No newline at end of file diff --git a/idea/resources/META-INF/kotlin-gradle-testing.xml b/idea/resources/META-INF/kotlin-gradle-testing.xml new file mode 100644 index 00000000000..2ee4ac36c39 --- /dev/null +++ b/idea/resources/META-INF/kotlin-gradle-testing.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/idea/resources/META-INF/plugin.xml b/idea/resources/META-INF/plugin.xml index 9d143c4d653..7b7c1e8f9ed 100644 --- a/idea/resources/META-INF/plugin.xml +++ b/idea/resources/META-INF/plugin.xml @@ -20,6 +20,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. JUnit com.intellij.gradle org.jetbrains.plugins.gradle + org.jetbrains.plugins.gradle org.intellij.groovy org.jetbrains.idea.maven TestNG-J diff --git a/idea/resources/META-INF/plugin.xml.191 b/idea/resources/META-INF/plugin.xml.191 index f2b7b3b0a89..c521ecea85e 100644 --- a/idea/resources/META-INF/plugin.xml.191 +++ b/idea/resources/META-INF/plugin.xml.191 @@ -20,6 +20,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. JUnit org.jetbrains.plugins.gradle org.jetbrains.plugins.gradle.java + org.jetbrains.plugins.gradle.java org.intellij.groovy org.jetbrains.idea.maven TestNG-J diff --git a/idea/resources/META-INF/plugin.xml.192 b/idea/resources/META-INF/plugin.xml.192 index 2610a203b30..ced1dbdda60 100644 --- a/idea/resources/META-INF/plugin.xml.192 +++ b/idea/resources/META-INF/plugin.xml.192 @@ -20,6 +20,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. JUnit org.jetbrains.plugins.gradle org.jetbrains.plugins.gradle.java + org.jetbrains.plugins.gradle.java org.intellij.groovy org.jetbrains.idea.maven TestNG-J diff --git a/idea/resources/META-INF/plugin.xml.as35 b/idea/resources/META-INF/plugin.xml.as35 index fcd7f3a9333..e8fdcd4009a 100644 --- a/idea/resources/META-INF/plugin.xml.as35 +++ b/idea/resources/META-INF/plugin.xml.as35 @@ -21,6 +21,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. JUnit org.jetbrains.plugins.gradle org.jetbrains.plugins.gradle.java + org.jetbrains.plugins.gradle.java org.intellij.groovy org.jetbrains.idea.maven TestNG-J diff --git a/idea/resources/META-INF/plugin.xml.as36 b/idea/resources/META-INF/plugin.xml.as36 index ef61ebb0aa1..e79b67fb965 100644 --- a/idea/resources/META-INF/plugin.xml.as36 +++ b/idea/resources/META-INF/plugin.xml.as36 @@ -21,6 +21,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. JUnit org.jetbrains.plugins.gradle org.jetbrains.plugins.gradle.java + org.jetbrains.plugins.gradle.java org.intellij.groovy org.jetbrains.idea.maven TestNG-J diff --git a/idea/resources/META-INF/plugin.xml.as40 b/idea/resources/META-INF/plugin.xml.as40 index 2d4e02e89f1..5e8c44f6c44 100644 --- a/idea/resources/META-INF/plugin.xml.as40 +++ b/idea/resources/META-INF/plugin.xml.as40 @@ -21,6 +21,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. JUnit com.intellij.gradle org.jetbrains.plugins.gradle + org.jetbrains.plugins.gradle org.intellij.groovy org.jetbrains.idea.maven TestNG-J diff --git a/idea/resources/org/jetbrains/kotlin/idea/gradle/testing/KotlinMppTestLogger.groovy b/idea/resources/org/jetbrains/kotlin/idea/gradle/testing/KotlinMppTestLogger.groovy new file mode 100644 index 00000000000..d4d64c7bfdd --- /dev/null +++ b/idea/resources/org/jetbrains/kotlin/idea/gradle/testing/KotlinMppTestLogger.groovy @@ -0,0 +1,106 @@ +import groovy.xml.MarkupBuilder +import org.gradle.api.internal.tasks.testing.TestDescriptorInternal + +class KotlinMppTestLogger { + static def configureTestEventLogging(def task) { + task.beforeSuite { descriptor -> logTestEvent("beforeSuite", descriptor, null, null) } + task.afterSuite { descriptor, result -> logTestEvent("afterSuite", descriptor, null, result) } + + task.beforeTest { descriptor -> logTestEvent("beforeTest", descriptor, null, null) } + task.onOutput { descriptor, event -> logTestEvent("onOutput", descriptor, event, null) } + task.afterTest { descriptor, result -> logTestEvent("afterTest", descriptor, null, result) } + } + + static def logTestEvent(testEventType, TestDescriptorInternal testDescriptor, testEvent, testResult) { + def writer = new StringWriter() + def xml = new MarkupBuilder(writer) + xml.event(type: testEventType) { + test(id: testDescriptor.id, parentId: testDescriptor.parent?.id ?: '') { + if (testDescriptor) { + descriptor(name: testDescriptor.displayName ?: '', className: testDescriptor.classDisplayName ?: '') + } + if (testEvent) { + def message = escapeCdata(testEvent.message) + event(destination: testEvent.destination) { + xml.mkp.yieldUnescaped("$message") + } + } + if (testResult) { + def errorMsg = escapeCdata(testResult.exception?.message ?: '') + def stackTrace = escapeCdata(getStackTrace(testResult.exception)) + result(resultType: testResult.resultType ?: '', startTime: testResult.startTime, endTime: testResult.endTime) { + def exception = testResult.exception + if (exception?.message?.trim()) xml.mkp.yieldUnescaped("$errorMsg") + if (exception) xml.mkp.yieldUnescaped("$stackTrace") + + if ('kotlin.AssertionError'.equals(exception?.class?.name) || exception instanceof AssertionError) { + failureType('assertionFailed') + return + } + + failureType('error') + } + } + } + } + + writeLog(writer.toString()) + } + + static String escapeCdata(String s) { + return ""; + } + + static def wrap(String s) { + if(!s) return s; + s.replaceAll("\r\n|\n\r|\n|\r","\n") + } + + static def writeLog(s) { + println String.format("\n%s", wrap(s)) + } + + static def logTestReportLocation(def report) { + if(!report) return + def writer = new StringWriter() + def xml = new MarkupBuilder(writer) + xml.event(type: 'reportLocation', testReport: report) + writeLog(writer.toString()); + } + + static def logConfigurationError(aTitle, aMessage, boolean openSettings) { + def writer = new StringWriter() + def xml = new MarkupBuilder(writer) + xml.event(type: 'configurationError', openSettings: openSettings) { + title(aTitle) + message(aMessage) + } + writeLog(writer.toString()); + } + + static def getStackTrace(Throwable t) { + if(!t) return '' + StringWriter sw = new StringWriter() + t.printStackTrace(new PrintWriter(sw)) + sw.toString() + } +} + +gradle.taskGraph.beforeTask { task -> + def taskSuperClass = task.class + while (taskSuperClass != Object.class) { + if (taskSuperClass.canonicalName == "org.jetbrains.kotlin.gradle.tasks.KotlinTest") { + try { + KotlinMppTestLogger.logTestReportLocation(task.reports?.html?.entryPoint?.path) + KotlinMppTestLogger.configureTestEventLogging(task) + task.testLogging.showStandardStreams = false + } + catch (all) { + logger.error("", all) + } + return + } else { + taskSuperClass = taskSuperClass.superclass + } + } +} \ No newline at end of file