Skip to content

Commit

Permalink
chore: refactor, clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
jangalinski committed Sep 22, 2024
1 parent d50b3f8 commit ad14f2e
Show file tree
Hide file tree
Showing 35 changed files with 180 additions and 115 deletions.
15 changes: 7 additions & 8 deletions _itest/builder-itest/src/test/kotlin/DelegateStringListITest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import com.tschuchort.compiletesting.KotlinCompilation
import io.toolisticon.kotlin.generation.KotlinCodeGeneration
import io.toolisticon.kotlin.generation.itest.KotlinCodeGenerationITestConfig.ROOT_PACKAGE
import io.toolisticon.kotlin.generation.spec.toFileSpec
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest
import io.toolisticon.kotlin.generation.test.model.KotlinCompilationCommand
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.compile
import io.toolisticon.kotlin.generation.test.callPrimaryConstructor
import io.toolisticon.kotlin.generation.test.model.requireOk
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.junit.jupiter.api.Test
import kotlin.reflect.KClass
import kotlin.reflect.full.primaryConstructor
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.assertThat as compileAssertThat

@Suppress("UNCHECKED_CAST")
Expand All @@ -20,21 +20,20 @@ internal class DelegateStringListITest {

@Test
fun `create and use string list`() {
val list = KotlinCodeGeneration.buildDelegateListValueClass(ROOT_PACKAGE, "StringList", String::class) {
val listSpec = KotlinCodeGeneration.buildDelegateListValueClass(ROOT_PACKAGE, "StringList", String::class) {
propertyName("list")
}.toFileSpec()

val result = KotlinCodeGenerationTest.compile(KotlinCompilationCommand(list))

val result = compile(listSpec).requireOk()

compileAssertThat(result).errorMessages().isEmpty()
compileAssertThat(result).hasExitCode(KotlinCompilation.ExitCode.OK)

val klass: KClass<out Any> = result.loadClass(list.className)
val klass: KClass<out Any> = result.loadClass(listSpec.className)

val values = listOf("a", "b", "c")

val instance: List<String> = klass.primaryConstructor!!.call(values) as List<String>
val instance: List<String> = klass.callPrimaryConstructor(values)

assertThat(instance).hasToString("StringList(list=[a, b, c])")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import com.tschuchort.compiletesting.KotlinCompilation
import io.toolisticon.kotlin.generation.KotlinCodeGeneration
import io.toolisticon.kotlin.generation.itest.KotlinCodeGenerationITestConfig.ROOT_PACKAGE
import io.toolisticon.kotlin.generation.spec.toFileSpec
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest
import io.toolisticon.kotlin.generation.test.model.KotlinCompilationCommand
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.compile
import io.toolisticon.kotlin.generation.test.callPrimaryConstructor
import io.toolisticon.kotlin.generation.test.model.requireOk
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.junit.jupiter.api.Test
import kotlin.reflect.KClass
import kotlin.reflect.full.primaryConstructor
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.assertThat as compileAssertThat

@Suppress("UNCHECKED_CAST")
Expand All @@ -21,23 +21,23 @@ internal class DelegateStringLongMapITest {

@Test
fun `create and use string long map`() {
val map = KotlinCodeGeneration.buildDelegateMapValueClass(
val mapSpec = KotlinCodeGeneration.buildDelegateMapValueClass(
packageName = ROOT_PACKAGE,
simpleName = "StringLongMap",
valueType = Long::class.asTypeName()
) {
propertyName("map")
}.toFileSpec()

val result = KotlinCodeGenerationTest.compile(KotlinCompilationCommand(map))
val result = compile(mapSpec).requireOk()
compileAssertThat(result).errorMessages().isEmpty()
compileAssertThat(result).hasExitCode(KotlinCompilation.ExitCode.OK)

val klass: KClass<out Any> = result.loadClass(map.className)
val klass: KClass<out Any> = result.loadClass(mapSpec.className)

val values = mapOf("a" to 1, "b" to 2, "c" to 3)

val instance: Map<String, Long> = klass.primaryConstructor!!.call(values) as Map<String, Long>
val instance: Map<String, Long> = klass.callPrimaryConstructor(values)

assertThat(instance).hasToString("StringLongMap(map={a=1, b=2, c=3})")
}
Expand Down
26 changes: 7 additions & 19 deletions _itest/builder-itest/src/test/kotlin/DummyExceptionITest.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
package io.toolisticon.kotlin.generation.itest

import com.squareup.kotlinpoet.ExperimentalKotlinPoetApi
import com.tschuchort.compiletesting.KotlinCompilation
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.buildRuntimeExceptionClass
import io.toolisticon.kotlin.generation.itest.KotlinCodeGenerationITestConfig.ROOT_PACKAGE
import io.toolisticon.kotlin.generation.spec.toFileSpec
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest
import io.toolisticon.kotlin.generation.test.model.KotlinCompilationCommand
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.compile
import io.toolisticon.kotlin.generation.test.callPrimaryConstructor
import io.toolisticon.kotlin.generation.test.getFieldValue
import io.toolisticon.kotlin.generation.test.model.requireOk
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.junit.jupiter.api.Test
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.assertThat as compileAssertThat

@OptIn(ExperimentalKotlinPoetApi::class, ExperimentalCompilerApi::class)
internal class DummyExceptionITest {
Expand All @@ -28,25 +25,16 @@ internal class DummyExceptionITest {
includeCause()
}.toFileSpec()


val result = KotlinCodeGenerationTest.compile(KotlinCompilationCommand(exceptionFile))

compileAssertThat(result).errorMessages().isEmpty()
compileAssertThat(result).hasExitCode(KotlinCompilation.ExitCode.OK)
val result = compile(exceptionFile).requireOk()

val c: KClass<out Any> = result.loadClass(exceptionFile.className)

val cause = IllegalStateException("foo")
val e: RuntimeException = c.primaryConstructor!!.call(true, "false", cause) as RuntimeException
val e: RuntimeException = c.callPrimaryConstructor(true, "false", cause)

assertThat(e.localizedMessage).isEqualTo("Dummy exception: expected: true, actual: 'false'.")

// TODO try to get value via pure kotlin without falling back to java
val expectedProperty: KProperty1<out Any, *> = c.memberProperties.single { it.name == "expected" }
val field = c.java.getDeclaredField("expected").apply { isAccessible = true }

val expectedValue = field.get(e) as Boolean

val expectedValue: Boolean = e.getFieldValue("expected")
assertThat(expectedValue).isTrue()
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package io.toolisticon.kotlin.generation.itest

import com.squareup.kotlinpoet.ClassName
Expand All @@ -8,7 +7,8 @@ import io.toolisticon.kotlin.generation.KotlinCodeGeneration.buildClass
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.buildFile
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.buildFun
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest
import io.toolisticon.kotlin.generation.test.model.KotlinCompilationCommand
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.compile
import io.toolisticon.kotlin.generation.test.model.requireOk
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -37,9 +37,7 @@ internal class HelloWorldExampleITest {

assertThat(file.packageName).isEqualTo("foo.bar")

val result = KotlinCodeGenerationTest.compile(cmd = KotlinCompilationCommand(fileSpec = file))

println(result)
val result = compile(file).requireOk()

KotlinCodeGenerationTest.assertThat(result).hasExitCode(KotlinCompilation.ExitCode.OK)
}
Expand Down
12 changes: 5 additions & 7 deletions _itest/builder-itest/src/test/kotlin/KotlinDataClassSpecITest.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package io.toolisticon.kotlin.generation.itest

import com.squareup.kotlinpoet.ClassName
Expand All @@ -7,12 +6,12 @@ import com.squareup.kotlinpoet.asTypeName
import com.tschuchort.compiletesting.KotlinCompilation
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.buildDataClass
import io.toolisticon.kotlin.generation.spec.toFileSpec
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest
import io.toolisticon.kotlin.generation.test.model.KotlinCompilationCommand
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.compile
import io.toolisticon.kotlin.generation.test.callPrimaryConstructor
import io.toolisticon.kotlin.generation.test.model.requireOk
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.junit.jupiter.api.Test
import kotlin.reflect.full.primaryConstructor
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.assertThat as compileAssertThat

@OptIn(ExperimentalKotlinPoetApi::class, ExperimentalCompilerApi::class)
Expand All @@ -29,14 +28,13 @@ internal class KotlinDataClassSpecITest {

val file = spec.toFileSpec()

val result = KotlinCodeGenerationTest.compile(KotlinCompilationCommand(file))
val result = compile(file).requireOk()

compileAssertThat(result).errorMessages().isEmpty()
compileAssertThat(result).hasExitCode(KotlinCompilation.ExitCode.OK)

val klass = result.loadClass(className)
assertThat(klass.primaryConstructor!!.call("hello world", 25))
assertThat(klass.callPrimaryConstructor<Any>("hello world", 25))
.hasToString("Bar(name=hello world, age=25)")
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package io.toolisticon.kotlin.generation.itest

import com.squareup.kotlinpoet.ClassName
Expand Down Expand Up @@ -42,8 +41,8 @@ internal class MyCustomAnnotationSpecITest {

val klass: KClass<out Any> = result.loadClass(name)
assertThat(klass.asClassName()).isEqualTo(name)
assertThat(klass.annotations).hasSize(1)
val annotation: Annotation = klass.annotations[0]
assertThat(klass.java.annotations).hasSize(2) // 2 because Meta is included
val annotation: Annotation = klass.java.annotations[0]
assertThat(annotation).hasToString("@io.toolisticon.kotlin.generation.itest.created.MyCustomAnnotation(\"hello\")")
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@

package io.toolisticon.kotlin.generation.itest.support

import com.squareup.kotlinpoet.ExperimentalKotlinPoetApi
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.buildDataClass
import io.toolisticon.kotlin.generation.spec.toFileSpec
import io.toolisticon.kotlin.generation.support.GeneratedAnnotation
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.assertThat
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.compile
import io.toolisticon.kotlin.generation.test.model.KotlinCompilationCommand
import io.toolisticon.kotlin.generation.test.model.KotlinCompilationResult
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
Expand All @@ -21,7 +21,7 @@ internal class GeneratedAnnotationITest {
addAnnotation(GeneratedAnnotation())
}.toFileSpec()

val result: KotlinCompilationResult = KotlinCodeGenerationTest.compile(cmd = KotlinCompilationCommand(file))
KotlinCodeGenerationTest.assertThat(result).errorMessages().isEmpty()
val result: KotlinCompilationResult = compile(cmd = KotlinCompilationCommand(file))
assertThat(result).errorMessages().isEmpty()
}
}
10 changes: 5 additions & 5 deletions _itest/spi-itest/src/test/kotlin/SpiITest.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package io.toolisticon.kotlin.generation.itest.spi

import com.squareup.kotlinpoet.ExperimentalKotlinPoetApi
import com.tschuchort.compiletesting.KotlinCompilation
import io.toolisticon.kotlin.generation.KotlinCodeGeneration
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.className
import io.toolisticon.kotlin.generation.KotlinCodeGeneration.spi.registry
import io.toolisticon.kotlin.generation.spi.strategy.executeSingle
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest
import io.toolisticon.kotlin.generation.test.KotlinCodeGenerationTest.compile
import io.toolisticon.kotlin.generation.test.callPrimaryConstructor
import io.toolisticon.kotlin.generation.test.model.KotlinCompilationCommand
import io.toolisticon.kotlin.generation.test.model.requireOk
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
Expand Down Expand Up @@ -44,10 +45,9 @@ internal class SpiITest {
val spec = requireNotNull(context.registry.strategies.filter(TestDataClassStrategy::class).executeSingle(context, input))
val file = KotlinCodeGeneration.builder.fileBuilder(input.className).addType(spec).build()

val result = KotlinCodeGenerationTest.compile(KotlinCompilationCommand(file))
KotlinCodeGenerationTest.assertThat(result).hasExitCode(KotlinCompilation.ExitCode.OK)
val result = compile(KotlinCompilationCommand(file)).requireOk()

val foo = result.loadClass(input.className).java.getDeclaredConstructor(String::class.java, Long::class.java).newInstance("Foo", 5L)
val foo: Any = result.loadClass(input.className).callPrimaryConstructor("Foo", 5L)

assertThat(foo).hasToString("ExampleDataClass(name=Foo, foo=5)")
}
Expand Down
6 changes: 1 addition & 5 deletions kotlin-code-generation-test/pom.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

Expand Down Expand Up @@ -41,15 +41,11 @@
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-annotation-processing-embeddable</artifactId>
<!-- TODO can be removed with parent 2024.8.2 -->
<version>${kotlin.version}</version>
</dependency>

<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-annotation-processing-compiler</artifactId>
<!-- TODO can be removed with parent 2024.8.2 -->
<version>${kotlin.version}</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.tschuchort.compiletesting.JvmCompilationResult
import com.tschuchort.compiletesting.KotlinCompilation
import com.tschuchort.compiletesting.SourceFile
import io.toolisticon.kotlin.generation.spec.KotlinFileSpec
import io.toolisticon.kotlin.generation.spec.KotlinFileSpecList
import io.toolisticon.kotlin.generation.test.model.KotlinCompilationCommand
import io.toolisticon.kotlin.generation.test.model.KotlinCompilationResult
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
Expand Down Expand Up @@ -35,12 +36,18 @@ import java.io.ByteArrayOutputStream
@ExperimentalKotlinPoetApi
object KotlinCodeGenerationTest {

/**
* @see KotlinCompilationAssert
*/
fun assertThat(actual: KotlinCompilationResult): KotlinCompilationAssert = KotlinCompilationAssert(actual)

/**
* Compiles files contained in command.
*/
fun compile(cmd: KotlinCompilationCommand): KotlinCompilationResult {

val result: JvmCompilationResult = KotlinCompilation().apply {
sources = cmd.sourceFiles
sources = cmd.toList()
inheritClassPath = true


Expand All @@ -52,5 +59,19 @@ object KotlinCodeGenerationTest {
return KotlinCompilationResult(cmd = cmd, result = result)
}


/**
* Convenience to compile files directly.
* @see [KotlinCodeGenerationTest.compile]
*/
fun compile(vararg fileSpec: KotlinFileSpec) = this.compile(
KotlinCompilationCommand(
KotlinFileSpecList.of(fileSpec.toList())
)
)

/**
* Extract sourceFile from spec.
*/
fun KotlinFileSpec.sourceFile() = SourceFile.kotlin(name = this.fileName, contents = this.code)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ class KotlinCompilationAssert(
actual: KotlinCompilationResult,
) : AbstractAssert<KotlinCompilationAssert, KotlinCompilationResult>(actual, KotlinCompilationAssert::class.java) {

/**
* Assertions on error messages.
*/
fun errorMessages() = Assertions.assertThat(actual.errors)

/**
* Assertion on exitCode.
*/
fun hasExitCode(exitCode: KotlinCompilation.ExitCode): KotlinCompilationAssert = apply {
Assertions.assertThat(actual.exitCode).isEqualTo(exitCode)
}
Expand Down
31 changes: 31 additions & 0 deletions kotlin-code-generation-test/src/main/kotlin/_reflection.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.toolisticon.kotlin.generation.test

import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.primaryConstructor


/**
* Create instance of type `<T>` from [KClass].
*/
inline fun <reified T : Any> KClass<out Any>.callPrimaryConstructor(vararg params: Any): T {
// TODO "call" is not resolvable
return if (params.isEmpty()) {
this.createInstance()
} else {
val pc = requireNotNull(this.primaryConstructor)
pc.call(*params)
} as T
}

/**
* Get value of type `T` of field.
*
* @param name name of the field
* @return value T
*/
// TODO looking for a pure kotlin reflect solution
inline fun <reified T : Any> Any.getFieldValue(name: String): T = this::class.java
.getDeclaredField(name)
.apply { isAccessible = true }
.get(this) as T
Loading

0 comments on commit ad14f2e

Please sign in to comment.