Skip to content

Commit

Permalink
Kotlin 2.0 support (fixes #23)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeppeman committed Jun 30, 2024
1 parent ebe6bad commit 0001259
Show file tree
Hide file tree
Showing 16 changed files with 63 additions and 55 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
.externalNativeBuild
.cxx
local.properties
.kotlin
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ buildscript {
dependencies {
classpath(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
classpath(libs.android.gradle)
classpath(libs.molecule)
classpath(libs.paparazzi)
classpath("com.jeppeman.mockposable:mockposable-gradle")
}
Expand All @@ -20,6 +19,7 @@ buildscript {
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
kotlin("jvm") version libs.versions.kotlin.get() apply false
alias(libs.plugins.compose.compiler) apply false
}

allprojects {
Expand Down
17 changes: 10 additions & 7 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
[versions]
kotlin = '1.9.23'
kotlin = '2.0.0'
compose-ui = '1.6.4'
compose-compiler = '1.5.11'
google-ksp = '1.9.23-1.0.19'
google-ksp = '2.0.0-1.0.22'
activity = '1.8.2'
agp = '8.2.2'
espresso = '3.5.1'
Expand All @@ -21,7 +20,8 @@ androidx-test-runner = { module = 'androidx.test:runner', version = '1.5.2'}
androidx-test-espresso-core = { module = 'androidx.test.espresso:espresso-core', version.ref = 'espresso' }
androidx-fragment-testing = { module = 'androidx.fragment:fragment-testing', version = '1.6.2' }

compose-compiler = { module = 'androidx.compose.compiler:compiler', version.ref = 'compose-compiler' }
compose-compiler = { module = 'org.jetbrains.kotlin:kotlin-compose-compiler-plugin-embeddable', version.ref = 'kotlin' }
compose-compiler-gradle = { module = 'org.jetbrains.kotlin.plugin.compose:org.jetbrains.kotlin.plugin.compose.gradle.plugin', version.ref = 'kotlin' }
compose-runtime = { module = 'org.jetbrains.compose.runtime:runtime', version = '1.4.1' }
compose-ui = { module = 'androidx.compose.ui:ui', version.ref = 'compose-ui' }
compose-material = { module = 'androidx.compose.material:material', version = '1.6.4' }
Expand All @@ -37,13 +37,13 @@ autoservice-annotations = { module = 'com.google.auto.service:auto-service-annot
google-ksp = { module = 'com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin', version.ref = 'google-ksp' }

# compile-testing = { module = 'com.github.tschuchortdev:kotlin-compile-testing', version = '1.5.0' }
compile-testing = { module = 'dev.zacsweers.kctfork:core', version = '0.4.0' }
compile-testing = { module = 'dev.zacsweers.kctfork:core', version = '0.5.1' }

junit = { module = 'androidx.test.ext:junit', version = '1.1.3' }

robolectric = { module = 'org.robolectric:robolectric', version = '4.9' }

molecule = { module = 'app.cash.molecule:molecule-gradle-plugin', version = '0.5.0' }
molecule = { module = 'app.cash.molecule:molecule-runtime', version = '2.0.0' }

hamcrest = { module = 'org.hamcrest:hamcrest', version = '2.2'}

Expand All @@ -54,4 +54,7 @@ mockito-kotlin = { module = 'org.mockito.kotlin:mockito-kotlin', version = '4.0.
mockito-inline = { module = 'org.mockito:mockito-inline', version.ref = 'mockito' }
mockito-android = { module = 'org.mockito:mockito-android', version.ref = 'mockito' }

paparazzi = { module = 'app.cash.paparazzi:paparazzi-gradle-plugin', version = '1.1.0' }
paparazzi = { module = 'app.cash.paparazzi:paparazzi-gradle-plugin', version = '1.3.4' }

[plugins]
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
9 changes: 3 additions & 6 deletions integration-tests/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import com.jeppeman.mockposable.gradle.COMPOSE_UI
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
alias(libs.plugins.compose.compiler)
id("com.jeppeman.mockposable")
id("app.cash.molecule")
id("app.cash.paparazzi")
}

mockposable {
plugins = listOf(MOCKK, MOCKITO, COMPOSE_UI)
composeCompilerPluginVersion = libs.versions.compose.compiler.get()
composeCompilerPluginVersion = libs.versions.kotlin.get()
}

android {
Expand All @@ -33,10 +33,6 @@ android {
)
}

composeOptions {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}

buildFeatures {
compose = true
buildConfig = true
Expand Down Expand Up @@ -70,6 +66,7 @@ dependencies {
implementation(libs.compose.material)
implementation(libs.compose.ui)
implementation(libs.compose.ui.test.manifest)
implementation(libs.molecule)

testImplementation(libs.hamcrest)
testImplementation(libs.robolectric)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import android.widget.TextView
import androidx.compose.runtime.Composable
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import app.cash.molecule.RecompositionClock
import app.cash.molecule.RecompositionMode
import app.cash.molecule.launchMolecule
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
Expand All @@ -38,7 +38,7 @@ class TestFragment : Fragment() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val models = lifecycleScope.launchMolecule(RecompositionClock.Immediate) {
val models = lifecycleScope.launchMolecule(RecompositionMode.Immediate) {
present(events)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ class SnapshotTest {
deviceConfig = DeviceConfig.PIXEL_5,
theme = "Theme.Material",
environment = detectEnvironment().copy(
platformDir = "${androidHome()}/platforms/android-32",
compileSdkVersion = 32
platformDir = "${androidHome()}/platforms/android-34",
compileSdkVersion = 34
)
)

Expand Down
1 change: 1 addition & 0 deletions mockposable/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ buildscript {
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
kotlin("jvm") version libs.versions.kotlin.get() apply false
alias(libs.plugins.compose.compiler) apply false
}

allprojects {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
package com.jeppeman.mockposable.compiler

import org.jetbrains.kotlin.backend.common.extensions.FirIncompatiblePluginAPI
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrContainerExpression
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
import org.jetbrains.kotlin.ir.expressions.IrGetField
import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.expressions.IrReturn
import org.jetbrains.kotlin.ir.expressions.IrTry
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.expressions.IrWhen
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.dumpKotlinLike
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.statements
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.Logger
import org.jetbrains.kotlin.utils.addToStdlib.safeAs

@OptIn(FirIncompatiblePluginAPI::class)
fun IrPluginContext.classSymbol(fqName: String): IrClassSymbol = referenceClass(FqName(fqName))!!
fun FqName.classId(
isLocal: Boolean = false
): ClassId = ClassId(parent(), FqName.topLevel(shortName()), isLocal)

fun IrPluginContext.classSymbol(
fqName: String
): IrClassSymbol = referenceClass(FqName(fqName).classId())!!

fun String.callableId(
packageName: FqName
): CallableId = CallableId(packageName, Name.identifier(this))

fun <T> IrPluginContext.buildIr(
declarationSymbol: IrSymbol,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,19 +132,19 @@ private val IrValueParameter.anyMatcherFunction: IrSimpleFunctionSymbol
)

private val everyComposableFqName: FqName
get() = FqName("${MOCKPOSABLE_MOCKK_FILE_NAME}.everyComposable")
get() = FqName("${MOCKPOSABLE_MOCKK_PACKAGE_NAME}.everyComposable")

private val verifyComposableFqName: FqName
get() = FqName("${MOCKPOSABLE_MOCKK_FILE_NAME}.verifyComposable")
get() = FqName("${MOCKPOSABLE_MOCKK_PACKAGE_NAME}.verifyComposable")

private val verifyComposableAllFqName: FqName
get() = FqName("${MOCKPOSABLE_MOCKK_FILE_NAME}.verifyComposableAll")
get() = FqName("${MOCKPOSABLE_MOCKK_PACKAGE_NAME}.verifyComposableAll")

private val verifyComposableOrderFqName: FqName
get() = FqName("${MOCKPOSABLE_MOCKK_FILE_NAME}.verifyComposableOrder")
get() = FqName("${MOCKPOSABLE_MOCKK_PACKAGE_NAME}.verifyComposableOrder")

private val verifyComposableSequenceFqName: FqName
get() = FqName("${MOCKPOSABLE_MOCKK_FILE_NAME}.verifyComposableSequence")
get() = FqName("${MOCKPOSABLE_MOCKK_PACKAGE_NAME}.verifyComposableSequence")

private val IrCall.isEveryComposable: Boolean
get() = symbol.owner.fqNameWhenAvailable == everyComposableFqName
Expand All @@ -169,5 +169,3 @@ private val IrCall.isMockKStubScopeReturnType: Boolean

private const val MOCKK_PACKAGE_NAME = "io.mockk"
private const val MOCKPOSABLE_MOCKK_PACKAGE_NAME = "com.jeppeman.mockposable.mockk"
private const val MOCKPOSABLE_MOCKK_FILE_NAME =
"${MOCKPOSABLE_MOCKK_PACKAGE_NAME}.MockposableMockKKt"
Original file line number Diff line number Diff line change
Expand Up @@ -108,25 +108,22 @@ private fun IrFunctionExpression.transformAllComposableCalls(
}

private val onComposableFqName: FqName
get() = FqName("${MOCKPOSABLE_MOCKITO_FILE_NAME}.onComposable")
get() = FqName("${MOCKPOSABLE_MOCKITO_PACKAGE_NAME}.onComposable")

private val verifyComposableFqName: FqName
get() = FqName("${MOCKPOSABLE_MOCKITO_FILE_NAME}.verifyComposable")
get() = FqName("${MOCKPOSABLE_MOCKITO_PACKAGE_NAME}.verifyComposable")

private val IrCall.isOnComposable: Boolean
get() = symbol.owner.fqNameWhenAvailable == onComposableFqName

private val IrCall.isVerifyComposable: Boolean
get() = symbol.owner.fqNameWhenAvailable == verifyComposableFqName

@OptIn(FirIncompatiblePluginAPI::class)
private val IrPluginContext.anyMatcherFunction: IrSimpleFunctionSymbol
get() = referenceFunctions(FqName("${MOCKPOSABLE_MOCKITO_PACKAGE_NAME}.any"))
get() = referenceFunctions("any".callableId(FqName(MOCKPOSABLE_MOCKITO_PACKAGE_NAME)))
.firstOrNull()
?: pluginError(
"\"${MOCKPOSABLE_MOCKITO_PACKAGE_NAME}.any\" not found, this should not happen."
)

private const val MOCKPOSABLE_MOCKITO_PACKAGE_NAME = "com.jeppeman.mockposable.mockito"
private const val MOCKPOSABLE_MOCKITO_FILE_NAME =
"${MOCKPOSABLE_MOCKITO_PACKAGE_NAME}.MockposableMockitoKt"
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.toLogger
import org.jetbrains.kotlin.cli.common.messages.toLogger
import org.jetbrains.kotlin.com.intellij.mock.MockProject
import org.jetbrains.kotlin.com.intellij.openapi.extensions.LoadingOrder
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
Expand All @@ -17,6 +17,9 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
@Suppress("unused") // Invoked by kotlinc
@AutoService(ComponentRegistrar::class)
class MockposablePlugin : ComponentRegistrar {
override val supportsK2: Boolean
get() = true

override fun registerProjectComponents(
project: MockProject,
configuration: CompilerConfiguration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ fun compile(
val mockposableCommandLineProcessor = MockposableCommandLineProcessor()
val composeCommandLineProcessor = ComposeCommandLineProcessor()
return KotlinCompilation().apply {
languageVersion = "1.9"
languageVersion = "2.0"
sources = sourceFiles
commandLineProcessors = listOf(mockposableCommandLineProcessor, composeCommandLineProcessor)
componentRegistrars = listOf(ComposePluginRegistrar(), MockposablePlugin())
Expand Down
3 changes: 1 addition & 2 deletions mockposable/mockposable-gradle/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import com.android.build.gradle.internal.tasks.factory.dependsOn
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

@Suppress("DSL_SCOPE_VIOLATION")
Expand All @@ -21,7 +20,6 @@ val copyVersionProvider = tasks.register<Copy>("copyVersion") {
"mockposableMockitoCoordinates" to "${group}:mockposable-runtime-mockito:${projectVersion}",
"mockposableMockKCoordinates" to "${group}:mockposable-runtime-mockk:${projectVersion}",
"mockposableComposeUiCoordinates" to "${group}:mockposable-runtime-composeui:${projectVersion}",
"composeCompilerCoordinates" to libs.compose.compiler.get(),
"composeRuntimeCoordinates" to libs.compose.runtime.get(),
"mockKCoordinates" to libs.mockk.core.get(),
"mockKAndroidCoordinates" to libs.mockk.android.get(),
Expand All @@ -40,6 +38,7 @@ dependencies {
implementation(gradleApi())
implementation(localGroovy())
implementation(libs.kotlin.gradle)
implementation(libs.compose.compiler.gradle)
}

gradlePlugin {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.jeppeman.mockposable.gradle

import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradleSubplugin
import org.jetbrains.kotlin.gradle.model.ComposeCompiler
import org.jetbrains.kotlin.gradle.plugin.*

@Suppress("unused") // Invoked by gradle
Expand Down Expand Up @@ -59,16 +61,7 @@ class MockposableSubPlugin : KotlinCompilerPluginSupportPlugin {
.dependencies
.add(project.dependencies.create(COMPOSE_RUNTIME_COORDINATES))

val composeCompilerCoordinates = if (extension.composeCompilerPluginVersion.isNotBlank()) {
COMPOSE_COMPILER_COORDINATES.dropLastWhile { it != ':' } + extension.composeCompilerPluginVersion
} else {
COMPOSE_COMPILER_COORDINATES
}

project.configurations
.getByName(PLUGIN_CLASSPATH_CONFIGURATION_NAME)
.dependencies
.add(project.dependencies.create(composeCompilerCoordinates))
project.plugins.apply(ComposeCompilerGradleSubplugin::class.java)

return project.provider {
listOf(
Expand Down
1 change: 0 additions & 1 deletion mockposable/mockposable-gradle/version/Version.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package com.jeppeman.mockposable.gradle

internal const val PROJECT_VERSION = "$projectVersion"
internal const val COMPOSE_COMPILER_COORDINATES = "$composeCompilerCoordinates"
internal const val COMPOSE_RUNTIME_COORDINATES = "$composeRuntimeCoordinates"
internal val MOCKITO_JVM_DEPENDENCIES = listOf("$mockitoCoordinates", "$mockposableMockitoCoordinates")
internal val MOCKITO_ANDROID_DEPENDENCIES = listOf("$mockitoCoordinates", "$mockitoAndroidCoordinates", "$mockposableMockitoCoordinates")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,13 @@ buildscript {
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
alias(libs.plugins.compose.compiler)
}

android {
compileSdk = 34
namespace = "com.jeppeman.mockposable.composeui"

composeOptions {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}

buildFeatures {
compose = true
}
Expand Down

0 comments on commit 0001259

Please sign in to comment.