CloseableLifecycleConformanceSuite
Reusable lifecycle contract test suite for ScopedCloseable implementations.
Subclass and implement create to bind any coordinator under test. The returned instance must be freshly constructed (owning its own child job) and must not be closed yet.
Lives in commonMain of :kuilt-conformance so every module can subclass it from its own commonTest source set. Every Test encodes a required invariant of the ScopedCloseable contract — a conforming implementation must pass all of them.
Test-body close requirement. Every test that creates a coordinator must call AutoCloseable.close before the test body exits (or use use {}), or otherwise stop it by cancelling the scope it was handed. An un-closed coordinator with a live anti-entropy loop can freeze virtual time in runTest (kotlinx.coroutines.test.UncompletedCoroutinesError).
What this suite asserts, and what it leaves to the binding
Two observables, deliberately distinct. backgroundJobsOf is what the binding reports it launched; the scope handed to create carries, in its children, what the instance structurally owns. Every property here is a quantifier over one of the two, and the pair is what keeps a quantifier from being satisfied by absence — see backgroundJobsOf for the floor that makes the first one non-vacuous and closeCancelsTheJobTheInstanceOwnsInTheGivenScope for the second.
What it still does not express, stated so a reader does not infer coverage from a green run: nothing here closes a coordinator that is mid-operation (every instance is idle at the moment it is closed), nothing calls a method after AutoCloseable.close — ScopedCloseable does not say what that does, so every subclass currently invents an answer — and nothing calls AutoCloseable.close from two threads at once, which is single-threaded-harness-unreachable and lives instead in :kuilt-core's real-threaded ScopedCloseableCloseOnceCapabilityConcurrencyTest.
Wiring
class MyCoordinatorLifecycleTest : CloseableLifecycleConformanceSuite() {
override fun create(scope: CoroutineScope): MyCoordinator =
MyCoordinator(scope)
override fun backgroundJobsOf(instance: ScopedCloseable): List<Job> =
(instance as MyCoordinator).backgroundJobsForTest
}Functions
AutoCloseable.close cancels the job the instance owns inside the scope it was given — the observable form of "ScopedCloseable.ownJob is cancelled on close".
Cancelling the scope create was handed stops the coordinator, with no call to AutoCloseable.close.