Skip to content

Commit 4b280b7

Browse files
committed
Fix contract in a test. To break compilation if contract is not applied.
1 parent 4ba062a commit 4b280b7

File tree

3 files changed

+59
-42
lines changed

3 files changed

+59
-42
lines changed

kstatemachine/src/commonMain/kotlin/ru/nsk/kstatemachine/statemachine/StateMachine.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import ru.nsk.kstatemachine.transition.EventAndArgument
2222
import ru.nsk.kstatemachine.transition.TransitionParams
2323
import ru.nsk.kstatemachine.visitors.CoVisitor
2424
import ru.nsk.kstatemachine.visitors.Visitor
25+
import kotlin.contracts.ExperimentalContracts
26+
import kotlin.contracts.InvocationKind
27+
import kotlin.contracts.contract
2528

2629
@DslMarker
2730
annotation class StateMachineDslMarker
@@ -242,13 +245,17 @@ interface BuildingStateMachine : StateMachine {
242245
* Factory method for creating [StateMachine].
243246
* Suspendable code will be called via Kotlin Standard library (without Kotlin Coroutines library support).
244247
*/
248+
@OptIn(ExperimentalContracts::class)
245249
fun createStdLibStateMachine(
246250
name: String? = null,
247251
childMode: ChildMode = ChildMode.EXCLUSIVE,
248252
start: Boolean = true,
249253
creationArguments: CreationArguments = buildCreationArguments {},
250254
init: suspend BuildingStateMachine.() -> Unit
251255
): StateMachine {
256+
contract {
257+
callsInPlace(init, InvocationKind.EXACTLY_ONCE)
258+
}
252259
return with(StdLibCoroutineAbstraction()) {
253260
runBlocking {
254261
createStateMachine(name, childMode, start, creationArguments, init)

tests/src/commonTest/kotlin/ru/nsk/kstatemachine/TestUtils.kt

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import ru.nsk.kstatemachine.state.*
1919
import ru.nsk.kstatemachine.statemachine.*
2020
import ru.nsk.kstatemachine.transition.TransitionBuilder
2121
import ru.nsk.kstatemachine.transition.onTriggered
22+
import kotlin.contracts.ExperimentalContracts
23+
import kotlin.contracts.InvocationKind
24+
import kotlin.contracts.contract
2225
import kotlin.coroutines.EmptyCoroutineContext
2326

2427
typealias Callback<T> = (T) -> Unit
@@ -81,54 +84,61 @@ enum class CoroutineStarterType {
8184

8285
@OptIn(ExperimentalCoroutinesApi::class)
8386
private val singleThreadContext = newSingleThreadContext("test single thread context") // context leaks
87+
8488
/**
8589
* Wraps [createStdLibStateMachine] so it can be easily switched to [createStdLibStateMachine]
8690
*/
91+
@OptIn(ExperimentalContracts::class)
8792
suspend fun createTestStateMachine(
8893
coroutineStarterType: CoroutineStarterType,
8994
name: String? = null,
9095
childMode: ChildMode = ChildMode.EXCLUSIVE,
9196
start: Boolean = true,
9297
creationArguments: CreationArguments = buildCreationArguments {},
9398
init: suspend BuildingStateMachine.() -> Unit
94-
) = when (coroutineStarterType) {
95-
CoroutineStarterType.STD_LIB -> createStdLibStateMachine(
96-
name,
97-
childMode,
98-
start,
99-
creationArguments,
100-
init = init
101-
)
102-
CoroutineStarterType.COROUTINES_LIB_EMPTY_CONTEXT -> createStateMachine(
103-
CoroutineScope(EmptyCoroutineContext), // does not perform internal context switching
104-
name,
105-
childMode,
106-
start,
107-
creationArguments,
108-
init = init
109-
)
110-
CoroutineStarterType.COROUTINES_LIB_UNCONFINED_DISPATCHER -> createStateMachine(
111-
CoroutineScope(Dispatchers.Unconfined),
112-
name,
113-
childMode,
114-
start,
115-
creationArguments,
116-
init = init
117-
)
118-
CoroutineStarterType.COROUTINES_LIB_SINGLE_THREAD_DISPATCHER -> createStateMachine(
119-
CoroutineScope(singleThreadContext),
120-
name,
121-
childMode,
122-
start,
123-
creationArguments,
124-
init = init
125-
)
126-
CoroutineStarterType.COROUTINES_LIB_DEFAULT_LIMITED_DISPATCHER -> createStateMachine(
127-
CoroutineScope(Dispatchers.Default.limitedParallelism(1)), // does not guarantee same thread for each task
128-
name,
129-
childMode,
130-
start,
131-
creationArguments,
132-
init = init
133-
)
99+
): StateMachine {
100+
contract {
101+
callsInPlace(init, InvocationKind.EXACTLY_ONCE)
102+
}
103+
return when (coroutineStarterType) {
104+
CoroutineStarterType.STD_LIB -> createStdLibStateMachine(
105+
name,
106+
childMode,
107+
start,
108+
creationArguments,
109+
init = init
110+
)
111+
CoroutineStarterType.COROUTINES_LIB_EMPTY_CONTEXT -> createStateMachine(
112+
CoroutineScope(EmptyCoroutineContext), // does not perform internal context switching
113+
name,
114+
childMode,
115+
start,
116+
creationArguments,
117+
init = init
118+
)
119+
CoroutineStarterType.COROUTINES_LIB_UNCONFINED_DISPATCHER -> createStateMachine(
120+
CoroutineScope(Dispatchers.Unconfined),
121+
name,
122+
childMode,
123+
start,
124+
creationArguments,
125+
init = init
126+
)
127+
CoroutineStarterType.COROUTINES_LIB_SINGLE_THREAD_DISPATCHER -> createStateMachine(
128+
CoroutineScope(singleThreadContext),
129+
name,
130+
childMode,
131+
start,
132+
creationArguments,
133+
init = init
134+
)
135+
CoroutineStarterType.COROUTINES_LIB_DEFAULT_LIMITED_DISPATCHER -> createStateMachine(
136+
CoroutineScope(Dispatchers.Default.limitedParallelism(1)), // does not guarantee same thread for each task
137+
name,
138+
childMode,
139+
start,
140+
creationArguments,
141+
init = init
142+
)
143+
}
134144
}

tests/src/commonTest/kotlin/ru/nsk/kstatemachine/statemachine/StateMachineTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ class StateMachineTest : FreeSpec({
5353
"on off dsl sample" {
5454
val callbacks = mockkCallbacks()
5555

56-
lateinit var on: State
57-
lateinit var off: State
56+
val on: State
57+
val off: State
5858

5959
val machine = createTestStateMachine(coroutineStarterType) {
6060
on = initialState("on") {

0 commit comments

Comments
 (0)