[Gradle] Implement IdeaKotlinEntityTest and IdeaKotlinEntity annotations

KT-55112
This commit is contained in:
Sebastian Sellmair
2022-11-25 14:53:58 +01:00
committed by Space Team
parent 2c55d2cea3
commit 2bfc559c50
10 changed files with 120 additions and 17 deletions
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.idea.tcs
import org.jetbrains.kotlin.tooling.core.MutableExtras
import java.io.Serializable
@IdeaKotlinModel
sealed interface IdeaKotlinDependency : Serializable {
val coordinates: IdeaKotlinDependencyCoordinates?
val extras: MutableExtras
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.idea.tcs
import java.io.File
import java.io.Serializable
@IdeaKotlinModel
sealed interface IdeaKotlinDependencyCoordinates : Serializable
data class IdeaKotlinBinaryCoordinates(
@@ -0,0 +1,15 @@
/*
* Copyright 2010-2022 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.gradle.idea.tcs
@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class IdeaKotlinEntity
@IdeaKotlinEntity
annotation class IdeaKotlinModel
@IdeaKotlinEntity
annotation class IdeaKotlinService
@@ -14,6 +14,7 @@ data class IdeaKotlinProjectArtifactDependency(
override val extras: MutableExtras = mutableExtrasOf()
) : IdeaKotlinDependency {
@IdeaKotlinService
fun interface Resolver {
fun resolve(dependency: IdeaKotlinProjectArtifactDependency): IdeaKotlinSourceDependency?
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.idea.tcs
import java.io.Serializable
@IdeaKotlinModel
data class IdeaKotlinProjectCoordinates(
val buildId: String,
val projectPath: String,
@@ -14,6 +14,8 @@ data class IdeaKotlinSourceDependency(
override val coordinates: IdeaKotlinSourceCoordinates,
override val extras: MutableExtras = mutableExtrasOf()
) : IdeaKotlinDependency {
@IdeaKotlinModel
enum class Type : Serializable {
Regular, Friend, DependsOn;
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2022 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.gradle.idea.tcs
import kotlin.reflect.KClass
import kotlin.reflect.full.allSuperclasses
import kotlin.reflect.full.findAnnotation
fun KClass<*>.findIdeaKotlinEntityAnnotations(): List<Annotation> {
return (annotations + allSuperclasses.flatMap { it.annotations }).filter { annotation ->
annotation.annotationClass.findAnnotation<IdeaKotlinEntity>() != null
}
}
val KClass<*>.isIdeaKotlinModel get() = findIdeaKotlinEntityAnnotations().singleOrNull() is IdeaKotlinModel
val KClass<*>.isIdeaKotlinService get() = findIdeaKotlinEntityAnnotations().singleOrNull() is IdeaKotlinService
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2022 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.gradle.idea.tcs
import org.jetbrains.kotlin.gradle.idea.tcs.ReflectionTestUtils.displayName
import org.jetbrains.kotlin.gradle.idea.tcs.ReflectionTestUtils.ideaTcsPackage
import org.jetbrains.kotlin.gradle.idea.tcs.ReflectionTestUtils.ideaTcsReflections
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.reflections.scanners.Scanners
import kotlin.reflect.KClass
import kotlin.test.Test
import kotlin.test.fail
@RunWith(Parameterized::class)
class IdeaKotlinEntityTest(private val node: KClass<*>, private val clazzName: String) {
@Test
fun `test - node is marked as IdeaKotlinEntity`() {
val entityAnnotations = node.findIdeaKotlinEntityAnnotations()
if (entityAnnotations.isEmpty())
fail("Expected class $clazzName to be marked with any ${IdeaKotlinEntity::class.java.simpleName} annotation")
if (entityAnnotations.size > 1)
fail("Conflicting ${IdeaKotlinEntity::class.java.simpleName} annotations on $clazzName ($entityAnnotations)")
}
companion object {
@JvmStatic
@Parameterized.Parameters(name = "{1}")
fun findClasses(): List<Array<Any>> {
return ideaTcsReflections.getAll(Scanners.SubTypes)
.map { Class.forName(it) }
.filter { !it.isAnnotation }
.map { it.kotlin }
.filter { it.qualifiedName.orEmpty().startsWith(ideaTcsPackage) }
.map { clazz -> arrayOf(clazz, checkNotNull(clazz.displayName())) }
}
}
}
@@ -5,26 +5,25 @@
package org.jetbrains.kotlin.gradle.idea.tcs
import org.jetbrains.kotlin.gradle.idea.tcs.ReflectionTestUtils.displayName
import org.jetbrains.kotlin.gradle.idea.tcs.ReflectionTestUtils.ideaTcsReflections
import org.jetbrains.kotlin.gradle.idea.tcs.ReflectionTestUtils.kotlinReflections
import org.jetbrains.kotlin.tooling.core.AbstractExtras
import org.jetbrains.kotlin.tooling.core.Extras
import org.jetbrains.kotlin.tooling.core.MutableExtras
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameters
import org.reflections.Reflections
import org.reflections.scanners.Scanners
import java.io.Serializable
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import kotlin.reflect.KClass
import kotlin.reflect.full.memberProperties
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import kotlin.test.*
@RunWith(Parameterized::class)
class IdeaKotlinObjectGraphTest(private val node: KClass<*>, private val clazzName: String) {
class IdeaKotlinModelObjectGraphTest(private val node: KClass<*>, private val clazzName: String) {
@Test
fun `test - node implements Serializable`() {
@@ -76,8 +75,6 @@ class IdeaKotlinObjectGraphTest(private val node: KClass<*>, private val clazzNa
companion object {
private val reflections = Reflections("org.jetbrains.kotlin")
private val ideaTcsReflections = Reflections("org.jetbrains.kotlin.gradle.idea.tcs")
private val ignoredNodes = setOf(
/*
Extras interface and AbstractExtras are okay for now:
@@ -95,8 +92,8 @@ class IdeaKotlinObjectGraphTest(private val node: KClass<*>, private val clazzNa
resolveQueue += ideaTcsReflections.getAll(Scanners.SubTypes)
.map { Class.forName(it) }
.filter { it.name.startsWith("org.jetbrains.kotlin.gradle.idea.tcs") }
.map { it.kotlin }
.filter { it.isIdeaKotlinModel }
while (resolveQueue.isNotEmpty()) {
val next = resolveQueue.removeFirst()
@@ -105,7 +102,7 @@ class IdeaKotlinObjectGraphTest(private val node: KClass<*>, private val clazzNa
next.resolveReachableClasses().forEach { child ->
resolveQueue.add(child)
if (child.java.isInterface || Modifier.isAbstract(child.java.modifiers)) {
val subtypes = reflections.getSubTypesOf(child.java).map { it.kotlin }
val subtypes = kotlinReflections.getSubTypesOf(child.java).map { it.kotlin }
assertTrue(subtypes.isNotEmpty(), "Missing implementations for $child")
resolveQueue.addAll(subtypes)
}
@@ -118,13 +115,6 @@ class IdeaKotlinObjectGraphTest(private val node: KClass<*>, private val clazzNa
}
private fun KClass<*>.displayName() = java.name
.removePrefix("org.jetbrains.kotlin")
.removePrefix(".gradle")
.removePrefix(".idea")
.removePrefix(".tcs")
.removePrefix(".")
private fun KClass<*>.resolveReachableClasses(): Set<KClass<*>> {
return this.memberProperties
.map { member -> member.returnType }
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2022 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.gradle.idea.tcs
import org.reflections.Reflections
import kotlin.reflect.KClass
object ReflectionTestUtils {
const val ideaTcsPackage = "org.jetbrains.kotlin.gradle.idea.tcs"
val ideaTcsReflections = Reflections(ideaTcsPackage)
const val kotlinPackage = "org.jetbrains.kotlin"
val kotlinReflections = Reflections(kotlinPackage)
fun KClass<*>.displayName() = java.name
.removePrefix("org.jetbrains.kotlin")
.removePrefix(".gradle")
.removePrefix(".idea")
.removePrefix(".tcs")
.removePrefix(".")
}