Skip to content

Commit aa84a23

Browse files
committed
Add test kit (with one test suite for now)
Fix #270
1 parent a61b2df commit aa84a23

File tree

20 files changed

+323
-116
lines changed

20 files changed

+323
-116
lines changed

build.gradle.kts

-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ dependencies {
2929

3030
// Standard dependencies
3131
implementation(libs.kotlin.dev.reflect)
32-
implementation(libs.kotlin.dev.stdlibJdk8)
3332
implementation(libs.jetbrains.annotations)
3433
implementation(libs.coroutines.core)
3534

gradle/libs.versions.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jupyterApi = "0.10.0-146"
1818
jetbrainsAnnotations = "21.0.1"
1919

2020
junit = "5.7.2"
21-
kotlinTest = "3.4.2"
21+
kotlinTest = "4.6.1"
2222
clikt = "2.8.0"
2323
zeromq = "0.5.2"
2424
gson = "2.8.7"
@@ -92,7 +92,7 @@ clikt = { module = "com.github.ajalt:clikt", version.ref = "clikt" }
9292
test-junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" }
9393
test-junit-params = { group = "org.junit.jupiter", name = "junit-jupiter-params", version.ref = "junit" }
9494
test-junit-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit" }
95-
test-kotlintest-assertions = { module = "io.kotlintest:kotlintest-assertions", version.ref = "kotlinTest" }
95+
test-kotlintest-assertions = { module = "io.kotest:kotest-assertions-core", version.ref = "kotlinTest" }
9696

9797
# Different libraries
9898
ext-jlatex = { module = "org.scilab.forge:jlatexmath", version.ref = "jlatexmath" }

jupyter-lib/kotlin-jupyter-api-gradle-plugin/build.gradle.kts

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import build.CreateResourcesTask
2+
import build.util.defaultVersionCatalog
3+
import build.util.devKotlin
24

35
plugins {
46
id("com.gradle.plugin-publish")
@@ -27,6 +29,7 @@ dependencies {
2729

2830
CreateResourcesTask.register(project, "saveVersion", tasks.processResources) {
2931
addSingleValueFile("VERSION", rootSettings.mavenVersion)
32+
addSingleValueFile("KOTLIN_VERSION", rootProject.defaultVersionCatalog.versions.devKotlin)
3033
}
3134

3235
java {

jupyter-lib/kotlin-jupyter-api-gradle-plugin/src/main/kotlin/org/jetbrains/kotlinx/jupyter/api/plugin/ApiGradlePlugin.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
1313
import org.jetbrains.kotlin.gradle.plugin.KaptExtension
1414
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
1515
import org.jetbrains.kotlinx.jupyter.api.plugin.tasks.JupyterApiResourcesTask
16-
import org.jetbrains.kotlinx.jupyter.api.plugin.tasks.whenAdded
16+
import org.jetbrains.kotlinx.jupyter.api.plugin.util.addMavenCentralIfDoesNotExist
17+
import org.jetbrains.kotlinx.jupyter.api.plugin.util.whenAdded
1718

1819
class ApiGradlePlugin : Plugin<Project> {
1920
override fun apply(target: Project): Unit = with(target) {
@@ -29,7 +30,7 @@ class ApiGradlePlugin : Plugin<Project> {
2930
}
3031

3132
repositories {
32-
mavenCentral()
33+
addMavenCentralIfDoesNotExist()
3334
}
3435

3536
val pluginExtension = KotlinJupyterPluginExtension(target)

jupyter-lib/kotlin-jupyter-api-gradle-plugin/src/main/kotlin/org/jetbrains/kotlinx/jupyter/api/plugin/KotlinJupyterPluginExtension.kt

+20-63
Original file line numberDiff line numberDiff line change
@@ -2,82 +2,39 @@ package org.jetbrains.kotlinx.jupyter.api.plugin
22

33
import org.gradle.api.Project
44
import org.gradle.kotlin.dsl.dependencies
5-
import org.gradle.kotlin.dsl.findByType
6-
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
7-
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
8-
import org.jetbrains.kotlinx.jupyter.api.plugin.tasks.whenAdded
9-
import java.util.Locale
10-
11-
private fun Project.configureDependency(scope: String, dependencyNotation: Any) {
12-
// apply configuration to JVM-only project
13-
plugins.withId("org.jetbrains.kotlin.jvm") {
14-
val configuration = project.configurations.findByName(scope)
15-
?: error("$scope configuration is not resolved for a Kotlin-JVM project")
16-
dependencies {
17-
configuration.invoke(dependencyNotation)
18-
}
19-
}
20-
// apply only to multiplatform plugin
21-
plugins.withId("org.jetbrains.kotlin.multiplatform") {
22-
extensions.findByType<KotlinMultiplatformExtension>()?.apply {
23-
targets.whenAdded(
24-
{ it is KotlinJvmTarget },
25-
{
26-
val jvmTargetName = it.name
27-
val configuration = project.configurations.findByName(jvmTargetName + scope.capitalize(Locale.ROOT))
28-
?: error("$scope configuration is not resolved for a multiplatform project")
29-
dependencies {
30-
configuration.invoke(dependencyNotation)
31-
}
32-
}
33-
)
34-
}
35-
}
36-
}
5+
import org.jetbrains.kotlinx.jupyter.api.plugin.util.configureDependency
6+
import org.jetbrains.kotlinx.jupyter.api.plugin.util.kernelDependency
7+
import org.jetbrains.kotlinx.jupyter.api.plugin.util.propertyByFlag
8+
import org.jetbrains.kotlinx.jupyter.api.plugin.util.whenAdded
379

3810
class KotlinJupyterPluginExtension(
3911
private val project: Project
4012
) {
13+
private val enableApiDependency = project.propertyByFlag("kotlin.jupyter.add.api", true)
14+
private val enableScannerDependency = project.propertyByFlag("kotlin.jupyter.add.scanner", true)
15+
private val enableTestKitDependency = project.propertyByFlag("kotlin.jupyter.add.testkit", true)
16+
17+
internal fun addDependenciesIfNeeded() {
18+
if (enableApiDependency.get()) addApiDependency()
19+
if (enableScannerDependency.get()) addScannerDependency()
20+
if (enableTestKitDependency.get()) addTestKitDependency()
21+
}
22+
4123
fun addApiDependency(version: String? = null) = with(project) {
42-
val apiVersion = version ?: apiVersion()
43-
configureDependency("compileOnly", "$GROUP_ID:kotlin-jupyter-api:$apiVersion")
24+
configureDependency("compileOnly", kernelDependency("api", version))
4425
}
4526

4627
fun addScannerDependency(version: String? = null) = with(project) {
4728
configurations.whenAdded({ it.name == "kapt" }) { kaptConf ->
48-
val apiVersion = version ?: apiVersion()
49-
val mavenCoordinates = "$GROUP_ID:kotlin-jupyter-api-annotations:$apiVersion"
29+
val annotationsDependency = kernelDependency("api-annotations", version)
5030
dependencies {
51-
kaptConf(mavenCoordinates)
31+
kaptConf(annotationsDependency)
5232
}
53-
configureDependency("implementation", mavenCoordinates)
33+
configureDependency("implementation", annotationsDependency)
5434
}
5535
}
5636

57-
internal fun addDependenciesIfNeeded() {
58-
if (project.getFlag("kotlin.jupyter.add.api", true)) {
59-
addApiDependency()
60-
}
61-
if (project.getFlag("kotlin.jupyter.add.scanner", true)) {
62-
addScannerDependency()
63-
}
64-
}
65-
66-
companion object {
67-
private const val GROUP_ID = "org.jetbrains.kotlinx"
68-
69-
private fun Project.getFlag(propertyName: String, default: Boolean = false): Boolean {
70-
return findProperty(propertyName)?.let {
71-
when (it) {
72-
"true", true -> true
73-
"false", false -> false
74-
else -> null
75-
}
76-
} ?: default
77-
}
78-
79-
fun apiVersion(): String {
80-
return ApiGradlePlugin::class.java.classLoader.getResource("VERSION")!!.readText()
81-
}
37+
fun addTestKitDependency(version: String? = null) = with(project) {
38+
configureDependency("testImplementation", kernelDependency("test-kit", version))
8239
}
8340
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.jetbrains.kotlinx.jupyter.api.plugin.util
2+
3+
import org.gradle.api.artifacts.ArtifactRepositoryContainer
4+
import org.gradle.api.artifacts.ExternalModuleDependency
5+
import org.gradle.api.artifacts.dsl.RepositoryHandler
6+
import org.gradle.api.artifacts.repositories.ArtifactRepository
7+
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
8+
import org.jetbrains.kotlinx.jupyter.api.plugin.ApiGradlePlugin
9+
import java.net.URI
10+
11+
private const val GROUP_ID = "org.jetbrains.kotlinx"
12+
internal const val KOTLIN_DEV_REPOSITORY_NAME = "Kotlin Dev repo"
13+
internal const val KOTLIN_DEV_REPOSITORY_URL = "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev"
14+
15+
internal val String.isDevKotlinVersion: Boolean get() = "-" in this
16+
17+
internal fun RepositoryHandler.addMavenIfDoesNotExist(name: String, url: String): ArtifactRepository {
18+
return findByName(name) ?: maven {
19+
this.name = name
20+
this.url = URI(url)
21+
}
22+
}
23+
24+
internal fun RepositoryHandler.addMavenCentralIfDoesNotExist(): ArtifactRepository {
25+
return addMavenIfDoesNotExist(ArtifactRepositoryContainer.DEFAULT_MAVEN_CENTRAL_REPO_NAME, ArtifactRepositoryContainer.MAVEN_CENTRAL_URL)
26+
}
27+
28+
private fun readFileProp(fileName: String): String {
29+
return ApiGradlePlugin::class.java.classLoader.getResource(fileName)!!.readText()
30+
}
31+
32+
internal fun kernelVersion(): String {
33+
return readFileProp("VERSION")
34+
}
35+
36+
internal fun kotlinVersion(): String {
37+
return readFileProp("KOTLIN_VERSION")
38+
}
39+
40+
internal fun kernelDependency(moduleName: String, version: String? = null): ExternalModuleDependency {
41+
return DefaultExternalModuleDependency(GROUP_ID, "kotlin-jupyter-$moduleName", version ?: kernelVersion())
42+
}
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
package org.jetbrains.kotlinx.jupyter.api.plugin.tasks
1+
package org.jetbrains.kotlinx.jupyter.api.plugin.util
22

33
import org.gradle.api.NamedDomainObjectCollection
44

5-
fun <T> NamedDomainObjectCollection<T>.whenAdded(condition: (T) -> Boolean, action: (T) -> Unit) {
5+
internal fun <T> NamedDomainObjectCollection<T>.whenAdded(condition: (T) -> Boolean, action: (T) -> Unit) {
66
val element = find(condition)
77
if (element != null) {
88
action(element)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.jetbrains.kotlinx.jupyter.api.plugin.util
2+
3+
import org.gradle.api.Project
4+
import org.gradle.api.artifacts.ExternalModuleDependency
5+
import org.gradle.api.provider.Property
6+
import org.gradle.kotlin.dsl.dependencies
7+
import org.gradle.kotlin.dsl.findByType
8+
import org.gradle.kotlin.dsl.property
9+
import org.gradle.kotlin.dsl.repositories
10+
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
11+
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
12+
import java.util.Locale
13+
14+
internal fun Project.configureDependency(scope: String, dependency: ExternalModuleDependency) {
15+
repositories {
16+
if (kotlinVersion().isDevKotlinVersion) {
17+
addMavenIfDoesNotExist(KOTLIN_DEV_REPOSITORY_NAME, KOTLIN_DEV_REPOSITORY_URL)
18+
}
19+
}
20+
21+
// apply configuration to JVM-only project
22+
plugins.withId("org.jetbrains.kotlin.jvm") {
23+
val configuration = project.configurations.findByName(scope)
24+
?: error("$scope configuration is not resolved for a Kotlin-JVM project")
25+
dependencies {
26+
configuration.invoke(dependency)
27+
}
28+
}
29+
// apply only to multiplatform plugin
30+
plugins.withId("org.jetbrains.kotlin.multiplatform") {
31+
extensions.findByType<KotlinMultiplatformExtension>()?.apply {
32+
targets.whenAdded(
33+
{ it is KotlinJvmTarget },
34+
{
35+
val jvmTargetName = it.name
36+
val configuration = project.configurations.findByName(jvmTargetName + scope.capitalize(Locale.ROOT))
37+
?: error("$scope configuration is not resolved for a multiplatform project")
38+
dependencies {
39+
configuration.invoke(dependency)
40+
}
41+
}
42+
)
43+
}
44+
}
45+
}
46+
47+
internal fun Project.getFlag(propertyName: String, default: Boolean = false): Boolean {
48+
return findProperty(propertyName)?.let {
49+
when (it) {
50+
"true", true -> true
51+
"false", false -> false
52+
else -> null
53+
}
54+
} ?: default
55+
}
56+
57+
internal fun Project.propertyByFlag(flagName: String, default: Boolean = false): Property<Boolean> {
58+
return objects.property<Boolean>().apply { set(provider { getFlag(flagName, default) }) }
59+
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package org.jetbrains.kotlinx.jupyter.api.plugin.test
22

3-
import org.jetbrains.kotlinx.jupyter.api.plugin.KotlinJupyterPluginExtension
3+
import org.jetbrains.kotlinx.jupyter.api.plugin.util.kernelVersion
4+
import org.jetbrains.kotlinx.jupyter.api.plugin.util.kotlinVersion
45
import org.junit.jupiter.api.Test
56
import kotlin.test.assertTrue
67

78
class UtilTests {
89

910
@Test
10-
fun testVersion() {
11-
val version = KotlinJupyterPluginExtension.apiVersion().trim()
11+
fun testVersions() {
12+
val version = kernelVersion().trim()
1213
assertTrue(version.isNotEmpty())
14+
15+
val kotlinVersion = kotlinVersion().trim()
16+
assertTrue(kotlinVersion.isNotEmpty())
1317
}
1418
}

jupyter-lib/test-kit/build.gradle.kts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
plugins {
2+
id("ru.ileasile.kotlin.publisher")
3+
kotlin("jvm")
4+
}
5+
6+
repositories {
7+
mavenCentral()
8+
}
9+
10+
dependencies {
11+
api(projects.kotlinJupyterKernel)
12+
implementation(libs.kotlin.dev.scriptingJvm)
13+
implementation(libs.test.kotlintest.assertions)
14+
}
15+
16+
buildSettings {
17+
withLanguageLevel(rootSettings.kotlinLanguageLevel)
18+
withTests()
19+
}
20+
21+
kotlinPublications {
22+
publication {
23+
publicationName.set("test-kit")
24+
description.set("Test suite for testing Kotlin kernel library integration")
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.jetbrains.kotlinx.jupyter.testkit
2+
3+
import io.kotest.matchers.nulls.shouldNotBeNull
4+
import io.kotest.matchers.types.shouldBeInstanceOf
5+
import jupyter.kotlin.DependsOn
6+
import org.jetbrains.kotlinx.jupyter.ReplForJupyterImpl
7+
import org.jetbrains.kotlinx.jupyter.api.Code
8+
import org.jetbrains.kotlinx.jupyter.api.MimeTypedResult
9+
import org.jetbrains.kotlinx.jupyter.libraries.EmptyResolutionInfoProvider
10+
import org.jetbrains.kotlinx.jupyter.repl.EvalResultEx
11+
import kotlin.script.experimental.jvm.util.classpathFromClassloader
12+
13+
abstract class JupyterReplTestCase {
14+
private val repl = ReplForJupyterImpl(EmptyResolutionInfoProvider, scriptClasspath, isEmbedded = true).apply {
15+
eval { librariesScanner.addLibrariesFromClassLoader(currentClassLoader, this) }
16+
}
17+
18+
fun execEx(code: Code): EvalResultEx {
19+
return repl.evalEx(code, null, -1)
20+
}
21+
22+
fun exec(code: Code): Any? {
23+
return execEx(code).renderedValue
24+
}
25+
26+
fun execRaw(code: Code): Any? {
27+
return execEx(code).rawValue
28+
}
29+
30+
@JvmName("execTyped")
31+
inline fun <reified T : Any> exec(code: Code): T {
32+
val res = exec(code)
33+
res.shouldBeInstanceOf<T>()
34+
return res
35+
}
36+
37+
fun execHtml(code: Code): String {
38+
val res = exec<MimeTypedResult>(code)
39+
val html = res["text/html"]
40+
html.shouldNotBeNull()
41+
return html
42+
}
43+
44+
companion object {
45+
private val currentClassLoader = DependsOn::class.java.classLoader
46+
private val scriptClasspath = classpathFromClassloader(currentClassLoader).orEmpty()
47+
}
48+
}

0 commit comments

Comments
 (0)