Skip to content

Commit

Permalink
Merge pull request #5 from xebia-functional/add-testing
Browse files Browse the repository at this point in the history
Add testing
  • Loading branch information
nomisRev authored Jun 13, 2023
2 parents e95f2bc + dfa2e1c commit 97472ef
Show file tree
Hide file tree
Showing 22 changed files with 551 additions and 35 deletions.
65 changes: 64 additions & 1 deletion compiler-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,40 @@ group = "com.xebia"
version = "0.0.1"
val autoService = "1.1.1"

sourceSets {
test {
java.srcDirs("src/test-gen")
}
}

dependencies {
compileOnly("com.google.auto.service:auto-service:$autoService")
compileOnly("org.jetbrains.kotlin:kotlin-compiler:1.8.22")
kapt("com.google.auto.service:auto-service:$autoService")
compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable:1.8.20")
compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable:1.8.22")
testImplementation("dev.zacsweers.kctfork:core:0.2.1")
testImplementation("junit:junit:4.13.2")
testImplementation("com.google.truth:truth:1.1.3")
testImplementation(kotlin("reflect"))
testRuntimeOnly("org.jetbrains.kotlin:kotlin-test:1.8.22")
testRuntimeOnly("org.jetbrains.kotlin:kotlin-script-runtime:1.8.22")
testRuntimeOnly("org.jetbrains.kotlin:kotlin-annotations-jvm:1.8.22")

testCompileOnly("org.jetbrains.kotlin:kotlin-compiler:1.8.22")
testImplementation("org.jetbrains.kotlin:kotlin-compiler:1.8.22")
testImplementation("org.jetbrains.kotlin:kotlin-compiler-internal-test-framework:1.8.22")
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.junit.platform:junit-platform-commons")
testImplementation("org.junit.platform:junit-platform-launcher")
testImplementation("org.junit.platform:junit-platform-runner")
testImplementation("org.junit.platform:junit-platform-suite-api")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
toolchain {
languageVersion.set(JavaLanguageVersion.of(11))
}
Expand All @@ -40,3 +63,43 @@ tasks.withType<KotlinCompile> {
}

//./gradlew clean :lib:compileKotlinJvm --no-daemon -Dorg.gradle.debug=true -Dkotlin.compiler.execution.strategy="in-process" -Dkotlin.daemon.jvm.options="-Xdebug,-Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n"

tasks.create<JavaExec>("generateTests") {
classpath = sourceSets.test.get().runtimeClasspath
mainClass.set("com.xebia.GenerateKotlinCompilerTestKt")
}


tasks.test {
testLogging { showStandardStreams = true }

useJUnitPlatform()
doFirst {
setLibraryProperty("org.jetbrains.kotlin.test.kotlin-stdlib", "kotlin-stdlib")
setLibraryProperty("org.jetbrains.kotlin.test.kotlin-stdlib-jdk8", "kotlin-stdlib-jdk8")
setLibraryProperty("org.jetbrains.kotlin.test.kotlin-reflect", "kotlin-reflect")
setLibraryProperty("org.jetbrains.kotlin.test.kotlin-test", "kotlin-test")
setLibraryProperty("org.jetbrains.kotlin.test.kotlin-script-runtime", "kotlin-script-runtime")
setLibraryProperty("org.jetbrains.kotlin.test.kotlin-annotations-jvm", "kotlin-annotations-jvm")
setLibraryProperty("coroutines.lib", "kotlinx-coroutines-core-jvm")
}

dependsOn("generateTests")
}

fun Test.setLibraryProperty(propName: String, jarName: String) {
//error(project.configurations.testRuntimeClasspath
// .get()
// .files.joinToString("\n") { it.absolutePath })
val path =
project.configurations.testRuntimeClasspath
.get()
.files
.find {
val matches = """$jarName-\d.*jar""".toRegex().matches(it.name)
matches
}
?.absolutePath
?: return
systemProperty(propName, path)
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ fun CompilerPluginRegistrar.ExtensionStorage.registerExtensionsCommon(configurat
messageCollector, asFutureAnnotation, coroutineScope
)
)
}
}
95 changes: 65 additions & 30 deletions compiler-plugin/src/main/java/com/xebia/IrGenerationExtension.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.xebia

import java.util.concurrent.CompletableFuture
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
Expand Down Expand Up @@ -40,39 +41,28 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.fir.declarations.builder.buildEnumEntry
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.declarations.IrTypeParameterBuilder
import org.jetbrains.kotlin.ir.builders.declarations.IrValueParameterBuilder
import org.jetbrains.kotlin.ir.builders.declarations.buildValueParameter
import org.jetbrains.kotlin.ir.builders.irCallConstructor
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.builders.parent
import org.jetbrains.kotlin.ir.declarations.IrAttributeContainer
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.declarations.copyAttributes
import org.jetbrains.kotlin.ir.declarations.impl.IrEnumEntryImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrEnumEntrySymbolImpl
import org.jetbrains.kotlin.ir.types.SimpleTypeNullability
import org.jetbrains.kotlin.ir.types.SimpleTypeNullability.DEFINITELY_NOT_NULL
import org.jetbrains.kotlin.ir.types.addAnnotations
import org.jetbrains.kotlin.ir.types.classFqName
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.util.irConstructorCall
import org.jetbrains.kotlin.ir.util.patchDeclarationParents

internal class IrGenerationExtension(
Expand All @@ -99,20 +89,41 @@ private class IrVisitor(
?: error("Internal error: Function $coroutineScope not found. Please include org.jetbrains.kotlinx:kotlinx-coroutines-core.")

val extensionFnConstructor: IrConstructorSymbol =
pluginContext.referenceClass(ClassId.fromString("kotlin/ExtensionFunctionType"))?.constructors?.firstOrNull()
pluginContext.referenceClass(classId<ExtensionFunctionType>())?.constructors?.firstOrNull()
?: error("Internal error: Class ExtensionFunctionType not found.")

val jvmNameConstructor: IrConstructorSymbol =
pluginContext.referenceClass(ClassId.fromString("kotlin/jvm/JvmName"))?.constructors?.firstOrNull()
?: error("Internal error: Class ExtensionFunctionType not found.")

val deprecatedConstructor: IrConstructorSymbol =
pluginContext.referenceClass(ClassId.fromString("kotlin/Deprecated"))?.constructors?.firstOrNull()
?: error("Internal error: Class ExtensionFunctionType not found.")

val scopeSimpleType = IrSimpleTypeImpl(
coroutineScopeType,
coroutineScopeType, DEFINITELY_NOT_NULL, emptyList(), emptyList()
)

val deprecationLevelType: IrClassSymbol = pluginContext.referenceClass(classId<DeprecationLevel>())
?: error("Internal error:DeprecationLevel not found.")

val deprecationLevelIrType = IrSimpleTypeImpl(
deprecationLevelType,
DEFINITELY_NOT_NULL,
emptyList(),
emptyList()
)

val HIDDEN: IrEnumEntry = pluginContext.irFactory.createEnumEntry(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB,
IrEnumEntrySymbolImpl(),
Name.identifier("HIDDEN")
).apply {
parent = deprecationLevelType.owner
}

override fun visitFunctionNew(declaration: IrFunction): IrStatement {
if (!declaration.hasAnnotation(annotationClassId.asSingleFqName())) return super.visitFunctionNew(declaration)

Expand All @@ -131,15 +142,16 @@ private class IrVisitor(

if (pluginContext.platform?.isJvm() == true) {
// This declaration is only available on JVM
val futureFn: IrSimpleFunctionSymbol = pluginContext.referenceFunctions(futureCallableId).singleOrNull()
?: error("Internal error: Function $futureCallableId not found. Please include org.jetbrains.kotlinx:kotlinx-coroutines-jdk8.")
val futureFn: IrSimpleFunctionSymbol = pluginContext.referenceFunctions(futureCallableId).singleOrNull() ?: error(
"Internal error: Function $futureCallableId not found. Please include org.jetbrains.kotlinx:kotlinx-coroutines-jdk8."
)

val futureClass: IrClassSymbol =
pluginContext.referenceClass(ClassId.fromString("java.util.concurrent.CompletableFuture"))
?: error("Internal error: Function $coroutineScope not found. Please include org.jetbrains.kotlinx:kotlinx-coroutines-core.")
val futureClass: IrClassSymbol = pluginContext.referenceClass(classId<CompletableFuture<*>>())
?: error("Internal error: Function \"java/util/concurrent/CompletableFuture\" not found. Please include the jdk8 module.")

val futureSimpleType = IrSimpleTypeImpl(
futureClass, DEFINITELY_NOT_NULL,
futureClass,
DEFINITELY_NOT_NULL,
listOf(makeTypeProjection(declaration.returnType, Variance.INVARIANT)),
emptyList()
)
Expand All @@ -151,7 +163,29 @@ private class IrVisitor(
copyParameterDeclarationsFrom(declaration)
val `this` = this.dispatchReceiverParameter!!

annotations += listOf(jvmNameAnnotation("${declaration.name}"))
annotations += listOf(
jvmNameAnnotation("${declaration.name}"),
IrConstructorCallImpl.fromSymbolOwner(
deprecatedConstructor.owner.returnType,
deprecatedConstructor
).apply {
putValueArgument(
0,
IrConstImpl.string(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
pluginContext.irBuiltIns.stringType,
"This function should not be called from Kotlin sources, only Java APIs."
)
)
putValueArgument(
2,
IrGetEnumValueImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, deprecationLevelIrType, HIDDEN.symbol
)
)
}
)

body = DeclarationIrBuilder(pluginContext, symbol).irBlockBody {
val lambda = buildSuspendLambda(declaration.returnType) {
Expand All @@ -176,11 +210,9 @@ private class IrVisitor(
putTypeArgument(0, declaration.returnType)
// value argument 0, 1 have default values we want to maintain.
putValueArgument(
2,
lambdaArgument(
2, lambdaArgument(
lambda,
pluginContext.irBuiltIns.suspendFunctionN(1)
.typeWith(scopeSimpleType, declaration.returnType)
pluginContext.irBuiltIns.suspendFunctionN(1).typeWith(scopeSimpleType, declaration.returnType)
.addAnnotations(listOf(irCall(extensionFnConstructor)))
)
)
Expand Down Expand Up @@ -216,11 +248,9 @@ private class IrVisitor(

/** Finds the line and column of [irElement] within this file. */
private fun IrFile.locationOf(irElement: IrElement?): CompilerMessageSourceLocation {
val sourceRangeInfo =
fileEntry.getSourceRangeInfo(
beginOffset = irElement?.startOffset ?: SYNTHETIC_OFFSET,
endOffset = irElement?.endOffset ?: SYNTHETIC_OFFSET
)
val sourceRangeInfo = fileEntry.getSourceRangeInfo(
beginOffset = irElement?.startOffset ?: SYNTHETIC_OFFSET, endOffset = irElement?.endOffset ?: SYNTHETIC_OFFSET
)
return CompilerMessageLocationWithRange.create(
path = sourceRangeInfo.filePath,
lineStart = sourceRangeInfo.startLineNumber + 1,
Expand Down Expand Up @@ -261,3 +291,8 @@ private class IrVisitor(
IrStatementOrigin.LAMBDA
)
}

private inline fun <reified T> classId(): ClassId {
val fqName = FqName(T::class.java.canonicalName)
return ClassId.topLevel(fqName)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@


package com.xebia.runners;

import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.util.regex.Pattern;

/** This class is generated by {@link com.xebia.GenerateKotlinCompilerTestKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("src/testData/box")
@TestDataPath("$PROJECT_ROOT")
public class BoxTestGenerated extends AbstractBoxTest {
@Test
public void testAllFilesPresentInBox() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("src/testData/box"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}

@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("src/testData/box/simple.kt");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@


package com.xebia.runners;

import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.util.regex.Pattern;

/** This class is generated by {@link com.xebia.GenerateKotlinCompilerTestKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("src/testData/diagnostics")
@TestDataPath("$PROJECT_ROOT")
public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
@Test
public void testAllFilesPresentInDiagnostics() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("src/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.xebia

import com.xebia.runners.AbstractBoxTest
import com.xebia.runners.AbstractDiagnosticTest
import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5

fun main() {
generateTestGroupSuiteWithJUnit5 {
testGroup(testDataRoot = "src/testData", testsRoot = "src/test-gen") {
testClass<AbstractDiagnosticTest> {
model("diagnostics")
}

testClass<AbstractBoxTest> {
model("box")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.xebia.runners

import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.test.backend.BlackBoxCodegenSuppressor
import org.jetbrains.kotlin.test.backend.handlers.IrTextDumpHandler
import org.jetbrains.kotlin.test.backend.handlers.IrTreeVerifierHandler
import org.jetbrains.kotlin.test.backend.handlers.JvmBoxRunner
import org.jetbrains.kotlin.test.backend.ir.JvmIrBackendFacade
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.builders.fir2IrStep
import org.jetbrains.kotlin.test.builders.irHandlersStep
import org.jetbrains.kotlin.test.builders.jvmArtifactsHandlersStep
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.DUMP_IR
import org.jetbrains.kotlin.test.runners.RunnerWithTargetBackendForTestGeneratorMarker

open class AbstractBoxTest : BaseTestRunner(), RunnerWithTargetBackendForTestGeneratorMarker {
override val targetBackend: TargetBackend = TargetBackend.JVM_IR

override fun TestConfigurationBuilder.configuration() {
defaultDirectives { +DUMP_IR }

commonFirWithPluginFrontendConfiguration()
fir2IrStep()
irHandlersStep {
useHandlers(
::IrTextDumpHandler,
::IrTreeVerifierHandler,
)
}
facadeStep(::JvmIrBackendFacade)
jvmArtifactsHandlersStep { useHandlers(::JvmBoxRunner) }

useAfterAnalysisCheckers(::BlackBoxCodegenSuppressor)
}
}
Loading

0 comments on commit 97472ef

Please sign in to comment.