Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gradle/elide.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ jsoup = { group = "org.jsoup", name = "jsoup", version.ref = "jsoup" }
junit-platform-commons = { group = "org.junit.platform", name = "junit-platform-commons", version.ref = "junit-platform" }
junit-platform-console = { group = "org.junit.platform", name = "junit-platform-console", version.ref = "junit-platform" }
junit-platform-engine = { group = "org.junit.platform", name = "junit-platform-engine", version.ref = "junit-platform" }
junit-platform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher", version.ref = "junit-platform" }
junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter", version.ref = "junit-jupiter" }
junit-jupiter-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit-jupiter" }
junit-jupiter-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit-jupiter" }
Expand Down
332 changes: 134 additions & 198 deletions packages/builder/api/builder.api

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions packages/builder/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ dependencies {
implementation(libs.mordant.coroutines)
implementation(libs.mordant.markdown)
implementation(libs.highlights)
implementation(libs.junit.platform.launcher)
implementation(libs.junit.jupiter.engine)
implementation(libs.ktoml)
implementation(libs.plugin.redacted.core)
implementation(libs.kotlin.compiler.embedded)
Expand Down
1 change: 1 addition & 0 deletions packages/builder/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<ID>FunctionParameterNaming:Statics.kt$Statics$`in`: InputStream</ID>
<ID>LongMethod:JvmBuildConfigurator.kt$JvmBuildConfigurator$private fun ActionScope.kotlinc( name: String, resolver: MavenAetherResolver?, state: ElideBuildState, config: BuildConfigurator.BuildConfiguration, srcSet: SourceSet, additionalDeps: Classpath? = null, tests: Boolean = false, dependencies: List&lt;Task> = emptyList(), argsAmender: K2JVMCompilerArguments.() -> Unit = {}, )</ID>
<ID>ObjectPropertyNaming:Statics.kt$Statics$public val `in`: InputStream get() = delegatedInStream.value ?: System.`in`</ID>
<ID>ReturnCount:GuestJvmTestDriver.kt$GuestJvmTestDriver$override suspend fun run(testCase: JvmTestCase): TestOutcome</ID>
<ID>UnusedParameter:NativeImageDriver.kt$NativeImageDriver$inputs: NativeImageInputs.Paths</ID>
<ID>UnusedParameter:NativeImageDriver.kt$NativeImageDriver$javaToolchainHome: Path</ID>
<ID>UnusedParameter:TestDriver.kt$TestDriver$binder: ExecutionBinder? = null</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ package elide.tooling.builder

import io.micronaut.context.BeanContext
import java.nio.file.Path
import kotlinx.coroutines.CoroutineScope
import elide.exec.Action
import elide.exec.ActionScope
import elide.exec.ExecutionBinder
import elide.runtime.intrinsics.testing.TestingRegistrar
import elide.runtime.core.PolyglotContext
import elide.tooling.config.TestConfigurator.*
import elide.tooling.config.TestConfigurators
import elide.tooling.project.ElideProject
import elide.tooling.project.load
import elide.tooling.registry.ResolverRegistry
import elide.tooling.testing.TestRegistry

/**
* # Test Driver
Expand All @@ -38,25 +38,23 @@ import elide.tooling.registry.ResolverRegistry
*
* // must already be in a coroutine scope, or establish one for the build
* coroutineScope {
* TestDriver.discoverTests(beanContext, project) {
* TestDriver.configureTests(beanContext, project) {
* // bind build events here
* }
* }
* ```
*/
public object TestDriver {
@JvmStatic
public suspend fun CoroutineScope.configureTests(
public suspend fun configureTests(
beanContext: BeanContext,
guestContext: () -> PolyglotContext,
project: ElideProject,
registrar: TestingRegistrar? = null,
registry: TestRegistry,
binder: ExecutionBinder? = null, // @TODO test binder
resolvers: ResolverRegistry? = null,
scope: ActionScope? = null,
): TestConfiguration {
val effectiveRegistrar = registrar ?: requireNotNull(beanContext.getBean(TestingRegistrar::class.java)) {
"Failed to resolve testing registrar from DI context"
}
val effectiveScope = scope ?: Action.scope()
val effectiveResolvers = resolvers ?: ResolverRegistry.create()
val settings = MutableTestSettings()
Expand All @@ -67,17 +65,7 @@ public object TestDriver {
override val projectRoot: Path get() = project.root
override val settings: MutableTestSettings get() = settings
}.also {
TestConfigurators.contribute(beanContext, project.load(), effectiveRegistrar, it)
TestConfigurators.contribute(beanContext, guestContext, project.load(), registry, it)
}
}

@JvmStatic
public suspend fun CoroutineScope.discoverTests(
beanContext: BeanContext,
project: ElideProject,
registrar: TestingRegistrar? = null,
binder: ExecutionBinder? = null,
) {
configureTests(beanContext, project, registrar, binder)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import io.micronaut.context.BeanContext
import java.nio.file.Path
import elide.exec.Action
import elide.exec.ActionScope
import elide.runtime.intrinsics.testing.TestingRegistrar
import elide.runtime.core.PolyglotContext
import elide.tooling.config.TestConfigurator.TestConfiguration
import elide.tooling.config.TestConfigurator.MutableTestSettings
import elide.tooling.project.ElideConfiguredProject
import elide.tooling.project.ElideProject
import elide.tooling.project.load
import elide.tooling.registry.ResolverRegistry
import elide.tooling.testing.TestRegistry

/**
* # Test Configuration
Expand Down Expand Up @@ -51,25 +52,27 @@ public object TestConfiguration {
@JvmStatic
public suspend fun ElideProject.configureTests(
beanContext: BeanContext,
guestContext: () -> PolyglotContext,
with: TestConfiguration,
registrar: TestingRegistrar,
registry: TestRegistry,
) {
when (this) {
is ElideConfiguredProject -> this
else -> load()
}.let {
TestConfigurators.contribute(beanContext, it, registrar, with)
TestConfigurators.contribute(beanContext, guestContext, it, registry, with)
}
}

@JvmStatic
public suspend fun ElideProject.configureTests(
ctx: BeanContext,
registrar: TestingRegistrar? = null,
guestContext: () -> PolyglotContext,
registry: TestRegistry? = null,
): TestConfiguration {
val effectiveRegistrar = registrar ?: requireNotNull(ctx.getBean(TestingRegistrar::class.java)) {
"Failed to configure tests: No available testing registrar"
val effectiveRegistry = registry ?: requireNotNull(ctx.getBean(TestRegistry ::class.java)) {
"Failed to configure tests: No available testing registry"
}
return create().also { configureTests(ctx, it, effectiveRegistrar) }
return create().also { configureTests(ctx, guestContext, it, effectiveRegistry) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,22 @@ package elide.tooling.config

import io.micronaut.context.BeanContext
import java.nio.file.Path
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext
import elide.exec.ActionScope
import elide.runtime.intrinsics.testing.TestingRegistrar
import elide.runtime.core.PolyglotContext
import elide.tooling.config.BuildConfigurator.ProjectDirectories
import elide.tooling.project.ElideConfiguredProject
import elide.tooling.project.manifest.ElidePackageManifest
import elide.tooling.registry.ResolverRegistry
import elide.tooling.testing.TestRegistry

/**
* # Test Configurator
Expand Down Expand Up @@ -67,7 +77,8 @@ public interface TestConfigurator : ProjectConfigurator {
public val layout: ProjectDirectories
public val manifest: ElidePackageManifest
public val resourcesPath: Path
public val registrar: TestingRegistrar
public val registry: TestRegistry
public val guestContextProvider: () -> PolyglotContext
}

/**
Expand All @@ -92,6 +103,9 @@ public interface TestConfigurator : ProjectConfigurator {
*/
public sealed interface TestStatus : TestNotify

/** Delivered when a test or suite is scheduled for execution. */
public data object TestSeen : TestStatus

/** Delivered when a test passes, or all tests in a suite pass. */
public data object TestPass : TestStatus

Expand Down Expand Up @@ -122,24 +136,9 @@ public interface TestConfigurator : ProjectConfigurator {
* and runners) call into the controller to emit events. The controller is expected to keep track of interested
* parties and deliver events to them as applicable.
*/
public interface TestEventController {
/**
* ### Binder
*
* Context receiver which allows fluid binding to the test event controller's emitted events.
*/
public interface Binder {
/**
* Bind an event handler to a method.
*
* @param T Type of event to bind to.
* @param X Type of context to bind to; default-bound to [Any].
* @param event Event type to bind to.
* @param contextType Type for the context value that will be bound.
* @param handler Handler method to dispatch.
*/
public fun <T: Any, X: Any> bind(event: TestNotify, contextType: Class<T>, handler: suspend T.(X) -> Unit): Binder
}
public interface TestEventController : AutoCloseable {
/** A _hot_ flow of events [emitted][emit] through the controller. Events are broadcast to all consumers. */
public val events: Flow<TestNotify>

/**
* Emit event.
Expand All @@ -155,12 +154,39 @@ public interface TestConfigurator : ProjectConfigurator {
* @param event Event to deliver.
* @param context Context related to this event; if no context applies, the test runner or configurator is provided.
*/
public suspend fun <E: TestNotify, T: Any> emit(event: E, context: T)
public fun <E : TestNotify> emit(event: E)

/** "Inert" event controller which does nothing. */
public data object Inert : TestEventController {
override suspend fun <E : TestNotify, T : Any> emit(event: E, context: T) {
// no-op
override val events: Flow<TestEvent> = emptyFlow()
override fun <E : TestNotify> emit(event: E) {
// noop
}

override fun close() {
// noop
}
}

public companion object {
/**
* Create a new event controller backed by a shared flow that uses a background scope to launch event emitting
* calls if they would suspend the caller.
*/
public fun create(
context: CoroutineContext = Dispatchers.IO
): TestEventController = object : TestEventController {
private val scope = CoroutineScope(context + SupervisorJob())
override val events = MutableSharedFlow<TestNotify>()

override fun <E : TestNotify> emit(event: E) {
if (events.tryEmit(event)) return
scope.launch { events.emit(event) }
}

override fun close() {
scope.cancel()
}
}
}
}
Expand All @@ -183,20 +209,20 @@ public interface TestConfigurator : ProjectConfigurator {
*
* API which provides read-only settings access for test facilities within an Elide project.
*/
@JvmRecord public data class ImmutableTestSettings (
@JvmRecord public data class ImmutableTestSettings(
override val enableCoverage: Boolean = true,
override val enableDiscovery: Boolean = true,
): TestSettings
) : TestSettings

/**
* ## Test Settings (Mutable)
*
* API which provides mutable settings access and control for test facilities within an Elide project.
*/
public data class MutableTestSettings (
public data class MutableTestSettings(
override var enableCoverage: Boolean = true,
override var enableDiscovery: Boolean = true,
): TestSettings
) : TestSettings

/**
* Contribute test configuration.
Expand All @@ -209,20 +235,3 @@ public interface TestConfigurator : ProjectConfigurator {
*/
public suspend fun contribute(state: ElideTestState, config: TestConfiguration)
}

/**
* Shorthand to bind a test event handler to the event controller.
*
* @param T Type of event to bind to.
* @param X Type of context to bind to; default-bound to [Any].
* @param event Event type to bind to.
* @param handler Handler method to dispatch.
*/
public inline fun <X: Any, reified T: TestConfigurator.TestNotify> TestConfigurator.TestEventController.Binder.on(
event: T,
noinline handler: suspend T.(X) -> Unit,
): TestConfigurator.TestEventController.Binder = bind(
event = event,
contextType = T::class.java,
handler = handler,
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ package elide.tooling.config
import io.micronaut.context.BeanContext
import java.nio.file.Path
import java.util.ServiceLoader
import elide.runtime.intrinsics.testing.TestingRegistrar
import elide.runtime.core.PolyglotContext
import elide.tooling.project.ElideConfiguredProject
import elide.tooling.project.manifest.ElidePackageManifest
import elide.tooling.testing.TestRegistry

/**
* ## Test Configurators
Expand Down Expand Up @@ -46,8 +47,9 @@ public object TestConfigurators {
@JvmStatic
public suspend fun contribute(
beanContext: BeanContext,
guestContext: () -> PolyglotContext,
project: ElideConfiguredProject,
registrar: TestingRegistrar,
registry: TestRegistry,
from: Sequence<TestConfigurator>,
to: TestConfigurator.TestConfiguration,
extraConfigurator: TestConfigurator? = null,
Expand All @@ -62,7 +64,8 @@ public object TestConfigurators {
override val manifest: ElidePackageManifest get() = project.manifest
override val layout: BuildConfigurator.ProjectDirectories get() = layout
override val resourcesPath: Path get() = project.resourcesPath
override val registrar: TestingRegistrar get() = registrar
override val registry: TestRegistry get() = registry
override val guestContextProvider: () -> PolyglotContext = guestContext
}
from.let {
when (extraConfigurator) {
Expand All @@ -76,11 +79,12 @@ public object TestConfigurators {

@JvmStatic public suspend fun contribute(
beanContext: BeanContext,
guestContext: () -> PolyglotContext,
project: ElideConfiguredProject,
registrar: TestingRegistrar,
registry: TestRegistry,
to: TestConfigurator.TestConfiguration,
extraConfigurator: TestConfigurator? = null,
) {
contribute(beanContext, project, registrar, collect(), to, extraConfigurator)
contribute(beanContext, guestContext, project, registry, collect(), to, extraConfigurator)
}
}
Loading
Loading