[Gradle] Implement IdeaKotlinObjectGraphTest and fix discovered issues
KT-54825
This commit is contained in:
committed by
Space Team
parent
9771e57951
commit
224a237385
-1
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.gradle.idea.proto.generated.kpm.IdeaKpmDependencyPro
|
|||||||
import org.jetbrains.kotlin.gradle.idea.proto.generated.kpm.IdeaKpmDependencyProto.DependencyCase
|
import org.jetbrains.kotlin.gradle.idea.proto.generated.kpm.IdeaKpmDependencyProto.DependencyCase
|
||||||
import org.jetbrains.kotlin.gradle.idea.proto.generated.kpm.ideaKpmDependencyProto
|
import org.jetbrains.kotlin.gradle.idea.proto.generated.kpm.ideaKpmDependencyProto
|
||||||
import org.jetbrains.kotlin.gradle.idea.serialize.IdeaSerializationContext
|
import org.jetbrains.kotlin.gradle.idea.serialize.IdeaSerializationContext
|
||||||
import org.jetbrains.kotlin.gradle.kpm.idea.*
|
|
||||||
|
|
||||||
internal fun IdeaSerializationContext.IdeaKpmDependencyProto(dependency: IdeaKpmDependency): IdeaKpmDependencyProto {
|
internal fun IdeaSerializationContext.IdeaKpmDependencyProto(dependency: IdeaKpmDependency): IdeaKpmDependencyProto {
|
||||||
return ideaKpmDependencyProto {
|
return ideaKpmDependencyProto {
|
||||||
|
|||||||
+10
-2
@@ -17,10 +17,18 @@ data class IdeaKotlinResolvedBinaryDependency(
|
|||||||
val binaryFile: File,
|
val binaryFile: File,
|
||||||
override val extras: MutableExtras,
|
override val extras: MutableExtras,
|
||||||
override val coordinates: IdeaKotlinBinaryCoordinates?
|
override val coordinates: IdeaKotlinBinaryCoordinates?
|
||||||
) : IdeaKotlinBinaryDependency()
|
) : IdeaKotlinBinaryDependency() {
|
||||||
|
internal companion object {
|
||||||
|
const val serialVersionUID = 0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class IdeaKotlinUnresolvedBinaryDependency(
|
data class IdeaKotlinUnresolvedBinaryDependency(
|
||||||
val cause: String?,
|
val cause: String?,
|
||||||
override val coordinates: IdeaKotlinBinaryCoordinates?,
|
override val coordinates: IdeaKotlinBinaryCoordinates?,
|
||||||
override val extras: MutableExtras
|
override val extras: MutableExtras
|
||||||
) : IdeaKotlinBinaryDependency()
|
) : IdeaKotlinBinaryDependency() {
|
||||||
|
internal companion object {
|
||||||
|
const val serialVersionUID = 0L
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
-1
@@ -5,7 +5,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.gradle.idea.tcs
|
package org.jetbrains.kotlin.gradle.idea.tcs
|
||||||
|
|
||||||
sealed interface IdeaKotlinDependencyCoordinates
|
import java.io.Serializable
|
||||||
|
|
||||||
|
sealed interface IdeaKotlinDependencyCoordinates : Serializable
|
||||||
|
|
||||||
data class IdeaKotlinBinaryCoordinates(
|
data class IdeaKotlinBinaryCoordinates(
|
||||||
val group: String,
|
val group: String,
|
||||||
@@ -16,6 +18,11 @@ data class IdeaKotlinBinaryCoordinates(
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "$group:$module:$version${sourceSetName?.let { ":$it" }.orEmpty()}"
|
return "$group:$module:$version${sourceSetName?.let { ":$it" }.orEmpty()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal companion object {
|
||||||
|
const val serialVersionUID = 0L
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class IdeaKotlinSourceCoordinates(
|
data class IdeaKotlinSourceCoordinates(
|
||||||
@@ -27,4 +34,8 @@ data class IdeaKotlinSourceCoordinates(
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "${buildId.takeIf { it != ":" }.orEmpty()}$projectPath/$sourceSetName"
|
return "${buildId.takeIf { it != ":" }.orEmpty()}$projectPath/$sourceSetName"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal companion object {
|
||||||
|
const val serialVersionUID = 0L
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-1
@@ -14,6 +14,14 @@ data class IdeaKotlinSourceDependency(
|
|||||||
override val extras: MutableExtras
|
override val extras: MutableExtras
|
||||||
) : IdeaKotlinDependency {
|
) : IdeaKotlinDependency {
|
||||||
enum class Type : Serializable {
|
enum class Type : Serializable {
|
||||||
Regular, Friend, DependsOn
|
Regular, Friend, DependsOn;
|
||||||
|
|
||||||
|
internal companion object {
|
||||||
|
const val serialVersionUID = 0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal companion object {
|
||||||
|
const val serialVersionUID = 0L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* 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.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
|
||||||
|
|
||||||
|
@RunWith(Parameterized::class)
|
||||||
|
class IdeaKotlinObjectGraphTest(private val node: KClass<*>, private val clazzName: String) {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - node implements Serializable`() {
|
||||||
|
assertTrue(
|
||||||
|
Serializable::class.java.isAssignableFrom(node.java),
|
||||||
|
"Expected clazz $clazzName to implement ${Serializable::class.qualifiedName}`"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - node is sealed`() {
|
||||||
|
if (node.java.isInterface || node.isAbstract) {
|
||||||
|
assertTrue(node.isSealed, "Expected $clazzName to be sealed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - node implementations contain serialVersionUID`() {
|
||||||
|
if (!node.java.isInterface && !Modifier.isAbstract(node.java.modifiers)) {
|
||||||
|
val serialVersionUID = assertNotNull(
|
||||||
|
node.java.getDeclaredFieldOrNull("serialVersionUID"),
|
||||||
|
"Expected $node to declare 'serialVersionUID' field"
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
Modifier.isStatic(serialVersionUID.modifiers),
|
||||||
|
"Expected $node to declare 'serialVersionUID' statically"
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
serialVersionUID.type.isPrimitive,
|
||||||
|
"Expected $node to declare primitive 'serialVersionUID'"
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
serialVersionUID.type, Long::class.javaPrimitiveType,
|
||||||
|
"Expected $node to declare 'serialVersionUID' of type Long"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Class<*>.getDeclaredFieldOrNull(name: String): Field? {
|
||||||
|
return try {
|
||||||
|
getDeclaredField(name)
|
||||||
|
} catch (t: NoSuchFieldException) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
||||||
|
Let's check known implementations for correctness
|
||||||
|
*/
|
||||||
|
Extras::class, MutableExtras::class, AbstractExtras::class
|
||||||
|
)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@Parameters(name = "{1}")
|
||||||
|
fun findClasses(): List<Array<Any>> {
|
||||||
|
val classes = mutableSetOf<KClass<*>>()
|
||||||
|
|
||||||
|
val resolveQueue = ArrayDeque<KClass<*>>()
|
||||||
|
|
||||||
|
resolveQueue += ideaTcsReflections.getAll(Scanners.SubTypes)
|
||||||
|
.map { Class.forName(it) }
|
||||||
|
.filter { it.name.startsWith("org.jetbrains.kotlin.gradle.idea.tcs") }
|
||||||
|
.map { it.kotlin }
|
||||||
|
|
||||||
|
while (resolveQueue.isNotEmpty()) {
|
||||||
|
val next = resolveQueue.removeFirst()
|
||||||
|
if (!classes.add(next)) continue
|
||||||
|
|
||||||
|
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 }
|
||||||
|
assertTrue(subtypes.isNotEmpty(), "Missing implementations for $child")
|
||||||
|
resolveQueue.addAll(subtypes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes
|
||||||
|
.filter { it !in ignoredNodes }
|
||||||
|
.map { clazz -> arrayOf(clazz, checkNotNull(clazz.displayName())) }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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 }
|
||||||
|
.flatMap { type -> setOf(type) + type.arguments.mapNotNull { it.type } }
|
||||||
|
.mapNotNull { type -> type.classifier as? KClass<*> }
|
||||||
|
.filter { clazz -> clazz.java.name.startsWith("org.jetbrains") }
|
||||||
|
.toSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
-1
@@ -8,7 +8,6 @@
|
|||||||
package org.jetbrains.kotlin.gradle.idea.testFixtures.kpm
|
package org.jetbrains.kotlin.gradle.idea.testFixtures.kpm
|
||||||
|
|
||||||
import org.jetbrains.kotlin.gradle.idea.kpm.*
|
import org.jetbrains.kotlin.gradle.idea.kpm.*
|
||||||
import org.jetbrains.kotlin.gradle.kpm.idea.*
|
|
||||||
import kotlin.test.fail
|
import kotlin.test.fail
|
||||||
|
|
||||||
fun IdeaKpmProject.assertIsNotEmpty(): IdeaKpmProject = apply {
|
fun IdeaKpmProject.assertIsNotEmpty(): IdeaKpmProject = apply {
|
||||||
|
|||||||
Reference in New Issue
Block a user