aboutsummaryrefslogtreecommitdiff
path: root/gradle-plugin/src/test/kotlin/com/google/devtools/ksp/gradle/testing/KspIntegrationTestRule.kt
blob: 752400b91cde89766baac6ea7f004a3a793a2270 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * Copyright 2020 Google LLC
 * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
 *
 * 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.google.devtools.ksp.gradle.testing

import com.google.devtools.ksp.processing.SymbolProcessorProvider
import org.gradle.testkit.runner.GradleRunner
import org.junit.rules.TemporaryFolder
import org.junit.rules.TestWatcher
import org.junit.runner.Description
import kotlin.reflect.KClass

/**
 * JUnit test rule to setup a [TestProject] which contains a KSP processor module and an
 * application. The application can either be an android app or jvm app.
 * Test must call [setupAppAsAndroidApp] or [setupAppAsJvmApp] before using the [runner].
 */
class KspIntegrationTestRule(
    private val tmpFolder: TemporaryFolder
) : TestWatcher() {
    /**
     * Initialized when the test starts.
     */
    private lateinit var testProject: TestProject

    /**
     * The application module in the test project
     */
    val appModule
        get() = testProject.appModule

    /**
     * The processor module in the test project
     */
    val processorModule
        get() = testProject.processorModule

    /**
     * The configuration passed from the KSP's main build which includes important setup information
     * like KSP version, local maven repo etc.
     */
    val testConfig = TestConfig.read()

    /**
     * Returns a gradle runner that is ready to run tasks on the test project.
     */
    fun runner(): GradleRunner {
        testProject.writeFiles()
        return GradleRunner.create()
            .withProjectDir(testProject.rootDir)
    }

    /**
     * Adds the given [SymbolProcessorProvider] to the list of providers in the processor module.
     * The processors built with these providers will run on the test application.
     *
     * The passed argument must be a class with a name (e.g. not inline) as it will be added to
     * the classpath of the processor and will be re-loaded when the test runs. For this reason,
     * these classes cannot access to the rest of the test instance.
     */
    fun addProvider(provider: KClass<out SymbolProcessorProvider>) {
        val qName = checkNotNull(provider.java.name) {
            "Must provide a class that can be loaded by qualified name"
        }
        testProject.processorModule.kspServicesFile.appendText("$qName\n")
    }

    /**
     * Sets up the app module as a jvm app, adding necessary plugin dependencies.
     */
    fun setupAppAsJvmApp() {
        testProject.appModule.plugins.addAll(listOf(
            PluginDeclaration.kotlin("jvm", testConfig.kotlinBaseVersion),
            PluginDeclaration.id("com.google.devtools.ksp", testConfig.kspVersion)
        ))
    }

    /**
     * Sets up the app module as an android app, adding necessary plugin dependencies, a manifest
     * file and necessary gradle configuration.
     */
    fun setupAppAsAndroidApp() {
        testProject.appModule.plugins.addAll(listOf(
            PluginDeclaration.id("com.android.application", testConfig.androidBaseVersion),
            PluginDeclaration.kotlin("android", testConfig.kotlinBaseVersion),
            PluginDeclaration.id("com.google.devtools.ksp", testConfig.kspVersion)
        ))
        testProject.appModule.buildFileAdditions.add(
            """
            android {
                compileSdkVersion="android-29"
            }
        """.trimIndent()
        )
        testProject.appModule.moduleRoot.resolve("src/main/AndroidManifest.xml")
            .also {
                it.parentFile.mkdirs()
            }.writeText(
                """
            <?xml version="1.0" encoding="utf-8"?>
            <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                package="com.example.kspandroidtestapp">
            </manifest>
            """.trimIndent()
            )
    }

    override fun starting(description: Description) {
        super.starting(description)
        testProject = TestProject(tmpFolder.newFolder(), testConfig)
    }
}