Skip to content

Commit 0f79642

Browse files
Throw error when recursion is detected in transition (#47)
* Use Xcode 12.5.1 * Throw error when recursion is detected in transition * Add test for recursion detected error
1 parent 6a493bf commit 0f79642

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

Swift/Sources/StateMachine/StateMachine.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ open class StateMachine<State: StateMachineHashable, Event: StateMachineHashable
2727
public struct Invalid: Error, Equatable {}
2828
}
2929

30+
public enum StateMachineError: Error {
31+
32+
case recursionDetected
33+
}
34+
3035
private struct Observer {
3136

3237
weak var object: AnyObject?
@@ -51,6 +56,8 @@ open class StateMachine<State: StateMachineHashable, Event: StateMachineHashable
5156
private let states: States
5257
private var observers: [Observer] = []
5358

59+
private var isNotifying: Bool = false
60+
5461
public init(@DefinitionBuilder build: () -> Definition) {
5562
let definition: Definition = build()
5663
state = definition.initialState.state
@@ -86,6 +93,8 @@ open class StateMachine<State: StateMachineHashable, Event: StateMachineHashable
8693

8794
@discardableResult
8895
public func transition(_ event: Event) throws -> Transition.Valid {
96+
guard !isNotifying
97+
else { throw StateMachineError.recursionDetected }
8998
let result: Transition.Result
9099
defer { notify(result) }
91100
do {
@@ -111,6 +120,8 @@ open class StateMachine<State: StateMachineHashable, Event: StateMachineHashable
111120
}
112121

113122
private func notify(_ result: Transition.Result) {
123+
isNotifying = true
124+
defer { isNotifying = false }
114125
var observers: [Observer] = []
115126
for observer in self.observers {
116127
guard observer.object != nil

Swift/Tests/StateMachineTests/StateMachineTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,28 @@ final class StateMachineTests: XCTestCase, StateMachineBuilder {
169169
// Then
170170
expect(transitionCount).to(equal(2))
171171
}
172+
173+
func testRecursionDetectedError() throws {
174+
175+
var error: TestStateMachine.StateMachineError? = nil
176+
177+
// Given
178+
let stateMachine: TestStateMachine = givenState(is: .stateOne)
179+
180+
stateMachine.startObserving(self) { [unowned stateMachine] _ in
181+
do {
182+
try stateMachine.transition(.eventOne)
183+
} catch let e as TestStateMachine.StateMachineError {
184+
error = e
185+
} catch {}
186+
}
187+
188+
// When
189+
try stateMachine.transition(.eventOne)
190+
191+
// Then
192+
expect(error).to(equal(.recursionDetected))
193+
}
172194
}
173195

174196
final class Logger {

0 commit comments

Comments
 (0)