diff --git a/docs/index.md b/docs/index.md index 9afcaeb..c977eaf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -113,6 +113,7 @@ val machine = createStateMachine( ``` By default, factory functions start state machine. You can control it using `start` argument. +All overloads accept optional argument `CreationArguments` which allows to change some options. Subsequent samples will use `createStateMachine()` function, but you can choose that one which fits your needs. @@ -422,8 +423,8 @@ like this `machine.processEvent(UndoEvent)`. State Machine will roll back last t to previous state (except target-less transitions). This API might be called as many times as needed. To implement this feature library stores transitions in a stack, it takes memory, -so this feature is disabled by default and must be enabled explicitly using `createStateMachine(enableUndo = true)` -argument. +so this feature is disabled by default and must be enabled explicitly using +`createStateMachine(creationArguments = CreationArguments(isUndoEnabled = true))` argument. Undo functionality is implemented as `Event`, so it possible to call `undo()` from notification callbacks, if you use `QueuePendingEventHandler` (which is default) or its analog. @@ -1065,7 +1066,7 @@ block to have a valid state references for transitions. Keep in mind that states are mutated by machine instance, defining them with `object` keyword (i.e. singleton) often makes your states live longer than machine. It is common use case when you have multiple similar machines that are using same singleton states sequentially. Library detects such cases automatically by default -(see `autoDestroyOnStatesReuse` argument of `createStateMachine` function) and cleans states allowing for future reuse. +(see `autoDestroyOnStatesReuse` argument of `CreationArguments` structure) and cleans states allowing for future reuse. You can disable automatic machine destruction on state reuse, and call `StateMachine.destroy()` manually if required, or just do not use `object` keyword for defining states. If you have your own `DefaultState` subclasses that are singletons and has data fields, use 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 c46b310..9278792 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 @@ -22,39 +22,20 @@ suspend fun createStateMachine( name: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, start: Boolean = true, - autoDestroyOnStatesReuse: Boolean = true, - enableUndo: Boolean = false, - doNotThrowOnMultipleTransitionsMatch: Boolean = false, + creationArguments: StateMachine.CreationArguments = StateMachine.CreationArguments(), init: suspend BuildingStateMachine.() -> Unit -) = CoroutinesLibCoroutineAbstraction(scope).createStateMachine( - name, - childMode, - start, - autoDestroyOnStatesReuse, - enableUndo, - doNotThrowOnMultipleTransitionsMatch, - init -) +) = CoroutinesLibCoroutineAbstraction(scope) + .createStateMachine(name, childMode, start, creationArguments, init) fun createStateMachineBlocking( scope: CoroutineScope, name: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, start: Boolean = true, - autoDestroyOnStatesReuse: Boolean = true, - enableUndo: Boolean = false, - doNotThrowOnMultipleTransitionsMatch: Boolean = false, + creationArguments: StateMachine.CreationArguments = StateMachine.CreationArguments(), init: suspend BuildingStateMachine.() -> Unit ) = with(CoroutinesLibCoroutineAbstraction(scope)) { runBlocking { - createStateMachine( - name, - childMode, - start, - autoDestroyOnStatesReuse, - enableUndo, - doNotThrowOnMultipleTransitionsMatch, - init - ) + createStateMachine(name, childMode, start, creationArguments, init) } } \ No newline at end of file diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/coroutines/CoroutineAbstraction.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/coroutines/CoroutineAbstraction.kt index 0e19f65..5618f67 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/coroutines/CoroutineAbstraction.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/coroutines/CoroutineAbstraction.kt @@ -50,16 +50,12 @@ suspend fun CoroutineAbstraction.createStateMachine( name: String?, childMode: ChildMode, start: Boolean, - autoDestroyOnStatesReuse: Boolean, - enableUndo: Boolean, - doNotThrowOnMultipleTransitionsMatch: Boolean, + creationArguments: StateMachine.CreationArguments = StateMachine.CreationArguments(), init: suspend BuildingStateMachine.() -> Unit ): StateMachine = StateMachineImpl( name, childMode, - autoDestroyOnStatesReuse, - enableUndo, - doNotThrowOnMultipleTransitionsMatch, + creationArguments, this, ).apply { init() diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/state/BaseStateImpl.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/state/BaseStateImpl.kt index 7d99594..b914fb7 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/state/BaseStateImpl.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/state/BaseStateImpl.kt @@ -81,7 +81,7 @@ open class BaseStateImpl( private fun onStateReuseDetected() { val machine = machine - if (machine.autoDestroyOnStatesReuse) + if (machine.creationArguments.autoDestroyOnStatesReuse) machine.destroyBlocking() else error("State $this is already used in another machine instance") @@ -173,7 +173,7 @@ open class BaseStateImpl( .filter { it !is StateMachine } // exclude nested machines .mapNotNull { it.recursiveFindUniqueResolvedTransition(eventAndArgument) } .ifEmpty { listOfNotNull(findUniqueResolvedTransition(eventAndArgument)) } // allow transition override - return if (!machine.doNotThrowOnMultipleTransitionsMatch) { + return if (!machine.creationArguments.doNotThrowOnMultipleTransitionsMatch) { check(resolvedTransitions.size <= 1) { "Multiple transitions match ${eventAndArgument.event}, $transitions in $this" } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/state/InternalState.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/state/InternalState.kt index 45b8189..7d85ec7 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/state/InternalState.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/state/InternalState.kt @@ -79,7 +79,7 @@ internal suspend fun InternalState.findUniqueResolvedTransition(even val transitions = findTransitionsByEvent(eventAndArgument.event) .map { it to it.produceTargetStateDirection(policy) } .filter { it.second !is NoTransition } - return if (!machine.doNotThrowOnMultipleTransitionsMatch) { + return if (!machine.creationArguments.doNotThrowOnMultipleTransitionsMatch) { check(transitions.size <= 1) { "Multiple transitions match ${eventAndArgument.event}, $transitions in $this" } transitions.singleOrNull() } else { diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/StateMachine.kt similarity index 84% rename from kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt rename to kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/StateMachine.kt index 9473fa3..994c2a1 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/StateMachine.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/StateMachine.kt @@ -25,6 +25,11 @@ interface StateMachine : State { val ignoredEventHandler: IgnoredEventHandler val pendingEventHandler: PendingEventHandler + /** + * Configuration arguments which were used to create the machine + */ + val creationArguments: CreationArguments + /** * If machine catches exception from client code (listeners callbacks) it stores it until event processing * completes, and passes it to this handler. That keeps machine in well-defined predictable state and allows @@ -38,24 +43,8 @@ interface StateMachine : State { val isRunning: Boolean val machineListeners: Collection - /** - * Allows the library to automatically call destroy() on current state owning machine instance if user tries - * to reuse its states in another machine. Usually this is a result of using object states in sequentially created - * similar machines. destroy() will be called on the previous machine instance. - * If set to false an exception will be thrown on state reuse attempt. - */ - val autoDestroyOnStatesReuse: Boolean val isDestroyed: Boolean - val isUndoEnabled: Boolean - - /** - * If set to true, when multiple transitions match event the first matching transition is selected. - * if set to false, when multiple transitions match event exception is thrown. - * Default if false. - */ - val doNotThrowOnMultipleTransitionsMatch: Boolean - val coroutineAbstraction: CoroutineAbstraction fun addListener(listener: L): L @@ -138,6 +127,23 @@ interface StateMachine : State { fun interface ListenerExceptionHandler { suspend fun onException(exception: Exception) } + + data class CreationArguments( + /** + * Allows the library to automatically call destroy() on current state owning machine instance if user tries + * to reuse its states in another machine. Usually this is a result of using object states in sequentially created + * similar machines. destroy() will be called on the previous machine instance. + * If set to false an exception will be thrown on state reuse attempt. + */ + val autoDestroyOnStatesReuse: Boolean = true, + val isUndoEnabled: Boolean = false, + /** + * If set to true, when multiple transitions match event the first matching transition is selected. + * if set to false, when multiple transitions match event exception is thrown. + * Default if false. + */ + val doNotThrowOnMultipleTransitionsMatch: Boolean = false, + ) } fun StateMachine.startBlocking(argument: Any? = null) = coroutineAbstraction.runBlocking { start(argument) } @@ -165,8 +171,10 @@ fun StateMachine.restartBlocking(argument: Any? = null) = coroutineAbstraction.r * This function has same effect as alternative syntax processEvent(UndoEvent), but throws if undo feature is not enabled. */ suspend fun StateMachine.undo(argument: Any? = null): ProcessingResult = coroutineAbstraction.withContext { - check(isUndoEnabled) { - "Undo functionality is not enabled, use createStateMachine(enableUndo = true) argument to enable it." + check(creationArguments.isUndoEnabled) { + "Undo functionality is not enabled, " + + "use createStateMachine(creationArguments = CreationArguments(isUndoEnabled = true)) " + + "argument to enable it." } return@withContext processEvent(UndoEvent, argument) } @@ -223,22 +231,12 @@ fun createStdLibStateMachine( name: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, start: Boolean = true, - autoDestroyOnStatesReuse: Boolean = true, - enableUndo: Boolean = false, - doNotThrowOnMultipleTransitionsMatch: Boolean = false, + creationArguments: StateMachine.CreationArguments = StateMachine.CreationArguments(), init: suspend BuildingStateMachine.() -> Unit ): StateMachine { return with(StdLibCoroutineAbstraction()) { runBlocking { - createStateMachine( - name, - childMode, - start, - autoDestroyOnStatesReuse, - enableUndo, - doNotThrowOnMultipleTransitionsMatch, - init - ) + createStateMachine(name, childMode, start, creationArguments, init) } } } diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineImpl.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineImpl.kt index 9714350..92cade3 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineImpl.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineImpl.kt @@ -1,14 +1,15 @@ package ru.nsk.kstatemachine.statemachine +import ru.nsk.kstatemachine.* import ru.nsk.kstatemachine.coroutines.CoroutineAbstraction import ru.nsk.kstatemachine.event.* -import ru.nsk.kstatemachine.isSubStateOf import ru.nsk.kstatemachine.state.* import ru.nsk.kstatemachine.state.pseudo.UndoState import ru.nsk.kstatemachine.transition.* import ru.nsk.kstatemachine.transition.TransitionDirectionProducerPolicy.DefaultPolicy import ru.nsk.kstatemachine.visitors.CheckUniqueNamesVisitor import ru.nsk.kstatemachine.visitors.CleanupVisitor +import kotlin.reflect.KClass /** * Defines state machine API for internal library usage. @@ -23,17 +24,31 @@ internal abstract class InternalStateMachine(name: String?, childMode: ChildMode internal class StateMachineImpl( name: String?, childMode: ChildMode, - override val autoDestroyOnStatesReuse: Boolean, - override val isUndoEnabled: Boolean, - override val doNotThrowOnMultipleTransitionsMatch: Boolean, + override val creationArguments: StateMachine.CreationArguments, override val coroutineAbstraction: CoroutineAbstraction, ) : InternalStateMachine(name, childMode) { private val _machineListeners = mutableSetOf() override val machineListeners: Collection get() = _machineListeners override var logger: StateMachine.Logger = StateMachine.Logger {} + set(value) { + checkPropertyNotMutedOnRunningMachine(StateMachine.Logger::class) + field = value + } override var ignoredEventHandler = StateMachine.IgnoredEventHandler {} + set(value) { + checkPropertyNotMutedOnRunningMachine(StateMachine.IgnoredEventHandler::class) + field = value + } override var pendingEventHandler: StateMachine.PendingEventHandler = queuePendingEventHandler() + set(value) { + checkPropertyNotMutedOnRunningMachine(StateMachine.PendingEventHandler::class) + field = value + } override var listenerExceptionHandler = StateMachine.ListenerExceptionHandler { throw it } + set(value) { + checkPropertyNotMutedOnRunningMachine(StateMachine.ListenerExceptionHandler::class) + field = value + } private var _isDestroyed: Boolean = false override val isDestroyed get() = _isDestroyed @@ -63,7 +78,7 @@ internal class StateMachineImpl( } } } - if (isUndoEnabled) { + if (creationArguments.isUndoEnabled) { val undoState = addState(UndoState()) transition("undo transition", undoState) } @@ -152,7 +167,7 @@ internal class StateMachineImpl( } private fun EventAndArgument<*>.wrap(): EventAndArgument<*> { - return if (isUndoEnabled && event is UndoEvent) { + return if (creationArguments.isUndoEnabled && event is UndoEvent) { val wrapped = requireState().makeWrappedEvent() EventAndArgument(wrapped, argument) } else { @@ -327,4 +342,7 @@ internal suspend inline fun makeStartTransitionParams( event, argument, ) -} \ No newline at end of file +} + +private fun StateMachine.checkPropertyNotMutedOnRunningMachine(propertyType: KClass<*>) = + check(!isRunning) { "Can not change ${propertyType.simpleName} after state machine started" } \ No newline at end of file diff --git a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/GetStructureHashCodeVisitor.kt b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/GetStructureHashCodeVisitor.kt index 613cb4b..5027003 100644 --- a/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/GetStructureHashCodeVisitor.kt +++ b/kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/visitors/GetStructureHashCodeVisitor.kt @@ -2,7 +2,6 @@ package ru.nsk.kstatemachine.visitors import ru.nsk.kstatemachine.event.Event import ru.nsk.kstatemachine.state.DataState -import ru.nsk.kstatemachine.state.DefaultDataState import ru.nsk.kstatemachine.state.HistoryState import ru.nsk.kstatemachine.state.IState import ru.nsk.kstatemachine.statemachine.StateMachine @@ -10,23 +9,26 @@ import ru.nsk.kstatemachine.transition.Transition /** * This visitor collects structural information about [StateMachine] in order to compare two [StateMachine] - * instances for structural equality + * instances for structural equality. + * There is no guaranty that his class will find the difference in two really different machines, + * but it tries to do its best. */ internal class GetStructureHashCodeVisitor : RecursiveVisitor { - private val nodes = mutableListOf() - val structureHashCode get() = nodes.hashCode() + private val records = mutableListOf() + val structureHashCode get() = records.hashCode() override fun visit(machine: StateMachine) { - nodes += machine.stateInfo() + records += machine.stateInfo() + records += "StateMachine creationArguments:${machine.creationArguments}" machine.visitChildren() } override fun visit(state: IState) { if (state !is StateMachine) { // do not check nested machines - nodes += state.stateInfo() + records += state.stateInfo() state.visitChildren() } else { - nodes += "class:${state::class.simpleName}, name:${state.name}" + records += "class:${state::class.simpleName}, name:${state.name}" } } @@ -36,10 +38,10 @@ internal class GetStructureHashCodeVisitor : RecursiveVisitor { "transitionsCount:${transitions.size}, " + "childMode:$childMode" + if (this is HistoryState) ", historyType:$historyType" else "" + - if (this is DataState<*>) ", dataClass:$dataClass, defaultData:$defaultData" else "" + if (this is DataState<*>) ", dataClass:$dataClass, defaultData:$defaultData" else "" override fun visit(transition: Transition) { - nodes += "class:${transition::class.simpleName}, " + + records += "class:${transition::class.simpleName}, " + "name:${transition.name}, " + "type:${transition.type}, " + "event:${transition.eventMatcher.eventClass}" diff --git a/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt b/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt index c117053..6847a78 100644 --- a/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt +++ b/samples/src/commonMain/kotlin/ru/nsk/samples/ComplexSyntaxSample.kt @@ -1,6 +1,7 @@ package ru.nsk.samples import kotlinx.coroutines.runBlocking +import ru.nsk.kstatemachine.statemachine.StateMachine import ru.nsk.kstatemachine.event.Event import ru.nsk.kstatemachine.state.* import ru.nsk.kstatemachine.statemachine.* diff --git a/samples/src/commonMain/kotlin/ru/nsk/samples/UndoTransitionSample.kt b/samples/src/commonMain/kotlin/ru/nsk/samples/UndoTransitionSample.kt index 742512b..5c4bb11 100644 --- a/samples/src/commonMain/kotlin/ru/nsk/samples/UndoTransitionSample.kt +++ b/samples/src/commonMain/kotlin/ru/nsk/samples/UndoTransitionSample.kt @@ -5,6 +5,7 @@ import ru.nsk.kstatemachine.event.Event import ru.nsk.kstatemachine.event.UndoEvent import ru.nsk.kstatemachine.state.* import ru.nsk.kstatemachine.statemachine.StateMachine +import ru.nsk.kstatemachine.statemachine.StateMachine.CreationArguments import ru.nsk.kstatemachine.statemachine.createStateMachine import ru.nsk.kstatemachine.statemachine.undo import ru.nsk.kstatemachine.transition.unwrappedEvent @@ -22,7 +23,10 @@ fun main() = runBlocking { lateinit var state2: State lateinit var state3: State - val machine = createStateMachine(this, enableUndo = true) { + val machine = createStateMachine( + this, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { state1 = initialState("state1") { transitionOn { targetState = { state2 } } } diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt index 6609d2f..642d688 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt @@ -82,18 +82,14 @@ fun createTestStateMachine( name: String? = null, childMode: ChildMode = ChildMode.EXCLUSIVE, start: Boolean = true, - autoDestroyOnStatesReuse: Boolean = true, - enableUndo: Boolean = false, - doNotThrowOnMultipleTransitionsMatch: Boolean = false, + creationArguments: StateMachine.CreationArguments = StateMachine.CreationArguments(), init: suspend BuildingStateMachine.() -> Unit ) = when (coroutineStarterType) { CoroutineStarterType.STD_LIB -> createStdLibStateMachine( name, childMode, start, - autoDestroyOnStatesReuse, - enableUndo, - doNotThrowOnMultipleTransitionsMatch, + creationArguments, init = init ) CoroutineStarterType.COROUTINES_LIB_EMPTY_CONTEXT -> createStateMachineBlocking( @@ -101,9 +97,7 @@ fun createTestStateMachine( name, childMode, start, - autoDestroyOnStatesReuse, - enableUndo, - doNotThrowOnMultipleTransitionsMatch, + creationArguments, init = init ) CoroutineStarterType.COROUTINES_LIB_UNCONFINED_DISPATCHER -> createStateMachineBlocking( @@ -111,9 +105,7 @@ fun createTestStateMachine( name, childMode, start, - autoDestroyOnStatesReuse, - enableUndo, - doNotThrowOnMultipleTransitionsMatch, + creationArguments, init = init ) CoroutineStarterType.COROUTINES_LIB_SINGLE_THREAD_DISPATCHER -> createStateMachineBlocking( @@ -121,9 +113,7 @@ fun createTestStateMachine( name, childMode, start, - autoDestroyOnStatesReuse, - enableUndo, - doNotThrowOnMultipleTransitionsMatch, + creationArguments, init = init ) CoroutineStarterType.COROUTINES_LIB_DEFAULT_LIMITED_DISPATCHER -> createStateMachineBlocking( @@ -131,9 +121,7 @@ fun createTestStateMachine( name, childMode, start, - autoDestroyOnStatesReuse, - enableUndo, - doNotThrowOnMultipleTransitionsMatch, + creationArguments, init = init ) } \ No newline at end of file diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/ObjectStatesTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/ObjectStatesTest.kt index 7ac0541..addf0f8 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/ObjectStatesTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/ObjectStatesTest.kt @@ -8,6 +8,7 @@ import ru.nsk.kstatemachine.createTestStateMachine import ru.nsk.kstatemachine.state.ObjectStatesTestData.State1 import ru.nsk.kstatemachine.state.ObjectStatesTestData.State2 import ru.nsk.kstatemachine.statemachine.StateMachine +import ru.nsk.kstatemachine.statemachine.StateMachine.* import ru.nsk.kstatemachine.statemachine.destroyBlocking import ru.nsk.kstatemachine.statemachine.processEventBlocking import ru.nsk.kstatemachine.statemachine.stop @@ -19,7 +20,7 @@ private object ObjectStatesTestData { /** * States are mutable, and it is not possible to use object states in multiple [StateMachine] instances if - * autoDestroyOnStatesReuse argument is false. + * [CreationArguments.autoDestroyOnStatesReuse] argument is false. */ class ObjectStatesTest : StringSpec({ CoroutineStarterType.entries.forEach { coroutineStarterType -> @@ -55,7 +56,10 @@ class ObjectStatesTest : StringSpec({ }) private fun useInMachine(coroutineStarterType: CoroutineStarterType, autoDestroyOnStatesReuse: Boolean): StateMachine { - val machine = createTestStateMachine(coroutineStarterType, autoDestroyOnStatesReuse = autoDestroyOnStatesReuse) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(autoDestroyOnStatesReuse = autoDestroyOnStatesReuse), + ) { addInitialState(State1) { transition { targetState = State2 diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/UndoTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/UndoTest.kt index 87bbe8f..2e4d5ec 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/UndoTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/state/UndoTest.kt @@ -12,6 +12,7 @@ import ru.nsk.kstatemachine.event.UndoEvent import ru.nsk.kstatemachine.event.WrappedEvent import ru.nsk.kstatemachine.state.UndoTestData.SwitchDataEvent import ru.nsk.kstatemachine.statemachine.* +import ru.nsk.kstatemachine.statemachine.StateMachine.* import ru.nsk.kstatemachine.transition.unwrappedArgument import ru.nsk.kstatemachine.transition.unwrappedEvent @@ -31,7 +32,10 @@ class UndoTest : StringSpec({ "undo throws with throwing PendingEventHandler" { lateinit var state1: State lateinit var state2: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { pendingEventHandler = throwingPendingEventHandler() state1 = initialState("state1") { transitionOn { targetState = { state2 } } @@ -49,7 +53,10 @@ class UndoTest : StringSpec({ "undo with QueuePendingEventHandler" { lateinit var state1: State lateinit var state2: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { state1 = initialState("state1") { transitionOn { targetState = { state2 } } } @@ -64,7 +71,10 @@ class UndoTest : StringSpec({ "undo to initial state" { lateinit var state1: State lateinit var state2: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { state1 = initialState("state1") { transitionOn { targetState = { state2 } } } @@ -81,7 +91,10 @@ class UndoTest : StringSpec({ "undo to initial state checking events and calling undo on initial state" { lateinit var state1: State lateinit var state2: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { state1 = initialState("state1") { transitionOn { targetState = { state2 } } } @@ -113,7 +126,10 @@ class UndoTest : StringSpec({ "undo mixed with processEvent()" { lateinit var state1: State lateinit var state2: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { state1 = initialState("state1") { transitionOn { targetState = { state2 } } } @@ -149,7 +165,10 @@ class UndoTest : StringSpec({ lateinit var state1: State lateinit var state2: State lateinit var state3: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { state1 = initialState("state1") { transitionOn { targetState = { state2 } } } @@ -173,7 +192,10 @@ class UndoTest : StringSpec({ "undo cross-level transition" { lateinit var state12: State lateinit var state2: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { initialState("state1") { initialState("state11") { transitionOn { targetState = { state12 } } @@ -195,7 +217,10 @@ class UndoTest : StringSpec({ "single undo with DataState" { lateinit var state12: DataState lateinit var state2: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { initialState("state1") { initialState("state11") { dataTransitionOn { targetState = { state12 } } @@ -221,8 +246,11 @@ class UndoTest : StringSpec({ lateinit var state11: State lateinit var state12: DataState lateinit var state2: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { - logger = StateMachine.Logger { println(it()) } + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { + logger = Logger { println(it()) } initialState("state1") { state11 = initialState("state11") { dataTransitionOn { targetState = { state12 } } @@ -259,7 +287,10 @@ class UndoTest : StringSpec({ lateinit var state1: State lateinit var state2: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { state1 = initialState("state1") { transitionOn { targetState = { state2 } } } @@ -289,7 +320,11 @@ class UndoTest : StringSpec({ "undo initial state" { lateinit var state1: State lateinit var state2: State - val machine = createTestStateMachine(coroutineStarterType, start = false, enableUndo = true) { + val machine = createTestStateMachine( + coroutineStarterType, + start = false, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { state1 = initialState("state1") { transitionOn { targetState = { state2 } } } @@ -316,7 +351,11 @@ class UndoTest : StringSpec({ lateinit var state1: State lateinit var state2: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true, start = false) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true), + start = false + ) { state1 = initialState("state1") { transitionOn { targetState = { state2 } } } @@ -344,8 +383,11 @@ class UndoTest : StringSpec({ } "undo ignored event" { - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { - ignoredEventHandler = StateMachine.IgnoredEventHandler { throw TestException("test") } + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { + ignoredEventHandler = IgnoredEventHandler { throw TestException("test") } initialState("state1") { transition() } @@ -360,7 +402,10 @@ class UndoTest : StringSpec({ lateinit var state2: State lateinit var state3: State - val machine = createTestStateMachine(coroutineStarterType, enableUndo = true) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { state1 = initialState("state1") { transitionOn { targetState = { state2 } } } 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 3c73c75..63e8698 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/transition/TransitionTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/transition/TransitionTest.kt @@ -11,6 +11,8 @@ import org.junit.jupiter.api.fail import ru.nsk.kstatemachine.* import ru.nsk.kstatemachine.state.* import ru.nsk.kstatemachine.statemachine.ProcessingResult.PROCESSED +import ru.nsk.kstatemachine.statemachine.StateMachine +import ru.nsk.kstatemachine.statemachine.StateMachine.CreationArguments import ru.nsk.kstatemachine.statemachine.processEventBlocking class TransitionTest : StringSpec({ @@ -151,7 +153,10 @@ class TransitionTest : StringSpec({ } "multiple matching transitions" { - val machine = createTestStateMachine(coroutineStarterType, doNotThrowOnMultipleTransitionsMatch = true) { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(doNotThrowOnMultipleTransitionsMatch = true) + ) { transition() transition() initialState() @@ -173,7 +178,7 @@ class TransitionTest : StringSpec({ val machine = createTestStateMachine( coroutineStarterType, childMode = ChildMode.PARALLEL, - doNotThrowOnMultipleTransitionsMatch = true + creationArguments = CreationArguments(doNotThrowOnMultipleTransitionsMatch = true) ) { state { transition() } state { transition() } diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/transition/TypesafeTransitionTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/transition/TypesafeTransitionTest.kt index 26dfe8e..1cf2214 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/transition/TypesafeTransitionTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/transition/TypesafeTransitionTest.kt @@ -17,7 +17,6 @@ import ru.nsk.kstatemachine.statemachine.processEventBlocking import ru.nsk.kstatemachine.transition.TypesafeTransitionTestData.CustomDataEvent import ru.nsk.kstatemachine.transition.TypesafeTransitionTestData.IdEvent import ru.nsk.kstatemachine.transition.TypesafeTransitionTestData.NameEvent -import kotlin.reflect.KClass private object TypesafeTransitionTestData { class CustomDataEvent(val value: Int) : Event diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/visitors/GetStructureHashCodeVisitorTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/visitors/GetStructureHashCodeVisitorTest.kt index 22fd1ab..1866062 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/visitors/GetStructureHashCodeVisitorTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/visitors/GetStructureHashCodeVisitorTest.kt @@ -5,6 +5,7 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import ru.nsk.kstatemachine.* import ru.nsk.kstatemachine.state.* +import ru.nsk.kstatemachine.statemachine.StateMachine.CreationArguments import ru.nsk.kstatemachine.transition.TransitionType class GetStructureHashCodeVisitorTest : StringSpec({ @@ -64,6 +65,24 @@ class GetStructureHashCodeVisitorTest : StringSpec({ machine.getStructureHashCode() shouldBe machine2.getStructureHashCode() } + "structure hash code is affected by CreationArguments" { + val machine = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = false) + ) { + initialState() + } + + val machine2 = createTestStateMachine( + coroutineStarterType, + creationArguments = CreationArguments(isUndoEnabled = true) + ) { + initialState() + } + + machine.getStructureHashCode() shouldNotBe machine2.getStructureHashCode() + } + "structure hash code is affected by state name" { val machine = createTestStateMachine(coroutineStarterType) { initialState("state1") diff --git a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/visitors/export/ExportPlantUmlVisitorTest.kt b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/visitors/export/ExportPlantUmlVisitorTest.kt index 93f904a..b362128 100644 --- a/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/visitors/export/ExportPlantUmlVisitorTest.kt +++ b/tests/src/commonTest/kotlin/ru/nsk/kstatemachine/visitors/export/ExportPlantUmlVisitorTest.kt @@ -11,6 +11,7 @@ import ru.nsk.kstatemachine.* import ru.nsk.kstatemachine.metainfo.UmlMetaInfo import ru.nsk.kstatemachine.state.* import ru.nsk.kstatemachine.statemachine.StateMachine +import ru.nsk.kstatemachine.statemachine.StateMachine.CreationArguments import ru.nsk.kstatemachine.statemachine.createStateMachine import ru.nsk.kstatemachine.transition.targetParallelStates @@ -253,7 +254,7 @@ private fun makeNestedMachine(coroutineStarterType: CoroutineStarterType): State } private fun makeChoiceMachine(coroutineStarterType: CoroutineStarterType): StateMachine { - return createTestStateMachine(coroutineStarterType, enableUndo = true) { + return createTestStateMachine(coroutineStarterType, creationArguments = CreationArguments(isUndoEnabled = true)) { val state1 = initialState("state1") val state2 = state("state2") {