Skip to content

Commit

Permalink
Write tests (#34)
Browse files Browse the repository at this point in the history
* Add test dependencies

* Gradle cleaning

* Configure test

* Improve testability

* Cleanup existing tests

* Test todos

* Write StabilityWrapper tests

* Write runtime tests

* LoadingOrder todos

* Write DurableKey tests

* Improve StabilityWrapper test

* Write InvalidationTrackableTransformer test (wip)

* Fix Out of index exception when additionalVisitor is not null

* Fix StabilityWrapper test

* Todo InvalidationTrackableTransformer test
  • Loading branch information
jisungbin committed Nov 19, 2023
1 parent d0a1971 commit 5bcf463
Show file tree
Hide file tree
Showing 28 changed files with 1,028 additions and 65 deletions.
15 changes: 15 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
* Please see full license: https://github.com/jisungbin/ComposeInvestigator/blob/main/LICENSE
*/

import com.adarshr.gradle.testlogger.TestLoggerExtension
import com.adarshr.gradle.testlogger.theme.ThemeType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jlleitschuh.gradle.ktlint.KtlintExtension

plugins {
alias(libs.plugins.test.gradle.logging) apply false
alias(libs.plugins.kotlin.ktlint) apply false
}

Expand All @@ -31,10 +34,16 @@ subprojects {
}

apply {
plugin(rootProject.libs.plugins.test.gradle.logging.get().pluginId)
plugin(rootProject.libs.plugins.kotlin.ktlint.get().pluginId)
}

afterEvaluate {
extensions.configure<TestLoggerExtension> {
theme = ThemeType.MOCHA_PARALLEL
slowThreshold = 10_000
}

extensions.configure<KtlintExtension> {
version.set(rootProject.libs.versions.kotlin.ktlint.source.get())
android.set(true)
Expand All @@ -43,12 +52,18 @@ subprojects {

tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "17"
freeCompilerArgs = freeCompilerArgs + listOf(
"-opt-in=kotlin.OptIn",
"-opt-in=kotlin.RequiresOptIn",
)
}
}

tasks.withType<Test>().configureEach {
useJUnitPlatform()
outputs.upToDateWhen { false }
}
}
}

Expand Down
12 changes: 7 additions & 5 deletions compiler-integration-test/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id("com.android.library")
kotlin("android")
Expand All @@ -19,9 +17,10 @@ android {
}
}

tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "17"
kotlin {
compilerOptions {
optIn.add("org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi")
optIn.add("org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction")
}
}

Expand All @@ -32,8 +31,11 @@ dependencies {
testImplementation(libs.compose.compiler)
testImplementation(libs.compose.material)

testImplementation(libs.kotlin.compiler.embedded)
testImplementation(libs.test.kotlin.compilation)

testImplementation(libs.test.mockk)
testImplementation(libs.test.kotest)
testImplementation(libs.test.junit.core)
testRuntimeOnly(libs.test.junit.enigne)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Designed and developed by Ji Sungbin 2023.
*
* Licensed under the MIT.
* Please see full license: https://github.com/jisungbin/ComposeInvestigator/blob/main/LICENSE
*/

@file:Suppress("UnusedReceiverParameter")

package land.sungbin.composeinvestigator.compiler.test

import androidx.compose.compiler.plugins.kotlin.ComposeCommandLineProcessor
import androidx.compose.compiler.plugins.kotlin.ComposePluginRegistrar
import com.tschuchort.compiletesting.KotlinCompilation
import com.tschuchort.compiletesting.PluginOption
import com.tschuchort.compiletesting.SourceFile
import land.sungbin.composeinvestigator.compiler.COMPOSE_INVESTIGATOR_PLUGIN_ID
import land.sungbin.composeinvestigator.compiler.ComposeInvestigatorCommandLineProcessor
import land.sungbin.composeinvestigator.compiler.ComposeInvestigatorPluginRegistrar
import land.sungbin.composeinvestigator.compiler.OPTION_VERBOSE
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.com.intellij.mock.MockProject
import org.jetbrains.kotlin.com.intellij.openapi.extensions.LoadingOrder
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import java.io.File

interface IrBaseTest

fun IrBaseTest.emptyIrElementVisitor() = object : IrElementVisitorVoid {}

@Suppress("DEPRECATION")
fun IrBaseTest.buildIrVisiterRegistrar(builder: (context: IrPluginContext) -> IrElementVisitorVoid): ComponentRegistrar =
object : ComponentRegistrar {
override val supportsK2 = false
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
@Suppress("UnstableApiUsage")
project.extensionArea
.getExtensionPoint(IrGenerationExtension.extensionPointName)
.registerExtension(
object : IrGenerationExtension {
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
moduleFragment.acceptVoid(builder(pluginContext))
}
},
// TODO: LoadingOrder("after $COMPOSE_INVESTIGATOR_PLUGIN_ID"),
// https://github.com/JetBrains/intellij-community/blob/17ca1d0afb43f824cda948fc3ea4467ebe55b346/platform/extensions/src/com/intellij/openapi/extensions/LoadingOrder.kt#L24
LoadingOrder.LAST,
project,
)
}
}

fun IrBaseTest.kotlinCompilation(
workingDir: File,
enableComposeCompiler: Boolean = true,
enableVerboseLogging: Boolean = true,
@Suppress("DEPRECATION") additionalVisitor: ComponentRegistrar? = null,
vararg sourceFiles: SourceFile,
) = KotlinCompilation().apply {
this.workingDir = workingDir
sources = sourceFiles.asList()
jvmTarget = JvmTarget.JVM_17.toString()
inheritClassPath = true
supportsK2 = false
pluginOptions = listOf(
PluginOption(
pluginId = COMPOSE_INVESTIGATOR_PLUGIN_ID,
optionName = OPTION_VERBOSE.optionName,
optionValue = enableVerboseLogging.toString(),
),
)
@Suppress("DEPRECATION")
componentRegistrars = mutableListOf<ComponentRegistrar>(ComposeInvestigatorPluginRegistrar()).also { registrars ->
if (enableComposeCompiler) registrars.add(0, ComposePluginRegistrar())
if (additionalVisitor != null) registrars.add(additionalVisitor)
}
commandLineProcessors = mutableListOf<CommandLineProcessor>(ComposeInvestigatorCommandLineProcessor()).also { processors ->
if (enableComposeCompiler) processors.add(0, ComposeCommandLineProcessor())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
* Please see full license: https://github.com/jisungbin/ComposeInvestigator/blob/main/LICENSE
*/

@file:OptIn(ExperimentalCompilerApi::class)
@file:Suppress("unused")

package land.sungbin.composeinvestigator.compiler.test

import androidx.compose.compiler.plugins.kotlin.ComposeCommandLineProcessor
Expand All @@ -20,19 +17,18 @@ import land.sungbin.composeinvestigator.compiler.ComposeInvestigatorCommandLineP
import land.sungbin.composeinvestigator.compiler.ComposeInvestigatorPluginRegistrar
import land.sungbin.composeinvestigator.compiler.OPTION_VERBOSE
import land.sungbin.composeinvestigator.compiler.test.utils.source
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.jetbrains.kotlin.config.JvmTarget
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class Test {
class IrDumpingTest {
@get:Rule
val tempDir: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()

@Test
fun debug() {
compile(source("test.kt"))
compile(source("SourceForIrDump.kt"))
}

private fun compile(vararg sourceFiles: SourceFile) =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Designed and developed by Ji Sungbin 2023.
*
* Licensed under the MIT.
* Please see full license: https://github.com/jisungbin/ComposeInvestigator/blob/main/LICENSE
*/

package land.sungbin.composeinvestigator.compiler.test.source

fun hello() = "Hello, world!"
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Designed and developed by Ji Sungbin 2023.
*
* Licensed under the MIT.
* Please see full license: https://github.com/jisungbin/ComposeInvestigator/blob/main/LICENSE
*/

package land.sungbin.composeinvestigator.compiler.test.source.tracker

fun one() {}
fun one(a: Any) {}
fun one(a: Any, b: Any) {}
fun one(a: Any, b: Any, c: Any) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Designed and developed by Ji Sungbin 2023.
*
* Licensed under the MIT.
* Please see full license: https://github.com/jisungbin/ComposeInvestigator/blob/main/LICENSE
*/

@file:Suppress("unused", "TestFunctionName", "LocalVariableName")

package land.sungbin.composeinvestigator.compiler.test.source.tracker

import androidx.compose.material.Text
import androidx.compose.runtime.Composable

@Composable
fun InTopLevel(param: Any) {
Text("Hello, World!: $param")
}

@Suppress("NOTHING_TO_INLINE")
@Composable
inline fun InTopLevelInline(param: Any) {
Text("Hello, World!: $param")
}

class Class {
@Composable
fun InClass(param: Any) {
Text("Hello, World!: $param")
}
}

object Object {
@Composable
fun InObject(param: Any) {
Text("Hello, World!: $param")
}
}

fun Local() {
@Composable
fun InLocal(param: Any) {
Text("Hello, World!: $param")
}
}

fun Lambda(param: Any) {
@Suppress("UNUSED_VARIABLE")
val InLambda = @Composable {
Text("Hello, World!: $param")
}
}

@Composable
fun InParameter(content: @Composable (param: Any) -> Unit = { param -> Text("Hello, World!: $param") }) {
content(Any())
}

class CompanionObject {
companion object {
@Composable
fun InCompanionObject(param: Any) {
Text("Hello, World!: $param")
}
}
}

fun AnonymousObject() {
@Suppress("UNUSED_VARIABLE")
val anonymousObject = object {
@Composable
fun InAnonymousObject(param: Any) {
Text("Hello, World!: $param")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Designed and developed by Ji Sungbin 2023.
*
* Licensed under the MIT.
* Please see full license: https://github.com/jisungbin/ComposeInvestigator/blob/main/LICENSE
*/

package land.sungbin.composeinvestigator.compiler.test.tracker

import io.kotest.core.spec.style.ShouldSpec
import io.kotest.engine.spec.tempdir
import io.kotest.matchers.shouldBe
import land.sungbin.composeinvestigator.compiler.internal.WeakBindingTracee
import land.sungbin.composeinvestigator.compiler.internal.irTracee
import land.sungbin.composeinvestigator.compiler.internal.tracker.key.DurableWritableSlices
import land.sungbin.composeinvestigator.compiler.test.IrBaseTest
import land.sungbin.composeinvestigator.compiler.test.buildIrVisiterRegistrar
import land.sungbin.composeinvestigator.compiler.test.kotlinCompilation
import land.sungbin.composeinvestigator.compiler.test.utils.source
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid

class DurableKeyTest : ShouldSpec(), IrBaseTest {
init {
should("Generates a unique key for the same function name") {
val irFunctions = mutableListOf<IrSimpleFunction>()
var irTrace: WeakBindingTracee? = null

val irVisitor = buildIrVisiterRegistrar { context ->
object : IrElementVisitorVoid {
override fun visitModuleFragment(declaration: IrModuleFragment) {
irFunctions += declaration.files.single().declarations.filterIsInstance<IrSimpleFunction>()
irTrace = context.irTracee
return super.visitModuleFragment(declaration)
}
}
}

kotlinCompilation(
workingDir = tempdir(),
enableComposeCompiler = false,
enableVerboseLogging = false,
additionalVisitor = irVisitor,
sourceFiles = arrayOf(source("tracker/DurableKeyTestSource.kt")),
).compile()

val zeroArgFn = irFunctions.single { fn -> fn.valueParameters.isEmpty() }
val oneArgFn = irFunctions.single { fn -> fn.valueParameters.size == 1 }
val twoArgFn = irFunctions.single { fn -> fn.valueParameters.size == 2 }
val threeArgFn = irFunctions.single { fn -> fn.valueParameters.size == 3 }

val zeroArgFnKey = irTrace!![DurableWritableSlices.DURABLE_FUNCTION_KEY, zeroArgFn]!!
val oneArgFnKey = irTrace!![DurableWritableSlices.DURABLE_FUNCTION_KEY, oneArgFn]!!
val twoArgFnKey = irTrace!![DurableWritableSlices.DURABLE_FUNCTION_KEY, twoArgFn]!!
val threeArgFnKey = irTrace!![DurableWritableSlices.DURABLE_FUNCTION_KEY, threeArgFn]!!

zeroArgFnKey.name shouldBe "fun-one()Unit/pkg-land.sungbin.composeinvestigator.compiler.test.source.tracker/file-DurableKeyTestSource.kt"
oneArgFnKey.name shouldBe "fun-one(Any)Unit/pkg-land.sungbin.composeinvestigator.compiler.test.source.tracker/file-DurableKeyTestSource.kt"
twoArgFnKey.name shouldBe "fun-one(Any,Any)Unit/pkg-land.sungbin.composeinvestigator.compiler.test.source.tracker/file-DurableKeyTestSource.kt"
threeArgFnKey.name shouldBe "fun-one(Any,Any,Any)Unit/pkg-land.sungbin.composeinvestigator.compiler.test.source.tracker/file-DurableKeyTestSource.kt"
}
}
}
Loading

0 comments on commit 5bcf463

Please sign in to comment.