From 8f4063b36c4281b099c21b7427c84dd7c108ba04 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Mon, 2 Sep 2019 18:26:32 +0900 Subject: [PATCH] Implement test icon provider for 'native' platform --- .../idea/platform/JsIdePlatformKindTooling.kt | 74 +++++-------------- .../platform/JvmIdePlatformKindTooling.kt | 3 +- .../kotlin/idea/platform/testRunningUtils.kt | 72 ++++++++++++++++++ .../KotlinNativeRunConfigurationProvider.kt | 10 +++ .../ide/konan/NativeIdePlatformKindTooling.kt | 19 ++++- .../KotlinTestRunLineMarkerContributor.kt | 6 +- 6 files changed, 123 insertions(+), 61 deletions(-) create mode 100644 idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/testRunningUtils.kt create mode 100644 idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativeRunConfigurationProvider.kt diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/JsIdePlatformKindTooling.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/JsIdePlatformKindTooling.kt index 692b163d51c..7b66769ec1c 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/JsIdePlatformKindTooling.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/JsIdePlatformKindTooling.kt @@ -11,28 +11,21 @@ import com.intellij.execution.actions.RunConfigurationProducer import com.intellij.openapi.project.Project import com.intellij.openapi.roots.libraries.Library import com.intellij.openapi.util.io.FileUtil -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes +import com.intellij.util.SmartList import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.gradle.KotlinPlatform import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JsCompilerArgumentsHolder import org.jetbrains.kotlin.idea.framework.JSLibraryKind import org.jetbrains.kotlin.idea.framework.JSLibraryStdDescription import org.jetbrains.kotlin.idea.framework.JsLibraryStdDetectionUtil -import org.jetbrains.kotlin.idea.highlighter.KotlinTestRunLineMarkerContributor.Companion.getTestStateIcon +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationData import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationDataProvider import org.jetbrains.kotlin.idea.platform.IdePlatformKindTooling +import org.jetbrains.kotlin.idea.platform.getGenericTestIcon import org.jetbrains.kotlin.idea.run.multiplatform.KotlinMultiplatformRunLocationsProvider -import org.jetbrains.kotlin.idea.util.string.joinWithEscape -import org.jetbrains.kotlin.js.resolve.JsResolverForModuleFactory -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.platform.impl.JsIdePlatformKind -import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtNamedDeclaration -import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject -import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.utils.ifEmpty import javax.swing.Icon @@ -40,9 +33,6 @@ import javax.swing.Icon class JsIdePlatformKindTooling : IdePlatformKindTooling() { companion object { private const val MAVEN_OLD_JS_STDLIB_ID = "kotlin-js-library" - - private val TEST_FQ_NAME = FqName("kotlin.test.Test") - private val IGNORE_FQ_NAME = FqName("kotlin.test.Ignore") } override val kind = JsIdePlatformKind @@ -61,39 +51,26 @@ class JsIdePlatformKindTooling : IdePlatformKindTooling() { } override fun getTestIcon(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor): Icon? { - if (!descriptor.isTest()) return null + return getGenericTestIcon(declaration, descriptor) { + val contexts by lazy { computeConfigurationContexts(declaration) } - val contexts by lazy { computeConfigurationContexts(declaration) } + val runConfigData = RunConfigurationProducer + .getProducers(declaration.project) + .asSequence() + .filterIsInstance>() + .filter { it.isForTests } + .flatMap { provider -> contexts.map { context -> provider.getConfigurationData(context) } } + .firstOrNull { it != null } + ?: return@getGenericTestIcon null - val runConfigData = RunConfigurationProducer - .getProducers(declaration.project) - .asSequence() - .filterIsInstance>() - .filter { it.isForTests } - .flatMap { provider -> contexts.map { context -> provider.getConfigurationData(context) } } - .firstOrNull { it != null } ?: return null + val location = if (runConfigData is KotlinJSRunConfigurationData) { + FileUtil.toSystemDependentName(runConfigData.jsOutputFilePath) + } else { + declaration.containingKtFile.packageFqName.asString() + } - val locations = ArrayList() - - locations += FileUtil.toSystemDependentName(runConfigData.jsOutputFilePath) - - val klass = when (declaration) { - is KtClassOrObject -> declaration - is KtNamedFunction -> declaration.containingClassOrObject ?: return null - else -> return null + return@getGenericTestIcon SmartList(location) } - locations += klass.parentsWithSelf.filterIsInstance().mapNotNull { it.name }.toList().asReversed() - - val testName = (declaration as? KtNamedFunction)?.name - if (testName != null) { - locations += "$testName" - } - - val prefix = if (testName != null) "test://" else "suite://" - - val url = prefix + locations.joinWithEscape('.') - - return getTestStateIcon(url, declaration.project) } override fun acceptsAsEntryPoint(function: KtFunction): Boolean { @@ -114,17 +91,4 @@ class JsIdePlatformKindTooling : IdePlatformKindTooling() { ConfigurationContext.createEmptyContextForLocation(it) }.ifEmpty { listOf(ConfigurationContext.createEmptyContextForLocation(location)) }.asSequence() } - - private fun DeclarationDescriptor.isIgnored(): Boolean = - annotations.any { it.fqName == IGNORE_FQ_NAME } || ((containingDeclaration as? ClassDescriptor)?.isIgnored() ?: false) - - private fun DeclarationDescriptor.isTest(): Boolean { - if (isIgnored()) return false - - if (annotations.any { it.fqName == TEST_FQ_NAME }) return true - if (this is ClassDescriptorWithResolutionScopes) { - return declaredCallableMembers.any { it.isTest() } - } - return false - } } \ No newline at end of file diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/JvmIdePlatformKindTooling.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/JvmIdePlatformKindTooling.kt index 6f06c73cf9e..c935abfe00f 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/JvmIdePlatformKindTooling.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/JvmIdePlatformKindTooling.kt @@ -24,7 +24,6 @@ import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.resolve.jvm.JvmResolverForModuleFactory import org.jetbrains.kotlin.utils.PathUtil import javax.swing.Icon @@ -73,7 +72,7 @@ class JvmIdePlatformKindTooling : IdePlatformKindTooling() { else -> return null } - return getTestStateIcon(url, declaration.project) ?: framework.icon + return getTestStateIcon(url, declaration.project, strict = false) ?: framework.icon } override fun acceptsAsEntryPoint(function: KtFunction) = true diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/testRunningUtils.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/testRunningUtils.kt new file mode 100644 index 00000000000..a14cd675231 --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/platform/testRunningUtils.kt @@ -0,0 +1,72 @@ +/* + * 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.platform + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.highlighter.KotlinTestRunLineMarkerContributor.Companion.getTestStateIcon +import org.jetbrains.kotlin.idea.util.string.joinWithEscape +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf +import javax.swing.Icon + +private val TEST_FQ_NAME = FqName("kotlin.test.Test") +private val IGNORE_FQ_NAME = FqName("kotlin.test.Ignore") + +fun getGenericTestIcon(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor, initialLocations: () -> List?): Icon? { + if (!descriptor.isKotlinTestDeclaration()) return null + + val locations = initialLocations()?.toMutableList() ?: return null + + val clazz = when (declaration) { + is KtClassOrObject -> declaration + is KtNamedFunction -> declaration.containingClassOrObject ?: return null + else -> return null + } + + locations += clazz.parentsWithSelf.filterIsInstance() + .mapNotNull { it.name } + .toList().asReversed() + + val testName = (declaration as? KtNamedFunction)?.name + if (testName != null) { + locations += "$testName" + } + + val prefix = if (testName != null) "test://" else "suite://" + val url = prefix + locations.joinWithEscape('.') + val javaUrl = "java:$url" + + val project = declaration.project + return getTestStateIcon(javaUrl, project, strict = true) ?: getTestStateIcon(url, project, strict = false) +} + +private tailrec fun DeclarationDescriptor.isIgnored(): Boolean { + if (annotations.any { it.fqName == IGNORE_FQ_NAME }) { + return true + } + + val containingClass = containingDeclaration as? ClassDescriptor ?: return false + return containingClass.isIgnored() +} + +fun DeclarationDescriptor.isKotlinTestDeclaration(): Boolean { + if (isIgnored()) { + return false + } + + if (annotations.any { it.fqName == TEST_FQ_NAME }) { + return true + } + + val classDescriptor = this as? ClassDescriptorWithResolutionScopes ?: return false + return classDescriptor.declaredCallableMembers.any { it.isKotlinTestDeclaration() } +} \ No newline at end of file diff --git a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativeRunConfigurationProvider.kt b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativeRunConfigurationProvider.kt new file mode 100644 index 00000000000..0c1080afa9e --- /dev/null +++ b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/KotlinNativeRunConfigurationProvider.kt @@ -0,0 +1,10 @@ +/* + * 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.ide.konan + +interface KotlinNativeRunConfigurationProvider { + val isForTests: Boolean +} \ No newline at end of file diff --git a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/NativeIdePlatformKindTooling.kt b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/NativeIdePlatformKindTooling.kt index 5157eaf5265..423c36ee795 100644 --- a/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/NativeIdePlatformKindTooling.kt +++ b/idea/idea-native/src/org/jetbrains/kotlin/ide/konan/NativeIdePlatformKindTooling.kt @@ -5,17 +5,18 @@ package org.jetbrains.kotlin.ide.konan +import com.intellij.execution.actions.RunConfigurationProducer import com.intellij.openapi.project.Project import com.intellij.openapi.roots.libraries.DummyLibraryProperties import com.intellij.openapi.roots.libraries.Library import com.intellij.openapi.roots.libraries.PersistentLibraryKind import com.intellij.openapi.roots.ui.configuration.libraries.CustomLibraryDescription -import org.jetbrains.kotlin.ide.konan.analyzer.NativeResolverForModuleFactory import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.gradle.KotlinPlatform import org.jetbrains.kotlin.idea.framework.KotlinLibraryKind import org.jetbrains.kotlin.idea.platform.IdePlatformKindTooling +import org.jetbrains.kotlin.idea.platform.getGenericTestIcon import org.jetbrains.kotlin.platform.impl.NativeIdePlatformKind import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtNamedDeclaration @@ -37,7 +38,21 @@ class NativeIdePlatformKindTooling : IdePlatformKindTooling() { override fun getLibraryDescription(project: Project): CustomLibraryDescription? = null override fun getLibraryVersionProvider(project: Project): (Library) -> String? = { null } - override fun getTestIcon(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor): Icon? = null + override fun getTestIcon(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor): Icon? { + return getGenericTestIcon(declaration, descriptor) { + val availableRunConfigurations = RunConfigurationProducer + .getProducers(declaration.project) + .asSequence() + .filterIsInstance() + .filter { it.isForTests } + + if (availableRunConfigurations.firstOrNull() == null) { + return@getGenericTestIcon null + } + + return@getGenericTestIcon emptyList() + } + } override fun acceptsAsEntryPoint(function: KtFunction) = false } diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt index c7b5810bf72..7241d0fcc5b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt @@ -38,9 +38,11 @@ import javax.swing.Icon class KotlinTestRunLineMarkerContributor : RunLineMarkerContributor() { companion object { - fun getTestStateIcon(url: String, project: Project): Icon? { + fun getTestStateIcon(url: String, project: Project, strict: Boolean): Icon? { val defaultIcon = AllIcons.RunConfigurations.TestState.Run - val state = TestStateStorage.getInstance(project).getState(url) ?: return defaultIcon + val state = TestStateStorage.getInstance(project).getState(url) + ?: return if (strict) null else defaultIcon + return when (TestIconMapper.getMagnitude(state.magnitude)) { TestStateInfo.Magnitude.ERROR_INDEX, TestStateInfo.Magnitude.FAILED_INDEX -> AllIcons.RunConfigurations.TestState.Red2