From 93bf2ac93f3fc7f1497566b1433b5fa9755fb06a Mon Sep 17 00:00:00 2001 From: Mikhail Fedotov Date: Wed, 21 Jan 2026 22:27:00 +0530 Subject: [PATCH 1/5] Update versions --- buildSrc/src/main/kotlin/ru/nsk/Versions.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/kotlin/ru/nsk/Versions.kt b/buildSrc/src/main/kotlin/ru/nsk/Versions.kt index b0b3be9..16c7894 100644 --- a/buildSrc/src/main/kotlin/ru/nsk/Versions.kt +++ b/buildSrc/src/main/kotlin/ru/nsk/Versions.kt @@ -5,10 +5,10 @@ object Versions { const val libraryVersion = "0.35.0" // tools - const val kotlin = "2.2.0" - const val kotlinDokka = "2.0.0" + const val kotlin = "2.3.0" + const val kotlinDokka = "2.1.0" const val kotlinBinaryCompatibilityValidatorPlugin = "0.18.1" - const val kotlinKoverPlugin = "0.9.1" + const val kotlinKoverPlugin = "0.9.4" // compatibility const val jdkVersion = 17 @@ -17,9 +17,9 @@ object Versions { // dependencies const val coroutinesCore = "1.10.2" - const val serialization = "1.9.0" + const val serialization = "1.10.0" // test dependencies const val mockk = "1.14.7" - const val kotest = "6.0.7" + const val kotest = "6.1.0" } \ No newline at end of file From da0297ac2c0d58515ca9ad0c12e1ff509a6a9076 Mon Sep 17 00:00:00 2001 From: Mikhail Fedotov Date: Wed, 21 Jan 2026 23:40:28 +0530 Subject: [PATCH 2/5] Update dependencies --- buildSrc/src/main/kotlin/ru/nsk/Versions.kt | 4 ++-- docs/notes/publishing.md | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../ru/nsk/kstatemachine/transition/TransitionParams.kt | 1 + .../kotlin/ru/nsk/kstatemachine/transition/TransitionTest.kt | 3 +-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/kotlin/ru/nsk/Versions.kt b/buildSrc/src/main/kotlin/ru/nsk/Versions.kt index 16c7894..e661910 100644 --- a/buildSrc/src/main/kotlin/ru/nsk/Versions.kt +++ b/buildSrc/src/main/kotlin/ru/nsk/Versions.kt @@ -12,8 +12,8 @@ object Versions { // compatibility const val jdkVersion = 17 - const val languageVersion = "1.8" - const val apiVersion = "1.8" + const val languageVersion = "2.0" + const val apiVersion = "2.0" // dependencies const val coroutinesCore = "1.10.2" diff --git a/docs/notes/publishing.md b/docs/notes/publishing.md index 96f9638..979995a 100644 --- a/docs/notes/publishing.md +++ b/docs/notes/publishing.md @@ -1,6 +1,6 @@ # Publishing to maven central The process of publishing to maven central is absolutely non-intuitive, not visualizable and confusing. -I faced unexpectable behaviour when my publications had a random count of components in the UI +I faced unexpectable behavior when my publications had a random count of components in the UI https://central.sonatype.com/publishing/deployments Publishing such a library version causes error on client's side, when he tries to resolve all required dependencies. There are absolutely no errors nor in publishing logs nor in web UI. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d4081da..19a6bde 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/transition/TransitionParams.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/transition/TransitionParams.kt index 4599dd6..b2750df 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/transition/TransitionParams.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/transition/TransitionParams.kt @@ -11,6 +11,7 @@ import ru.nsk.kstatemachine.event.Event import ru.nsk.kstatemachine.event.WrappedEvent import ru.nsk.kstatemachine.statemachine.StateMachineDslMarker +@ConsistentCopyVisibility @StateMachineDslMarker data class TransitionParams internal constructor( val transition: Transition, diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/transition/TransitionTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/transition/TransitionTest.kt index 9a38dfa..3fc8694 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/transition/TransitionTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/transition/TransitionTest.kt @@ -16,7 +16,6 @@ import io.kotest.matchers.types.shouldBeSameInstanceAs import io.mockk.confirmVerified import io.mockk.verify import io.mockk.verifySequence -import org.junit.jupiter.api.fail import ru.nsk.kstatemachine.* import ru.nsk.kstatemachine.state.* import ru.nsk.kstatemachine.statemachine.ProcessingResult.PROCESSED @@ -54,7 +53,7 @@ class TransitionTest : FreeSpec({ if (it.direction.targetState === state2) callbacks.onStateExit(this) else - fail("incorrect direction ${it.direction}") + error("incorrect direction ${it.direction}") } transitionOn { From bb35577f89daa5a48df1662984fa8af4584a67cd Mon Sep 17 00:00:00 2001 From: Mikhail Fedotov Date: Wed, 21 Jan 2026 23:54:28 +0530 Subject: [PATCH 3/5] Add isStartTransition method --- .../transition/TransitionParams.kt | 11 +++++++++- .../statemachine/StateMachineTest.kt | 22 +++++++++++++++---- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/transition/TransitionParams.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/transition/TransitionParams.kt index b2750df..a23e255 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/transition/TransitionParams.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/transition/TransitionParams.kt @@ -8,6 +8,7 @@ package ru.nsk.kstatemachine.transition import ru.nsk.kstatemachine.event.Event +import ru.nsk.kstatemachine.event.StartEvent import ru.nsk.kstatemachine.event.WrappedEvent import ru.nsk.kstatemachine.statemachine.StateMachineDslMarker @@ -35,4 +36,12 @@ val TransitionParams<*>.unwrappedEvent get() = if (event is WrappedEvent) event. * Convenience property for unwrapping original argument. * If the event is not [WrappedEvent] this is same as [TransitionParams.argument] property */ -val TransitionParams<*>.unwrappedArgument get() = if (event is WrappedEvent) event.argument else argument \ No newline at end of file +val TransitionParams<*>.unwrappedArgument get() = if (event is WrappedEvent) event.argument else argument + +/** + * Returns true is the transition is triggered by [StartEvent]. + * This means that the StateMachine is starting. + * Might be useful to check if you're entering some State just by a machine startup + * or by outside event. + */ +val TransitionParams<*>.isStartTransition get() = event is StartEvent \ No newline at end of file diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineTest.kt index 637a3d7..22dcd67 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineTest.kt @@ -12,6 +12,7 @@ import io.kotest.assertions.throwables.shouldThrowUnitWithMessage import io.kotest.assertions.throwables.shouldThrowWithMessage import io.kotest.core.spec.style.FreeSpec import io.kotest.datatest.withData +import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldEndWith @@ -26,10 +27,7 @@ import ru.nsk.kstatemachine.state.* import ru.nsk.kstatemachine.statemachine.StateMachineTestData.OffEvent import ru.nsk.kstatemachine.statemachine.StateMachineTestData.OnEvent import ru.nsk.kstatemachine.testing.Testing.startFromBlocking -import ru.nsk.kstatemachine.transition.DefaultTransition -import ru.nsk.kstatemachine.transition.Transition -import ru.nsk.kstatemachine.transition.TransitionType -import ru.nsk.kstatemachine.transition.onTriggered +import ru.nsk.kstatemachine.transition.* private object StateMachineTestData { object OnEvent : Event @@ -363,6 +361,22 @@ class StateMachineTest : FreeSpec({ } } + "isStartTransition" { + lateinit var state2: State + val machine = createTestStateMachine(coroutineStarterType) { + state2 = state("state2") { + onEntry { it.isStartTransition shouldBe false } + } + initialState("initial") { + onEntry { it.isStartTransition shouldBe true } + transitionOn { targetState = { state2 } } + } + onStarted { it.isStartTransition shouldBe true } + } + machine.processEvent(SwitchEvent) + machine.activeStates().shouldContain(state2) + } + "destroy from onStart" { val callbacks = mockkCallbacks() val machine = createTestStateMachine(coroutineStarterType) { From 8e922142aa3d1be7da51cf29557a1e959febf1bb Mon Sep 17 00:00:00 2001 From: Mikhail Fedotov Date: Thu, 22 Jan 2026 00:34:50 +0530 Subject: [PATCH 4/5] Throw if using Dispatchers.Default or Dispatchers.IO --- .../statemachine/CoroutinesStateMachine.kt | 22 +++++++++++++ .../statemachine/CreationArguments.kt | 13 +++++++- .../kotlin/ru/nsk/kstatemachine/TestUtils.kt | 9 ----- .../statemachine/StateMachineTest.kt | 33 +++++++++++++++++++ 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/CoroutinesStateMachine.kt b/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/CoroutinesStateMachine.kt index 4f0f994..6cf132f 100644 --- a/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/CoroutinesStateMachine.kt +++ b/kstatemachine-coroutines/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/CoroutinesStateMachine.kt @@ -15,6 +15,7 @@ import ru.nsk.kstatemachine.state.ChildMode import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.coroutines.ContinuationInterceptor import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -42,10 +43,31 @@ suspend fun createStateMachine( contract { callsInPlace(init, InvocationKind.EXACTLY_ONCE) } + checkCoroutineScopeValidity(scope, creationArguments) return CoroutinesLibCoroutineAbstraction(scope) .createStateMachine(name, childMode, start, creationArguments, init) } +private fun checkCoroutineScopeValidity(scope: CoroutineScope, creationArguments: CreationArguments) { + if (creationArguments.skipCoroutineScopeValidityCheck) return + + val dispatcher = scope.coroutineContext[ContinuationInterceptor] + val dispatcherName = dispatcher.toString() + if (dispatcher === Dispatchers.Default || + dispatcherName == "Dispatchers.Default" || + dispatcherName.startsWith("Dispatchers.Default.limitedParallelism") || + dispatcherName == "Dispatchers.IO" || // can't get IO dispatcher in commonMain + dispatcherName.startsWith("Dispatchers.IO.limitedParallelism") + ) { + error( + "Using Dispatchers.Default or Dispatchers.IO for StateMachine even with limitedParallelism(1) is the most likely an error," + + " as it is multi-threaded, see the docs: \n" + + "https://kstatemachine.github.io/kstatemachine/pages/multithreading.html#use-single-threaded-coroutinescope" + + "You can opt-out this check by CreationArguments::skipCoroutineScopeValidityCheck flag." + ) + } +} + /** * Processes event in async fashion (using launch() to start new coroutine). * diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/CreationArguments.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/CreationArguments.kt index 75128a5..325a9fc 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/CreationArguments.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/CreationArguments.kt @@ -48,6 +48,15 @@ interface CreationArguments { * Default: null */ val eventRecordingArguments: EventRecordingArguments? + + /** + * The library checks if you are trying to use multithreaded Dispatcher like + * Dispatchers.Default or Dispatcher.IO which is usually an error. + * @see [https://kstatemachine.github.io/kstatemachine/pages/multithreading.html#use-single-threaded-coroutinescope] + * You can skip this validation setting the flag to true. + * Default: false + */ + val skipCoroutineScopeValidityCheck: Boolean } interface CreationArgumentsBuilder : CreationArguments { @@ -56,6 +65,7 @@ interface CreationArgumentsBuilder : CreationArguments { override var doNotThrowOnMultipleTransitionsMatch: Boolean override var requireNonBlankNames: Boolean override var eventRecordingArguments: EventRecordingArguments? + override var skipCoroutineScopeValidityCheck: Boolean } private data class CreationArgumentsBuilderImpl( @@ -63,7 +73,8 @@ private data class CreationArgumentsBuilderImpl( override var isUndoEnabled: Boolean = false, override var doNotThrowOnMultipleTransitionsMatch: Boolean = false, override var requireNonBlankNames: Boolean = false, - override var eventRecordingArguments: EventRecordingArguments? = null + override var eventRecordingArguments: EventRecordingArguments? = null, + override var skipCoroutineScopeValidityCheck: Boolean = false, ) : CreationArgumentsBuilder @OptIn(ExperimentalContracts::class) diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt index 8c610a8..ff7df26 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt @@ -79,7 +79,6 @@ enum class CoroutineStarterType { * but it should be ok as it happens sequentially. */ COROUTINES_LIB_SINGLE_THREAD_DISPATCHER, - COROUTINES_LIB_DEFAULT_LIMITED_DISPATCHER, } @OptIn(ExperimentalCoroutinesApi::class) @@ -132,13 +131,5 @@ suspend fun createTestStateMachine( creationArguments, init = init ) - CoroutineStarterType.COROUTINES_LIB_DEFAULT_LIMITED_DISPATCHER -> createStateMachine( - CoroutineScope(Dispatchers.Default.limitedParallelism(1)), // does not guarantee same thread for each task - name, - childMode, - start, - creationArguments, - init = init - ) } } \ No newline at end of file diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineTest.kt index 22dcd67..201e491 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineTest.kt @@ -19,6 +19,9 @@ import io.kotest.matchers.string.shouldEndWith import io.mockk.called import io.mockk.verify import io.mockk.verifySequence +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel import ru.nsk.kstatemachine.* import ru.nsk.kstatemachine.event.Event import ru.nsk.kstatemachine.event.EventMatcher @@ -35,6 +38,36 @@ private object StateMachineTestData { } class StateMachineTest : FreeSpec({ + withData( + nameFn = { "dispatcher: $it" }, + CoroutineScope(Dispatchers.Default), + CoroutineScope(Dispatchers.Default.limitedParallelism(1)), + CoroutineScope(Dispatchers.IO), + CoroutineScope(Dispatchers.IO.limitedParallelism(1)), + ) { scope -> + "scope validation" { + try { + createStateMachine( + scope, + creationArguments = buildCreationArguments { skipCoroutineScopeValidityCheck = true } + ) { + initialState("initial") + } + + shouldThrowWithMessage( + "Using Dispatchers.Default or Dispatchers.IO for StateMachine even with limitedParallelism(1) is the most likely an error, as it is multi-threaded, see the docs: \n" + + "https://kstatemachine.github.io/kstatemachine/pages/multithreading.html#use-single-threaded-coroutinescopeYou can opt-out this check by CreationArguments::skipCoroutineScopeValidityCheck flag." + ) { + createStateMachine(scope) { + initialState("initial") + } + } + } finally { + scope.cancel() + } + } + } + CoroutineStarterType.entries.forEach { coroutineStarterType -> "$coroutineStarterType" - { "no initial state" { From 70646b873fe279def0f951c18b40189c00c7c804 Mon Sep 17 00:00:00 2001 From: Mikhail Fedotov Date: Fri, 23 Jan 2026 00:19:17 +0530 Subject: [PATCH 5/5] Fix test falling due to unexpected mockk behaviour (looks like a bug) --- kstatemachine/api/kstatemachine.api | 11 +++++++-- .../kstatemachine/state/StateCleanupTest.kt | 24 ++++++++++++------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/kstatemachine/api/kstatemachine.api b/kstatemachine/api/kstatemachine.api index cd26188..3dd7f0d 100644 --- a/kstatemachine/api/kstatemachine.api +++ b/kstatemachine/api/kstatemachine.api @@ -219,6 +219,7 @@ public final class ru/nsk/kstatemachine/persistence/StrictValidator : ru/nsk/kst public final class ru/nsk/kstatemachine/persistence/WarningType : java/lang/Enum { public static final field ProcessingResultNotMatch Lru/nsk/kstatemachine/persistence/WarningType; public static final field RecordedAndProcessedEventCountNotMatch Lru/nsk/kstatemachine/persistence/WarningType; + public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lru/nsk/kstatemachine/persistence/WarningType; public static fun values ()[Lru/nsk/kstatemachine/persistence/WarningType; } @@ -256,6 +257,7 @@ public class ru/nsk/kstatemachine/state/BaseStateImpl : ru/nsk/kstatemachine/sta public final class ru/nsk/kstatemachine/state/ChildMode : java/lang/Enum { public static final field EXCLUSIVE Lru/nsk/kstatemachine/state/ChildMode; public static final field PARALLEL Lru/nsk/kstatemachine/state/ChildMode; + public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lru/nsk/kstatemachine/state/ChildMode; public static fun values ()[Lru/nsk/kstatemachine/state/ChildMode; } @@ -372,6 +374,7 @@ public final class ru/nsk/kstatemachine/state/HistoryState$DefaultImpls { public final class ru/nsk/kstatemachine/state/HistoryType : java/lang/Enum { public static final field DEEP Lru/nsk/kstatemachine/state/HistoryType; public static final field SHALLOW Lru/nsk/kstatemachine/state/HistoryType; + public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lru/nsk/kstatemachine/state/HistoryType; public static fun values ()[Lru/nsk/kstatemachine/state/HistoryType; } @@ -616,6 +619,7 @@ public abstract interface class ru/nsk/kstatemachine/statemachine/CreationArgume public abstract fun getDoNotThrowOnMultipleTransitionsMatch ()Z public abstract fun getEventRecordingArguments ()Lru/nsk/kstatemachine/statemachine/EventRecordingArguments; public abstract fun getRequireNonBlankNames ()Z + public abstract fun getSkipCoroutineScopeValidityCheck ()Z public abstract fun isUndoEnabled ()Z } @@ -624,11 +628,13 @@ public abstract interface class ru/nsk/kstatemachine/statemachine/CreationArgume public abstract fun getDoNotThrowOnMultipleTransitionsMatch ()Z public abstract fun getEventRecordingArguments ()Lru/nsk/kstatemachine/statemachine/EventRecordingArguments; public abstract fun getRequireNonBlankNames ()Z + public abstract fun getSkipCoroutineScopeValidityCheck ()Z public abstract fun isUndoEnabled ()Z public abstract fun setAutoDestroyOnStatesReuse (Z)V public abstract fun setDoNotThrowOnMultipleTransitionsMatch (Z)V public abstract fun setEventRecordingArguments (Lru/nsk/kstatemachine/statemachine/EventRecordingArguments;)V public abstract fun setRequireNonBlankNames (Z)V + public abstract fun setSkipCoroutineScopeValidityCheck (Z)V public abstract fun setUndoEnabled (Z)V } @@ -662,6 +668,7 @@ public final class ru/nsk/kstatemachine/statemachine/ProcessingResult : java/lan public static final field IGNORED Lru/nsk/kstatemachine/statemachine/ProcessingResult; public static final field PENDING Lru/nsk/kstatemachine/statemachine/ProcessingResult; public static final field PROCESSED Lru/nsk/kstatemachine/statemachine/ProcessingResult; + public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lru/nsk/kstatemachine/statemachine/ProcessingResult; public static fun values ()[Lru/nsk/kstatemachine/statemachine/ProcessingResult; } @@ -959,8 +966,6 @@ public final class ru/nsk/kstatemachine/transition/TransitionParams { public final fun component2 ()Lru/nsk/kstatemachine/transition/TransitionDirection; public final fun component3 ()Lru/nsk/kstatemachine/event/Event; public final fun component4 ()Ljava/lang/Object; - public final fun copy (Lru/nsk/kstatemachine/transition/Transition;Lru/nsk/kstatemachine/transition/TransitionDirection;Lru/nsk/kstatemachine/event/Event;Ljava/lang/Object;)Lru/nsk/kstatemachine/transition/TransitionParams; - public static synthetic fun copy$default (Lru/nsk/kstatemachine/transition/TransitionParams;Lru/nsk/kstatemachine/transition/Transition;Lru/nsk/kstatemachine/transition/TransitionDirection;Lru/nsk/kstatemachine/event/Event;Ljava/lang/Object;ILjava/lang/Object;)Lru/nsk/kstatemachine/transition/TransitionParams; public fun equals (Ljava/lang/Object;)Z public final fun getArgument ()Ljava/lang/Object; public final fun getDirection ()Lru/nsk/kstatemachine/transition/TransitionDirection; @@ -973,11 +978,13 @@ public final class ru/nsk/kstatemachine/transition/TransitionParams { public final class ru/nsk/kstatemachine/transition/TransitionParamsKt { public static final fun getUnwrappedArgument (Lru/nsk/kstatemachine/transition/TransitionParams;)Ljava/lang/Object; public static final fun getUnwrappedEvent (Lru/nsk/kstatemachine/transition/TransitionParams;)Lru/nsk/kstatemachine/event/Event; + public static final fun isStartTransition (Lru/nsk/kstatemachine/transition/TransitionParams;)Z } public final class ru/nsk/kstatemachine/transition/TransitionType : java/lang/Enum { public static final field EXTERNAL Lru/nsk/kstatemachine/transition/TransitionType; public static final field LOCAL Lru/nsk/kstatemachine/transition/TransitionType; + public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lru/nsk/kstatemachine/transition/TransitionType; public static fun values ()[Lru/nsk/kstatemachine/transition/TransitionType; } diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/StateCleanupTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/StateCleanupTest.kt index 9e7b17e..850d657 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/StateCleanupTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/StateCleanupTest.kt @@ -10,37 +10,45 @@ package ru.nsk.kstatemachine.state import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.shouldBe import io.mockk.coVerify -import io.mockk.spyk +import io.mockk.mockk import ru.nsk.kstatemachine.CoroutineStarterType import ru.nsk.kstatemachine.createTestStateMachine import ru.nsk.kstatemachine.state.StateCleanupTestData.State1 import ru.nsk.kstatemachine.statemachine.destroyBlocking private object StateCleanupTestData { - class State1 : DefaultState("state1") + class State1(private val onCleanupListener: () -> Unit) : DefaultState("state1") { + override suspend fun onCleanup() { + super.onCleanup() + onCleanupListener() + } + } } class StateCleanupTest : FreeSpec({ CoroutineStarterType.entries.forEach { coroutineStarterType -> "$coroutineStarterType" - { "cleanup is not called" { - val state = spyk() + val listener = mockk<() -> Unit>(relaxed = true) + val state = State1(listener) useInMachine(coroutineStarterType, state) - coVerify(inverse = true) { state.onCleanup() } + coVerify(inverse = true) { listener() } } "cleanup is called on machine manual destruction" { - val state = spyk() + val listener = mockk<() -> Unit>(relaxed = true) + val state = State1(listener) useInMachine(coroutineStarterType, state).destroyBlocking() - coVerify(exactly = 1) { state.onCleanup() } + coVerify(exactly = 1) { listener() } } "cleanup is called on machine auto destruction" { - val state = spyk() + val listener = mockk<() -> Unit>(relaxed = true) + val state = State1(listener) val machine1 = useInMachine(coroutineStarterType, state) val machine2 = useInMachine(coroutineStarterType, state) - coVerify(exactly = 1) { state.onCleanup() } + coVerify(exactly = 1) { listener() } machine1.isDestroyed shouldBe true machine2.isDestroyed shouldBe false }