diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 4ba15dc..ff36858 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -7,6 +7,7 @@ kotlinx-datetime = "0.6.1" # https://github.com/Kotlin/kotlinx-datetime/relea
gradle-testkit = "8.10.1" # https://gradle.org/releases/
parameterize = "0.3.3" # https://github.com/BenWoodworth/Parameterize/releases
ktor = "3.0.1" # https://ktor.io/docs/releases.html#release-details
+kti = { strictly = "0.1.0" } # https://gitlab.com/opensavvy/groundwork/kotlin-test-initiative/-/releases
[plugins]
@@ -18,5 +19,6 @@ gradle-testkit = { module = "dev.gradleplugins:gradle-test-kit", version.ref = "
arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" }
parameterize = { module = "com.benwoodworth.parameterize:parameterize", version.ref = "parameterize" }
ktor-testHost = { module = "io.ktor:ktor-server-test-host", version.ref = "ktor" }
+kti = { module = "dev.opensavvy.kti:kotlin-test-initiative", version.ref = "kti" }
[bundles]
diff --git a/runners/runner-test-initiative/README.md b/runners/runner-test-initiative/README.md
new file mode 100644
index 0000000..8e49c27
--- /dev/null
+++ b/runners/runner-test-initiative/README.md
@@ -0,0 +1,7 @@
+# Module Execute with Kotlin Test Initiative compatibility
+
+Execute Prepared tests with any runner compatible with the Kotlin Test Initiative.
+
+
+
+This module is highly experimental. To learn more, see the [Test Initiative repository](https://gitlab.com/opensavvy/groundwork/kotlin-test-initiative).
diff --git a/runners/runner-test-initiative/build.gradle.kts b/runners/runner-test-initiative/build.gradle.kts
new file mode 100644
index 0000000..0e251b2
--- /dev/null
+++ b/runners/runner-test-initiative/build.gradle.kts
@@ -0,0 +1,54 @@
+@file:OptIn(ExperimentalWasmDsl::class)
+
+import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
+
+plugins {
+ alias(opensavvyConventions.plugins.base)
+ alias(opensavvyConventions.plugins.kotlin.library)
+}
+
+kotlin {
+ jvm()
+ js {
+ browser()
+ nodejs()
+ }
+ linuxX64()
+ linuxArm64()
+ macosX64()
+ macosArm64()
+ iosArm64()
+ iosX64()
+ iosSimulatorArm64()
+ watchosX64()
+ watchosArm32()
+ watchosArm64()
+ watchosSimulatorArm64()
+ tvosX64()
+ tvosArm64()
+ tvosSimulatorArm64()
+ mingwX64()
+ wasmJs {
+ browser()
+ nodejs()
+ }
+
+ sourceSets.commonMain {
+ dependencies {
+ api(projects.suite)
+
+ api(libs.kti)
+ }
+ }
+}
+
+library {
+ name.set("Execute with Kotlin Test Initiative compatibility")
+ description.set("Execute Prepared test suites with Kotlin Test Initiative compatibility")
+ homeUrl.set("https://opensavvy.gitlab.io/groundwork/prepared/api-docs/runners/runner-test-initiative/index.html")
+
+ license.set {
+ name.set("Apache 2.0")
+ url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
+ }
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 95020a3..818d965 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -68,4 +68,5 @@ include(
"runners:runner-kotlin-test",
"runners:runner-kotest",
+ "runners:runner-test-initiative",
)
--
2.51.2
From 94139984f2c7da82a785a9c85240be9db9895f5d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?=
Date: Wed, 11 Dec 2024 21:59:44 +0100
Subject: [PATCH 2/2] feat(runner-test-initiative): First prototype for the KTI
runner
---
.../src/commonMain/kotlin/Suite.kt | 156 ++++++++++++++++++
1 file changed, 156 insertions(+)
create mode 100644 runners/runner-test-initiative/src/commonMain/kotlin/Suite.kt
diff --git a/runners/runner-test-initiative/src/commonMain/kotlin/Suite.kt b/runners/runner-test-initiative/src/commonMain/kotlin/Suite.kt
new file mode 100644
index 0000000..df8ccd8
--- /dev/null
+++ b/runners/runner-test-initiative/src/commonMain/kotlin/Suite.kt
@@ -0,0 +1,156 @@
+package opensavvy.prepared.runner.kti
+
+import kotlinx.coroutines.currentCoroutineContext
+import kotlinx.coroutines.ensureActive
+import kotlinx.coroutines.test.TestScope
+import opensavvy.kti.*
+import opensavvy.prepared.suite.SuiteDsl
+import opensavvy.prepared.suite.TestDsl
+import opensavvy.prepared.suite.config.Ignored
+import opensavvy.prepared.suite.config.TestConfig
+import opensavvy.prepared.suite.config.get
+import opensavvy.prepared.suite.config.plus
+import opensavvy.prepared.suite.runTestDslSuspend
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.coroutineContext
+
+@ExperimentalKtiApi
+fun preparedSuite(@TestName name: String, block: SuiteDsl.() -> Unit): TestRoot =
+ PreparedTestRoot(name).apply { discover(block) }
+
+@ExperimentalKtiApi
+private class PreparedTestRoot(
+ private val name: String,
+) : TestRoot {
+
+ private val builder = PreparedTestNode(
+ parentName = name,
+ parentConfig = TestConfig.Empty,
+ )
+
+ fun discover(block: SuiteDsl.() -> Unit) {
+ builder.block()
+ }
+
+ override suspend fun execute(context: TestCommand, reporter: TestReporter) {
+ val suite = PreparedTestTree.Suite(
+ name = name,
+ config = TestConfig.Empty,
+ children = builder.children,
+ )
+
+ suite.execute(context, reporter)
+ }
+}
+
+@ExperimentalKtiApi
+private class PreparedTestNode(
+ private val parentName: String,
+ private val parentConfig: TestConfig,
+) : SuiteDsl {
+ val children = ArrayList()
+
+ override fun suite(
+ name: String,
+ config: TestConfig,
+ block: SuiteDsl.() -> Unit,
+ ) {
+ val fullName = "$parentName.$name"
+ val fullConfig = parentConfig + config
+
+ children += PreparedTestTree.Suite(
+ name = fullName,
+ config = fullConfig,
+ children = PreparedTestNode(fullName, fullConfig)
+ .apply { block() }
+ .children
+ )
+ }
+
+ override fun test(
+ name: String,
+ context: CoroutineContext,
+ config: TestConfig,
+ block: suspend TestDsl.() -> Unit,
+ ) {
+ val fullName = "$parentName.$name"
+ val fullConfig = parentConfig + config
+
+ children += PreparedTestTree.Test(
+ name = fullName,
+ context = context,
+ config = fullConfig,
+ ) {
+ block()
+ }
+ }
+}
+
+@ExperimentalKtiApi
+private sealed class PreparedTestTree {
+
+ abstract val name: String
+ abstract val config: TestConfig
+
+ abstract suspend fun execute(
+ context: TestCommand,
+ reporter: TestReporter,
+ )
+
+ class Suite(
+ override val name: String,
+ override val config: TestConfig,
+ val children: List,
+ ) : PreparedTestTree() {
+
+ override suspend fun execute(context: TestCommand, reporter: TestReporter) {
+ for (child in children) {
+ child.execute(context, reporter)
+ }
+ }
+ }
+
+ class Test(
+ override val name: String,
+ val context: CoroutineContext,
+ override val config: TestConfig,
+ val block: suspend TestDsl.() -> Unit,
+ ) : PreparedTestTree() {
+
+ override suspend fun execute(context: TestCommand, reporter: TestReporter) {
+ val shouldRun = !context.dryRun &&
+ (context.includes.toList().isEmpty() || context.includes.any { it matches name }) &&
+ context.excludes.none { it matches name } &&
+ config[Ignored] == null
+
+ if (!shouldRun) {
+ reporter.report(object : TestEvent.Skipped {
+ override val testName: String
+ get() = name
+ })
+ return
+ }
+
+ reporter.report(object : TestEvent.Started {
+ override val testName: String
+ get() = name
+ })
+ try {
+ val scope = TestScope(currentCoroutineContext())
+ scope.runTestDslSuspend(name, this.context, config, block)
+
+ reporter.report(object : TestEvent.Finished {
+ override val testName: String
+ get() = name
+ })
+ } catch (e: Exception) {
+ coroutineContext.ensureActive()
+ println("Failed with: $e") // In the future, report that in the test event somehow
+ reporter.report(object : TestEvent.Failed {
+ override val testName: String
+ get() = name
+ })
+ }
+ }
+ }
+}