Skip to content

Commit

Permalink
Migrating generation logic to KSP (#123)
Browse files Browse the repository at this point in the history
Migrating generation logic to KSP

This patch is migrating the mock generation logic to Ksp, the logic will be the same just porting to the KS types.
  • Loading branch information
MarcoSignoretto authored Sep 26, 2022
1 parent 89c3cdb commit b951415
Show file tree
Hide file tree
Showing 31 changed files with 1,152 additions and 113 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
---

## master
* Introduced Ksp code generation using the @Mock annotation
* Fix for mock generation to set the right visibility

## 2.6.0
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
49 changes: 49 additions & 0 deletions mockingbird-processor/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
*
* Copyright Careem, an Uber Technologies Inc. company
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


plugins {
kotlin("jvm")
id("maven-publish")
}

apply(from = "../publishing.gradle")

repositories {
mavenCentral()
google()
gradlePluginPortal()
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
}

publishing {
publications {
create<MavenPublication>("mockingbird-processor") {
from(components["java"])
}
}
}

dependencies {
implementation(libs.google.ksp)
implementation(libs.square.kotlinpoet.ksp)
implementation(project(":mockingbird"))
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright Careem, an Uber Technologies Inc. company
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.careem.mockingbird.processor

import com.google.devtools.ksp.getAllSuperTypes
import com.google.devtools.ksp.getDeclaredFunctions
import com.google.devtools.ksp.getDeclaredProperties
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.Modifier

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: KSClassDeclaration): Pair<List<KSFunctionDeclaration>, List<KSPropertyDeclaration>> {
val functions: MutableList<KSFunctionDeclaration> = mutableListOf()
val properties: MutableList<KSPropertyDeclaration> = mutableListOf()

val kmSuperTypes = kmClass.getAllSuperTypes()
.filter { it.fullyQualifiedName() != "kotlin.Any" }
.map { it.declaration }
.filter { it is KSClassDeclaration }
.map { it as KSClassDeclaration }

(listOf(kmClass) + kmSuperTypes).forEach { rawExtractFunctionsAndProperties(it, functions, properties) }
return functions to properties
}

private fun rawExtractFunctionsAndProperties(
kmClass: KSClassDeclaration,
functions: MutableList<KSFunctionDeclaration>,
properties: MutableList<KSPropertyDeclaration>
) {
// get functions and properties for current class
functions.addAll(kmClass.getDeclaredFunctions().filterNot { it.modifiers.contains(Modifier.OVERRIDE) })
properties.addAll(kmClass.getDeclaredProperties().filterNot { it.modifiers.contains(Modifier.OVERRIDE) })
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright Careem, an Uber Technologies Inc. company
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.careem.mockingbird.processor

import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.KSVisitorVoid
import com.google.devtools.ksp.validate
import com.squareup.kotlinpoet.ksp.KotlinPoetKspPreview
import com.squareup.kotlinpoet.ksp.toClassName
import com.squareup.kotlinpoet.ksp.toTypeName
import com.squareup.kotlinpoet.ksp.writeTo

class GenerateMocksSymbolProcessor(
val codeGenerator: CodeGenerator,
val logger: KSPLogger
) : SymbolProcessor {

private lateinit var mockGenerator: MockGenerator
private lateinit var functionsMiner: FunctionsMiner

@OptIn(KotlinPoetKspPreview::class)
override fun process(resolver: Resolver): List<KSAnnotated> {
functionsMiner = FunctionsMiner()
mockGenerator = MockGenerator(resolver, logger, functionsMiner)

val fields = resolver.getSymbolsWithAnnotation(MOCK_ANNOTATION)
fields.forEach { set ->
if (set !is KSPropertyDeclaration) error("$set is not a property declaration but is annotated with @Mock, not supported")
mockGenerator.createClass(set.type).writeTo(
codeGenerator = codeGenerator,
aggregating = false
)
}
return emptyList()
}

companion object {
const val MOCK_ANNOTATION = "com.careem.mockingbird.test.annotations.Mock"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright Careem, an Uber Technologies Inc. company
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.careem.mockingbird.processor

import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider

class GenerateMocksSymbolProcessorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return GenerateMocksSymbolProcessor(environment.codeGenerator, environment.logger)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.careem.mockingbird.processor

import com.google.devtools.ksp.symbol.KSType

internal fun KSType.fullyQualifiedName() =
"${this.declaration.qualifiedName?.asString()}"
Loading

0 comments on commit b951415

Please sign in to comment.