diff --git a/.gitignore b/.gitignore index 8b975d9..5b415de 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ .externalNativeBuild .cxx local.properties +.kotlin diff --git a/build.gradle.kts b/build.gradle.kts index 095decf..dfde62c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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") } @@ -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 { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a54f155..eebc275 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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' @@ -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' } @@ -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'} @@ -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' } \ No newline at end of file +paparazzi = { module = 'app.cash.paparazzi:paparazzi-gradle-plugin', version = '1.3.4' } + +[plugins] +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } diff --git a/integration-tests/android/build.gradle.kts b/integration-tests/android/build.gradle.kts index 23fc85b..5b124c9 100644 --- a/integration-tests/android/build.gradle.kts +++ b/integration-tests/android/build.gradle.kts @@ -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 { @@ -33,10 +33,6 @@ android { ) } - composeOptions { - kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get() - } - buildFeatures { compose = true buildConfig = true @@ -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) diff --git a/integration-tests/android/src/main/kotlin/com/jeppeman/mockposable/integrationtests/android/TestFragment.kt b/integration-tests/android/src/main/kotlin/com/jeppeman/mockposable/integrationtests/android/TestFragment.kt index 56894c6..7e80432 100644 --- a/integration-tests/android/src/main/kotlin/com/jeppeman/mockposable/integrationtests/android/TestFragment.kt +++ b/integration-tests/android/src/main/kotlin/com/jeppeman/mockposable/integrationtests/android/TestFragment.kt @@ -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 @@ -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) } diff --git a/integration-tests/android/src/test/kotlin/com/jeppeman/mockposable/integrationtests/android/SnapshotTest.kt b/integration-tests/android/src/test/kotlin/com/jeppeman/mockposable/integrationtests/android/SnapshotTest.kt index 8dae63a..eddbf24 100644 --- a/integration-tests/android/src/test/kotlin/com/jeppeman/mockposable/integrationtests/android/SnapshotTest.kt +++ b/integration-tests/android/src/test/kotlin/com/jeppeman/mockposable/integrationtests/android/SnapshotTest.kt @@ -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 ) ) diff --git a/mockposable/build.gradle.kts b/mockposable/build.gradle.kts index ae7e77c..6763362 100644 --- a/mockposable/build.gradle.kts +++ b/mockposable/build.gradle.kts @@ -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 { diff --git a/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/IrUtils.kt b/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/IrUtils.kt index acc0f1c..513baa7 100644 --- a/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/IrUtils.kt +++ b/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/IrUtils.kt @@ -1,12 +1,21 @@ 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 @@ -14,12 +23,23 @@ 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 IrPluginContext.buildIr( declarationSymbol: IrSymbol, diff --git a/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockKIrGenerationExtension.kt b/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockKIrGenerationExtension.kt index 38416b7..d1c7952 100644 --- a/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockKIrGenerationExtension.kt +++ b/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockKIrGenerationExtension.kt @@ -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 @@ -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" diff --git a/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockitoIrGenerationExtension.kt b/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockitoIrGenerationExtension.kt index fd6c218..a34907e 100644 --- a/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockitoIrGenerationExtension.kt +++ b/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockitoIrGenerationExtension.kt @@ -108,10 +108,10 @@ 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 @@ -119,14 +119,11 @@ private val IrCall.isOnComposable: Boolean 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" diff --git a/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockposablePlugin.kt b/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockposablePlugin.kt index eb77152..cf3ff5e 100644 --- a/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockposablePlugin.kt +++ b/mockposable/mockposable-compiler/src/main/java/com/jeppeman/mockposable/compiler/MockposablePlugin.kt @@ -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 @@ -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 diff --git a/mockposable/mockposable-compiler/src/test/java/com/jeppeman/mockposable/compiler/MockposableCompilerTest.kt b/mockposable/mockposable-compiler/src/test/java/com/jeppeman/mockposable/compiler/MockposableCompilerTest.kt index 5c289af..7d095bd 100644 --- a/mockposable/mockposable-compiler/src/test/java/com/jeppeman/mockposable/compiler/MockposableCompilerTest.kt +++ b/mockposable/mockposable-compiler/src/test/java/com/jeppeman/mockposable/compiler/MockposableCompilerTest.kt @@ -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()) diff --git a/mockposable/mockposable-gradle/build.gradle.kts b/mockposable/mockposable-gradle/build.gradle.kts index 3252328..8a90539 100644 --- a/mockposable/mockposable-gradle/build.gradle.kts +++ b/mockposable/mockposable-gradle/build.gradle.kts @@ -1,4 +1,3 @@ -import com.android.build.gradle.internal.tasks.factory.dependsOn import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @Suppress("DSL_SCOPE_VIOLATION") @@ -21,7 +20,6 @@ val copyVersionProvider = tasks.register("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(), @@ -40,6 +38,7 @@ dependencies { implementation(gradleApi()) implementation(localGroovy()) implementation(libs.kotlin.gradle) + implementation(libs.compose.compiler.gradle) } gradlePlugin { diff --git a/mockposable/mockposable-gradle/src/main/java/com/jeppeman/mockposable/gradle/MockposableSubPlugin.kt b/mockposable/mockposable-gradle/src/main/java/com/jeppeman/mockposable/gradle/MockposableSubPlugin.kt index 1d793de..8e26be0 100644 --- a/mockposable/mockposable-gradle/src/main/java/com/jeppeman/mockposable/gradle/MockposableSubPlugin.kt +++ b/mockposable/mockposable-gradle/src/main/java/com/jeppeman/mockposable/gradle/MockposableSubPlugin.kt @@ -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 @@ -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( diff --git a/mockposable/mockposable-gradle/version/Version.kt b/mockposable/mockposable-gradle/version/Version.kt index f5f41d4..700c870 100644 --- a/mockposable/mockposable-gradle/version/Version.kt +++ b/mockposable/mockposable-gradle/version/Version.kt @@ -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") diff --git a/mockposable/mockposable-runtime/mockposable-runtime-composeui/build.gradle.kts b/mockposable/mockposable-runtime/mockposable-runtime-composeui/build.gradle.kts index c49ebf8..27da074 100644 --- a/mockposable/mockposable-runtime/mockposable-runtime-composeui/build.gradle.kts +++ b/mockposable/mockposable-runtime/mockposable-runtime-composeui/build.gradle.kts @@ -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 }