diff --git a/engine/src/main/kotlin/io/canopy/engine/core/flows/Context.kt b/engine/src/main/kotlin/io/canopy/engine/core/flows/Context.kt index f8d074e..3c3f90a 100644 --- a/engine/src/main/kotlin/io/canopy/engine/core/flows/Context.kt +++ b/engine/src/main/kotlin/io/canopy/engine/core/flows/Context.kt @@ -89,8 +89,9 @@ fun Node<*>.fromContextOrNull(key: String): T? { while (current != null) { if (current is Context) { - current.provided[key]?.let { - return it.invoke() as? T ?: continue + val value = current.provided[key] + if (value != null) { + return value() as T } } current = current.parent diff --git a/engine/src/test/kotlin/io/canopy/engine/core/flows/ContextTests.kt b/engine/src/test/kotlin/io/canopy/engine/core/flows/ContextTests.kt index 2fbefa2..e1a71fb 100644 --- a/engine/src/test/kotlin/io/canopy/engine/core/flows/ContextTests.kt +++ b/engine/src/test/kotlin/io/canopy/engine/core/flows/ContextTests.kt @@ -1,9 +1,16 @@ package io.canopy.engine.core.flows +import kotlin.test.assertFailsWith +import kotlin.time.Duration.Companion.seconds +import java.util.concurrent.atomic.AtomicBoolean import io.canopy.engine.core.managers.ManagersRegistry import io.canopy.engine.core.managers.SceneManager import io.canopy.engine.core.nodes.Node import io.canopy.engine.core.nodes.types.empty.EmptyNode +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test @@ -207,6 +214,34 @@ class ContextTests { assertEquals(1, c.fromContext("keyA")) } + @Test + fun `resolving un-provided data shouldn't hang the thread`() = runBlocking { + val root = n("root") { + Context { + n("a") + } + } + + root.buildTree() + + val a = root.getNode("./a") + + val noHang = AtomicBoolean(false) + + val job = launch(Dispatchers.Default) { + withTimeout(2.seconds) { + assertFailsWith { + a.fromContext("data") + } + noHang.set(true) + } + } + + job.join() + + assertTrue(noHang.get()) + } + // --- Tiny adapter ------------------------------------------------------- // If your Node doesn't expose children()/name, replace these calls with your real APIs.