Skip to content

Commit 38fe6da

Browse files
committed
Add argument passing to start() and startFrom() methods
Initial states are triggered on machine start so they could not receive argments through processEvent() method.
1 parent 49feaa6 commit 38fe6da

File tree

9 files changed

+73
-52
lines changed

9 files changed

+73
-52
lines changed

kstatemachine/src/main/kotlin/ru/nsk/kstatemachine/BaseStateImpl.kt

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,26 +147,27 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
147147
return resolvedTransitions.singleOrNull()
148148
}
149149

150-
override fun recursiveEnterInitialStates() {
150+
override fun recursiveEnterInitialStates(argument: Any?) {
151151
if (states.isEmpty()) return
152152

153153
when (childMode) {
154154
ChildMode.EXCLUSIVE -> {
155-
val initialState =
156-
checkNotNull(initialState) { "Initial state is not set, call setInitialState() first" }
157-
setCurrentState(initialState, makeStartTransitionParams(initialState))
158-
initialState.recursiveEnterInitialStates()
155+
val initialState = checkNotNull(initialState) {
156+
"Initial state is not set, call setInitialState() first"
157+
}
158+
setCurrentState(initialState, makeStartTransitionParams(initialState, argument = argument))
159+
initialState.recursiveEnterInitialStates(argument)
159160
}
160161
ChildMode.PARALLEL -> data.states.forEach {
161-
notifyStateEntry(it, makeStartTransitionParams(it))
162-
it.recursiveEnterInitialStates()
162+
notifyStateEntry(it, makeStartTransitionParams(it, argument = argument))
163+
it.recursiveEnterInitialStates(argument)
163164
}
164165
}
165166
}
166167

167168
override fun recursiveEnterStatePath(path: MutableList<InternalState>, transitionParams: TransitionParams<*>) {
168169
if (path.isEmpty()) {
169-
recursiveEnterInitialStates()
170+
recursiveEnterInitialStates(transitionParams.argument)
170171
} else {
171172
val state = path.removeLast()
172173
setCurrentState(state, transitionParams)
@@ -251,7 +252,8 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
251252

252253
internal fun makeStartTransitionParams(
253254
sourceState: IState,
254-
targetState: IState = sourceState
255+
targetState: IState = sourceState,
256+
argument: Any?
255257
): TransitionParams<*> {
256258
val transition = DefaultTransition(
257259
"Starting",
@@ -264,6 +266,7 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
264266
transition,
265267
transition.produceTargetStateDirection(TransitionDirectionProducerPolicy.DefaultPolicy(StartEvent)),
266268
StartEvent,
269+
argument,
267270
)
268271
}
269272
}

kstatemachine/src/main/kotlin/ru/nsk/kstatemachine/InternalState.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ abstract class InternalState : IState {
1818
internal abstract fun afterChildFinished(finishedChild: InternalState, transitionParams: TransitionParams<*>)
1919

2020
internal abstract fun <E : Event> recursiveFindUniqueResolvedTransition(event: E): ResolvedTransition<E>?
21-
internal abstract fun recursiveEnterInitialStates()
21+
internal abstract fun recursiveEnterInitialStates(argument: Any?)
2222
internal abstract fun recursiveEnterStatePath(
2323
path: MutableList<InternalState>,
2424
transitionParams: TransitionParams<*>

kstatemachine/src/main/kotlin/ru/nsk/kstatemachine/StateMachine.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ interface StateMachine : State {
3838
/**
3939
* Starts state machine
4040
*/
41-
fun start()
41+
fun start(argument: Any? = null)
4242

4343
/**
4444
* Forces state machine to stop

kstatemachine/src/main/kotlin/ru/nsk/kstatemachine/StateMachineImpl.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import ru.nsk.kstatemachine.visitors.CleanupVisitor
77
* Defines state machine API for internal library usage.
88
*/
99
abstract class InternalStateMachine(name: String?, childMode: ChildMode) : StateMachine, DefaultState(name, childMode) {
10-
internal abstract fun startFrom(state: IState)
10+
internal abstract fun startFrom(state: IState, argument: Any?)
1111
internal abstract fun delayListenerException(exception: Exception)
1212
}
1313

@@ -57,21 +57,21 @@ internal class StateMachineImpl(name: String?, childMode: ChildMode, override va
5757
_machineListeners.remove(listener)
5858
}
5959

60-
override fun start() {
60+
override fun start(argument: Any?) {
6161
accept(CheckUniqueNamesVisitor())
6262
checkBeforeRunMachine()
6363

6464
runCheckingExceptions {
65-
runMachine(makeStartTransitionParams(this))
66-
recursiveEnterInitialStates()
65+
runMachine(makeStartTransitionParams(this, argument = argument))
66+
recursiveEnterInitialStates(argument)
6767
}
6868
}
6969

70-
override fun startFrom(state: IState) {
70+
override fun startFrom(state: IState, argument: Any?) {
7171
checkBeforeRunMachine()
7272

7373
runCheckingExceptions {
74-
val transitionParams = makeStartTransitionParams(this, state)
74+
val transitionParams = makeStartTransitionParams(this, state, argument)
7575
runMachine(transitionParams)
7676
switchToTargetState(state as InternalState, this, transitionParams)
7777
}

kstatemachine/src/main/kotlin/ru/nsk/kstatemachine/Testing.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ object Testing {
44
/**
55
* Method for testing purpose. It allows starting machine from particular [state]
66
*/
7-
fun StateMachine.startFrom(state: IState) = (this as InternalStateMachine).startFrom(state)
7+
fun StateMachine.startFrom(state: IState, argument: Any? = null) =
8+
(this as InternalStateMachine).startFrom(state, argument)
89
}

kstatemachine/src/test/kotlin/ru/nsk/kstatemachine/ActiveStatesTest.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,6 @@ class ActiveStatesTest : StringSpec({
6464
}
6565
}
6666

67-
val s = DefaultState()
68-
s.recursiveEnterInitialStates()
69-
7067
machine.activeStates().shouldContainExactly(initialState, nestedMachine)
7168
}
7269
})

kstatemachine/src/test/kotlin/ru/nsk/kstatemachine/StateMachineTest.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,7 @@ class StateMachineTest : StringSpec({
202202

203203
lateinit var first: State
204204
val machine = createStateMachine {
205-
first = initialState {
206-
onEntry { callbacks.onEntryState(this) }
207-
}
205+
first = initialState { callbacks.listen(this) }
208206
onStarted { callbacks.onStarted(this) }
209207
}
210208

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package ru.nsk.kstatemachine
2+
3+
import io.kotest.core.spec.style.StringSpec
4+
import io.kotest.matchers.shouldBe
5+
import io.kotest.matchers.types.shouldBeSameInstanceAs
6+
import io.mockk.verify
7+
import io.mockk.verifySequence
8+
import org.junit.jupiter.api.fail
9+
10+
private const val ARGUMENT = 1
11+
12+
class TransitionArgumentTest : StringSpec({
13+
"transition argument" {
14+
val callbacks = mockkCallbacks()
15+
16+
val second = object : DefaultState("second") {}
17+
18+
val machine = createStateMachine {
19+
addState(second) {
20+
callbacks.listen(this)
21+
onEntry { it.transition.argument shouldBe ARGUMENT }
22+
}
23+
initialState("first") {
24+
transition<SwitchEvent> {
25+
targetState = second
26+
onTriggered { it.transition.argument = ARGUMENT }
27+
}
28+
}
29+
}
30+
31+
machine.processEvent(SwitchEvent)
32+
verifySequence { callbacks.onEntryState(second) }
33+
}
34+
35+
"transition argument on start" {
36+
val callbacks = mockkCallbacks()
37+
lateinit var state1: State
38+
39+
val machine = createStateMachine(start = false) {
40+
state1 = initialState("first") {
41+
callbacks.listen(this)
42+
onEntry { it.argument shouldBe ARGUMENT }
43+
}
44+
}
45+
machine.start(ARGUMENT)
46+
47+
machine.processEvent(SwitchEvent)
48+
verifySequence { callbacks.onEntryState(state1) }
49+
}
50+
})

kstatemachine/src/test/kotlin/ru/nsk/kstatemachine/TransitionTest.kt

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,7 @@ import io.mockk.verify
77
import io.mockk.verifySequence
88
import org.junit.jupiter.api.fail
99

10-
private const val ARGUMENT = 1
11-
1210
class TransitionTest : StringSpec({
13-
"transition argument" {
14-
val callbacks = mockkCallbacks()
15-
16-
val second = object : DefaultState("second") {}
17-
18-
val machine = createStateMachine {
19-
addState(second) {
20-
onEntry {
21-
callbacks.onEntryState(this)
22-
it.transition.argument shouldBe ARGUMENT
23-
}
24-
}
25-
initialState("first") {
26-
transition<SwitchEvent> {
27-
targetState = second
28-
onTriggered {
29-
it.transition.argument = ARGUMENT
30-
}
31-
}
32-
}
33-
}
34-
35-
machine.processEvent(SwitchEvent)
36-
verifySequence { callbacks.onEntryState(second) }
37-
}
38-
3911
"transition direction" {
4012
val callbacks = mockkCallbacks()
4113

0 commit comments

Comments
 (0)