diff --git a/build.gradle b/build.gradle index 2d4127c..4cf389b 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { ext.supportLibrary_version = '28.0.0' ext.rxjava_version = '2.1.9' ext.contraint_layout_version = '1.1.3' - ext.jacoco_version = '0.8.3' + ext.jacoco_version = '0.8.4' repositories { google() @@ -35,7 +35,7 @@ subprojects { String buildNumber = System.getenv('CIRCLE_BUILD_NUM') ?: "0" group 'com.ToxicBakery.kfinstatemachine' - version "4.0.$buildNumber" + (isTag ? "" : "-SNAPSHOT") + version "4.1.$buildNumber" + (isTag ? "" : "-SNAPSHOT") repositories { mavenCentral() diff --git a/core/build.gradle b/core/build.gradle index f8a78d6..52f43b0 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -95,12 +95,19 @@ kotlin { task jacocoTestReportJvm(type: JacocoReport) { classDirectories = files( "$buildDir/classes/kotlin/common/main", - "$buildDir/classes/kotlin/jvm/main") + "$buildDir/classes/kotlin/jvm/main", + "$buildDir/classes/kotlin/jvmRx/main" + ) sourceDirectories = files( "src/commonMain/kotlin", - "src/jvmMain/kotlin") + "src/jvmMain/kotlin", + "src/jvmRxMain/kotlin" + ) //noinspection ChangeToOperator - executionData = files("$buildDir/jacoco/jvmTest.exec") + executionData = files( + "$buildDir/jacoco/jvmTest.exec", + "$buildDir/jacoco/jvmRxTest.exec" + ) reports { xml.enabled = true diff --git a/core/src/commonMain/kotlin/com/toxicbakery/kfinstatemachine/StateMachine.kt b/core/src/commonMain/kotlin/com/toxicbakery/kfinstatemachine/StateMachine.kt index ce58c70..b55de48 100644 --- a/core/src/commonMain/kotlin/com/toxicbakery/kfinstatemachine/StateMachine.kt +++ b/core/src/commonMain/kotlin/com/toxicbakery/kfinstatemachine/StateMachine.kt @@ -2,10 +2,20 @@ package com.toxicbakery.kfinstatemachine import kotlin.reflect.KClass +/** + * A basic state machine that is not thread safe. + */ open class StateMachine : IStateMachine { private val transitionRules: Array> - private val transitionCallbacks: MutableList> = mutableListOf() + + private val _transitionCallbacks: MutableList> = mutableListOf() + + /** + * An immutable list of the currently registered callbacks. + */ + val transitionCallbacks: List> + get() = _transitionCallbacks final override var state: S @@ -35,9 +45,13 @@ open class StateMachine : IStateMachine { override fun transition(transition: T) { val currentState = state val edge = edge(transition) - transitionCallbacks.forEach { cb -> cb.enteringState(state, transition, edge.newState) } + transitionCallbacks.forEach { cb -> + cb.enteringState(this, state, transition, edge.newState) + } state = edge.newState - transitionCallbacks.forEach { cb -> cb.enteredState(currentState, transition, edge.newState) } + transitionCallbacks.forEach { callback -> + callback.enteredState(this, currentState, transition, edge.newState) + } } @Suppress("UNCHECKED_CAST") @@ -56,7 +70,7 @@ open class StateMachine : IStateMachine { * @param transitionCallback to be registered */ fun registerCallback(transitionCallback: TransitionCallback) = - transitionCallbacks.add(transitionCallback) + _transitionCallbacks.add(transitionCallback) /** * Unregister a callback from state transition updates. @@ -64,7 +78,7 @@ open class StateMachine : IStateMachine { * @param transitionCallback to be unregistered */ fun unregisterCallback(transitionCallback: TransitionCallback) = - transitionCallbacks.remove(transitionCallback) + _transitionCallbacks.remove(transitionCallback) private fun edge(transition: Any): TransitionDef = transitionRules .filter { transitionRule -> @@ -107,11 +121,13 @@ interface TransitionCallback { /** * After a state transition has been verified to be legal but has not yet been applied to the machine. * + * @param stateMachine the machine notifying the state change * @param currentState the current state of the machine * @param transition the transition that initiated the state change * @param targetState the resulting state of this transition */ fun enteringState( + stateMachine: StateMachine, currentState: S, transition: T, targetState: S @@ -120,11 +136,13 @@ interface TransitionCallback { /** * After a state transition has been verified to be legal and also applied to a machine. * + * @param stateMachine the machine notifying the state change * @param previousState the previous state of the machine before the transition was applied * @param transition the transition that initiated the state change * @param currentState the resulting state of this transition */ fun enteredState( + stateMachine: StateMachine, previousState: S, transition: T, currentState: S diff --git a/core/src/jvmRxMain/kotlin/com/toxicbakery/kfinstatemachine/RxStateMachine.kt b/core/src/jvmRxMain/kotlin/com/toxicbakery/kfinstatemachine/RxStateMachine.kt index fe84cac..3e35b58 100644 --- a/core/src/jvmRxMain/kotlin/com/toxicbakery/kfinstatemachine/RxStateMachine.kt +++ b/core/src/jvmRxMain/kotlin/com/toxicbakery/kfinstatemachine/RxStateMachine.kt @@ -24,6 +24,7 @@ sealed class TransitionEvent { * @param targetState the resulting state of this transition */ data class EnterTransition( + val stateMachine: StateMachine, val currentState: S, val transition: T, val targetState: S @@ -37,6 +38,7 @@ sealed class TransitionEvent { * @param currentState the resulting state of this transition */ data class ExitTransition( + val stateMachine: StateMachine, val previousState: S, val transition: T, val currentState: S @@ -54,15 +56,17 @@ private class RxStateCallback( ) : TransitionCallback { override fun enteringState( + stateMachine: StateMachine, currentState: S, transition: T, targetState: S - ) = emitter.onNext(EnterTransition(currentState, transition, targetState)) + ) = emitter.onNext(EnterTransition(stateMachine, currentState, transition, targetState)) override fun enteredState( + stateMachine: StateMachine, previousState: S, transition: T, currentState: S - ) = emitter.onNext(ExitTransition(previousState, transition, currentState)) + ) = emitter.onNext(ExitTransition(stateMachine, previousState, transition, currentState)) } diff --git a/core/src/jvmRxTest/kotlin/com/toxicbakery/kfinstatemachine/RxStateMachineTest.kt b/core/src/jvmRxTest/kotlin/com/toxicbakery/kfinstatemachine/RxStateMachineTest.kt index 5d4529a..6d7f3d5 100644 --- a/core/src/jvmRxTest/kotlin/com/toxicbakery/kfinstatemachine/RxStateMachineTest.kt +++ b/core/src/jvmRxTest/kotlin/com/toxicbakery/kfinstatemachine/RxStateMachineTest.kt @@ -30,7 +30,7 @@ class RxStateMachineTest { var currentState: Energy = stateMachine.state - stateMachine.stateObservable + val disposable = stateMachine.stateObservable .filter { event -> event is ExitTransition } .map { event -> event as ExitTransition } .map { event -> event.currentState } @@ -62,7 +62,10 @@ class RxStateMachineTest { setOf(Release::class), stateMachine.transitionsTo(Kinetic)) + // Cleanup + disposable.dispose() + assertEquals(0, stateMachine.transitionCallbacks.size) } }