[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 org.jetbrains.kotlin.tooling.core.MutableExtras
import java.io.Serializable import java.io.Serializable
@IdeaKotlinModel
sealed interface IdeaKotlinDependency : Serializable { sealed interface IdeaKotlinDependency : Serializable {
val coordinates: IdeaKotlinDependencyCoordinates? val coordinates: IdeaKotlinDependencyCoordinates?
val extras: MutableExtras val extras: MutableExtras
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.idea.tcs
import java.io.File import java.io.File
import java.io.Serializable import java.io.Serializable
@IdeaKotlinModel
sealed interface IdeaKotlinDependencyCoordinates : Serializable sealed interface IdeaKotlinDependencyCoordinates : Serializable
data class IdeaKotlinBinaryCoordinates( 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() override val extras: MutableExtras = mutableExtrasOf()
) : IdeaKotlinDependency { ) : IdeaKotlinDependency {
@IdeaKotlinService
fun interface Resolver { fun interface Resolver {
fun resolve(dependency: IdeaKotlinProjectArtifactDependency): IdeaKotlinSourceDependency? fun resolve(dependency: IdeaKotlinProjectArtifactDependency): IdeaKotlinSourceDependency?
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.idea.tcs
import java.io.Serializable import java.io.Serializable
@IdeaKotlinModel
data class IdeaKotlinProjectCoordinates( data class IdeaKotlinProjectCoordinates(
val buildId: String, val buildId: String,
val projectPath: String, val projectPath: String,
@@ -14,6 +14,8 @@ data class IdeaKotlinSourceDependency(
override val coordinates: IdeaKotlinSourceCoordinates, override val coordinates: IdeaKotlinSourceCoordinates,
override val extras: MutableExtras = mutableExtrasOf() override val extras: MutableExtras = mutableExtrasOf()
) : IdeaKotlinDependency { ) : IdeaKotlinDependency {
@IdeaKotlinModel
enum class Type : Serializable { enum class Type : Serializable {
Regular, Friend, DependsOn; 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 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.AbstractExtras
import org.jetbrains.kotlin.tooling.core.Extras import org.jetbrains.kotlin.tooling.core.Extras
import org.jetbrains.kotlin.tooling.core.MutableExtras import org.jetbrains.kotlin.tooling.core.MutableExtras
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameters import org.junit.runners.Parameterized.Parameters
import org.reflections.Reflections
import org.reflections.scanners.Scanners import org.reflections.scanners.Scanners
import java.io.Serializable import java.io.Serializable
import java.lang.reflect.Field import java.lang.reflect.Field
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.full.memberProperties import kotlin.reflect.full.memberProperties
import kotlin.test.Test import kotlin.test.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@RunWith(Parameterized::class) @RunWith(Parameterized::class)
class IdeaKotlinObjectGraphTest(private val node: KClass<*>, private val clazzName: String) { class IdeaKotlinModelObjectGraphTest(private val node: KClass<*>, private val clazzName: String) {
@Test @Test
fun `test - node implements Serializable`() { fun `test - node implements Serializable`() {
@@ -76,8 +75,6 @@ class IdeaKotlinObjectGraphTest(private val node: KClass<*>, private val clazzNa
companion object { companion object {
private val reflections = Reflections("org.jetbrains.kotlin")
private val ideaTcsReflections = Reflections("org.jetbrains.kotlin.gradle.idea.tcs")
private val ignoredNodes = setOf( private val ignoredNodes = setOf(
/* /*
Extras interface and AbstractExtras are okay for now: 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) resolveQueue += ideaTcsReflections.getAll(Scanners.SubTypes)
.map { Class.forName(it) } .map { Class.forName(it) }
.filter { it.name.startsWith("org.jetbrains.kotlin.gradle.idea.tcs") }
.map { it.kotlin } .map { it.kotlin }
.filter { it.isIdeaKotlinModel }
while (resolveQueue.isNotEmpty()) { while (resolveQueue.isNotEmpty()) {
val next = resolveQueue.removeFirst() val next = resolveQueue.removeFirst()
@@ -105,7 +102,7 @@ class IdeaKotlinObjectGraphTest(private val node: KClass<*>, private val clazzNa
next.resolveReachableClasses().forEach { child -> next.resolveReachableClasses().forEach { child ->
resolveQueue.add(child) resolveQueue.add(child)
if (child.java.isInterface || Modifier.isAbstract(child.java.modifiers)) { 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") assertTrue(subtypes.isNotEmpty(), "Missing implementations for $child")
resolveQueue.addAll(subtypes) 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<*>> { private fun KClass<*>.resolveReachableClasses(): Set<KClass<*>> {
return this.memberProperties return this.memberProperties
.map { member -> member.returnType } .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(".")
}