From cffcc405883ed4376b30c2980de1b06a976f222a Mon Sep 17 00:00:00 2001 From: sksamuel Date: Sun, 28 Aug 2022 14:44:00 -0500 Subject: [PATCH 1/2] Added lazy mountable extensions --- .../core/extensions/MountableExtension.kt | 49 +++++++++++++++++-- .../extensions/LazyMountableExtensionTest.kt | 36 ++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/extensions/LazyMountableExtensionTest.kt diff --git a/kotest-framework/kotest-framework-api/src/commonMain/kotlin/io/kotest/core/extensions/MountableExtension.kt b/kotest-framework/kotest-framework-api/src/commonMain/kotlin/io/kotest/core/extensions/MountableExtension.kt index f7f9466b389..be404c839c7 100644 --- a/kotest-framework/kotest-framework-api/src/commonMain/kotlin/io/kotest/core/extensions/MountableExtension.kt +++ b/kotest-framework/kotest-framework-api/src/commonMain/kotlin/io/kotest/core/extensions/MountableExtension.kt @@ -27,11 +27,54 @@ interface MountableExtension : Extension { fun mount(configure: CONFIG.() -> Unit): MATERIALIZED } +/** + * A [LazyMountableExtension] is an [Extension] that can return a materialized value to the + * user and allows for a configuration block. + * + * This allows extensions to return control objects which differ from the extension itself. + * + * For example: + * + * class MyTest : FunSpec() { + * init { + * val kafka = install(EmbeddedKafka) { + * port = 9092 + * } + * } + * } + * + * Here `kafka` is a materialized value that contains details of the host/port of the + * started kafka instance and `EmbeddedKafka` is the extension itself. + * + */ +interface LazyMountableExtension : Extension { + // cannot be suspending as it is invoked by install that is used in constructors + fun mount(configure: CONFIG.() -> Unit): LazyMaterialized +} + +data class LazyMaterialized(private var value: MATERIALIZED) { + + fun set(value: MATERIALIZED) { + this.value = value + } + + fun get() = value +} + // cannot be suspending as it is used in constructors fun Spec.install( - mountable: MountableExtension, + ext: MountableExtension, configure: CONFIG.() -> Unit = {} ): MATERIALIZED { - extensions(mountable) - return mountable.mount(configure) + extensions(ext) + return ext.mount(configure) +} + +// cannot be suspending as it is used in constructors +fun Spec.install( + ext: LazyMountableExtension, + configure: CONFIG.() -> Unit = {}, +): LazyMaterialized { + extensions(ext) + return ext.mount(configure) } diff --git a/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/extensions/LazyMountableExtensionTest.kt b/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/extensions/LazyMountableExtensionTest.kt new file mode 100644 index 00000000000..60bec7eea21 --- /dev/null +++ b/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/extensions/LazyMountableExtensionTest.kt @@ -0,0 +1,36 @@ +package com.sksamuel.kotest.engine.extensions + +import io.kotest.core.extensions.LazyMaterialized +import io.kotest.core.extensions.LazyMountableExtension +import io.kotest.core.extensions.install +import io.kotest.core.listeners.BeforeSpecListener +import io.kotest.core.spec.Spec +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.delay + +class LazyMountableExtensionTest : FunSpec() { + + private val mountable = MyLazyMountable() + private val m: LazyMaterialized = install(mountable) + + init { + test("lazy materialized values") { + m.get() shouldBe "ready" + } + } +} + +class MyLazyMountable : LazyMountableExtension, BeforeSpecListener { + + private val m = LazyMaterialized("notready") + + override suspend fun beforeSpec(spec: Spec) { + delay(5) // simulate slow db + m.set("ready") + } + + override fun mount(configure: (Unit) -> Unit): LazyMaterialized { + return m + } +} From e443e16e423cf998ba7d0d7e3185558f2da7eec9 Mon Sep 17 00:00:00 2001 From: sksamuel Date: Sun, 2 Oct 2022 17:09:41 -0500 Subject: [PATCH 2/2] Changed interface --- .../core/extensions/MountableExtension.kt | 9 ++------ .../extensions/LazyMountableExtensionTest.kt | 22 +++++++++---------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/kotest-framework/kotest-framework-api/src/commonMain/kotlin/io/kotest/core/extensions/MountableExtension.kt b/kotest-framework/kotest-framework-api/src/commonMain/kotlin/io/kotest/core/extensions/MountableExtension.kt index be404c839c7..df37a91ebc4 100644 --- a/kotest-framework/kotest-framework-api/src/commonMain/kotlin/io/kotest/core/extensions/MountableExtension.kt +++ b/kotest-framework/kotest-framework-api/src/commonMain/kotlin/io/kotest/core/extensions/MountableExtension.kt @@ -52,13 +52,8 @@ interface LazyMountableExtension : Extension { fun mount(configure: CONFIG.() -> Unit): LazyMaterialized } -data class LazyMaterialized(private var value: MATERIALIZED) { - - fun set(value: MATERIALIZED) { - this.value = value - } - - fun get() = value +interface LazyMaterialized { + suspend fun get(): MATERIALIZED } // cannot be suspending as it is used in constructors diff --git a/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/extensions/LazyMountableExtensionTest.kt b/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/extensions/LazyMountableExtensionTest.kt index 60bec7eea21..78544891e69 100644 --- a/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/extensions/LazyMountableExtensionTest.kt +++ b/kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/com/sksamuel/kotest/engine/extensions/LazyMountableExtensionTest.kt @@ -3,15 +3,13 @@ package com.sksamuel.kotest.engine.extensions import io.kotest.core.extensions.LazyMaterialized import io.kotest.core.extensions.LazyMountableExtension import io.kotest.core.extensions.install -import io.kotest.core.listeners.BeforeSpecListener -import io.kotest.core.spec.Spec import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import kotlinx.coroutines.delay class LazyMountableExtensionTest : FunSpec() { - private val mountable = MyLazyMountable() + private val mountable = MyLazyMountableExtension() private val m: LazyMaterialized = install(mountable) init { @@ -21,16 +19,18 @@ class LazyMountableExtensionTest : FunSpec() { } } -class MyLazyMountable : LazyMountableExtension, BeforeSpecListener { +class MyLazyMountableExtension : LazyMountableExtension { - private val m = LazyMaterialized("notready") + override fun mount(configure: (Unit) -> Unit): LazyMaterialized { + return object : LazyMaterialized { - override suspend fun beforeSpec(spec: Spec) { - delay(5) // simulate slow db - m.set("ready") - } + var state: String? = null - override fun mount(configure: (Unit) -> Unit): LazyMaterialized { - return m + override suspend fun get(): String { + delay(1) + if (state == null) state = "ready" + return state ?: error("Must be initialized") + } + } } }