Skip to content

Commit

Permalink
Bumping KotlinPoet and metadata (#108)
Browse files Browse the repository at this point in the history
* Bumping KotlinPoet and metadata

* KotlinPoet to 1.10.2, this version remove `ImmutableKm*` and relies directly on kotlin metadata types, addressing here
* Kotlin metadata 0.2.0

* missing change
  • Loading branch information
MarcoSignoretto authored Nov 22, 2021
1 parent bcbac05 commit 2721807
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 46 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
---

## master
* KotlinPoet to 1.10.2 and addressed breaking changes
* Kotlin metadata to 0.2.0

## 2.1.0
* Fixed issue that was preventing the plugin from been applied in the `plugins` block
Expand Down
3 changes: 1 addition & 2 deletions mockingbird-compiler/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ dependencies {
implementation(libs.kotlin.reflectjvm)
implementation(libs.square.kotlinpoet)
implementation(libs.square.kotlinpoet.metadata)
implementation(libs.square.kotlinpoet.metadata.specs)
implementation(libs.kotlinx.metadatajvm)
implementation(libs.kotlinx.metadata.jvm)
implementation(project(":mockingbird"))

testImplementation(libs.kotlin.test)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@

package com.careem.mockingbird

import com.squareup.kotlinpoet.metadata.ImmutableKmClass
import com.squareup.kotlinpoet.metadata.ImmutableKmType
import com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
import kotlinx.metadata.KmClass
import kotlinx.metadata.KmClassifier
import kotlinx.metadata.KmType
import org.gradle.api.Project
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
import org.gradle.api.logging.Logger
Expand Down Expand Up @@ -56,9 +56,9 @@ class ClassLoaderWrapper(
@Throws(ClassNotFoundException::class)
fun loadClass(name: String): KClass<*> = classLoader.loadClass(name).kotlin

fun loadClass(kmClass: ImmutableKmType): KClass<*> = loadClassFromDirectory(extractTypeString(kmClass))
fun loadClass(kmClass: KmType): KClass<*> = loadClassFromDirectory(extractTypeString(kmClass))

fun loadClass(kmClass: ImmutableKmClass): KClass<*> = loadClassFromDirectory(kmClass.name)
fun loadClass(kmClass: KmClass): KClass<*> = loadClassFromDirectory(kmClass.name)

fun loadClassFromDirectory(path: String): KClass<*> {
return loadClass(path.toJavaFullyQualifiedName())
Expand Down Expand Up @@ -94,7 +94,7 @@ class ClassLoaderWrapper(
}
}

private fun extractTypeString(type: ImmutableKmType): String {
private fun extractTypeString(type: KmType): String {
return if (type.classifier is KmClassifier.Class) {
(type.classifier as KmClassifier.Class).name
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@

package com.careem.mockingbird

import com.squareup.kotlinpoet.metadata.ImmutableKmClass
import com.squareup.kotlinpoet.metadata.ImmutableKmFunction
import com.squareup.kotlinpoet.metadata.ImmutableKmProperty
import com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
import com.squareup.kotlinpoet.metadata.toImmutableKmClass
import com.squareup.kotlinpoet.metadata.toKmClass
import kotlinx.metadata.KmClass
import kotlinx.metadata.KmClassifier
import kotlinx.metadata.KmFunction
import kotlinx.metadata.KmProperty
import kotlinx.metadata.jvm.getterSignature
import kotlinx.metadata.jvm.setterSignature
import kotlinx.metadata.jvm.signature

@Suppress("UnstableApiUsage")
@KotlinPoetMetadataPreview
Expand All @@ -33,25 +36,25 @@ class FunctionsMiner(
* Extract all functions and properties, this will extract also functions that are defined into the supertypes
* these functions are all the functions that the mock class should provide
*/
fun extractFunctionsAndProperties(kmClass: ImmutableKmClass): Pair<List<ImmutableKmFunction>, List<ImmutableKmProperty>> {
val functions: MutableList<ImmutableKmFunction> = mutableListOf()
val properties: MutableList<ImmutableKmProperty> = mutableListOf()
fun extractFunctionsAndProperties(kmClass: KmClass): Pair<List<KmFunction>, List<KmProperty>> {
val functions: MutableList<KmFunction> = mutableListOf()
val properties: MutableList<KmProperty> = mutableListOf()
rawExtractFunctionsAndProperties(kmClass, functions, properties)
return functions.distinctBy { it.signature } to properties
.filter { it.getterSignature != null || it.setterSignature != null }
.distinctBy { it.getterSignature ?: it.setterSignature }
}

private fun rawExtractFunctionsAndProperties(
kmClass: ImmutableKmClass,
functions: MutableList<ImmutableKmFunction>,
properties: MutableList<ImmutableKmProperty>
kmClass: KmClass,
functions: MutableList<KmFunction>,
properties: MutableList<KmProperty>
) { // TODO optimize with tailrec
val kmSuperTypes = kmClass.supertypes
.map { it.classifier }
.filterIsInstance<KmClassifier.Class>()
.filter { it.name != "kotlin/Any" }
.map { classLoaderWrapper.loadClassFromDirectory(it.name).toImmutableKmClass() }
.map { classLoaderWrapper.loadClassFromDirectory(it.name).toKmClass() }

// get functions and properties for current class
functions.addAll(kmClass.functions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.metadata.ImmutableKmClass
import com.squareup.kotlinpoet.metadata.ImmutableKmFunction
import com.squareup.kotlinpoet.metadata.ImmutableKmProperty
import com.squareup.kotlinpoet.metadata.ImmutableKmType
import com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
import com.squareup.kotlinpoet.metadata.isNullable
import com.squareup.kotlinpoet.metadata.isSuspend
import kotlinx.metadata.KmClass
import kotlinx.metadata.KmClassifier
import kotlinx.metadata.KmFunction
import kotlinx.metadata.KmProperty
import kotlinx.metadata.KmType
import kotlinx.metadata.jvm.getterSignature
import kotlinx.metadata.jvm.setterSignature
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import kotlin.reflect.KClass
Expand All @@ -46,7 +48,7 @@ class MockGenerator constructor(
private val logger: Logger = Logging.getLogger(this::class.java)


fun createClass(kmClass: ImmutableKmClass): FileSpec {
fun createClass(kmClass: KmClass): FileSpec {
val classToMock = classLoader.loadClass(kmClass)
val simpleName = kmClass.name.substringAfterLast("/")

Expand Down Expand Up @@ -78,7 +80,7 @@ class MockGenerator constructor(
.build()
}

private fun List<ImmutableKmFunction>.buildMethodObject(): TypeSpec {
private fun List<KmFunction>.buildMethodObject(): TypeSpec {
logger.info("Generating methods")
val methodObjectBuilder = TypeSpec.objectBuilder(METHOD)
val visitedFunctionSet = mutableSetOf<String>()
Expand All @@ -97,7 +99,7 @@ class MockGenerator constructor(
return methodObjectBuilder.build()
}

private fun List<ImmutableKmFunction>.buildArgObject(): TypeSpec {
private fun List<KmFunction>.buildArgObject(): TypeSpec {
logger.info("Generating arguments")
val argObjectBuilder = TypeSpec.objectBuilder(ARG)
val visitedPropertySet = mutableSetOf<String>()
Expand All @@ -120,7 +122,7 @@ class MockGenerator constructor(
return argObjectBuilder.build()
}

private fun List<ImmutableKmProperty>.buildPropertyObject(): TypeSpec {
private fun List<KmProperty>.buildPropertyObject(): TypeSpec {
logger.info("Generating properties")
val propertyObjectBuilder = TypeSpec.objectBuilder(PROPERTY)
var haveMutableProps = false
Expand Down Expand Up @@ -166,14 +168,14 @@ class MockGenerator constructor(
.addModifiers(KModifier.CONST)
.build()

private fun isUnitFunction(function: ImmutableKmFunction): Boolean {
private fun isUnitFunction(function: KmFunction): Boolean {
val classifier = function.returnType.classifier
return classifier is KmClassifier.Class && classifier.name == "kotlin/Unit"
}

private fun mockProperty(
mockClassBuilder: TypeSpec.Builder,
property: ImmutableKmProperty
property: KmProperty
) {
logger.debug("===> Mocking Property ${property.getterSignature?.name} and ${property.setterSignature?.name} and ${property.setterSignature}")
val propertyBuilder = PropertySpec
Expand Down Expand Up @@ -242,7 +244,7 @@ class MockGenerator constructor(

@OptIn(ExperimentalStdlibApi::class)
private fun buildFunctionModifiers(
function: ImmutableKmFunction
function: KmFunction
) : List<KModifier> {
return buildList {
add(KModifier.OVERRIDE)
Expand All @@ -254,7 +256,7 @@ class MockGenerator constructor(

private fun mockFunction(
mockClassBuilder: TypeSpec.Builder,
function: ImmutableKmFunction,
function: KmFunction,
isUnit: Boolean
) {
logger.info("Mocking function")
Expand All @@ -274,7 +276,7 @@ class MockGenerator constructor(
)
}

private fun ImmutableKmType.buildType(): TypeName {
private fun KmType.buildType(): TypeName {
val subTypes = this.arguments.map { it.type!! }
return classLoader.loadClass(this)
.asTypeName()
Expand All @@ -293,7 +295,7 @@ class MockGenerator constructor(
}


private fun FunSpec.Builder.addMockStatement(function: ImmutableKmFunction, isUnit: Boolean) {
private fun FunSpec.Builder.addMockStatement(function: KmFunction, isUnit: Boolean) {
// TODO remove duplicates in args and method names
val mockFunction = if (isUnit) {
MOCK_UNIT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
*/
package com.careem.mockingbird

import com.squareup.kotlinpoet.metadata.ImmutableKmClass
import com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
import com.squareup.kotlinpoet.metadata.toImmutableKmClass
import com.squareup.kotlinpoet.metadata.toKmClass
import kotlinx.metadata.KmClass
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.logging.Logger
Expand Down Expand Up @@ -98,7 +98,7 @@ abstract class MockingbirdPlugin : Plugin<Project> {
outputDir.mkdirs()

pluginExtensions.generateMocksFor
.map { classLoader.loadClass(it).toImmutableKmClass() }
.map { classLoader.loadClass(it).toKmClass() }
.let { generateClasses(it, outputDir) }
}

Expand All @@ -112,7 +112,7 @@ abstract class MockingbirdPlugin : Plugin<Project> {
}


private fun generateClasses(classNames: List<ImmutableKmClass>, outputDir: File) {
private fun generateClasses(classNames: List<KmClass>, outputDir: File) {
for (kmClass in classNames) {
mockGenerator.createClass(kmClass).writeTo(outputDir)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package com.careem.mockingbird

import com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
import com.squareup.kotlinpoet.metadata.toImmutableKmClass
import com.squareup.kotlinpoet.metadata.toKmClass
import io.mockk.every
import io.mockk.mockk
import org.junit.Before
Expand All @@ -37,24 +37,24 @@ class FunctionsMinerTest {

@Test
fun testExtractFunctionsAndPropertiesWithSuperInterface() {
val (functions, properties) = functionsMiner.extractFunctionsAndProperties(SuperInterface::class.toImmutableKmClass())
val (functions, properties) = functionsMiner.extractFunctionsAndProperties(SuperInterface::class.toKmClass())
assertEquals(2, functions.size)
assertEquals(0, properties.size)
}

@Test
fun testExtractFunctionsAndPropertiesWithChildInterface() {
every { classLoaderWrapper.loadClassFromDirectory(any()) } returns SuperInterface::class
val (functions, properties) = functionsMiner.extractFunctionsAndProperties(ChildInterface::class.toImmutableKmClass())
val (functions, properties) = functionsMiner.extractFunctionsAndProperties(ChildInterface::class.toKmClass())
assertEquals(4, functions.size)
assertEquals(0, properties.size)
}

@Test
fun testExtractFunctionsAndPropertiesWithChildInterfaceImpl() {
every { classLoaderWrapper.loadClassFromDirectory(SuperInterface::class.toImmutableKmClass().name) } returns SuperInterface::class
every { classLoaderWrapper.loadClassFromDirectory(ChildInterface::class.toImmutableKmClass().name) } returns ChildInterface::class
val (functions, properties) = functionsMiner.extractFunctionsAndProperties(ChildInterfaceImpl::class.toImmutableKmClass())
every { classLoaderWrapper.loadClassFromDirectory(SuperInterface::class.toKmClass().name) } returns SuperInterface::class
every { classLoaderWrapper.loadClassFromDirectory(ChildInterface::class.toKmClass().name) } returns ChildInterface::class
val (functions, properties) = functionsMiner.extractFunctionsAndProperties(ChildInterfaceImpl::class.toKmClass())
assertEquals(4, functions.size)
assertEquals(2, properties.size)
}
Expand Down
7 changes: 3 additions & 4 deletions versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ junit = "4.13.1"
jacoco = "0.8.7"
stately = "1.1.10-a1"
atomicFu = "0.16.3"
kotlinPoet = "1.9.0"
kotlinxMetadata = "0.1.0"
kotlinPoet = "1.10.2"
kotlinxMetadata = "0.2.0"
mockk = "1.12.0"

[libraries]
Expand All @@ -20,8 +20,7 @@ junit-junit = { module = "junit:junit", version.ref = "junit" }
kotlin-reflectjvm = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
square-kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinPoet" }
square-kotlinpoet-metadata = { module = "com.squareup:kotlinpoet-metadata", version.ref = "kotlinPoet" }
square-kotlinpoet-metadata-specs = { module = "com.squareup:kotlinpoet-metadata-specs", version.ref = "kotlinPoet" }
kotlinx-metadatajvm = { module = "org.jetbrains.kotlinx:kotlinx-metadata-jvm", version.ref = "kotlinxMetadata" }
kotlinx-metadata-jvm = { module = "org.jetbrains.kotlinx:kotlinx-metadata-jvm", version.ref = "kotlinxMetadata" }

kotlinx-atomicfu-gradle = { module = "org.jetbrains.kotlinx:atomicfu-gradle-plugin", version.ref = "atomicFu" }
kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
Expand Down

0 comments on commit 2721807

Please sign in to comment.