Skip to content

Commit 733ec31

Browse files
committed
Add FinishedDataEvent which is generated on final data state entry
1 parent 7fc1ee5 commit 733ec31

File tree

4 files changed

+64
-14
lines changed

4 files changed

+64
-14
lines changed

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
6969
override fun <S : IState> addState(state: S, init: StateBlock<S>?): S {
7070
check(!machine.isRunning) { "Can not add state after state machine started" }
7171
if (childMode == ChildMode.PARALLEL) {
72-
require(state !is FinalState) { "Can not add FinalState in parallel child mode" }
72+
require(state !is IFinalState) { "Can not add IFinalState in parallel child mode" }
7373
require(state !is PseudoState) { "Can not add PseudoState in parallel child mode" }
7474
}
7575

@@ -114,10 +114,12 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
114114

115115
override fun doEnter(transitionParams: TransitionParams<*>) {
116116
if (!isActive) {
117+
val machine = machine as InternalStateMachine
117118
if (parent != null) machine.log { "Parent $parent entering child $this" }
118119
data.isActive = true
119120
onDoEnter(transitionParams)
120121
stateNotify { onEntry(transitionParams) }
122+
machine.machineNotify { onStateEntry(this@BaseStateImpl) }
121123
}
122124
}
123125

@@ -135,7 +137,7 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
135137
override fun afterChildFinished(finishedChild: InternalState, transitionParams: TransitionParams<*>) {
136138
if (childMode == ChildMode.PARALLEL && states.all { it.isFinished }) {
137139
data.isFinished = true
138-
notifyStateFinish(transitionParams)
140+
notifyStateFinish(finishedChild, transitionParams)
139141
}
140142
}
141143

@@ -239,21 +241,26 @@ open class BaseStateImpl(override val name: String?, override val childMode: Chi
239241

240242
state.doEnter(transitionParams)
241243

242-
val machine = machine as InternalStateMachine
243-
machine.machineNotify { onStateEntry(state) }
244-
245244
if (finished) {
246-
notifyStateFinish(transitionParams)
245+
notifyStateFinish(state, transitionParams)
247246
internalParent?.afterChildFinished(this, transitionParams)
248247
}
249248
}
250249

251-
private fun notifyStateFinish(transitionParams: TransitionParams<*>) {
250+
private fun notifyStateFinish(state: InternalState, transitionParams: TransitionParams<*>) {
252251
machine.log { "$this finished" }
253252
stateNotify { onFinished(transitionParams) }
254253
// there is no sense to send event on state machine finish as it stops processing events in this case
255254
if (this !is StateMachine)
256-
machine.processEvent(FinishedEvent(this))
255+
machine.processEvent(makeFinishedEvent(state))
256+
}
257+
258+
private fun makeFinishedEvent(state: InternalState): FinishedEvent {
259+
// check for both interfaces as client is not forced to use FinalDataState
260+
return if (state is DataState<*> && state is IFinalState) // possible in exclusive child mode only
261+
FinishedDataEventImpl(this, state.data)
262+
else
263+
FinishedEventImpl(this)
257264
}
258265

259266
internal fun switchToTargetState(

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@ interface GeneratedEvent : Event
2626
* Special event generated by the library when a state is finished.
2727
* Transitions use special event matcher by default to match only related events.
2828
*/
29-
class FinishedEvent internal constructor(val state: IState) : GeneratedEvent
29+
interface FinishedEvent : GeneratedEvent {
30+
val state: IState
31+
}
32+
33+
interface FinishedDataEvent<D : Any> : FinishedEvent, DataEvent<D>
34+
35+
internal class FinishedEventImpl(override val state: IState) : FinishedEvent
36+
internal class FinishedDataEventImpl<D : Any>(override val state: IState, override val data: D) : FinishedDataEvent<D>
3037

3138
/**
3239
* Initial event which is processed on state machine start

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

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import io.kotest.matchers.shouldBe
55
import io.mockk.verifySequence
66

77
class FinishedEventTest : StringSpec({
8-
"finished event in machine is not working as machine ignores events" {
8+
"FinishedEvent in machine is not working as machine ignores events" {
99
val callbacks = mockkCallbacks()
1010
lateinit var state2: State
1111
val machine = createStateMachine {
@@ -28,7 +28,7 @@ class FinishedEventTest : StringSpec({
2828
machine.isFinished shouldBe true
2929
}
3030

31-
"finished event in composite state" {
31+
"FinishedEvent in composite state" {
3232
val callbacks = mockkCallbacks()
3333
lateinit var state1: State
3434
lateinit var state2: State
@@ -56,4 +56,36 @@ class FinishedEventTest : StringSpec({
5656
state1.isFinished shouldBe false
5757
machine.isFinished shouldBe false
5858
}
59+
60+
"FinishedDataEvent" {
61+
val callbacks = mockkCallbacks()
62+
data class IntEvent(override val data: Int) : DataEvent<Int>
63+
val intData = 42
64+
65+
val machine = createStateMachine {
66+
val state2 = state("state2")
67+
68+
initialState("state1") {
69+
val childFinal = finalDataState<Int>("state11")
70+
71+
initialState("state12") {
72+
dataTransition<IntEvent, Int> {
73+
targetState = childFinal
74+
}
75+
}
76+
77+
transitionOn<FinishedDataEvent<Int>> {
78+
targetState = {
79+
event.data shouldBe intData
80+
state2
81+
}
82+
callbacks.listen(this)
83+
}
84+
}
85+
}
86+
machine.processEvent(IntEvent(intData))
87+
verifySequence {
88+
callbacks.onTriggeredTransition(ofType<FinishedDataEvent<Int>>())
89+
}
90+
}
5991
})

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,16 +130,19 @@ class StateMachineTest : StringSpec({
130130
}
131131

132132

133-
"onStateChanged() notification" {
133+
"onEntryState() notification" {
134134
val callbacks = mockkCallbacks()
135135
lateinit var first: State
136136

137-
createStateMachine {
137+
val machine = createStateMachine {
138138
first = initialState("first")
139139
onStateEntry { callbacks.onEntryState(it) }
140140
}
141141

142-
verifySequence { callbacks.onEntryState(first) }
142+
verifySequence {
143+
callbacks.onEntryState(machine)
144+
callbacks.onEntryState(first)
145+
}
143146
}
144147

145148
"add same state listener" {
@@ -295,6 +298,7 @@ class StateMachineTest : StringSpec({
295298

296299
verifySequence {
297300
callbacks.onStarted(machine)
301+
callbacks.onEntryState(machine)
298302
callbacks.onEntryState(state1)
299303
callbacks.onEntryState(state2)
300304
callbacks.onFinished(machine)

0 commit comments

Comments
 (0)