ZacSweers / kotlin-compile-testing

A library for testing Kotlin and Java annotation processors, compiler plugins and code generation
Mozilla Public License 2.0
109 stars 7 forks source link

How to use KSP? #296

Closed IceBlizz6 closed 6 days ago

IceBlizz6 commented 2 weeks ago

Been spending a lot of time trying to figure out how to make compilation use KSP. Tried adding kspWithCompilation, calling different combinations of useKsp2 and configureKsp. I think i'm just stumbling around in the dark.

A guide would be very helpful.

So this test works as expected with kotlin-compile-testing-ksp:1.6.0

import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
import com.google.devtools.ksp.symbol.KSAnnotated
import com.tschuchort.compiletesting.KotlinCompilation
import com.tschuchort.compiletesting.symbolProcessorProviders
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.junit.Assert
import kotlin.test.Test

class Tests {
    @Test
    @OptIn(ExperimentalCompilerApi::class)
    fun isMySymbolProcessorProviderCalled() {
        val provider = MySymbolProcessorProvider()
        val compilation = KotlinCompilation().apply {
            symbolProcessorProviders = listOf(provider)
        }
        compilation.compile()
        Assert.assertTrue(provider.called)
    }
}

class MySymbolProcessorProvider : SymbolProcessorProvider {
    var called = false

    override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
        called = true
        return MySymbolProcessor()
    }
}

class MySymbolProcessor : SymbolProcessor {
    override fun process(resolver: Resolver): List<KSAnnotated> {
        return emptyList()
    }
}
plugins {
    kotlin("jvm") version "1.9.25"
    `maven-publish`
}

repositories {
    mavenCentral()
}

sourceSets.test {
    kotlin.srcDir("test")
}

dependencies {
    implementation("com.google.devtools.ksp:symbol-processing-api:1.9.25-1.0.20")
    implementation("com.github.tschuchortdev:kotlin-compile-testing-ksp:1.6.0")
    implementation("org.jetbrains.kotlin:kotlin-test:1.9.25")
}

Changing dependencies to the below and now it seems like no matter what i do the test will never pass.

plugins {
    kotlin("jvm") version "2.0.21"
    `maven-publish`
}

repositories {
    mavenCentral()
}

sourceSets.test {
    kotlin.srcDir("test")
}

dependencies {
    implementation("com.google.devtools.ksp:symbol-processing-api:2.0.21-1.0.25")
    implementation("dev.zacsweers.kctfork:ksp:0.5.1")
    implementation("org.jetbrains.kotlin:kotlin-test:2.0.21")
}
solonovamax commented 6 days ago

Looking at their tests: the issue is that you have done none of the following:

  1. set the language version to <2.0

    
    class Tests {
       @Test
       @OptIn(ExperimentalCompilerApi::class)
       fun isMySymbolProcessorProviderCalled() {
           val compilation = KotlinCompilation().apply {
               languageVersion = "1.9" // this can really just be anything that is less than 2.0
               configureKsp {
                   symbolProcessorProviders += MySymbolProcessorProvider()
               }
           }
           compilation.compile()
           Assert.assertTrue(provider.called)
       }
    }
  2. enabled ksp2:

    
    class Tests {
       @Test
       @OptIn(ExperimentalCompilerApi::class)
       fun isMySymbolProcessorProviderCalled() {
           val compilation = KotlinCompilation().apply {
               configureKsp(useKsp2 = true) {
                   symbolProcessorProviders += MySymbolProcessorProvider()
               }
           }
           compilation.compile()
           Assert.assertTrue(provider.called)
       }
    }
IceBlizz6 commented 6 days ago

Yeah, second part works with Kotlin 2.0.21 from what i can see, the code passes now.

You forgot to make the provider variable in your sample so i fixed it and here is the new code:

@Test
@OptIn(ExperimentalCompilerApi::class)
fun isMySymbolProcessorProviderCalled() {
    val provider = MySymbolProcessorProvider()
    val compilation = KotlinCompilation().apply {
        configureKsp(useKsp2 = true) {
            symbolProcessorProviders += provider
        }
    }
    compilation.compile()
    Assert.assertTrue(provider.called)
}

So the important change here is the line symbolProcessorProviders += provider inside configureKsp(useKsp2 = true) { because that is different from referencing symbolProcessorProviders directly inside KotlinCompilation().apply

I will close this as it has been solved, but do consider adding something like this to the readme.