kosi-libs / MocKMP

A mocking processor for Kotlin Multiplatform
https://kosi-libs.org/mockmp
MIT License
177 stars 12 forks source link

Support Kotlin 2.0 #77

Open SalomonBrys opened 1 month ago

SalomonBrys commented 1 month ago

This issue is here to discuss the path to supporting K2. It outlines the issues I've encountered as well as their possible workarounds. Feel free to enter the discussion, especially if you have any insight or wild idea ;)

MocKMP generates source code for the Test source-sets, which is not properly supported by KSP. KSP does support generating code for the commonMain sourceset, but not for the commonTest sourceset.

Up until now, we've used the following trick: register the MocKMP processor only in the kspJvmTest configuration and have the commonTest sourceset use the build/generated/ksp/jvm/jvmTest/kotlin directory. This requires some more Gradle configuration (namely having all targets compilation task to depend on kspTestKotlinJvm) but it worked allright (even though it made my heart ache).

Kotlin 2.0 brings a new issue:

e: Files '/[PROJECT]/build/generated/ksp/jvm/jvmTest/kotlin/tests/InjectionTests_injectMocks.kt', ... can be a part of only one module, but is listed as a source for both `jvmTest` and `commonTest`, please check you -Xfragment-sources options.

It looks like K2 is more restrictive than K1, and does not allow the same file to be in multiple sourcesets at once. I also haven't found a way to emulate running KSP to commonTest only, or to remove the generated/ksp/jvm/jvmTest/kotlin directory from the JVM test compilation.

I've tried another approach that consists of applying the processor to all test targets:

dependencies {
    configurations.names
        .filter { it.startsWith("ksp") && it.endsWith("Test") }
        .forEach {
            it(projects.mockmpProcessor)
        }
}

...but that brings a new issue: because K2 is also more restrictive as to which sources are accessed when compiling common code, this approach does not compile at all (yielding unresolved symbol all around errors when compiling common code).

So far, the only approach I've thought of that would (I think) resolve the issue is to follow the KotlinX.RPC & KotlinX.Serialization paths and have a Compiler plugin that adds an AssociatedObjectKey annotation to each class annotated with @Mock or @Fake. This cannot be done with KSP as it notoriously cannot modify the sources of the code it is processing. Such annotations allow a Native & Js code to access an object associated to a class at compile-time (it does not exist in JVM, but the JVM provides reflection so there's no need for it). This in turn allows to have a common code that does not directly reference generated code. This is a path I'd rather not take, because:

MocKMP is now at a crossroad as I have found, at the moment, no satisfactory path to have it compatible with K2. Let's keep looking! Make sure to share any idea or knowledge I've missed ;)

GoodSir42 commented 1 month ago

Thanks for the detailed explanation of the problem. Even if I am pretty sure that I am only beginning to understand the basic setup maybe we can exchange a few thoughts here. If I understand the KSP docs here correctly there seems to be a kspTest task for multiplatform. Could we maybe hook into that? Also: Did you already investigate whether KSP 2.0 might or might not have an impact or helpful changes for the issue? I am struggling with finding specific information about it.

SalomonBrys commented 1 month ago

There's no kspTest in a Kotlin/Multiplatform project. Here are the available configurations on my mac:

kspCommonMainMetadata kspIosSimulatorArm64 kspIosSimulatorArm64Test kspIosX64 kspIosX64Test kspJs kspJsTest kspJvm kspJvmTest kspLinuxX64 kspLinuxX64Test kspMacosArm64 kspMacosArm64Test kspMacosX64 kspMacosX64Test kspMingwX64 kspMingwX64Test kspTvosSimulatorArm64 kspTvosSimulatorArm64Test kspTvosX64 kspTvosX64Test kspWatchosSimulatorArm64 kspWatchosSimulatorArm64Test kspWatchosX64 kspWatchosX64Test

As I've explained applying KSP to each test sourceset does not work because of K2 Common / Target sourceset separation.

GoodSir42 commented 1 month ago

Thanks for the explanation! So it looks like the issue here is really just that KSP runs for anything except commonTest if I see it well. Does this mean we could generate files for jvm test for example and run the tests there? I know it is not the goal of a multiplatform library to limit tests to the different sub packages, I am just trying to understand the underlying issue. In the meantime I will add my vote to the KSP issue and ask the ksp team to shed a light on why they seem to exclude commonTest explicitly.

Edit: would it be possible to move the generated code from one folder to the other in the generation step? I know this is hacky but it might keep the project running until the underlying issues in KSP are resolved/KSP is extended

GrayaMax commented 5 days ago

Hello, i get the same issue, i choose to stay on ksp 1.9.24-1.0.20 for now instead removing MocKMP usage. But has a workaround been found ? Or should i remove MockMP usage ?