diff --git a/Configurations/Base.xcconfig b/Configurations/Base.xcconfig index a8a1138..522fbff 100644 --- a/Configurations/Base.xcconfig +++ b/Configurations/Base.xcconfig @@ -10,4 +10,6 @@ CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer; MACOSX_DEPLOYMENT_TARGET = 10.9; IPHONEOS_DEPLOYMENT_TARGET = 8.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; -TVOS_DEPLOYMENT_TARGET = 9.0; \ No newline at end of file +TVOS_DEPLOYMENT_TARGET = 9.0; + +SWIFT_VERSION = 5.0 diff --git a/Package.swift b/Package.swift index f0b256b..8c11573 100644 --- a/Package.swift +++ b/Package.swift @@ -1,13 +1,30 @@ -// -// Package.swift -// SwiftState -// -// Created by Yasuhiro Inami on 2015-12-05. -// Copyright © 2015 Yasuhiro Inami. All rights reserved. -// +// swift-tools-version:5.3 +// The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( - name: "SwiftState" + name: "SwiftState", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "SwiftState", + targets: ["SwiftState"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "SwiftState", + dependencies: [], + path:"Sources"), + .testTarget( + name: "SwiftStateTests", + dependencies: ["SwiftState"], + path:"Sources"), + ] ) diff --git a/README.md b/README.md index 4a5db53..590a353 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -SwiftState [![Circle CI](https://circleci.com/gh/ReactKit/SwiftState/tree/swift%2F2.0.svg?style=svg)](https://circleci.com/gh/ReactKit/SwiftState/tree/swift%2F2.0) +SwiftState ========== Elegant state machine for Swift. @@ -10,20 +10,20 @@ Elegant state machine for Swift. ```swift enum MyState: StateType { - case State0, State1, State2 + case state0, state1, state2 } ``` ```swift // setup state machine -let machine = StateMachine(state: .State0) { machine in +let machine = StateMachine(state: .state0) { machine in - machine.addRoute(.State0 => .State1) - machine.addRoute(.Any => .State2) { context in print("Any => 2, msg=\(context.userInfo)") } - machine.addRoute(.State2 => .Any) { context in print("2 => Any, msg=\(context.userInfo)") } + machine.addRoute(.state0 => .state1) + machine.addRoute(.any => .state2) { context in print("Any => 2, msg=\(context.userInfo)") } + machine.addRoute(.state2 => .any) { context in print("2 => Any, msg=\(context.userInfo)") } // add handler (`context = (event, fromState, toState, userInfo)`) - machine.addHandler(.State0 => .State1) { context in + machine.addHandler(.state0 => .state1) { context in print("0 => 1") } @@ -34,21 +34,21 @@ let machine = StateMachine(state: .State0) { machine in } // initial -XCTAssertEqual(machine.state, MyState.State0) +XCTAssertEqual(machine.state, MyState.state0) // tryState 0 => 1 => 2 => 1 => 0 -machine <- .State1 -XCTAssertEqual(machine.state, MyState.State1) +machine <- .state1 +XCTAssertEqual(machine.state, MyState.state1) -machine <- (.State2, "Hello") -XCTAssertEqual(machine.state, MyState.State2) +machine <- (.state2, "Hello") +XCTAssertEqual(machine.state, MyState.state2) -machine <- (.State1, "Bye") -XCTAssertEqual(machine.state, MyState.State1) +machine <- (.state1, "Bye") +XCTAssertEqual(machine.state, MyState.state1) -machine <- .State0 // fail: no 1 => 0 -XCTAssertEqual(machine.state, MyState.State1) +machine <- .state0 // fail: no 1 => 0 +XCTAssertEqual(machine.state, MyState.state1) ``` This will print: @@ -57,7 +57,7 @@ This will print: 0 => 1 Any => 2, msg=Optional("Hello") 2 => Any, msg=Optional("Bye") -[ERROR] State1 => State0 +[ERROR] state1 => state0 ``` ### Transition by Event @@ -66,39 +66,39 @@ Use `<-!` operator to try transition by `Event` rather than specifying target `S ```swift enum MyEvent: EventType { - case Event0, Event1 + case event0, event1 } ``` ```swift -let machine = StateMachine(state: .State0) { machine in +let machine = StateMachine(state: .state0) { machine in // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, ]) // add event handler - machine.addHandler(event: .Event0) { context in - print(".Event0 triggered!") + machine.addHandler(event: .event0) { context in + print(".event0 triggered!") } } // initial -XCTAssertEqual(machine.state, MyState.State0) +XCTAssertEqual(machine.state, MyState.state0) // tryEvent -machine <-! .Event0 -XCTAssertEqual(machine.state, MyState.State1) +machine <-! .event0 +XCTAssertEqual(machine.state, MyState.state1) // tryEvent -machine <-! .Event0 -XCTAssertEqual(machine.state, MyState.State2) +machine <-! .event0 +XCTAssertEqual(machine.state, MyState.state2) // tryEvent (fails) -machine <-! .Event0 -XCTAssertEqual(machine.state, MyState.State2, "Event0 doesn't have 2 => Any") +machine <-! .event0 +XCTAssertEqual(machine.state, MyState.state2, "event0 doesn't have 2 => Any") ``` If there is no `Event`-based transition, use built-in `NoEvent` instead. @@ -108,33 +108,33 @@ If there is no `Event`-based transition, use built-in `NoEvent` instead. Above examples use _arrow-style routing_ which are easy to understand, but it lacks in ability to handle **state & event enums with associated values**. In such cases, use either of the following functions to apply _closure-style routing_: - `machine.addRouteMapping(routeMapping)` - - `RouteMapping`: `(event: E?, fromState: S, userInfo: Any?) -> S?` + - `RouteMapping`: `(_ event: E?, _ fromState: S, _ userInfo: Any?) -> S?` - `machine.addStateRouteMapping(stateRouteMapping)` - - `StateRouteMapping`: `(fromState: S, userInfo: Any?) -> [S]?` + - `StateRouteMapping`: `(_ fromState: S, _ userInfo: Any?) -> [S]?` For example: ```swift enum StrState: StateType { - case Str(String) ... + case str(String) ... } enum StrEvent: EventType { - case Str(String) ... + case str(String) ... } -let machine = Machine(state: .Str("initial")) { machine in +let machine = Machine(state: .str("initial")) { machine in machine.addRouteMapping { event, fromState, userInfo -> StrState? in // no route for no-event guard let event = event else { return nil } switch (event, fromState) { - case (.Str("gogogo"), .Str("initial")): - return .Str("Phase 1") - case (.Str("gogogo"), .Str("Phase 1")): - return .Str("Phase 2") - case (.Str("finish"), .Str("Phase 2")): - return .Str("end") + case (.str("gogogo"), .str("initial")): + return .str("Phase 1") + case (.str("gogogo"), .str("Phase 1")): + return .str("Phase 2") + case (.str("finish"), .str("Phase 2")): + return .str("end") default: return nil } @@ -143,31 +143,31 @@ let machine = Machine(state: .Str("initial")) { machine in } // initial -XCTAssertEqual(machine.state, StrState.Str("initial")) +XCTAssertEqual(machine.state, StrState.str("initial")) // tryEvent (fails) -machine <-! .Str("go?") -XCTAssertEqual(machine.state, StrState.Str("initial"), "No change.") +machine <-! .str("go?") +XCTAssertEqual(machine.state, StrState.str("initial"), "No change.") // tryEvent -machine <-! .Str("gogogo") -XCTAssertEqual(machine.state, StrState.Str("Phase 1")) +machine <-! .str("gogogo") +XCTAssertEqual(machine.state, StrState.str("Phase 1")) // tryEvent (fails) -machine <-! .Str("finish") -XCTAssertEqual(machine.state, StrState.Str("Phase 1"), "No change.") +machine <-! .str("finish") +XCTAssertEqual(machine.state, StrState.str("Phase 1"), "No change.") // tryEvent -machine <-! .Str("gogogo") -XCTAssertEqual(machine.state, StrState.Str("Phase 2")) +machine <-! .str("gogogo") +XCTAssertEqual(machine.state, StrState.str("Phase 2")) // tryEvent (fails) -machine <-! .Str("gogogo") -XCTAssertEqual(machine.state, StrState.Str("Phase 2"), "No change.") +machine <-! .str("gogogo") +XCTAssertEqual(machine.state, StrState.str("Phase 2"), "No change.") // tryEvent -machine <-! .Str("finish") -XCTAssertEqual(machine.state, StrState.Str("end")) +machine <-! .str("finish") +XCTAssertEqual(machine.state, StrState.str("end")) ``` This behaves very similar to JavaScript's safe state-container [rackt/Redux](https://github.com/rackt/redux), where `RouteMapping` can be interpretted as `Redux.Reducer`. @@ -178,22 +178,22 @@ For more examples, please see XCTest cases. ## Features - Easy Swift syntax - - Transition: `.State0 => .State1`, `[.State0, .State1] => .State2` - - Try state: `machine <- .State1` - - Try state + messaging: `machine <- (.State1, "GoGoGo")` - - Try event: `machine <-! .Event1` + - Transition: `.state0 => .state1`, `[.state0, .state1] => .state2` + - Try state: `machine <- .state1` + - Try state + messaging: `machine <- (.state1, "GoGoGo")` + - Try event: `machine <-! .event1` - Highly flexible transition routing - Using `Condition` - - Using `.Any` state - - Entry handling: `.Any => .SomeState` - - Exit handling: `.SomeState => .Any` - - Blacklisting: `.Any => .Any` + `Condition` - - Using `.Any` event + - Using `.any` state + - Entry handling: `.any => .someState` + - Exit handling: `.someState => .any` + - Blacklisting: `.any => .any` + `Condition` + - Using `.any` event - Route Mapping (closure-based routing): [#36](https://github.com/ReactKit/SwiftState/pull/36) - Success/Error handlers with `order: UInt8` (more flexible than before/after handlers) - Removable routes and handlers using `Disposable` -- Route Chaining: `.State0 => .State1 => .State2` +- Route Chaining: `.state0 => .state1 => .state2` - Hierarchical State Machine: [#10](https://github.com/ReactKit/SwiftState/pull/10) @@ -201,17 +201,17 @@ For more examples, please see XCTest cases. Term | Type | Description ------------- | ----------------------------- | ------------------------------------------ -State | `StateType` (protocol) | Mostly enum, describing each state e.g. `.State0`. +State | `StateType` (protocol) | Mostly enum, describing each state e.g. `.state0`. Event | `EventType` (protocol) | Name for route-group. Transition can be fired via `Event` instead of explicitly targeting next `State`. State Machine | `Machine` | State transition manager which can register `Route`/`RouteMapping` and `Handler` separately for variety of transitions. -Transition | `Transition` | `From-` and `to-` states represented as `.State1 => .State2`. Also, `.Any` can be used to represent _any state_. +Transition | `Transition` | `From-` and `to-` states represented as `.state1 => .state2`. Also, `.any` can be used to represent _any state_. Route | `Route` | `Transition` + `Condition`. Condition | `Context -> Bool` | Closure for validating transition. If condition returns `false`, transition will fail and associated handlers will not be invoked. Route Mapping | `(event: E?, fromState: S, userInfo: Any?) -> S?` | Another way of defining routes **using closure instead of transition arrows (`=>`)**. This is useful when state & event are enum with associated values. Return value (`S?`) means preferred-`toState`, where passing `nil` means no routes available. See [#36](https://github.com/ReactKit/SwiftState/pull/36) for more info. -State Route Mapping | `(fromState: S, userInfo: Any?) -> [S]?` | Another way of defining routes **using closure instead of transition arrows (`=>`)**. This is useful when state is enum with associated values. Return value (`[S]?`) means multiple `toState`s from single `fromState` (synonym for multiple routing e.g. `.State0 => [.State1, .State2]`). See [#36](https://github.com/ReactKit/SwiftState/pull/36) for more info. +State Route Mapping | `(fromState: S, userInfo: Any?) -> [S]?` | Another way of defining routes **using closure instead of transition arrows (`=>`)**. This is useful when state is enum with associated values. Return value (`[S]?`) means multiple `toState`s from single `fromState` (synonym for multiple routing e.g. `.state0 => [.state1, .state2]`). See [#36](https://github.com/ReactKit/SwiftState/pull/36) for more info. Handler | `Context -> Void` | Transition callback invoked when state has been changed successfully. Context | `(event: E?, fromState: S, toState: S, userInfo: Any?)` | Closure argument for `Condition` & `Handler`. -Chain | `TransitionChain` / `RouteChain` | Group of continuous routes represented as `.State1 => .State2 => .State3` +Chain | `TransitionChain` / `RouteChain` | Group of continuous routes represented as `.state1 => .state2 => .state3` ## Related Articles diff --git a/Sources/Disposable.swift b/Sources/Disposable.swift index fe9489e..3689560 100644 --- a/Sources/Disposable.swift +++ b/Sources/Disposable.swift @@ -34,7 +34,7 @@ public final class ActionDisposable: Disposable { } /// Initializes the disposable to run the given action upon disposal. - public init(action: () -> ()) { + public init(action: @escaping (() -> ())) { self.action = action } diff --git a/Sources/EventType.swift b/Sources/EventType.swift index 3af6a43..b531f41 100644 --- a/Sources/EventType.swift +++ b/Sources/EventType.swift @@ -10,11 +10,11 @@ public protocol EventType: Hashable {} // MARK: Event -/// `EventType` wrapper for handling `.Any` event. +/// `EventType` wrapper for handling `.any` event. public enum Event { - case Some(E) - case Any + case some(E) + case any } extension Event: Hashable @@ -22,8 +22,8 @@ extension Event: Hashable public var hashValue: Int { switch self { - case .Some(let x): return x.hashValue - case .Any: return _hashValueForAny + case .some(let x): return x.hashValue + case .any: return _hashValueForAny } } } @@ -33,50 +33,50 @@ extension Event: RawRepresentable public init(rawValue: E?) { if let rawValue = rawValue { - self = .Some(rawValue) + self = .some(rawValue) } else { - self = .Any + self = .any } } public var rawValue: E? { switch self { - case .Some(let x): return x + case .some(let x): return x default: return nil } } } -public func == (lhs: Event, rhs: Event) -> Bool +public func == (lhs: Event, rhs: Event) -> Bool { switch (lhs, rhs) { - case let (.Some(x1), .Some(x2)) where x1 == x2: + case let (.some(x1), .some(x2)) where x1 == x2: return true - case (.Any, .Any): + case (.any, .any): return true default: return false } } -public func == (lhs: Event, rhs: E) -> Bool +public func == (lhs: Event, rhs: E) -> Bool { switch lhs { - case .Some(let x): + case .some(let x): return x == rhs - case .Any: + case .any: return false } } -public func == (lhs: E, rhs: Event) -> Bool +public func == (lhs: E, rhs: Event) -> Bool { switch rhs { - case .Some(let x): + case .some(let x): return x == lhs - case .Any: + case .any: return false } } @@ -86,9 +86,8 @@ public func == (lhs: E, rhs: Event) -> Bool /// Useful for creating StateMachine without events, i.e. `StateMachine`. public enum NoEvent: EventType { - public var hashValue: Int + public func hash(into hasher: inout Hasher) { - return 0 } } diff --git a/Sources/Info.plist b/Sources/Info.plist index 0cefffe..b237a65 100644 --- a/Sources/Info.plist +++ b/Sources/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.1.0 + 4.2.0 CFBundleSignature ???? CFBundleVersion diff --git a/Sources/Machine.swift b/Sources/Machine.swift index 1eebc4c..23aa909 100644 --- a/Sources/Machine.swift +++ b/Sources/Machine.swift @@ -17,18 +17,24 @@ public class Machine { /// Closure argument for `Condition` & `Handler`. - public typealias Context = (event: E?, fromState: S, toState: S, userInfo: Any?) + public struct Context + { + public let event: E? + public let fromState: S + public let toState: S + public let userInfo: Any? + } /// Closure for validating transition. /// If condition returns `false`, transition will fail and associated handlers will not be invoked. - public typealias Condition = Context -> Bool + public typealias Condition = (Context) -> Bool /// Transition callback invoked when state has been changed successfully. - public typealias Handler = Context -> () + public typealias Handler = (Context) -> () /// Closure-based route, mainly for `tryEvent()` (and also works for subclass's `tryState()`). /// - Returns: Preferred `toState`. - public typealias RouteMapping = (event: E?, fromState: S, userInfo: Any?) -> S? + public typealias RouteMapping = (_ event: E?, _ fromState: S, _ userInfo: Any?) -> S? internal typealias _RouteDict = [Transition : [String : Condition?]] @@ -46,14 +52,14 @@ public class Machine // MARK: - Init //-------------------------------------------------- - public init(state: S, initClosure: (Machine -> ())? = nil) + public init(state: S, initClosure: ((Machine) -> ())? = nil) { self._state = state initClosure?(self) } - public func configure(closure: Machine -> ()) + public func configure(_ closure: (Machine) -> ()) { closure(self) } @@ -68,12 +74,12 @@ public class Machine //-------------------------------------------------- /// Check for added routes & routeMappings. - public func hasRoute(event event: E, transition: Transition, userInfo: Any? = nil) -> Bool + public func hasRoute(event: E, transition: Transition, userInfo: Any? = nil) -> Bool { guard let fromState = transition.fromState.rawValue, - toState = transition.toState.rawValue else + let toState = transition.toState.rawValue else { - assertionFailure("State = `.Any` is not supported for `hasRoute()` (always returns `false`)") + assertionFailure("State = `.any` is not supported for `hasRoute()` (always returns `false`)") return false } @@ -81,18 +87,18 @@ public class Machine } /// Check for added routes & routeMappings. - public func hasRoute(event event: E, fromState: S, toState: S, userInfo: Any? = nil) -> Bool + public func hasRoute(event: E, fromState: S, toState: S, userInfo: Any? = nil) -> Bool { return self._hasRoute(event: event, fromState: fromState, toState: toState, userInfo: userInfo) } - internal func _hasRoute(event event: E?, fromState: S, toState: S, userInfo: Any? = nil) -> Bool + internal func _hasRoute(event: E?, fromState: S, toState: S, userInfo: Any? = nil) -> Bool { if self._hasRouteInDict(event: event, fromState: fromState, toState: toState, userInfo: userInfo) { return true } - if self._hasRouteMappingInDict(event: event, fromState: fromState, toState: .Some(toState), userInfo: userInfo) != nil { + if self._hasRouteMappingInDict(event: event, fromState: fromState, toState: .some(toState), userInfo: userInfo) != nil { return true } @@ -100,7 +106,7 @@ public class Machine } /// Check for `_routes`. - private func _hasRouteInDict(event event: E?, fromState: S, toState: S, userInfo: Any? = nil) -> Bool + private func _hasRouteInDict(event: E?, fromState: S, toState: S, userInfo: Any? = nil) -> Bool { let validTransitions = _validTransitions(fromState: fromState, toState: toState) @@ -110,7 +116,7 @@ public class Machine if let event = event { for (ev, routeDict) in self._routes { - if ev.rawValue == event || ev == .Any { + if ev.rawValue == event || ev == .any { routeDicts += [routeDict] } } @@ -139,11 +145,11 @@ public class Machine } /// Check for `_routeMappings`. - private func _hasRouteMappingInDict(event event: E?, fromState: S, toState: S?, userInfo: Any? = nil) -> S? + private func _hasRouteMappingInDict(event: E?, fromState: S, toState: S?, userInfo: Any? = nil) -> S? { for mapping in self._routeMappings.values { - if let preferredToState = mapping(event: event, fromState: fromState, userInfo: userInfo) - where preferredToState == toState || toState == nil + if let preferredToState = mapping(event, fromState, userInfo), + preferredToState == toState || toState == nil { return preferredToState } @@ -157,14 +163,14 @@ public class Machine //-------------------------------------------------- /// - Returns: Preferred-`toState`. - public func canTryEvent(event: E, userInfo: Any? = nil) -> S? + public func canTryEvent(_ event: E, userInfo: Any? = nil) -> S? { // check for `_routes` - for case let routeDict? in [self._routes[.Some(event)], self._routes[.Any]] { + for case let routeDict? in [self._routes[.some(event)], self._routes[.any]] { for (transition, keyConditionDict) in routeDict { - if transition.fromState == .Some(self.state) || transition.fromState == .Any { + if transition.fromState == .some(self.state) || transition.fromState == .any { for (_, condition) in keyConditionDict { - // if toState is `.Any`, always treat as identity transition + // if toState is `.any`, always treat as identity transition let toState = transition.toState.rawValue ?? self.state if _canPassCondition(condition, forEvent: event, fromState: self.state, toState: toState, userInfo: userInfo) { @@ -183,7 +189,8 @@ public class Machine return nil } - public func tryEvent(event: E, userInfo: Any? = nil) -> Bool + @discardableResult + public func tryEvent(_ event: E, userInfo: Any? = nil) -> Bool { let fromState = self.state @@ -212,14 +219,14 @@ public class Machine } } - private func _validHandlerInfos(event event: E, fromState: S, toState: S) -> [_HandlerInfo] + private func _validHandlerInfos(event: E, fromState: S, toState: S) -> [_HandlerInfo] { - let validHandlerInfos = [ self._handlers[.Some(event)], self._handlers[.Any] ] + let validHandlerInfos = [ self._handlers[.some(event)], self._handlers[.any] ] .filter { $0 != nil } .map { $0! } - .flatten() + .joined() - return validHandlerInfos.sort { info1, info2 in + return validHandlerInfos.sorted { info1, info2 in return info1.order < info2.order } } @@ -230,23 +237,27 @@ public class Machine // MARK: addRoutes(event:) - public func addRoutes(event event: E, transitions: [Transition], condition: Machine.Condition? = nil) -> Disposable + @discardableResult + public func addRoutes(event: E, transitions: [Transition], condition: Machine.Condition? = nil) -> Disposable { - return self.addRoutes(event: .Some(event), transitions: transitions, condition: condition) + return self.addRoutes(event: .some(event), transitions: transitions, condition: condition) } - public func addRoutes(event event: Event, transitions: [Transition], condition: Machine.Condition? = nil) -> Disposable + @discardableResult + public func addRoutes(event: Event, transitions: [Transition], condition: Machine.Condition? = nil) -> Disposable { let routes = transitions.map { Route(transition: $0, condition: condition) } return self.addRoutes(event: event, routes: routes) } - public func addRoutes(event event: E, routes: [Route]) -> Disposable + @discardableResult + public func addRoutes(event: E, routes: [Route]) -> Disposable { - return self.addRoutes(event: .Some(event), routes: routes) + return self.addRoutes(event: .some(event), routes: routes) } - public func addRoutes(event event: Event, routes: [Route]) -> Disposable + @discardableResult + public func addRoutes(event: Event, routes: [Route]) -> Disposable { // NOTE: uses `map` with side-effects let disposables = routes.map { self._addRoute(event: event, route: $0) } @@ -256,7 +267,7 @@ public class Machine } } - internal func _addRoute(event event: Event = .Any, route: Route) -> Disposable + internal func _addRoute(event: Event = .any, route: Route) -> Disposable { let transition = route.transition let condition = route.condition @@ -287,23 +298,27 @@ public class Machine // MARK: addRoutes(event:) + conditional handler - public func addRoutes(event event: E, transitions: [Transition], condition: Condition? = nil, handler: Handler) -> Disposable + @discardableResult + public func addRoutes(event: E, transitions: [Transition], condition: Condition? = nil, handler: @escaping Handler) -> Disposable { - return self.addRoutes(event: .Some(event), transitions: transitions, condition: condition, handler: handler) + return self.addRoutes(event: .some(event), transitions: transitions, condition: condition, handler: handler) } - public func addRoutes(event event: Event, transitions: [Transition], condition: Condition? = nil, handler: Handler) -> Disposable + @discardableResult + public func addRoutes(event: Event, transitions: [Transition], condition: Condition? = nil, handler: @escaping Handler) -> Disposable { let routes = transitions.map { Route(transition: $0, condition: condition) } return self.addRoutes(event: event, routes: routes, handler: handler) } - public func addRoutes(event event: E, routes: [Route], handler: Handler) -> Disposable + @discardableResult + public func addRoutes(event: E, routes: [Route], handler: @escaping Handler) -> Disposable { - return self.addRoutes(event: .Some(event), routes: routes, handler: handler) + return self.addRoutes(event: .some(event), routes: routes, handler: handler) } - public func addRoutes(event event: Event, routes: [Route], handler: Handler) -> Disposable + @discardableResult + public func addRoutes(event: Event, routes: [Route], handler: @escaping Handler) -> Disposable { let routeDisposable = self.addRoutes(event: event, routes: routes) let handlerDisposable = self.addHandler(event: event, handler: handler) @@ -316,7 +331,8 @@ public class Machine // MARK: removeRoute - private func _removeRoute(_routeID: _RouteID) -> Bool + @discardableResult + private func _removeRoute(_ _routeID: _RouteID) -> Bool { guard let event = _routeID.event else { return false } @@ -356,7 +372,8 @@ public class Machine // MARK: addRouteMapping - public func addRouteMapping(routeMapping: RouteMapping) -> Disposable + @discardableResult + public func addRouteMapping(_ routeMapping: @escaping RouteMapping) -> Disposable { let key = _createUniqueString() @@ -371,14 +388,15 @@ public class Machine // MARK: addRouteMapping + conditional handler - public func addRouteMapping(routeMapping: RouteMapping, order: HandlerOrder = _defaultOrder, handler: Machine.Handler) -> Disposable + @discardableResult + public func addRouteMapping(_ routeMapping: @escaping RouteMapping, order: HandlerOrder = _defaultOrder, handler: @escaping Machine.Handler) -> Disposable { let routeDisposable = self.addRouteMapping(routeMapping) - let handlerDisposable = self._addHandler(event: .Any, order: order) { context in + let handlerDisposable = self._addHandler(event: .any, order: order) { context in - guard let preferredToState = routeMapping(event: context.event, fromState: context.fromState, userInfo: context.userInfo) - where preferredToState == context.toState else + guard let preferredToState = routeMapping(context.event, context.fromState, context.userInfo), + preferredToState == context.toState else { return } @@ -394,7 +412,8 @@ public class Machine // MARK: removeRouteMapping - private func _removeRouteMapping(routeMappingID: _RouteMappingID) -> Bool + @discardableResult + private func _removeRouteMapping(_ routeMappingID: _RouteMappingID) -> Bool { if self._routeMappings[routeMappingID.key] != nil { self._routeMappings[routeMappingID.key] = nil @@ -411,12 +430,14 @@ public class Machine // MARK: addHandler(event:) - public func addHandler(event event: E, order: HandlerOrder = _defaultOrder, handler: Machine.Handler) -> Disposable + @discardableResult + public func addHandler(event: E, order: HandlerOrder = _defaultOrder, handler: @escaping Machine.Handler) -> Disposable { - return self.addHandler(event: .Some(event), order: order, handler: handler) + return self.addHandler(event: .some(event), order: order, handler: handler) } - public func addHandler(event event: Event, order: HandlerOrder = _defaultOrder, handler: Machine.Handler) -> Disposable + @discardableResult + public func addHandler(event: Event, order: HandlerOrder = _defaultOrder, handler: @escaping Machine.Handler) -> Disposable { return self._addHandler(event: event, order: order) { context in // skip if not event-based transition @@ -424,13 +445,14 @@ public class Machine return } - if triggeredEvent == event.rawValue || event == .Any { + if triggeredEvent == event.rawValue || event == .any { handler(context) } } } - private func _addHandler(event event: Event, order: HandlerOrder = _defaultOrder, handler: Handler) -> Disposable + @discardableResult + private func _addHandler(event: Event, order: HandlerOrder = _defaultOrder, handler: @escaping Handler) -> Disposable { if self._handlers[event] == nil { self._handlers[event] = [] @@ -444,7 +466,7 @@ public class Machine self._handlers[event] = handlerInfos - let handlerID = _HandlerID(event: event, transition: .Any => .Any, key: key) // NOTE: use non-`nil` transition + let handlerID = _HandlerID(event: event, transition: .any => .any, key: key) // NOTE: use non-`nil` transition return ActionDisposable { [weak self] in self?._removeHandler(handlerID) @@ -453,7 +475,8 @@ public class Machine // MARK: addErrorHandler - public func addErrorHandler(order order: HandlerOrder = _defaultOrder, handler: Handler) -> Disposable + @discardableResult + public func addErrorHandler(order: HandlerOrder = _defaultOrder, handler: @escaping Handler) -> Disposable { let key = _createUniqueString() @@ -469,7 +492,8 @@ public class Machine // MARK: removeHandler - private func _removeHandler(handlerID: _HandlerID) -> Bool + @discardableResult + private func _removeHandler(_ handlerID: _HandlerID) -> Bool { if let event = handlerID.event { if let handlerInfos_ = self._handlers[event] { @@ -500,15 +524,17 @@ public class Machine // MARK: `<-!` (tryEvent) -infix operator <-! { associativity left } +infix operator <-! : AdditionPrecedence -public func <-! (machine: Machine, event: E) -> Machine +@discardableResult +public func <-! (machine: Machine, event: E) -> Machine { machine.tryEvent(event) return machine } -public func <-! (machine: Machine, tuple: (E, Any?)) -> Machine +@discardableResult +public func <-! (machine: Machine, tuple: (E, Any?)) -> Machine { machine.tryEvent(tuple.0, userInfo: tuple.1) return machine @@ -521,7 +547,7 @@ public func <-! (machine: Machine, tuple: (E, /// Precedence for registered handlers (higher number is called later). public typealias HandlerOrder = UInt8 -internal let _defaultOrder: HandlerOrder = 100 +public let _defaultOrder: HandlerOrder = 100 //-------------------------------------------------- // MARK: - Internal @@ -532,45 +558,45 @@ internal func _createUniqueString() -> String { var uniqueString: String = "" for _ in 1...8 { - uniqueString += String(UnicodeScalar(_random(0xD800))) // 0xD800 = 55296 = 15.755bit + uniqueString += String(describing: UnicodeScalar(_random(0xD800))) // 0xD800 = 55296 = 15.755bit } return uniqueString } -internal func _validTransitions(fromState fromState: S, toState: S) -> [Transition] +internal func _validTransitions(fromState: S, toState: S) -> [Transition] { return [ fromState => toState, - fromState => .Any, - .Any => toState, - .Any => .Any + fromState => .any, + .any => toState, + .any => .any ] } -internal func _canPassCondition(condition: Machine.Condition?, forEvent event: E?, fromState: S, toState: S, userInfo: Any?) -> Bool +internal func _canPassCondition(_ condition: Machine.Condition?, forEvent event: E?, fromState: S, toState: S, userInfo: Any?) -> Bool { - return condition?((event, fromState, toState, userInfo)) ?? true + return condition?(Machine.Context(event: event, fromState: fromState, toState: toState, userInfo: userInfo)) ?? true } -internal func _insertHandlerIntoArray(inout handlerInfos: [_HandlerInfo], newHandlerInfo: _HandlerInfo) +internal func _insertHandlerIntoArray(_ handlerInfos: inout [_HandlerInfo], newHandlerInfo: _HandlerInfo) { var index = handlerInfos.count - for i in Array(0..(inout handlerInfos: [_HandlerInfo], removingHandlerID: _HandlerID) -> Bool +internal func _removeHandlerFromArray(_ handlerInfos: inout [_HandlerInfo], removingHandlerID: _HandlerID) -> Bool { for i in 0.. // MARK: - Custom Operators //-------------------------------------------------- -/// e.g. [.State0, .State1] => .State, allowing [0 => 2, 1 => 2] -public func => (leftStates: [S], right: State) -> Route +/// e.g. [.state0, .state1] => .state, allowing [0 => 2, 1 => 2] +public func => (leftStates: [S], right: State) -> Route { - // NOTE: don't reuse ".Any => .Any + condition" for efficiency - return Route(transition: .Any => right, condition: { context -> Bool in + // NOTE: don't reuse ".any => .any + condition" for efficiency + return Route(transition: .any => right, condition: { context -> Bool in return leftStates.contains(context.fromState) }) } -public func => (leftStates: [S], right: S) -> Route +public func => (leftStates: [S], right: S) -> Route { - return leftStates => .Some(right) + return leftStates => .some(right) } -/// e.g. .State0 => [.State1, .State], allowing [0 => 1, 0 => 2] -public func => (left: State, rightStates: [S]) -> Route +/// e.g. .state0 => [.state1, .state], allowing [0 => 1, 0 => 2] +public func => (left: State, rightStates: [S]) -> Route { - return Route(transition: left => .Any, condition: { context -> Bool in + return Route(transition: left => .any, condition: { context -> Bool in return rightStates.contains(context.toState) }) } -public func => (left: S, rightStates: [S]) -> Route +public func => (left: S, rightStates: [S]) -> Route { - return .Some(left) => rightStates + return .some(left) => rightStates } -/// e.g. [.State0, .State1] => [.State, .State3], allowing [0 => 2, 0 => 3, 1 => 2, 1 => 3] -public func => (leftStates: [S], rightStates: [S]) -> Route +/// e.g. [.state0, .state1] => [.state, .state3], allowing [0 => 2, 0 => 3, 1 => 2, 1 => 3] +public func => (leftStates: [S], rightStates: [S]) -> Route { - return Route(transition: .Any => .Any, condition: { context -> Bool in + return Route(transition: .any => .any, condition: { context -> Bool in return leftStates.contains(context.fromState) && rightStates.contains(context.toState) }) } diff --git a/Sources/StateMachine.swift b/Sources/StateMachine.swift index 847054b..ae8ff6e 100644 --- a/Sources/StateMachine.swift +++ b/Sources/StateMachine.swift @@ -15,8 +15,8 @@ public final class StateMachine: Machine { /// Closure-based routes for `tryState()`. - /// - Returns: Multiple `toState`s from single `fromState`, similar to `.State0 => [.State1, .State2]` - public typealias StateRouteMapping = (fromState: S, userInfo: Any?) -> [S]? + /// - Returns: Multiple `toState`s from single `fromState`, similar to `.state0 => [.state1, .state2]` + public typealias StateRouteMapping = (_ fromState: S, _ userInfo: Any?) -> [S]? private lazy var _routes: _RouteDict = [:] private lazy var _routeMappings: [String : StateRouteMapping] = [:] // NOTE: `StateRouteMapping`, not `RouteMapping` @@ -28,7 +28,7 @@ public final class StateMachine: Machine // MARK: - Init //-------------------------------------------------- - public override init(state: S, initClosure: (StateMachine -> ())? = nil) + public override init(state: S, initClosure: ((StateMachine) -> ())? = nil) { super.init(state: state, initClosure: { machine in initClosure?(machine as! StateMachine) // swiftlint:disable:this force_cast @@ -36,7 +36,7 @@ public final class StateMachine: Machine }) } - public override func configure(closure: StateMachine -> ()) + public override func configure(_ closure: (StateMachine) -> ()) { closure(self) } @@ -47,12 +47,12 @@ public final class StateMachine: Machine /// Check for added routes & routeMappings. /// - Note: This method also checks for event-based-routes. - public func hasRoute(transition: Transition, userInfo: Any? = nil) -> Bool + public func hasRoute(_ transition: Transition, userInfo: Any? = nil) -> Bool { guard let fromState = transition.fromState.rawValue, - toState = transition.toState.rawValue else + let toState = transition.toState.rawValue else { - assertionFailure("State = `.Any` is not supported for `hasRoute()` (always returns `false`)") + assertionFailure("State = `.any` is not supported for `hasRoute()` (always returns `false`)") return false } @@ -61,7 +61,7 @@ public final class StateMachine: Machine /// Check for added routes & routeMappings. /// - Note: This method also checks for event-based-routes. - public func hasRoute(fromState fromState: S, toState: S, userInfo: Any? = nil) -> Bool + public func hasRoute(fromState: S, toState: S, userInfo: Any? = nil) -> Bool { if self._hasRouteInDict(fromState: fromState, toState: toState, userInfo: userInfo) { return true @@ -76,7 +76,7 @@ public final class StateMachine: Machine } /// Check for `_routes`. - private func _hasRouteInDict(fromState fromState: S, toState: S, userInfo: Any? = nil) -> Bool + private func _hasRouteInDict(fromState: S, toState: S, userInfo: Any? = nil) -> Bool { let validTransitions = _validTransitions(fromState: fromState, toState: toState) @@ -96,10 +96,10 @@ public final class StateMachine: Machine } /// Check for `_routeMappings`. - private func _hasRouteMappingInDict(fromState fromState: S, toState: S, userInfo: Any? = nil) -> S? + private func _hasRouteMappingInDict(fromState: S, toState: S, userInfo: Any? = nil) -> S? { for mapping in self._routeMappings.values { - if let preferredToStates = mapping(fromState: fromState, userInfo: userInfo) { + if let preferredToStates = mapping(fromState, userInfo) { return preferredToStates.contains(toState) ? toState : nil } } @@ -112,13 +112,14 @@ public final class StateMachine: Machine //-------------------------------------------------- /// - Note: This method also checks for event-based-routes. - public func canTryState(toState: S, userInfo: Any? = nil) -> Bool + public func canTryState(_ toState: S, userInfo: Any? = nil) -> Bool { return self.hasRoute(fromState: self.state, toState: toState, userInfo: userInfo) } /// - Note: This method also tries state-change for event-based-routes. - public func tryState(toState: S, userInfo: Any? = nil) -> Bool + @discardableResult + public func tryState(_ toState: S, userInfo: Any? = nil) -> Bool { let fromState = self.state @@ -154,7 +155,7 @@ public final class StateMachine: Machine return false } - private func _validHandlerInfos(fromState fromState: S, toState: S) -> [_HandlerInfo] + private func _validHandlerInfos(fromState: S, toState: S) -> [_HandlerInfo] { var validHandlerInfos: [_HandlerInfo] = [] @@ -168,7 +169,7 @@ public final class StateMachine: Machine } } - validHandlerInfos.sortInPlace { info1, info2 in + validHandlerInfos.sort { info1, info2 in return info1.order < info2.order } @@ -181,13 +182,15 @@ public final class StateMachine: Machine // MARK: addRoute (no-event) - public func addRoute(transition: Transition, condition: Condition? = nil) -> Disposable + @discardableResult + public func addRoute(_ transition: Transition, condition: Condition? = nil) -> Disposable { let route = Route(transition: transition, condition: condition) return self.addRoute(route) } - public func addRoute(route: Route) -> Disposable + @discardableResult + public func addRoute(_ route: Route) -> Disposable { let transition = route.transition let condition = route.condition @@ -202,7 +205,7 @@ public final class StateMachine: Machine keyConditionDict[key] = condition self._routes[transition] = keyConditionDict - let _routeID = _RouteID(event: Optional>.None, transition: transition, key: key) + let _routeID = _RouteID(event: Optional>.none, transition: transition, key: key) return ActionDisposable { [weak self] in self?._removeRoute(_routeID) @@ -211,13 +214,15 @@ public final class StateMachine: Machine // MARK: addRoute (no-event) + conditional handler - public func addRoute(transition: Transition, condition: Condition? = nil, handler: Handler) -> Disposable + @discardableResult + public func addRoute(_ transition: Transition, condition: Condition? = nil, handler: @escaping Handler) -> Disposable { let route = Route(transition: transition, condition: condition) return self.addRoute(route, handler: handler) } - public func addRoute(route: Route, handler: Handler) -> Disposable + @discardableResult + public func addRoute(_ route: Route, handler: @escaping Handler) -> Disposable { let transition = route.transition let condition = route.condition @@ -238,7 +243,8 @@ public final class StateMachine: Machine // MARK: removeRoute - private func _removeRoute(_routeID: _RouteID) -> Bool + @discardableResult + private func _removeRoute(_ _routeID: _RouteID) -> Bool { guard _routeID.event == nil else { return false @@ -251,7 +257,7 @@ public final class StateMachine: Machine } var keyConditionDict = keyConditionDict_ - let removed = keyConditionDict.removeValueForKey(_routeID.key) != nil + let removed = keyConditionDict.removeValue(forKey: _routeID.key) != nil if keyConditionDict.isEmpty == false { self._routes[transition] = keyConditionDict @@ -271,7 +277,8 @@ public final class StateMachine: Machine /// Add `handler` that is called when `tryState()` succeeds for target `transition`. /// - Note: `handler` will not be invoked for `tryEvent()`. - public func addHandler(transition: Transition, order: HandlerOrder = _defaultOrder, handler: Handler) -> Disposable + @discardableResult + public func addHandler(_ transition: Transition, order: HandlerOrder = _defaultOrder, handler: @escaping Handler) -> Disposable { if self._handlers[transition] == nil { self._handlers[transition] = [] @@ -294,7 +301,8 @@ public final class StateMachine: Machine // MARK: removeHandler - private func _removeHandler(handlerID: _HandlerID) -> Bool + @discardableResult + private func _removeHandler(_ handlerID: _HandlerID) -> Bool { if let transition = handlerID.transition { if let handlerInfos_ = self._handlers[transition] { @@ -313,12 +321,13 @@ public final class StateMachine: Machine // MARK: addAnyHandler (event-based & state-based) /// Add `handler` that is called when either `tryEvent()` or `tryState()` succeeds for target `transition`. - public func addAnyHandler(transition: Transition, order: HandlerOrder = _defaultOrder, handler: Handler) -> Disposable + @discardableResult + public func addAnyHandler(_ transition: Transition, order: HandlerOrder = _defaultOrder, handler: @escaping Handler) -> Disposable { let disposable1 = self.addHandler(transition, order: order, handler: handler) - let disposable2 = self.addHandler(event: .Any, order: order) { context in - if (transition.fromState == .Any || transition.fromState == context.fromState) && - (transition.toState == .Any || transition.toState == context.toState) + let disposable2 = self.addHandler(event: .any, order: order) { context in + if (transition.fromState == .any || transition.fromState == context.fromState) && + (transition.toState == .any || transition.toState == context.toState) { handler(context) } @@ -341,13 +350,15 @@ public final class StateMachine: Machine // MARK: addRouteChain + conditional handler - public func addRouteChain(chain: TransitionChain, condition: Condition? = nil, handler: Handler) -> Disposable + @discardableResult + public func addRouteChain(_ chain: TransitionChain, condition: Condition? = nil, handler: @escaping Handler) -> Disposable { let routeChain = RouteChain(transitionChain: chain, condition: condition) return self.addRouteChain(routeChain, handler: handler) } - public func addRouteChain(chain: RouteChain, handler: Handler) -> Disposable + @discardableResult + public func addRouteChain(_ chain: RouteChain, handler: @escaping Handler) -> Disposable { let routeDisposables = chain.routes.map { self.addRoute($0) } let handlerDisposable = self.addChainHandler(chain, handler: handler) @@ -360,29 +371,34 @@ public final class StateMachine: Machine // MARK: addChainHandler - public func addChainHandler(chain: TransitionChain, order: HandlerOrder = _defaultOrder, handler: Handler) -> Disposable + @discardableResult + public func addChainHandler(_ chain: TransitionChain, order: HandlerOrder = _defaultOrder, handler: @escaping Handler) -> Disposable { return self.addChainHandler(RouteChain(transitionChain: chain), order: order, handler: handler) } - public func addChainHandler(chain: RouteChain, order: HandlerOrder = _defaultOrder, handler: Handler) -> Disposable + @discardableResult + public func addChainHandler(_ chain: RouteChain, order: HandlerOrder = _defaultOrder, handler: @escaping Handler) -> Disposable { return self._addChainHandler(chain, order: order, handler: handler, isError: false) } // MARK: addChainErrorHandler - public func addChainErrorHandler(chain: TransitionChain, order: HandlerOrder = _defaultOrder, handler: Handler) -> Disposable + @discardableResult + public func addChainErrorHandler(_ chain: TransitionChain, order: HandlerOrder = _defaultOrder, handler: @escaping Handler) -> Disposable { return self.addChainErrorHandler(RouteChain(transitionChain: chain), order: order, handler: handler) } - public func addChainErrorHandler(chain: RouteChain, order: HandlerOrder = _defaultOrder, handler: Handler) -> Disposable + @discardableResult + public func addChainErrorHandler(_ chain: RouteChain, order: HandlerOrder = _defaultOrder, handler: @escaping Handler) -> Disposable { return self._addChainHandler(chain, order: order, handler: handler, isError: true) } - private func _addChainHandler(chain: RouteChain, order: HandlerOrder = _defaultOrder, handler: Handler, isError: Bool) -> Disposable + @discardableResult + private func _addChainHandler(_ chain: RouteChain, order: HandlerOrder = _defaultOrder, handler: @escaping Handler, isError: Bool) -> Disposable { var handlerDisposables: [Disposable] = [] @@ -423,7 +439,7 @@ public final class StateMachine: Machine } // increment allCount (+ invoke chainErrorHandler) on any routes - handlerDisposable = self.addHandler(.Any => .Any, order: 150) { context in + handlerDisposable = self.addHandler(.any => .any, order: 150) { context in shouldIncrementChainingCount = true @@ -466,7 +482,8 @@ public final class StateMachine: Machine // MARK: addStateRouteMapping - public func addStateRouteMapping(routeMapping: StateRouteMapping) -> Disposable + @discardableResult + public func addStateRouteMapping(_ routeMapping: @escaping StateRouteMapping) -> Disposable { let key = _createUniqueString() @@ -481,16 +498,16 @@ public final class StateMachine: Machine // MARK: addStateRouteMapping + conditional handler - public func addStateRouteMapping(routeMapping: StateRouteMapping, handler: Handler) -> Disposable + @discardableResult + public func addStateRouteMapping(_ routeMapping: @escaping StateRouteMapping, handler: @escaping Handler) -> Disposable { let routeDisposable = self.addStateRouteMapping(routeMapping) - let handlerDisposable = self.addHandler(.Any => .Any) { context in + let handlerDisposable = self.addHandler(.any => .any) { context in guard context.event == nil else { return } - guard let preferredToStates = routeMapping(fromState: context.fromState, userInfo: context.userInfo) - where preferredToStates.contains(context.toState) else + guard let preferredToStates = routeMapping(context.fromState, context.userInfo), preferredToStates.contains(context.toState) else { return } @@ -506,7 +523,8 @@ public final class StateMachine: Machine // MARK: removeStateRouteMapping - private func _removeStateRouteMapping(routeMappingID: _RouteMappingID) -> Bool + @discardableResult + private func _removeStateRouteMapping(_ routeMappingID: _RouteMappingID) -> Bool { if self._routeMappings[routeMappingID.key] != nil { self._routeMappings[routeMappingID.key] = nil @@ -525,15 +543,17 @@ public final class StateMachine: Machine // MARK: `<-` (tryState) -infix operator <- { associativity left } +infix operator <- : AdditionPrecedence -public func <- (machine: StateMachine, state: S) -> StateMachine +@discardableResult +public func <- (machine: StateMachine, state: S) -> StateMachine { machine.tryState(state) return machine } -public func <- (machine: StateMachine, tuple: (S, Any?)) -> StateMachine +@discardableResult +public func <- (machine: StateMachine, tuple: (S, Any?)) -> StateMachine { machine.tryState(tuple.0, userInfo: tuple.1) return machine diff --git a/Sources/StateType.swift b/Sources/StateType.swift index 5fb0df9..bf5e278 100644 --- a/Sources/StateType.swift +++ b/Sources/StateType.swift @@ -10,11 +10,11 @@ public protocol StateType: Hashable {} // MARK: State -/// `StateType` wrapper for handling `.Any` state. +/// `StateType` wrapper for handling `.any` state. public enum State { - case Some(S) - case Any + case some(S) + case any } extension State: Hashable @@ -22,8 +22,8 @@ extension State: Hashable public var hashValue: Int { switch self { - case .Some(let x): return x.hashValue - case .Any: return _hashValueForAny + case .some(let x): return x.hashValue + case .any: return _hashValueForAny } } } @@ -33,50 +33,50 @@ extension State: RawRepresentable public init(rawValue: S?) { if let rawValue = rawValue { - self = .Some(rawValue) + self = .some(rawValue) } else { - self = .Any + self = .any } } public var rawValue: S? { switch self { - case .Some(let x): return x + case .some(let x): return x default: return nil } } } -public func == (lhs: State, rhs: State) -> Bool +public func == (lhs: State, rhs: State) -> Bool { switch (lhs, rhs) { - case let (.Some(x1), .Some(x2)) where x1 == x2: + case let (.some(x1), .some(x2)) where x1 == x2: return true - case (.Any, .Any): + case (.any, .any): return true default: return false } } -public func == (lhs: State, rhs: S) -> Bool +public func == (lhs: State, rhs: S) -> Bool { switch lhs { - case .Some(let x): + case .some(let x): return x == rhs - case .Any: + case .any: return false } } -public func == (lhs: S, rhs: State) -> Bool +public func == (lhs: S, rhs: State) -> Bool { switch rhs { - case .Some(let x): + case .some(let x): return x == lhs - case .Any: + case .any: return false } } diff --git a/Sources/Transition.swift b/Sources/Transition.swift index 9e2a644..445b183 100644 --- a/Sources/Transition.swift +++ b/Sources/Transition.swift @@ -7,8 +7,8 @@ // /// -/// "From-" and "to-" states represented as `.State1 => .State2`. -/// Also, `.Any` can be used to represent _any state_. +/// "From-" and "to-" states represented as `.state1 => .state2`. +/// Also, `.any` can be used to represent _any state_. /// public struct Transition: Hashable { @@ -23,27 +23,28 @@ public struct Transition: Hashable public init(fromState: S, toState: State) { - self.init(fromState: .Some(fromState), toState: toState) + self.init(fromState: .some(fromState), toState: toState) } public init(fromState: State, toState: S) { - self.init(fromState: fromState, toState: .Some(toState)) + self.init(fromState: fromState, toState: .some(toState)) } public init(fromState: S, toState: S) { - self.init(fromState: .Some(fromState), toState: .Some(toState)) + self.init(fromState: .some(fromState), toState: .some(toState)) } - public var hashValue: Int + public func hash(into hasher: inout Hasher) { - return self.fromState.hashValue &+ self.toState.hashValue.byteSwapped + hasher.combine(fromState.hashValue) + hasher.combine(toState.hashValue.byteSwapped) } } // for Transition Equatable -public func == (left: Transition, right: Transition) -> Bool +public func == (left: Transition, right: Transition) -> Bool { return left.fromState == right.fromState && left.toState == right.toState } @@ -52,27 +53,27 @@ public func == (left: Transition, right: Transition) -> Bool // MARK: - Custom Operators //-------------------------------------------------- -infix operator => { associativity left } +infix operator => : AdditionPrecedence -/// e.g. .State0 => .State1 -public func => (left: State, right: State) -> Transition +/// e.g. .state0 => .state1 +public func => (left: State, right: State) -> Transition { return Transition(fromState: left, toState: right) } -public func => (left: State, right: S) -> Transition +public func => (left: State, right: S) -> Transition { - return left => .Some(right) + return left => .some(right) } -public func => (left: S, right: State) -> Transition +public func => (left: S, right: State) -> Transition { - return .Some(left) => right + return .some(left) => right } -public func => (left: S, right: S) -> Transition +public func => (left: S, right: S) -> Transition { - return .Some(left) => .Some(right) + return .some(left) => .some(right) } //-------------------------------------------------- diff --git a/Sources/TransitionChain.swift b/Sources/TransitionChain.swift index b2172a6..c0fa93e 100644 --- a/Sources/TransitionChain.swift +++ b/Sources/TransitionChain.swift @@ -6,7 +6,7 @@ // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. // -/// Group of continuous `Transition`s represented as `.State1 => .State2 => .State3`. +/// Group of continuous `Transition`s represented as `.state1 => .state2 => .state3`. public struct TransitionChain { public private(set) var states: [State] @@ -37,44 +37,44 @@ public struct TransitionChain // MARK: - Custom Operators //-------------------------------------------------- -// e.g. (.State0 => .State1) => .State -public func => (left: Transition, right: State) -> TransitionChain +// e.g. (.state0 => .state1) => .state +public func => (left: Transition, right: State) -> TransitionChain { return TransitionChain(states: [left.fromState, left.toState]) => right } -public func => (left: Transition, right: S) -> TransitionChain +public func => (left: Transition, right: S) -> TransitionChain { - return left => .Some(right) + return left => .some(right) } -public func => (left: TransitionChain, right: State) -> TransitionChain +public func => (left: TransitionChain, right: State) -> TransitionChain { return TransitionChain(states: left.states + [right]) } -public func => (left: TransitionChain, right: S) -> TransitionChain +public func => (left: TransitionChain, right: S) -> TransitionChain { - return left => .Some(right) + return left => .some(right) } -// e.g. .State0 => (.State1 => .State) -public func => (left: State, right: Transition) -> TransitionChain +// e.g. .state0 => (.state1 => .state) +public func => (left: State, right: Transition) -> TransitionChain { return left => TransitionChain(states: [right.fromState, right.toState]) } -public func => (left: S, right: Transition) -> TransitionChain +public func => (left: S, right: Transition) -> TransitionChain { - return .Some(left) => right + return .some(left) => right } -public func => (left: State, right: TransitionChain) -> TransitionChain +public func => (left: State, right: TransitionChain) -> TransitionChain { return TransitionChain(states: [left] + right.states) } -public func => (left: S, right: TransitionChain) -> TransitionChain +public func => (left: S, right: TransitionChain) -> TransitionChain { - return .Some(left) => right + return .some(left) => right } diff --git a/Sources/_HandlerInfo.swift b/Sources/_HandlerInfo.swift index 706b867..530a02b 100644 --- a/Sources/_HandlerInfo.swift +++ b/Sources/_HandlerInfo.swift @@ -12,7 +12,7 @@ internal final class _HandlerInfo internal let key: String internal let handler: Machine.Handler - internal init(order: HandlerOrder, key: String, handler: Machine.Handler) + internal init(order: HandlerOrder, key: String, handler: @escaping Machine.Handler) { self.order = order self.key = key diff --git a/Sources/_Random.swift b/Sources/_Random.swift index cf38838..cf79e73 100644 --- a/Sources/_Random.swift +++ b/Sources/_Random.swift @@ -12,9 +12,9 @@ import Darwin import Glibc #endif -internal func _random(upperBound: Int) -> Int +internal func _random(_ upperBound: Int) -> Int { - #if os(OSX) || os(iOS) + #if os(OSX) || os(iOS) || os(watchOS) || os(tvOS) return Int(arc4random_uniform(UInt32(upperBound))) #else return Int(random() % upperBound) diff --git a/SwiftState.podspec b/SwiftState.podspec index cb2cbdd..5443b3b 100644 --- a/SwiftState.podspec +++ b/SwiftState.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'SwiftState' - s.version = '4.1.0' + s.version = '6.0.0' s.license = { :type => 'MIT' } s.homepage = 'https://github.com/ReactKit/SwiftState' s.authors = { 'Yasuhiro Inami' => 'inamiy@gmail.com' } diff --git a/SwiftState.xcodeproj/project.pbxproj b/SwiftState.xcodeproj/project.pbxproj index 8f67de5..a8453b6 100644 --- a/SwiftState.xcodeproj/project.pbxproj +++ b/SwiftState.xcodeproj/project.pbxproj @@ -7,19 +7,12 @@ objects = { /* Begin PBXBuildFile section */ - 1F198C5C19972320001C3700 /* QiitaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F198C5B19972320001C3700 /* QiitaTests.swift */; }; - 1F1F74C31C0A02EA00675EAA /* HierarchicalMachineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F1F74C11C0A02E700675EAA /* HierarchicalMachineTests.swift */; }; - 1F27771E1BE68D1D00C57CC9 /* RouteMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F27771C1BE68C7F00C57CC9 /* RouteMappingTests.swift */; }; - 1F4336AD1C17113F00E7C1AC /* StateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4336AC1C17113F00E7C1AC /* StateTests.swift */; }; - 1F4336B01C17115700E7C1AC /* EventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4336AF1C17115700E7C1AC /* EventTests.swift */; }; 1F532C541C12B5BC00D3813A /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F532C531C12B5BC00D3813A /* Disposable.swift */; }; 1F532C571C12BBBB00D3813A /* _HandlerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F532C561C12BBBB00D3813A /* _HandlerInfo.swift */; }; - 1F532C611C12D7BD00D3813A /* MiscTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F532C601C12D7BD00D3813A /* MiscTests.swift */; }; 1F532C641C12EA8000D3813A /* _Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F532C631C12EA8000D3813A /* _Random.swift */; }; 1F70FB661BF0F46000E5AC8C /* _RouteID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F70FB651BF0F46000E5AC8C /* _RouteID.swift */; }; 1F70FB6C1BF0F47700E5AC8C /* _RouteMappingID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F70FB6B1BF0F47700E5AC8C /* _RouteMappingID.swift */; }; 1F70FB6F1BF0F59600E5AC8C /* _HandlerID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F70FB6E1BF0F59600E5AC8C /* _HandlerID.swift */; }; - 1F70FB791BF0FB7000E5AC8C /* String+TestExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F70FB761BF0FB2400E5AC8C /* String+TestExt.swift */; }; 1FA620061996601000460108 /* SwiftState.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FA620051996601000460108 /* SwiftState.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1FA620201996606300460108 /* EventType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA620191996606200460108 /* EventType.swift */; }; 1FA620221996606300460108 /* Route.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA6201B1996606300460108 /* Route.swift */; }; @@ -27,20 +20,27 @@ 1FA620241996606300460108 /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA6201D1996606300460108 /* Transition.swift */; }; 1FA620251996606300460108 /* TransitionChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA6201E1996606300460108 /* TransitionChain.swift */; }; 1FA620261996606300460108 /* StateType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA6201F1996606300460108 /* StateType.swift */; }; - 1FA62030199660CA00460108 /* _TestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA62027199660CA00460108 /* _TestCase.swift */; }; - 1FA62031199660CA00460108 /* BasicTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA62028199660CA00460108 /* BasicTests.swift */; }; - 1FA62032199660CA00460108 /* MyState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA62029199660CA00460108 /* MyState.swift */; }; - 1FA62033199660CA00460108 /* RouteChainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA6202A199660CA00460108 /* RouteChainTests.swift */; }; - 1FA62034199660CA00460108 /* StateMachineEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA6202B199660CA00460108 /* StateMachineEventTests.swift */; }; - 1FA62035199660CA00460108 /* StateMachineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA6202C199660CA00460108 /* StateMachineTests.swift */; }; - 1FA62036199660CA00460108 /* RouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA6202D199660CA00460108 /* RouteTests.swift */; }; - 1FA62037199660CA00460108 /* TransitionChainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA6202E199660CA00460108 /* TransitionChainTests.swift */; }; - 1FA62038199660CA00460108 /* TransitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA6202F199660CA00460108 /* TransitionTests.swift */; }; - 1FB1EC8A199E515B00ABD937 /* MyEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB1EC89199E515B00ABD937 /* MyEvent.swift */; }; 1FF692041996625900E3CE40 /* SwiftState.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FA620001996601000460108 /* SwiftState.framework */; }; 4836FF5A1C0EFD700038B7D2 /* StateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4836FF591C0EFD700038B7D2 /* StateMachine.swift */; }; 483F35571C0EB192007C70D7 /* Machine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 483F35561C0EB192007C70D7 /* Machine.swift */; }; - 4876510F1C0ECBEB005961AC /* MachineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4876510E1C0ECBEB005961AC /* MachineTests.swift */; }; + 5243E3DC1E9821E900D031A9 /* _TestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3B61E9821E200D031A9 /* _TestCase.swift */; }; + 5243E3DD1E9821E900D031A9 /* BasicTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3B71E9821E200D031A9 /* BasicTests.swift */; }; + 5243E3DE1E9821E900D031A9 /* EventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3B81E9821E200D031A9 /* EventTests.swift */; }; + 5243E3DF1E9821E900D031A9 /* HierarchicalMachineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3B91E9821E200D031A9 /* HierarchicalMachineTests.swift */; }; + 5243E3E11E9821E900D031A9 /* MachineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3BB1E9821E200D031A9 /* MachineTests.swift */; }; + 5243E3E21E9821E900D031A9 /* MiscTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3BC1E9821E200D031A9 /* MiscTests.swift */; }; + 5243E3E31E9821E900D031A9 /* MyEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3BD1E9821E200D031A9 /* MyEvent.swift */; }; + 5243E3E41E9821E900D031A9 /* MyState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3BE1E9821E200D031A9 /* MyState.swift */; }; + 5243E3E51E9821E900D031A9 /* QiitaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3BF1E9821E200D031A9 /* QiitaTests.swift */; }; + 5243E3E61E9821E900D031A9 /* RouteChainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3C01E9821E200D031A9 /* RouteChainTests.swift */; }; + 5243E3E71E9821E900D031A9 /* RouteMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3C11E9821E200D031A9 /* RouteMappingTests.swift */; }; + 5243E3E81E9821E900D031A9 /* RouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3C21E9821E200D031A9 /* RouteTests.swift */; }; + 5243E3E91E9821E900D031A9 /* StateMachineEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3C31E9821E200D031A9 /* StateMachineEventTests.swift */; }; + 5243E3EA1E9821E900D031A9 /* StateMachineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3C41E9821E200D031A9 /* StateMachineTests.swift */; }; + 5243E3EB1E9821E900D031A9 /* StateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3C51E9821E200D031A9 /* StateTests.swift */; }; + 5243E3EC1E9821E900D031A9 /* String+TestExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3C61E9821E200D031A9 /* String+TestExt.swift */; }; + 5243E3ED1E9821E900D031A9 /* TransitionChainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3C71E9821E200D031A9 /* TransitionChainTests.swift */; }; + 5243E3EE1E9821E900D031A9 /* TransitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5243E3C81E9821E200D031A9 /* TransitionTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -54,39 +54,21 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 1F198C5B19972320001C3700 /* QiitaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaTests.swift; sourceTree = ""; }; - 1F1F74C11C0A02E700675EAA /* HierarchicalMachineTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HierarchicalMachineTests.swift; sourceTree = ""; }; - 1F27771C1BE68C7F00C57CC9 /* RouteMappingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteMappingTests.swift; sourceTree = ""; }; - 1F4336AC1C17113F00E7C1AC /* StateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateTests.swift; sourceTree = ""; }; - 1F4336AF1C17115700E7C1AC /* EventTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventTests.swift; sourceTree = ""; }; 1F532C531C12B5BC00D3813A /* Disposable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Disposable.swift; sourceTree = ""; }; 1F532C561C12BBBB00D3813A /* _HandlerInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _HandlerInfo.swift; sourceTree = ""; }; - 1F532C601C12D7BD00D3813A /* MiscTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MiscTests.swift; sourceTree = ""; }; 1F532C631C12EA8000D3813A /* _Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _Random.swift; sourceTree = ""; }; 1F70FB651BF0F46000E5AC8C /* _RouteID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _RouteID.swift; sourceTree = ""; }; 1F70FB6B1BF0F47700E5AC8C /* _RouteMappingID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _RouteMappingID.swift; sourceTree = ""; }; 1F70FB6E1BF0F59600E5AC8C /* _HandlerID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _HandlerID.swift; sourceTree = ""; }; - 1F70FB761BF0FB2400E5AC8C /* String+TestExt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+TestExt.swift"; sourceTree = ""; }; 1FA620001996601000460108 /* SwiftState.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftState.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1FA620051996601000460108 /* SwiftState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftState.h; sourceTree = ""; }; 1FA6200B1996601000460108 /* SwiftStateTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftStateTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 1FA6200E1996601000460108 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1FA620191996606200460108 /* EventType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventType.swift; sourceTree = ""; }; 1FA6201B1996606300460108 /* Route.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Route.swift; sourceTree = ""; }; 1FA6201C1996606300460108 /* RouteChain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteChain.swift; sourceTree = ""; }; 1FA6201D1996606300460108 /* Transition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Transition.swift; sourceTree = ""; }; 1FA6201E1996606300460108 /* TransitionChain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionChain.swift; sourceTree = ""; }; 1FA6201F1996606300460108 /* StateType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateType.swift; sourceTree = ""; }; - 1FA62027199660CA00460108 /* _TestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _TestCase.swift; sourceTree = ""; }; - 1FA62028199660CA00460108 /* BasicTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicTests.swift; sourceTree = ""; }; - 1FA62029199660CA00460108 /* MyState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyState.swift; sourceTree = ""; }; - 1FA6202A199660CA00460108 /* RouteChainTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteChainTests.swift; sourceTree = ""; }; - 1FA6202B199660CA00460108 /* StateMachineEventTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateMachineEventTests.swift; sourceTree = ""; }; - 1FA6202C199660CA00460108 /* StateMachineTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateMachineTests.swift; sourceTree = ""; }; - 1FA6202D199660CA00460108 /* RouteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteTests.swift; sourceTree = ""; }; - 1FA6202E199660CA00460108 /* TransitionChainTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionChainTests.swift; sourceTree = ""; }; - 1FA6202F199660CA00460108 /* TransitionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionTests.swift; sourceTree = ""; }; - 1FB1EC89199E515B00ABD937 /* MyEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyEvent.swift; sourceTree = ""; }; 1FD79F961C1736D600CE7060 /* UniversalFramework_Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = UniversalFramework_Base.xcconfig; sourceTree = ""; }; 1FD79F971C1736D600CE7060 /* UniversalFramework_Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = UniversalFramework_Framework.xcconfig; sourceTree = ""; }; 1FD79F981C1736D600CE7060 /* UniversalFramework_Test.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = UniversalFramework_Test.xcconfig; sourceTree = ""; }; @@ -95,7 +77,25 @@ 1FD79FA61C17412600CE7060 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 4836FF591C0EFD700038B7D2 /* StateMachine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateMachine.swift; sourceTree = ""; }; 483F35561C0EB192007C70D7 /* Machine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Machine.swift; sourceTree = ""; }; - 4876510E1C0ECBEB005961AC /* MachineTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachineTests.swift; sourceTree = ""; }; + 5243E3B61E9821E200D031A9 /* _TestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _TestCase.swift; sourceTree = ""; }; + 5243E3B71E9821E200D031A9 /* BasicTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicTests.swift; sourceTree = ""; }; + 5243E3B81E9821E200D031A9 /* EventTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventTests.swift; sourceTree = ""; }; + 5243E3B91E9821E200D031A9 /* HierarchicalMachineTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HierarchicalMachineTests.swift; sourceTree = ""; }; + 5243E3BA1E9821E200D031A9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5243E3BB1E9821E200D031A9 /* MachineTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachineTests.swift; sourceTree = ""; }; + 5243E3BC1E9821E200D031A9 /* MiscTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MiscTests.swift; sourceTree = ""; }; + 5243E3BD1E9821E200D031A9 /* MyEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyEvent.swift; sourceTree = ""; }; + 5243E3BE1E9821E200D031A9 /* MyState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyState.swift; sourceTree = ""; }; + 5243E3BF1E9821E200D031A9 /* QiitaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QiitaTests.swift; sourceTree = ""; }; + 5243E3C01E9821E200D031A9 /* RouteChainTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteChainTests.swift; sourceTree = ""; }; + 5243E3C11E9821E200D031A9 /* RouteMappingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteMappingTests.swift; sourceTree = ""; }; + 5243E3C21E9821E200D031A9 /* RouteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteTests.swift; sourceTree = ""; }; + 5243E3C31E9821E200D031A9 /* StateMachineEventTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateMachineEventTests.swift; sourceTree = ""; }; + 5243E3C41E9821E200D031A9 /* StateMachineTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateMachineTests.swift; sourceTree = ""; }; + 5243E3C51E9821E200D031A9 /* StateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateTests.swift; sourceTree = ""; }; + 5243E3C61E9821E200D031A9 /* String+TestExt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+TestExt.swift"; sourceTree = ""; }; + 5243E3C71E9821E200D031A9 /* TransitionChainTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionChainTests.swift; sourceTree = ""; }; + 5243E3C81E9821E200D031A9 /* TransitionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitionTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -143,9 +143,9 @@ 1FA61FF61996601000460108 = { isa = PBXGroup; children = ( + 5243E3B51E9821E200D031A9 /* SwiftStateTests */, 1FD79F9E1C173C6C00CE7060 /* Configurations */, 1FA620021996601000460108 /* Sources */, - 1FA6200C1996601000460108 /* Tests */, 1FA620011996601000460108 /* Products */, ); sourceTree = ""; @@ -173,38 +173,6 @@ path = Sources; sourceTree = ""; }; - 1FA6200C1996601000460108 /* Tests */ = { - isa = PBXGroup; - children = ( - 1FA62027199660CA00460108 /* _TestCase.swift */, - 1FB1EC8D199E609900ABD937 /* State & Event */, - 1FA62028199660CA00460108 /* BasicTests.swift */, - 1F532C601C12D7BD00D3813A /* MiscTests.swift */, - 1F198C5B19972320001C3700 /* QiitaTests.swift */, - 4876510E1C0ECBEB005961AC /* MachineTests.swift */, - 1FA6202C199660CA00460108 /* StateMachineTests.swift */, - 1FA6202B199660CA00460108 /* StateMachineEventTests.swift */, - 1F4336AC1C17113F00E7C1AC /* StateTests.swift */, - 1F4336AF1C17115700E7C1AC /* EventTests.swift */, - 1FA6202F199660CA00460108 /* TransitionTests.swift */, - 1FA6202E199660CA00460108 /* TransitionChainTests.swift */, - 1FA6202D199660CA00460108 /* RouteTests.swift */, - 1FA6202A199660CA00460108 /* RouteChainTests.swift */, - 1F27771C1BE68C7F00C57CC9 /* RouteMappingTests.swift */, - 1F1F74C11C0A02E700675EAA /* HierarchicalMachineTests.swift */, - 1FA6200D1996601000460108 /* Supporting Files */, - ); - path = Tests; - sourceTree = ""; - }; - 1FA6200D1996601000460108 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 1FA6200E1996601000460108 /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; 1FB1EC88199E4ABC00ABD937 /* Protocols */ = { isa = PBXGroup; children = ( @@ -214,16 +182,6 @@ name = Protocols; sourceTree = ""; }; - 1FB1EC8D199E609900ABD937 /* State & Event */ = { - isa = PBXGroup; - children = ( - 1FA62029199660CA00460108 /* MyState.swift */, - 1FB1EC89199E515B00ABD937 /* MyEvent.swift */, - 1F70FB761BF0FB2400E5AC8C /* String+TestExt.swift */, - ); - name = "State & Event"; - sourceTree = ""; - }; 1FD79F931C1736D600CE7060 /* mrackwitz/xcconfigs (for target) */ = { isa = PBXGroup; children = ( @@ -255,6 +213,33 @@ path = Configurations; sourceTree = ""; }; + 5243E3B51E9821E200D031A9 /* SwiftStateTests */ = { + isa = PBXGroup; + children = ( + 5243E3B61E9821E200D031A9 /* _TestCase.swift */, + 5243E3B71E9821E200D031A9 /* BasicTests.swift */, + 5243E3B81E9821E200D031A9 /* EventTests.swift */, + 5243E3B91E9821E200D031A9 /* HierarchicalMachineTests.swift */, + 5243E3BA1E9821E200D031A9 /* Info.plist */, + 5243E3BB1E9821E200D031A9 /* MachineTests.swift */, + 5243E3BC1E9821E200D031A9 /* MiscTests.swift */, + 5243E3BD1E9821E200D031A9 /* MyEvent.swift */, + 5243E3BE1E9821E200D031A9 /* MyState.swift */, + 5243E3BF1E9821E200D031A9 /* QiitaTests.swift */, + 5243E3C01E9821E200D031A9 /* RouteChainTests.swift */, + 5243E3C11E9821E200D031A9 /* RouteMappingTests.swift */, + 5243E3C21E9821E200D031A9 /* RouteTests.swift */, + 5243E3C31E9821E200D031A9 /* StateMachineEventTests.swift */, + 5243E3C41E9821E200D031A9 /* StateMachineTests.swift */, + 5243E3C51E9821E200D031A9 /* StateTests.swift */, + 5243E3C61E9821E200D031A9 /* String+TestExt.swift */, + 5243E3C71E9821E200D031A9 /* TransitionChainTests.swift */, + 5243E3C81E9821E200D031A9 /* TransitionTests.swift */, + ); + name = SwiftStateTests; + path = Tests/SwiftStateTests; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -312,25 +297,26 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "Yasuhiro Inami"; TargetAttributes = { 1FA61FFF1996601000460108 = { CreatedOnToolsVersion = 6.0; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; }; 1FA6200A1996601000460108 = { CreatedOnToolsVersion = 6.0; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; }; }; }; buildConfigurationList = 1FA61FFA1996601000460108 /* Build configuration list for PBXProject "SwiftState" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 1FA61FF61996601000460108; productRefGroup = 1FA620011996601000460108 /* Products */; @@ -386,24 +372,24 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1FB1EC8A199E515B00ABD937 /* MyEvent.swift in Sources */, - 1F198C5C19972320001C3700 /* QiitaTests.swift in Sources */, - 1F70FB791BF0FB7000E5AC8C /* String+TestExt.swift in Sources */, - 4876510F1C0ECBEB005961AC /* MachineTests.swift in Sources */, - 1FA62038199660CA00460108 /* TransitionTests.swift in Sources */, - 1FA62033199660CA00460108 /* RouteChainTests.swift in Sources */, - 1FA62030199660CA00460108 /* _TestCase.swift in Sources */, - 1F1F74C31C0A02EA00675EAA /* HierarchicalMachineTests.swift in Sources */, - 1F4336AD1C17113F00E7C1AC /* StateTests.swift in Sources */, - 1FA62036199660CA00460108 /* RouteTests.swift in Sources */, - 1F532C611C12D7BD00D3813A /* MiscTests.swift in Sources */, - 1FA62031199660CA00460108 /* BasicTests.swift in Sources */, - 1FA62034199660CA00460108 /* StateMachineEventTests.swift in Sources */, - 1FA62035199660CA00460108 /* StateMachineTests.swift in Sources */, - 1FA62037199660CA00460108 /* TransitionChainTests.swift in Sources */, - 1F27771E1BE68D1D00C57CC9 /* RouteMappingTests.swift in Sources */, - 1F4336B01C17115700E7C1AC /* EventTests.swift in Sources */, - 1FA62032199660CA00460108 /* MyState.swift in Sources */, + 5243E3DF1E9821E900D031A9 /* HierarchicalMachineTests.swift in Sources */, + 5243E3E61E9821E900D031A9 /* RouteChainTests.swift in Sources */, + 5243E3EC1E9821E900D031A9 /* String+TestExt.swift in Sources */, + 5243E3E21E9821E900D031A9 /* MiscTests.swift in Sources */, + 5243E3EE1E9821E900D031A9 /* TransitionTests.swift in Sources */, + 5243E3DD1E9821E900D031A9 /* BasicTests.swift in Sources */, + 5243E3E31E9821E900D031A9 /* MyEvent.swift in Sources */, + 5243E3EA1E9821E900D031A9 /* StateMachineTests.swift in Sources */, + 5243E3E91E9821E900D031A9 /* StateMachineEventTests.swift in Sources */, + 5243E3E81E9821E900D031A9 /* RouteTests.swift in Sources */, + 5243E3DE1E9821E900D031A9 /* EventTests.swift in Sources */, + 5243E3DC1E9821E900D031A9 /* _TestCase.swift in Sources */, + 5243E3E11E9821E900D031A9 /* MachineTests.swift in Sources */, + 5243E3E51E9821E900D031A9 /* QiitaTests.swift in Sources */, + 5243E3EB1E9821E900D031A9 /* StateTests.swift in Sources */, + 5243E3E71E9821E900D031A9 /* RouteMappingTests.swift in Sources */, + 5243E3ED1E9821E900D031A9 /* TransitionChainTests.swift in Sources */, + 5243E3E41E9821E900D031A9 /* MyState.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -423,17 +409,28 @@ baseConfigurationReference = 1FD79FA51C17412600CE7060 /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -443,6 +440,7 @@ ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -468,17 +466,28 @@ baseConfigurationReference = 1FD79FA61C17412600CE7060 /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -488,6 +497,7 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; @@ -495,6 +505,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MTL_ENABLE_DEBUG_INFO = NO; + SWIFT_COMPILATION_MODE = wholemodule; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -504,6 +515,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 1FD79F971C1736D600CE7060 /* UniversalFramework_Framework.xcconfig */; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; @@ -516,7 +528,6 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.inamiy.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 2.3; }; name = Debug; }; @@ -524,6 +535,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 1FD79F971C1736D600CE7060 /* UniversalFramework_Framework.xcconfig */; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; @@ -536,7 +548,6 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.inamiy.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 2.3; }; name = Release; }; @@ -553,10 +564,9 @@ "DEBUG=1", "$(inherited)", ); - INFOPLIST_FILE = Tests/Info.plist; + INFOPLIST_FILE = Tests/SwiftStateTests/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = "com.inamiy.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; }; name = Debug; }; @@ -569,10 +579,9 @@ "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", ); - INFOPLIST_FILE = Tests/Info.plist; + INFOPLIST_FILE = Tests/SwiftStateTests/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = "com.inamiy.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; }; name = Release; }; diff --git a/SwiftState.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftState.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/SwiftState.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftState.xcodeproj/xcshareddata/xcschemes/SwiftState.xcscheme b/SwiftState.xcodeproj/xcshareddata/xcschemes/SwiftState.xcscheme index 87e399e..49e1b54 100644 --- a/SwiftState.xcodeproj/xcshareddata/xcschemes/SwiftState.xcscheme +++ b/SwiftState.xcodeproj/xcshareddata/xcschemes/SwiftState.xcscheme @@ -1,6 +1,6 @@ + codeCoverageEnabled = "YES" + shouldUseLaunchSchemeArgsEnv = "YES"> diff --git a/Tests/BasicTests.swift b/Tests/BasicTests.swift deleted file mode 100644 index 75b2941..0000000 --- a/Tests/BasicTests.swift +++ /dev/null @@ -1,133 +0,0 @@ -// -// BasicTests.swift -// SwiftState -// -// Created by Yasuhiro Inami on 2014/08/08. -// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. -// - -import SwiftState -import XCTest - -class BasicTests: _TestCase -{ - func testREADME() - { - // setup state machine - let machine = StateMachine(state: .State0) { machine in - - machine.addRoute(.State0 => .State1) - machine.addRoute(.Any => .State2) { context in print("Any => 2, msg=\(context.userInfo)") } - machine.addRoute(.State2 => .Any) { context in print("2 => Any, msg=\(context.userInfo)") } - - // add handler (`context = (event, fromState, toState, userInfo)`) - machine.addHandler(.State0 => .State1) { context in - print("0 => 1") - } - - // add errorHandler - machine.addErrorHandler { event, fromState, toState, userInfo in - print("[ERROR] \(fromState) => \(toState)") - } - } - - // initial - XCTAssertEqual(machine.state, MyState.State0) - - // tryState 0 => 1 => 2 => 1 => 0 - - machine <- .State1 - XCTAssertEqual(machine.state, MyState.State1) - - machine <- (.State2, "Hello") - XCTAssertEqual(machine.state, MyState.State2) - - machine <- (.State1, "Bye") - XCTAssertEqual(machine.state, MyState.State1) - - machine <- .State0 // fail: no 1 => 0 - XCTAssertEqual(machine.state, MyState.State1) - } - - func testREADME_tryEvent() - { - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - // add event handler - machine.addHandler(event: .Event0) { context in - print(".Event0 triggered!") - } - } - - // initial - XCTAssertEqual(machine.state, MyState.State0) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2) - - // tryEvent (fails) - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2, "Event0 doesn't have 2 => Any") - } - - func testREADME_routeMapping() - { - let machine = Machine(state: .Str("initial")) { machine in - - machine.addRouteMapping { event, fromState, userInfo -> StrState? in - // no route for no-event - guard let event = event else { return nil } - - switch (event, fromState) { - case (.Str("gogogo"), .Str("initial")): - return .Str("Phase 1") - case (.Str("gogogo"), .Str("Phase 1")): - return .Str("Phase 2") - case (.Str("finish"), .Str("Phase 2")): - return .Str("end") - default: - return nil - } - } - - } - - // initial - XCTAssertEqual(machine.state, StrState.Str("initial")) - - // tryEvent (fails) - machine <-! .Str("go?") - XCTAssertEqual(machine.state, StrState.Str("initial"), "No change.") - - // tryEvent - machine <-! .Str("gogogo") - XCTAssertEqual(machine.state, StrState.Str("Phase 1")) - - // tryEvent (fails) - machine <-! .Str("finish") - XCTAssertEqual(machine.state, StrState.Str("Phase 1"), "No change.") - - // tryEvent - machine <-! .Str("gogogo") - XCTAssertEqual(machine.state, StrState.Str("Phase 2")) - - // tryEvent (fails) - machine <-! .Str("gogogo") - XCTAssertEqual(machine.state, StrState.Str("Phase 2"), "No change.") - - // tryEvent - machine <-! .Str("finish") - XCTAssertEqual(machine.state, StrState.Str("end")) - } -} diff --git a/Tests/HierarchicalMachineTests.swift b/Tests/HierarchicalMachineTests.swift deleted file mode 100644 index 7ab399c..0000000 --- a/Tests/HierarchicalMachineTests.swift +++ /dev/null @@ -1,285 +0,0 @@ -// -// HierarchicalMachineTests.swift -// SwiftState -// -// Created by Yasuhiro Inami on 2015-11-29. -// Copyright © 2015 Yasuhiro Inami. All rights reserved. -// - -import SwiftState -import XCTest - -private enum _MainState: StateType -{ - case MainState0 - case SubMachine1(_SubState) - case SubMachine2(_SubState) - - var hashValue: Int - { - switch self { - case .MainState0: - return "MainState0".hashValue - case let .SubMachine1(state): - return "SubMachine1-\(state)".hashValue - case let .SubMachine2(state): - return "SubMachine2-\(state)".hashValue - } - } -} - -private enum _SubState: StateType -{ - case SubState0, SubState1, SubState2 -} - -private func == (lhs: _MainState, rhs: _MainState) -> Bool -{ - switch (lhs, rhs) { - case (.MainState0, .MainState0): - return true - case let (.SubMachine1(state1), .SubMachine1(state2)): - return state1 == state2 - case let (.SubMachine2(state1), .SubMachine2(state2)): - return state1 == state2 - default: - return false - } -} - -class HierarchicalMachineTests: _TestCase -{ - /// - /// Hierarchical state machine. - /// - /// - mainMachine - /// - MainState0 (initial) - /// - subMachine1 - /// - SubState0 - /// - SubState1 - /// - subMachine2 - /// - SubState0 - /// - SubState1 - /// - /// - Warning: - /// This is a naive implementation and easily lose consistency when `subMachine.state` is changed directly, e.g. `subMachine1 <- .SubState1`. - /// - private var mainMachine: StateMachine<_MainState, NoEvent>? - - private var subMachine1: StateMachine<_SubState, NoEvent>? - private var subMachine2: StateMachine<_SubState, NoEvent>? - - override func setUp() - { - super.setUp() - - let subMachine1 = StateMachine<_SubState, NoEvent>(state: .SubState0) { subMachine1 in - // add Sub1-0 => Sub1-1 - subMachine1.addRoute(.SubState0 => .SubState1) - - subMachine1.addHandler(.Any => .Any) { print("[Sub1] \($0.fromState) => \($0.toState)") } - subMachine1.addErrorHandler { print("[ERROR][Sub1] \($0.fromState) => \($0.toState)") } - } - - let subMachine2 = StateMachine<_SubState, NoEvent>(state: .SubState0) { subMachine2 in - // add Sub2-0 => Sub2-1 - subMachine2.addRoute(.SubState0 => .SubState1) - - subMachine2.addHandler(.Any => .Any) { print("[Sub2] \($0.fromState) => \($0.toState)") } - subMachine2.addErrorHandler { print("[ERROR][Sub2] \($0.fromState) => \($0.toState)") } - } - - let mainMachine = StateMachine<_MainState, NoEvent>(state: .MainState0) { mainMachine in - - // add routes & handle for same-subMachine internal transitions - mainMachine.addRoute(.Any => .Any, condition: { _, fromState, toState, userInfo in - - switch (fromState, toState) { - case let (.SubMachine1(state1), .SubMachine1(state2)): - return subMachine1.hasRoute(fromState: state1, toState: state2) - case let (.SubMachine2(state1), .SubMachine2(state2)): - return subMachine2.hasRoute(fromState: state1, toState: state2) - default: - return false - } - - }, handler: { _, fromState, toState, userInfo in - switch (fromState, toState) { - case let (.SubMachine1, .SubMachine1(state2)): - subMachine1 <- state2 - case let (.SubMachine2, .SubMachine2(state2)): - subMachine2 <- state2 - default: - break - } - }) - - // add routes for mainMachine-state transitions (submachine switching) - mainMachine.addRouteMapping { event, fromState, userInfo -> _MainState? in - - // NOTE: use external submachine's states only for evaluating `toState`, but not for `fromState` - switch fromState { - // "Main0" => "Sub1-0" - case .MainState0 where subMachine1.state == .SubState0: - return .SubMachine1(.SubState0) - - // "Sub1-1" => "Sub2-0" - case let .SubMachine1(state) where state == .SubState1 && subMachine2.state == .SubState0: - return .SubMachine2(.SubState0) - - // "Sub2-1" => "Main0" - case let .SubMachine2(state) where state == .SubState1: - return .MainState0 - - default: - return nil - } - - } - - mainMachine.addHandler(.Any => .Any) { print("[Main] \($0.fromState) => \($0.toState)") } - mainMachine.addErrorHandler { print("[ERROR][Main] \($0.fromState) => \($0.toState)") } - } - - self.mainMachine = mainMachine - self.subMachine1 = subMachine1 - self.subMachine2 = subMachine2 - } - - /// `mainMachine.hasRoute()` test to check submachine's internal routes - func testHasRoute_submachine_internal() - { - let mainMachine = self.mainMachine! - let subMachine1 = self.subMachine1! - let subMachine2 = self.subMachine2! - - // initial - XCTAssertTrue(mainMachine.state == .MainState0) - XCTAssertTrue(subMachine1.state == .SubState0) - XCTAssertTrue(subMachine2.state == .SubState0) - - // subMachine1 internal routes - XCTAssertFalse(mainMachine.hasRoute(.SubMachine1(.SubState0) => .SubMachine1(.SubState0))) - XCTAssertTrue(mainMachine.hasRoute(.SubMachine1(.SubState0) => .SubMachine1(.SubState1))) - XCTAssertFalse(mainMachine.hasRoute(.SubMachine1(.SubState1) => .SubMachine1(.SubState0))) - XCTAssertFalse(mainMachine.hasRoute(.SubMachine1(.SubState1) => .SubMachine1(.SubState1))) - - // subMachine2 internal routes - XCTAssertFalse(mainMachine.hasRoute(.SubMachine2(.SubState0) => .SubMachine2(.SubState0))) - XCTAssertTrue(mainMachine.hasRoute(.SubMachine2(.SubState0) => .SubMachine2(.SubState1))) - XCTAssertFalse(mainMachine.hasRoute(.SubMachine2(.SubState1) => .SubMachine2(.SubState0))) - XCTAssertFalse(mainMachine.hasRoute(.SubMachine2(.SubState1) => .SubMachine2(.SubState1))) - } - - /// `mainMachine.hasRoute()` test to check switchable submachines - func testHasRoute_submachine_switching() - { - let mainMachine = self.mainMachine! - let subMachine1 = self.subMachine1! - let subMachine2 = self.subMachine2! - - // NOTE: mainMachine can check switchable submachines - // (external routes between submachines = SubState1, SubState2, or nil) - - // initial - XCTAssertTrue(mainMachine.state == .MainState0) - XCTAssertTrue(subMachine1.state == .SubState0) - XCTAssertTrue(subMachine2.state == .SubState0) - - // from Main0 - XCTAssertTrue(mainMachine.hasRoute(.MainState0 => .SubMachine1(.SubState0))) - XCTAssertFalse(mainMachine.hasRoute(.MainState0 => .SubMachine1(.SubState1))) - XCTAssertFalse(mainMachine.hasRoute(.MainState0 => .SubMachine2(.SubState0))) - XCTAssertFalse(mainMachine.hasRoute(.MainState0 => .SubMachine2(.SubState1))) - - // from Sub1-0 - XCTAssertFalse(mainMachine.hasRoute(.SubMachine1(.SubState0) => .SubMachine2(.SubState0))) - XCTAssertFalse(mainMachine.hasRoute(.SubMachine1(.SubState0) => .SubMachine2(.SubState1))) - XCTAssertFalse(mainMachine.hasRoute(.SubMachine1(.SubState0) => .MainState0)) - - // from Sub1-1 - XCTAssertTrue(mainMachine.hasRoute(.SubMachine1(.SubState1) => .SubMachine2(.SubState0))) - XCTAssertFalse(mainMachine.hasRoute(.SubMachine1(.SubState1) => .SubMachine2(.SubState1))) - XCTAssertFalse(mainMachine.hasRoute(.SubMachine1(.SubState1) => .MainState0)) - - // from Sub2-0 - XCTAssertFalse(mainMachine.hasRoute(.SubMachine2(.SubState0) => .SubMachine1(.SubState0))) - XCTAssertFalse(mainMachine.hasRoute(.SubMachine2(.SubState0) => .SubMachine1(.SubState1))) - XCTAssertFalse(mainMachine.hasRoute(.SubMachine2(.SubState0) => .MainState0)) - - // from Sub2-1 - XCTAssertFalse(mainMachine.hasRoute(.SubMachine2(.SubState1) => .SubMachine1(.SubState0))) - XCTAssertFalse(mainMachine.hasRoute(.SubMachine2(.SubState1) => .SubMachine1(.SubState1))) - XCTAssertTrue(mainMachine.hasRoute(.SubMachine2(.SubState1) => .MainState0)) - - } - - func testTryState() - { - let mainMachine = self.mainMachine! - - // initial - XCTAssertTrue(mainMachine.state == .MainState0) - - // Main0 => Sub1-0 - mainMachine <- .SubMachine1(.SubState0) - XCTAssertTrue(mainMachine.state == .SubMachine1(.SubState0)) - - // Sub1-0 => Sub1-1 (Sub1 internal transition) - mainMachine <- .SubMachine1(.SubState1) - XCTAssertTrue(mainMachine.state == .SubMachine1(.SubState1)) - - // Sub1-1 => Sub1-2 (Sub1 internal transition, but fails) - mainMachine <- .SubMachine1(.SubState2) - XCTAssertTrue(mainMachine.state == .SubMachine1(.SubState1), "No change.") - - // Sub1-1 => Sub2-2 (fails) - mainMachine <- .SubMachine2(.SubState2) - XCTAssertTrue(mainMachine.state == .SubMachine1(.SubState1), "No change.") - - // Sub1-1 => Sub2-0 - mainMachine <- .SubMachine2(.SubState0) - XCTAssertTrue(mainMachine.state == .SubMachine2(.SubState0)) - - // Sub2-0 => Main0 (fails) - mainMachine <- .MainState0 - XCTAssertTrue(mainMachine.state == .SubMachine2(.SubState0), "No change.") - - // Sub2-0 => Sub2-1 - mainMachine <- .SubMachine2(.SubState1) - XCTAssertTrue(mainMachine.state == .SubMachine2(.SubState1)) - - // Sub2-1 => Main - mainMachine <- .MainState0 - XCTAssertTrue(mainMachine.state == .MainState0) - - } - - func testAddHandler() - { - let mainMachine = self.mainMachine! - - var didPass = false - - // NOTE: this handler is added to mainMachine and doesn't make submachines dirty - mainMachine.addHandler(.MainState0 => .SubMachine1(.SubState0)) { context in - print("[Main] 1-1 => 1-2 (specific)") - didPass = true - } - - // initial - XCTAssertTrue(mainMachine.state == .MainState0) - XCTAssertFalse(didPass) - - // Main0 => Sub1-1 (fails) - mainMachine <- .SubMachine1(.SubState1) - XCTAssertTrue(mainMachine.state == .MainState0, "No change.") - XCTAssertFalse(didPass) - - // Main0 => Sub1-0 - mainMachine <- .SubMachine1(.SubState0) - XCTAssertTrue(mainMachine.state == .SubMachine1(.SubState0)) - XCTAssertTrue(didPass) - } - -} diff --git a/Tests/MachineTests.swift b/Tests/MachineTests.swift deleted file mode 100644 index 0b567d9..0000000 --- a/Tests/MachineTests.swift +++ /dev/null @@ -1,540 +0,0 @@ -// -// MachineTests.swift -// SwiftState -// -// Created by Yasuhiro Inami on 2014/08/05. -// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. -// - -import SwiftState -import XCTest - -class MachineTests: _TestCase -{ - func testConfigure() - { - let machine = Machine(state: .State0) - - machine.configure { - $0.addRoutes(event: .Event0, transitions: [ .State0 => .State1 ]) - } - - XCTAssertTrue(machine.canTryEvent(.Event0) != nil) - } - - //-------------------------------------------------- - // MARK: - tryEvent a.k.a `<-!` - //-------------------------------------------------- - - func testCanTryEvent() - { - let machine = Machine(state: .State0) - - // add 0 => 1 & 1 => 2 - // (NOTE: this is not chaining e.g. 0 => 1 => 2) - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - XCTAssertTrue(machine.canTryEvent(.Event0) != nil) - } - - func testTryEvent() - { - let machine = Machine(state: .State0) { machine in - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - } - - // initial - XCTAssertEqual(machine.state, MyState.State0) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2, "Event0 doesn't have 2 => Any") - } - - func testTryEvent_userInfo() - { - var userInfo: Any? = nil - - let machine = Machine(state: .State0) { machine in - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ], handler: { context in - userInfo = context.userInfo - }) - } - - // initial - XCTAssertEqual(machine.state, MyState.State0) - XCTAssertNil(userInfo) - - // tryEvent - machine <-! (.Event0, "gogogo") - XCTAssertEqual(machine.state, MyState.State1) - XCTAssertTrue(userInfo as? String == "gogogo") - - // tryEvent - machine <-! (.Event0, "done") - XCTAssertEqual(machine.state, MyState.State2) - XCTAssertTrue(userInfo as? String == "done") - } - - func testTryEvent_twice() - { - let machine = Machine(state: .State0) { machine in - // add 0 => 1 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - ]) - // add 0 => 1 - machine.addRoutes(event: .Event1, transitions: [ - .State1 => .State2, - ]) - } - - // tryEvent (twice) - machine <-! .Event0 <-! .Event1 - XCTAssertEqual(machine.state, MyState.State2) - } - - func testTryEvent_string() - { - let machine = Machine(state: .State0) - - // add 0 => 1 => 2 - machine.addRoutes(event: "Run", transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - // tryEvent - machine <-! "Run" - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! "Run" - XCTAssertEqual(machine.state, MyState.State2) - - // tryEvent - machine <-! "Run" - XCTAssertEqual(machine.state, MyState.State2, "Event=Run doesn't have 2 => Any") - } - - // https://github.com/ReactKit/SwiftState/issues/20 - func testTryEvent_issue20() - { - let machine = Machine(state: MyState.State2) { machine in - machine.addRoutes(event: .Event0, transitions: [.Any => .State0]) - } - - XCTAssertEqual(machine.state, MyState.State2) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State0) - } - - // Fix for transitioning of routes w/ multiple from-states - // https://github.com/ReactKit/SwiftState/pull/32 - func testTryEvent_issue32() - { - let machine = Machine(state: .State0) { machine in - machine.addRoutes(event: .Event0, transitions: [ .State0 => .State1 ]) - machine.addRoutes(event: .Event1, routes: [ [ .State1, .State2 ] => .State3 ]) - } - - XCTAssertEqual(machine.state, MyState.State0) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event1 - XCTAssertEqual(machine.state, MyState.State3) - } - - //-------------------------------------------------- - // MARK: - add/removeRoute - //-------------------------------------------------- - - func testAddRoute_multiple() - { - let machine = Machine(state: .State0) { machine in - - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - // add 2 => 1 => 0 - machine.addRoutes(event: .Event1, transitions: [ - .State2 => .State1, - .State1 => .State0, - ]) - } - - // initial - XCTAssertEqual(machine.state, MyState.State0) - - // tryEvent - machine <-! .Event1 - XCTAssertEqual(machine.state, MyState.State0, "Event1 doesn't have 0 => Any.") - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2, "Event0 doesn't have 2 => Any.") - - // tryEvent - machine <-! .Event1 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event1 - XCTAssertEqual(machine.state, MyState.State0) - } - - func testAddRoute_handler() - { - var invokeCount = 0 - - let machine = Machine(state: .State0) { machine in - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ], handler: { context in - invokeCount += 1 - return - }) - } - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2) - - XCTAssertEqual(invokeCount, 2) - } - - func testRemoveRoute() - { - var invokeCount = 0 - - let machine = Machine(state: .State0) { machine in - - // add 0 => 1 => 2 - let routeDisposable = machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - machine.addHandler(event: .Event0) { context in - invokeCount += 1 - return - } - - // removeRoute - routeDisposable.dispose() - - } - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State0, "Route should be removed.") - - XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") - } - - func testRemoveRoute_handler() - { - var invokeCount = 0 - - let machine = Machine(state: .State0) { machine in - - // add 0 => 1 => 2 - let routeDisposable = machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ], handler: { context in - invokeCount += 1 - return - }) - - // removeRoute - routeDisposable.dispose() - - } - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State0, "Route should be removed.") - - XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") - } - - //-------------------------------------------------- - // MARK: - add/removeHandler - //-------------------------------------------------- - - func testAddHandler() - { - var invokeCount = 0 - - let machine = Machine(state: .State0) { machine in - - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - machine.addHandler(event: .Event0) { context in - invokeCount += 1 - return - } - - } - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2) - - XCTAssertEqual(invokeCount, 2) - } - - func testAddErrorHandler() - { - var invokeCount = 0 - - let machine = Machine(state: .State0) { machine in - machine.addRoutes(event: .Event0, transitions: [ .State0 => .State1 ]) - machine.addErrorHandler { event, fromState, toState, userInfo in - invokeCount += 1 - } - } - - XCTAssertEqual(invokeCount, 0) - - // tryEvent (fails) - machine <-! .Event1 - - XCTAssertEqual(invokeCount, 1, "Error handler should be called.") - - } - - func testRemoveHandler() - { - var invokeCount = 0 - - let machine = Machine(state: .State0) { machine in - - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - let handlerDisposable = machine.addHandler(event: .Event0) { context in - invokeCount += 1 - return - } - - // remove handler - handlerDisposable.dispose() - - } - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1, "0 => 1 should be succesful") - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2, "1 => 2 should be succesful") - - XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") - } - - //-------------------------------------------------- - // MARK: - RouteMapping - //-------------------------------------------------- - - func testAddRouteMapping() - { - var invokeCount = 0 - - let machine = Machine(state: .Str("initial")) { machine in - - machine.addRouteMapping { event, fromState, userInfo -> StrState? in - // no route for no-event - guard let event = event else { return nil } - - switch (event, fromState) { - case (.Str("gogogo"), .Str("initial")): - return .Str("Phase 1") - case (.Str("gogogo"), .Str("Phase 1")): - return .Str("Phase 2") - case (.Str("finish"), .Str("Phase 2")): - return .Str("end") - default: - return nil - } - } - - machine.addHandler(event: .Str("gogogo")) { context in - invokeCount += 1 - return - } - - } - - // initial - XCTAssertEqual(machine.state, StrState.Str("initial")) - - // tryEvent (fails) - machine <-! .Str("go?") - XCTAssertEqual(machine.state, StrState.Str("initial"), "No change.") - XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") - - // tryEvent - machine <-! .Str("gogogo") - XCTAssertEqual(machine.state, StrState.Str("Phase 1")) - XCTAssertEqual(invokeCount, 1) - - // tryEvent (fails) - machine <-! .Str("finish") - XCTAssertEqual(machine.state, StrState.Str("Phase 1"), "No change.") - XCTAssertEqual(invokeCount, 1, "Handler should NOT be performed") - - // tryEvent - machine <-! .Str("gogogo") - XCTAssertEqual(machine.state, StrState.Str("Phase 2")) - XCTAssertEqual(invokeCount, 2) - - // tryEvent (fails) - machine <-! .Str("gogogo") - XCTAssertEqual(machine.state, StrState.Str("Phase 2"), "No change.") - XCTAssertEqual(invokeCount, 2, "Handler should NOT be performed") - - // tryEvent - machine <-! .Str("finish") - XCTAssertEqual(machine.state, StrState.Str("end")) - XCTAssertEqual(invokeCount, 2, "gogogo-Handler should NOT be performed") - - } - - func testAddRouteMapping_handler() - { - var invokeCount1 = 0 - var invokeCount2 = 0 - var disposables = [Disposable]() - - let machine = Machine(state: .Str("initial")) { machine in - - let d = machine.addRouteMapping({ event, fromState, userInfo -> StrState? in - // no route for no-event - guard let event = event else { return nil } - - switch (event, fromState) { - case (.Str("gogogo"), .Str("initial")): - return .Str("Phase 1") - default: - return nil - } - }, handler: { context in - invokeCount1 += 1 - }) - - disposables += [d] - - let d2 = machine.addRouteMapping({ event, fromState, userInfo -> StrState? in - // no route for no-event - guard let event = event else { return nil } - - switch (event, fromState) { - case (.Str("finish"), .Str("Phase 1")): - return .Str("end") - default: - return nil - } - }, handler: { context in - invokeCount2 += 1 - }) - - disposables += [d2] - - } - - // initial - XCTAssertEqual(machine.state, StrState.Str("initial")) - - // tryEvent (fails) - machine <-! .Str("go?") - XCTAssertEqual(machine.state, StrState.Str("initial"), "No change.") - XCTAssertEqual(invokeCount1, 0) - XCTAssertEqual(invokeCount2, 0) - - // tryEvent - machine <-! .Str("gogogo") - XCTAssertEqual(machine.state, StrState.Str("Phase 1")) - XCTAssertEqual(invokeCount1, 1) - XCTAssertEqual(invokeCount2, 0) - - // tryEvent (fails) - machine <-! .Str("gogogo") - XCTAssertEqual(machine.state, StrState.Str("Phase 1"), "No change.") - XCTAssertEqual(invokeCount1, 1) - XCTAssertEqual(invokeCount2, 0) - - // tryEvent - machine <-! .Str("finish") - XCTAssertEqual(machine.state, StrState.Str("end")) - XCTAssertEqual(invokeCount1, 1) - XCTAssertEqual(invokeCount2, 1) - - // hasRoute (before dispose) - XCTAssertEqual(machine.hasRoute(event: .Str("gogogo"), transition: .Str("initial") => .Str("Phase 1")), true) - XCTAssertEqual(machine.hasRoute(event: .Str("finish"), transition: .Str("Phase 1") => .Str("end")), true) - - disposables.forEach { $0.dispose() } - - // hasRoute (after dispose) - XCTAssertEqual(machine.hasRoute(event: .Str("gogogo"), transition: .Str("initial") => .Str("Phase 1")), false, "Routes & handlers should be disposed.") - XCTAssertEqual(machine.hasRoute(event: .Str("finish"), transition: .Str("Phase 1") => .Str("end")), false, "Routes & handlers should be disposed.") - - } - -} diff --git a/Tests/MiscTests.swift b/Tests/MiscTests.swift deleted file mode 100644 index 59f946c..0000000 --- a/Tests/MiscTests.swift +++ /dev/null @@ -1,194 +0,0 @@ -// -// MiscTests.swift -// SwiftState -// -// Created by Yasuhiro Inami on 2015-12-05. -// Copyright © 2015 Yasuhiro Inami. All rights reserved. -// - -import SwiftState -import XCTest - -/// Unarranged tests. -class MiscTests: _TestCase -{ - func testREADME_string() - { - let machine = StateMachine(state: ".State0") { machine in - - machine.addRoute(".State0" => ".State1") - machine.addRoute(.Any => ".State2") { context in print("Any => 2, msg=\(context.userInfo)") } - machine.addRoute(".State2" => .Any) { context in print("2 => Any, msg=\(context.userInfo)") } - - // add handler (handlerContext = (event, transition, order, userInfo)) - machine.addHandler(".State0" => ".State1") { context in - print("0 => 1") - } - - // add errorHandler - machine.addErrorHandler { event, fromState, toState, userInfo in - print("[ERROR] \(fromState) => \(toState)") - } - } - - // tryState 0 => 1 => 2 => 1 => 0 - - machine <- ".State1" - XCTAssertEqual(machine.state, ".State1") - - machine <- (".State2", "Hello") - XCTAssertEqual(machine.state, ".State2") - - machine <- (".State1", "Bye") - XCTAssertEqual(machine.state, ".State1") - - machine <- ".State0" // fail: no 1 => 0 - XCTAssertEqual(machine.state, ".State1") - - print("machine.state = \(machine.state)") - } - - // StateType + associated value - func testREADME_associatedValue() - { - let machine = StateMachine(state: .Str("0")) { machine in - - machine.addRoute(.Str("0") => .Str("1")) - machine.addRoute(.Any => .Str("2")) { context in print("Any => 2, msg=\(context.userInfo)") } - machine.addRoute(.Str("2") => .Any) { context in print("2 => Any, msg=\(context.userInfo)") } - - // add handler (handlerContext = (event, transition, order, userInfo)) - machine.addHandler(.Str("0") => .Str("1")) { context in - print("0 => 1") - } - - // add errorHandler - machine.addErrorHandler { event, fromState, toState, userInfo in - print("[ERROR] \(fromState) => \(toState)") - } - } - - // tryState 0 => 1 => 2 => 1 => 0 - - machine <- .Str("1") - XCTAssertEqual(machine.state, StrState.Str("1")) - - machine <- (.Str("2"), "Hello") - XCTAssertEqual(machine.state, StrState.Str("2")) - - machine <- (.Str("1"), "Bye") - XCTAssertEqual(machine.state, StrState.Str("1")) - - machine <- .Str("0") // fail: no 1 => 0 - XCTAssertEqual(machine.state, StrState.Str("1")) - - print("machine.state = \(machine.state)") - } - - func testExample() - { - let machine = StateMachine(state: .State0) { - - // add 0 => 1 - $0.addRoute(.State0 => .State1) { context in - print("[Transition 0=>1] \(context.fromState) => \(context.toState)") - } - // add 0 => 1 once more - $0.addRoute(.State0 => .State1) { context in - print("[Transition 0=>1b] \(context.fromState) => \(context.toState)") - } - // add 2 => Any - $0.addRoute(.State2 => .Any) { context in - print("[Transition exit 2] \(context.fromState) => \(context.toState) (Any)") - } - // add Any => 2 - $0.addRoute(.Any => .State2) { context in - print("[Transition Entry 2] \(context.fromState) (Any) => \(context.toState)") - } - // add 1 => 0 (no handler) - $0.addRoute(.State1 => .State0) - - } - - // 0 => 1 - XCTAssertTrue(machine.hasRoute(.State0 => .State1)) - - // 1 => 0 - XCTAssertTrue(machine.hasRoute(.State1 => .State0)) - - // 2 => Any - XCTAssertTrue(machine.hasRoute(.State2 => .State0)) - XCTAssertTrue(machine.hasRoute(.State2 => .State1)) - XCTAssertTrue(machine.hasRoute(.State2 => .State2)) - XCTAssertTrue(machine.hasRoute(.State2 => .State3)) - - // Any => 2 - XCTAssertTrue(machine.hasRoute(.State0 => .State2)) - XCTAssertTrue(machine.hasRoute(.State1 => .State2)) - XCTAssertTrue(machine.hasRoute(.State3 => .State2)) - - // others - XCTAssertFalse(machine.hasRoute(.State0 => .State0)) - XCTAssertFalse(machine.hasRoute(.State0 => .State3)) - XCTAssertFalse(machine.hasRoute(.State1 => .State1)) - XCTAssertFalse(machine.hasRoute(.State1 => .State3)) - XCTAssertFalse(machine.hasRoute(.State3 => .State0)) - XCTAssertFalse(machine.hasRoute(.State3 => .State1)) - XCTAssertFalse(machine.hasRoute(.State3 => .State3)) - - machine.configure { - - // add error handlers - $0.addErrorHandler { context in - print("[ERROR 1] \(context.fromState) => \(context.toState)") - } - - // add entry handlers - $0.addHandler(.Any => .State0) { context in - print("[Entry 0] \(context.fromState) => \(context.toState)") // NOTE: this should not be called - } - $0.addHandler(.Any => .State1) { context in - print("[Entry 1] \(context.fromState) => \(context.toState)") - } - $0.addHandler(.Any => .State2) { context in - print("[Entry 2] \(context.fromState) => \(context.toState), userInfo = \(context.userInfo)") - } - $0.addHandler(.Any => .State2) { context in - print("[Entry 2b] \(context.fromState) => \(context.toState), userInfo = \(context.userInfo)") - } - - // add exit handlers - $0.addHandler(.State0 => .Any) { context in - print("[Exit 0] \(context.fromState) => \(context.toState)") - } - $0.addHandler(.State1 => .Any) { context in - print("[Exit 1] \(context.fromState) => \(context.toState)") - } - $0.addHandler(.State2 => .Any) { context in - print("[Exit 2] \(context.fromState) => \(context.toState), userInfo = \(context.userInfo)") - } - $0.addHandler(.State2 => .Any) { context in - print("[Exit 2b] \(context.fromState) => \(context.toState), userInfo = \(context.userInfo)") - } - } - - XCTAssertEqual(machine.state, MyState.State0) - - // tryState 0 => 1 => 2 => 1 => 0 => 3 - - machine <- .State1 - XCTAssertEqual(machine.state, MyState.State1) - - machine <- (.State2, "State2 activate") - XCTAssertEqual(machine.state, MyState.State2) - - machine <- (.State1, "State2 deactivate") - XCTAssertEqual(machine.state, MyState.State1) - - machine <- .State0 - XCTAssertEqual(machine.state, MyState.State0) - - machine <- .State3 - XCTAssertEqual(machine.state, MyState.State0, "No 0 => 3.") - } -} diff --git a/Tests/RouteMappingTests.swift b/Tests/RouteMappingTests.swift deleted file mode 100644 index a04cb82..0000000 --- a/Tests/RouteMappingTests.swift +++ /dev/null @@ -1,189 +0,0 @@ -// -// RouteMappingTests.swift -// SwiftState -// -// Created by Yasuhiro Inami on 2015-11-02. -// Copyright © 2015 Yasuhiro Inami. All rights reserved. -// - -import SwiftState -import XCTest - -// -// RouteMapping for StateType & EventType using associated values -// -// https://github.com/ReactKit/SwiftState/issues/34 -// https://github.com/ReactKit/SwiftState/pull/36 -// - -private enum _State: StateType, Hashable -{ - case Pending - case Loading(Int) - - var hashValue: Int - { - switch self { - case .Pending: - return "Pending".hashValue - case let .Loading(x): - return "Loading\(x)".hashValue - } - } -} - -private func == (lhs: _State, rhs: _State) -> Bool -{ - switch (lhs, rhs) { - case (.Pending, .Pending): - return true - case let (.Loading(x1), .Loading(x2)): - return x1 == x2 - default: - return false - } -} - -private enum _Event: EventType, Hashable -{ - case CancelAction - case LoadAction(Int) - - var hashValue: Int - { - switch self { - case .CancelAction: - return "CancelAction".hashValue - case let .LoadAction(x): - return "LoadAction\(x)".hashValue - } - } -} - -private func == (lhs: _Event, rhs: _Event) -> Bool -{ - switch (lhs, rhs) { - case (.CancelAction, .CancelAction): - return true - case let (.LoadAction(x1), .LoadAction(x2)): - return x1 == x2 - default: - return false - } -} - -class RouteMappingTests: _TestCase -{ - /// Test for state & event with associated values - func testAddRouteMapping() - { - var count = 0 - - let machine = StateMachine<_State, _Event>(state: .Pending) { machine in - - machine.addRouteMapping { event, fromState, userInfo in - // no routes for no event - guard let event = event else { - return nil - } - - switch event { - case .CancelAction: - // can transit to `.Pending` if current state is not the same - return fromState == .Pending ? nil : .Pending - case .LoadAction(let actionId): - // can transit to `.Loading(actionId)` if current state is not the same - return fromState == .Loading(actionId) ? nil : .Loading(actionId) - } - } - - // increment `count` when any events i.e. `.CancelAction` and `.LoadAction(x)` succeed. - machine.addHandler(event: .Any) { event, transition, order, userInfo in - count += 1 - } - - } - - // initial - XCTAssertEqual(machine.state, _State.Pending) - XCTAssertEqual(count, 0) - - // CancelAction (to .Pending state, same as before) - machine <-! .CancelAction - XCTAssertEqual(machine.state, _State.Pending) - XCTAssertEqual(count, 0, "`tryEvent()` failed, and `count` should not be incremented.") - - // LoadAction(1) (to .Loading(1) state) - machine <-! .LoadAction(1) - XCTAssertEqual(machine.state, _State.Loading(1)) - XCTAssertEqual(count, 1) - - // LoadAction(1) (same as before) - machine <-! .LoadAction(1) - XCTAssertEqual(machine.state, _State.Loading(1)) - XCTAssertEqual(count, 1, "`tryEvent()` failed, and `count` should not be incremented.") - - machine <-! .LoadAction(2) - XCTAssertEqual(machine.state, _State.Loading(2)) - XCTAssertEqual(count, 2) - - machine <-! .CancelAction - XCTAssertEqual(machine.state, _State.Pending) - XCTAssertEqual(count, 3) - } - - /// Test for state with associated values - func testAddStateRouteMapping() - { - var count = 0 - - let machine = StateMachine<_State, _Event>(state: .Pending) { machine in - - // Add following routes: - // - `.Pending => .Loading(1)` - // - `.Loading(x) => .Loading(x+10)` - // - `.Loading(x) => .Loading(x+100)` - machine.addStateRouteMapping { fromState, userInfo in - switch fromState { - case .Pending: - return [.Loading(1)] - case .Loading(let actionId): - return [.Loading(actionId+10), .Loading(actionId+100)] - } - } - - // increment `count` when any events i.e. `.CancelAction` and `.LoadAction(x)` succeed. - machine.addHandler(.Any => .Any) { event, transition, order, userInfo in - count += 1 - } - - } - - // initial - XCTAssertEqual(machine.state, _State.Pending) - XCTAssertEqual(count, 0) - - // .Loading(999) (fails) - machine <- .Loading(999) - XCTAssertEqual(machine.state, _State.Pending) - XCTAssertEqual(count, 0, "`tryState()` failed, and `count` should not be incremented.") - - // .Loading(1) - machine <- .Loading(1) - XCTAssertEqual(machine.state, _State.Loading(1)) - XCTAssertEqual(count, 1) - - // .Loading(999) (fails) - machine <- .Loading(999) - XCTAssertEqual(machine.state, _State.Loading(1)) - XCTAssertEqual(count, 1, "`tryState()` failed, and `count` should not be incremented.") - - machine <- .Loading(11) - XCTAssertEqual(machine.state, _State.Loading(11)) - XCTAssertEqual(count, 2) - - machine <- .Loading(111) - XCTAssertEqual(machine.state, _State.Loading(111)) - XCTAssertEqual(count, 3) - } -} diff --git a/Tests/StateMachineEventTests.swift b/Tests/StateMachineEventTests.swift deleted file mode 100644 index b403255..0000000 --- a/Tests/StateMachineEventTests.swift +++ /dev/null @@ -1,448 +0,0 @@ -// -// StateMachineEventTests.swift -// SwiftState -// -// Created by Yasuhiro Inami on 2014/08/05. -// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. -// - -import SwiftState -import XCTest - -class StateMachineEventTests: _TestCase -{ - func testCanTryEvent() - { - let machine = StateMachine(state: .State0) - - // add 0 => 1 & 1 => 2 - // (NOTE: this is not chaining e.g. 0 => 1 => 2) - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - XCTAssertTrue(machine.canTryEvent(.Event0) != nil) - } - - //-------------------------------------------------- - // MARK: - tryEvent a.k.a `<-!` - //-------------------------------------------------- - - func testTryEvent() - { - let machine = StateMachine(state: .State0) - - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2, "Event0 doesn't have 2 => Any") - } - - func testTryEvent_string() - { - let machine = StateMachine(state: .State0) - - // add 0 => 1 => 2 - machine.addRoutes(event: "Run", transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - // tryEvent - machine <-! "Run" - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! "Run" - XCTAssertEqual(machine.state, MyState.State2) - - // tryEvent - machine <-! "Run" - XCTAssertEqual(machine.state, MyState.State2, "Event=Run doesn't have 2 => Any") - } - - // https://github.com/ReactKit/SwiftState/issues/20 - func testTryEvent_issue20() - { - let machine = StateMachine(state: MyState.State2) { machine in - machine.addRoutes(event: .Event0, transitions: [.Any => .State0]) - } - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State0) - } - - // https://github.com/ReactKit/SwiftState/issues/28 - func testTryEvent_issue28() - { - var eventCount = 0 - - let machine = StateMachine(state: .State0) { machine in - machine.addRoute(.State0 => .State1) - machine.addRoutes(event: .Event0, transitions: [.Any => .Any]) { _ in - eventCount += 1 - } - } - - XCTAssertEqual(eventCount, 0) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(eventCount, 1) - XCTAssertEqual(machine.state, MyState.State0, "State should NOT be changed") - - // tryEvent - machine <- .State1 - XCTAssertEqual(machine.state, MyState.State1, "State should be changed") - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(eventCount, 2) - XCTAssertEqual(machine.state, MyState.State1, "State should NOT be changed") - } - - // Fix for transitioning of routes w/ multiple from-states - // https://github.com/ReactKit/SwiftState/pull/32 - func testTryEvent_issue32() - { - let machine = StateMachine(state: .State0) { machine in - machine.addRoutes(event: .Event0, transitions: [ .State0 => .State1 ]) - machine.addRoutes(event: .Event1, routes: [ [ .State1, .State2 ] => .State3 ]) - } - - XCTAssertEqual(machine.state, MyState.State0) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event1 - XCTAssertEqual(machine.state, MyState.State3) - } - - // MARK: hasRoute + event - - func testHasRoute_anyEvent() - { - ({ - let machine = StateMachine(state: .State0) { machine in - machine.addRoute(.State0 => .State1) - machine.addRoutes(event: .Any, transitions: [.State0 => .State1]) - } - - let hasRoute = machine.hasRoute(event: .Event0, transition: .State0 => .State1) - XCTAssertTrue(hasRoute) - })() - - ({ - let machine = StateMachine(state: .State0) { machine in - machine.addRoute(.State0 => .State1) - machine.addRoutes(event: .Any, transitions: [.State2 => .State3]) - } - - let hasRoute = machine.hasRoute(event: .Event0, transition: .State0 => .State1) - XCTAssertFalse(hasRoute) - })() - } - - // Fix hasRoute() bug when there are routes for no-event & with-event. - // https://github.com/ReactKit/SwiftState/pull/19 - func testHasRoute_issue19() - { - let machine = StateMachine(state: .State0) { machine in - machine.addRoute(.State0 => .State1) // no-event - machine.addRoutes(event: .Event0, transitions: [.State1 => .State2]) // with-event - } - - let hasRoute = machine.hasRoute(event: .Event0, transition: .State1 => .State2) - XCTAssertTrue(hasRoute) - } - - //-------------------------------------------------- - // MARK: - add/removeRoute - //-------------------------------------------------- - - func testAddRoute_tryState() - { - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 & 1 => 2 - // (NOTE: this is not chaining e.g. 0 => 1 => 2) - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - } - - // tryState 0 => 1 - machine <- .State1 - XCTAssertEqual(machine.state, MyState.State1) - - // tryState 1 => 2 - machine <- .State2 - XCTAssertEqual(machine.state, MyState.State2) - - // tryState 2 => 3 - machine <- .State3 - XCTAssertEqual(machine.state, MyState.State2, "2 => 3 is not registered.") - } - - func testAddRoute_multiple() - { - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - // add 2 => 1 => 0 - machine.addRoutes(event: .Event1, transitions: [ - .State2 => .State1, - .State1 => .State0, - ]) - } - - // initial - XCTAssertEqual(machine.state, MyState.State0) - - // tryEvent - machine <-! .Event1 - XCTAssertEqual(machine.state, MyState.State0, "Event1 doesn't have 0 => Any.") - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2, "Event0 doesn't have 2 => Any.") - - // tryEvent - machine <-! .Event1 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event1 - XCTAssertEqual(machine.state, MyState.State0) - } - - func testAddRoute_handler() - { - var invokeCount = 0 - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ], handler: { context in - invokeCount += 1 - return - }) - } - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2) - - XCTAssertEqual(invokeCount, 2) - } - - func testRemoveRoute() - { - var invokeCount = 0 - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 => 2 - let routeDisposable = machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - machine.addHandler(event: .Event0) { context in - invokeCount += 1 - return - } - - // removeRoute - routeDisposable.dispose() - - } - - // initial - XCTAssertEqual(machine.state, MyState.State0) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State0, "Route should be removed.") - - XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") - } - - //-------------------------------------------------- - // MARK: - add/removeHandler - //-------------------------------------------------- - - func testAddHandler() - { - var invokeCount = 0 - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - machine.addHandler(event: .Event0) { context in - invokeCount += 1 - return - } - - } - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2) - - XCTAssertEqual(invokeCount, 2) - } - - func testRemoveHandler() - { - var invokeCount = 0 - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 => 2 - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - let handlerDisposable = machine.addHandler(event: .Event0) { context in - invokeCount += 1 - return - } - - // remove handler - handlerDisposable.dispose() - - } - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1, "0 => 1 should be succesful") - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2, "1 => 2 should be succesful") - - XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") - } - - //-------------------------------------------------- - // MARK: - addAnyHandler - //-------------------------------------------------- - - func testAddAnyHandler() - { - var invokeCounts = [0, 0, 0, 0, 0, 0] - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 => 2 (event-based) - machine.addRoutes(event: .Event0, transitions: [ - .State0 => .State1, - .State1 => .State2, - ]) - - // add 2 => 3 (state-based) - machine.addRoute(.State2 => .State3) - - // - // addAnyHandler (for both event-based & state-based) - // - - machine.addAnyHandler(.State0 => .State1) { context in - invokeCounts[0] += 1 - } - - machine.addAnyHandler(.State1 => .State2) { context in - invokeCounts[1] += 1 - } - - machine.addAnyHandler(.State2 => .State3) { context in - invokeCounts[2] += 1 - } - - machine.addAnyHandler(.Any => .State3) { context in - invokeCounts[3] += 1 - } - - machine.addAnyHandler(.State0 => .Any) { context in - invokeCounts[4] += 1 - } - - machine.addAnyHandler(.Any => .Any) { context in - invokeCounts[5] += 1 - } - - } - - // initial - XCTAssertEqual(machine.state, MyState.State0) - XCTAssertEqual(invokeCounts, [0, 0, 0, 0, 0, 0]) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State1) - XCTAssertEqual(invokeCounts, [1, 0, 0, 0, 1, 1]) - - // tryEvent - machine <-! .Event0 - XCTAssertEqual(machine.state, MyState.State2) - XCTAssertEqual(invokeCounts, [1, 1, 0, 0, 1, 2]) - - // tryState - machine <- .State3 - XCTAssertEqual(machine.state, MyState.State3) - XCTAssertEqual(invokeCounts, [1, 1, 1, 1, 1, 3]) - - } - -} diff --git a/Tests/StateMachineTests.swift b/Tests/StateMachineTests.swift deleted file mode 100644 index 432674e..0000000 --- a/Tests/StateMachineTests.swift +++ /dev/null @@ -1,780 +0,0 @@ -// -// StateMachineTests.swift -// SwiftState -// -// Created by Yasuhiro Inami on 2014/08/03. -// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. -// - -import SwiftState -import XCTest - -class StateMachineTests: _TestCase -{ - func testInit() - { - let machine = StateMachine(state: .State0) - - XCTAssertEqual(machine.state, MyState.State0) - } - - //-------------------------------------------------- - // MARK: - tryState a.k.a `<-` - //-------------------------------------------------- - - // machine <- state - func testTryState() - { - let machine = StateMachine(state: .State0) - - // tryState 0 => 1, without registering any transitions - machine <- .State1 - - XCTAssertEqual(machine.state, MyState.State0, "0 => 1 should fail because transition is not added yet.") - - // add 0 => 1 - machine.addRoute(.State0 => .State1) - - // tryState 0 => 1 - machine <- .State1 - XCTAssertEqual(machine.state, MyState.State1) - } - - func testTryState_string() - { - let machine = StateMachine(state: "0") - - // tryState 0 => 1, without registering any transitions - machine <- "1" - - XCTAssertEqual(machine.state, "0", "0 => 1 should fail because transition is not added yet.") - - // add 0 => 1 - machine.addRoute("0" => "1") - - // tryState 0 => 1 - machine <- "1" - XCTAssertEqual(machine.state, "1") - } - - //-------------------------------------------------- - // MARK: - addRoute - //-------------------------------------------------- - - // add state1 => state2 - func testAddRoute() - { - let machine = StateMachine(state: .State0) { machine in - machine.addRoute(.State0 => .State1) - } - - XCTAssertFalse(machine.hasRoute(.State0 => .State0)) - XCTAssertTrue(machine.hasRoute(.State0 => .State1)) // true - XCTAssertFalse(machine.hasRoute(.State0 => .State2)) - XCTAssertFalse(machine.hasRoute(.State1 => .State0)) - XCTAssertFalse(machine.hasRoute(.State1 => .State1)) - XCTAssertFalse(machine.hasRoute(.State1 => .State2)) - } - - // add .Any => state - func testAddRoute_fromAnyState() - { - let machine = StateMachine(state: .State0) { machine in - machine.addRoute(.Any => .State1) // Any => State1 - } - - XCTAssertFalse(machine.hasRoute(.State0 => .State0)) - XCTAssertTrue(machine.hasRoute(.State0 => .State1)) // true - XCTAssertFalse(machine.hasRoute(.State0 => .State2)) - XCTAssertFalse(machine.hasRoute(.State1 => .State0)) - XCTAssertTrue(machine.hasRoute(.State1 => .State1)) // true - XCTAssertFalse(machine.hasRoute(.State1 => .State2)) - } - - // add state => .Any - func testAddRoute_toAnyState() - { - let machine = StateMachine(state: .State0) { machine in - machine.addRoute(.State1 => .Any) // State1 => Any - } - - XCTAssertFalse(machine.hasRoute(.State0 => .State0)) - XCTAssertFalse(machine.hasRoute(.State0 => .State1)) - XCTAssertFalse(machine.hasRoute(.State0 => .State2)) - XCTAssertTrue(machine.hasRoute(.State1 => .State0)) // true - XCTAssertTrue(machine.hasRoute(.State1 => .State1)) // true - XCTAssertTrue(machine.hasRoute(.State1 => .State2)) // true - } - - // add .Any => .Any - func testAddRoute_bothAnyState() - { - let machine = StateMachine(state: .State0) { machine in - machine.addRoute(.Any => .Any) // Any => Any - } - - XCTAssertTrue(machine.hasRoute(.State0 => .State0)) // true - XCTAssertTrue(machine.hasRoute(.State0 => .State1)) // true - XCTAssertTrue(machine.hasRoute(.State0 => .State2)) // true - XCTAssertTrue(machine.hasRoute(.State1 => .State0)) // true - XCTAssertTrue(machine.hasRoute(.State1 => .State1)) // true - XCTAssertTrue(machine.hasRoute(.State1 => .State2)) // true - } - - // add state0 => state0 - func testAddRoute_sameState() - { - let machine = StateMachine(state: .State0) { machine in - machine.addRoute(.State0 => .State0) - } - - XCTAssertTrue(machine.hasRoute(.State0 => .State0)) - } - - // add route + condition - func testAddRoute_condition() - { - var flag = false - - let machine = StateMachine(state: .State0) { machine in - // add 0 => 1 - machine.addRoute(.State0 => .State1, condition: { _ in flag }) - - } - - XCTAssertFalse(machine.hasRoute(.State0 => .State1)) - - flag = true - - XCTAssertTrue(machine.hasRoute(.State0 => .State1)) - } - - // add route + condition + blacklist - func testAddRoute_condition_blacklist() - { - let machine = StateMachine(state: .State0) { machine in - // add 0 => Any, except 0 => 2 - machine.addRoute(.State0 => .Any, condition: { context in - return context.toState != .State2 - }) - } - - XCTAssertTrue(machine.hasRoute(.State0 => .State0)) - XCTAssertTrue(machine.hasRoute(.State0 => .State1)) - XCTAssertFalse(machine.hasRoute(.State0 => .State2)) - XCTAssertTrue(machine.hasRoute(.State0 => .State3)) - } - - // add route + handler - func testAddRoute_handler() - { - var invokedCount = 0 - - let machine = StateMachine(state: .State0) { machine in - - machine.addRoute(.State0 => .State1) { context in - XCTAssertEqual(context.fromState, MyState.State0) - XCTAssertEqual(context.toState, MyState.State1) - - invokedCount += 1 - } - - } - - XCTAssertEqual(invokedCount, 0, "Transition has not started yet.") - - // tryState 0 => 1 - machine <- .State1 - - XCTAssertEqual(invokedCount, 1) - } - - // add route + conditional handler - func testAddRoute_conditionalHandler() - { - var invokedCount = 0 - var flag = false - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 without condition to guarantee 0 => 1 transition - machine.addRoute(.State0 => .State1) - - // add 0 => 1 with condition + conditionalHandler - machine.addRoute(.State0 => .State1, condition: { _ in flag }) { context in - XCTAssertEqual(context.fromState, MyState.State0) - XCTAssertEqual(context.toState, MyState.State1) - - invokedCount += 1 - } - - // add 1 => 0 for resetting state - machine.addRoute(.State1 => .State0) - - } - - // tryState 0 => 1 - machine <- .State1 - - XCTAssertEqual(machine.state, MyState.State1) - XCTAssertEqual(invokedCount, 0, "Conditional handler should NOT be performed because flag=false.") - - // tryState 1 => 0 (resetting to 0) - machine <- .State0 - - XCTAssertEqual(machine.state, MyState.State0) - - flag = true - - // tryState 0 => 1 - machine <- .State1 - - XCTAssertEqual(machine.state, MyState.State1) - XCTAssertEqual(invokedCount, 1) - - } - - // MARK: addRoute using array - - func testAddRoute_array_left() - { - let machine = StateMachine(state: .State0) { machine in - // add 0 => 2 or 1 => 2 - machine.addRoute([.State0, .State1] => .State2) - } - - XCTAssertFalse(machine.hasRoute(.State0 => .State0)) - XCTAssertFalse(machine.hasRoute(.State0 => .State1)) - XCTAssertTrue(machine.hasRoute(.State0 => .State2)) - XCTAssertFalse(machine.hasRoute(.State1 => .State0)) - XCTAssertFalse(machine.hasRoute(.State1 => .State1)) - XCTAssertTrue(machine.hasRoute(.State1 => .State2)) - } - - func testAddRoute_array_right() - { - let machine = StateMachine(state: .State0) { machine in - // add 0 => 1 or 0 => 2 - machine.addRoute(.State0 => [.State1, .State2]) - } - - XCTAssertFalse(machine.hasRoute(.State0 => .State0)) - XCTAssertTrue(machine.hasRoute(.State0 => .State1)) - XCTAssertTrue(machine.hasRoute(.State0 => .State2)) - XCTAssertFalse(machine.hasRoute(.State1 => .State0)) - XCTAssertFalse(machine.hasRoute(.State1 => .State1)) - XCTAssertFalse(machine.hasRoute(.State1 => .State2)) - } - - func testAddRoute_array_both() - { - let machine = StateMachine(state: .State0) { machine in - // add 0 => 2 or 0 => 3 or 1 => 2 or 1 => 3 - machine.addRoute([MyState.State0, MyState.State1] => [MyState.State2, MyState.State3]) - } - - XCTAssertFalse(machine.hasRoute(.State0 => .State0)) - XCTAssertFalse(machine.hasRoute(.State0 => .State1)) - XCTAssertTrue(machine.hasRoute(.State0 => .State2)) - XCTAssertTrue(machine.hasRoute(.State0 => .State3)) - XCTAssertFalse(machine.hasRoute(.State1 => .State0)) - XCTAssertFalse(machine.hasRoute(.State1 => .State1)) - XCTAssertTrue(machine.hasRoute(.State1 => .State2)) - XCTAssertTrue(machine.hasRoute(.State1 => .State3)) - XCTAssertFalse(machine.hasRoute(.State2 => .State0)) - XCTAssertFalse(machine.hasRoute(.State2 => .State1)) - XCTAssertFalse(machine.hasRoute(.State2 => .State2)) - XCTAssertFalse(machine.hasRoute(.State2 => .State3)) - XCTAssertFalse(machine.hasRoute(.State3 => .State0)) - XCTAssertFalse(machine.hasRoute(.State3 => .State1)) - XCTAssertFalse(machine.hasRoute(.State3 => .State2)) - XCTAssertFalse(machine.hasRoute(.State3 => .State3)) - } - - //-------------------------------------------------- - // MARK: - removeRoute - //-------------------------------------------------- - - func testRemoveRoute() - { - let machine = StateMachine(state: .State0) - - let routeDisposable = machine.addRoute(.State0 => .State1) - - XCTAssertTrue(machine.hasRoute(.State0 => .State1)) - - // remove route - routeDisposable.dispose() - - XCTAssertFalse(machine.hasRoute(.State0 => .State1)) - } - - func testRemoveRoute_handler() - { - let machine = StateMachine(state: .State0) - - let routeDisposable = machine.addRoute(.State0 => .State1, handler: { _ in }) - - XCTAssertTrue(machine.hasRoute(.State0 => .State1)) - - // remove route - routeDisposable.dispose() - - XCTAssertFalse(machine.hasRoute(.State0 => .State1)) - } - - //-------------------------------------------------- - // MARK: - addHandler - //-------------------------------------------------- - - func testAddHandler() - { - var invokedCount = 0 - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 - machine.addRoute(.State0 => .State1) - - machine.addHandler(.State0 => .State1) { context in - XCTAssertEqual(context.fromState, MyState.State0) - XCTAssertEqual(context.toState, MyState.State1) - - invokedCount += 1 - } - - } - - // not tried yet - XCTAssertEqual(invokedCount, 0, "Transition has not started yet.") - - // tryState 0 => 1 - machine <- .State1 - - XCTAssertEqual(invokedCount, 1) - } - - func testAddHandler_order() - { - var invokedCount = 0 - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 - machine.addRoute(.State0 => .State1) - - // order = 100 (default) - machine.addHandler(.State0 => .State1) { context in - XCTAssertEqual(invokedCount, 1) - - XCTAssertEqual(context.fromState, MyState.State0) - XCTAssertEqual(context.toState, MyState.State1) - - invokedCount += 1 - } - - // order = 99 - machine.addHandler(.State0 => .State1, order: 99) { context in - XCTAssertEqual(invokedCount, 0) - - XCTAssertEqual(context.fromState, MyState.State0) - XCTAssertEqual(context.toState, MyState.State1) - - invokedCount += 1 - } - - } - - XCTAssertEqual(invokedCount, 0) - - // tryState 0 => 1 - machine <- .State1 - - XCTAssertEqual(invokedCount, 2) - } - - - func testAddHandler_multiple() - { - var passed1 = false - var passed2 = false - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 - machine.addRoute(.State0 => .State1) - - machine.addHandler(.State0 => .State1) { context in - passed1 = true - } - - // add 0 => 1 once more - machine.addRoute(.State0 => .State1) - - machine.addHandler(.State0 => .State1) { context in - passed2 = true - } - - } - - // tryState 0 => 1 - machine <- .State1 - - XCTAssertTrue(passed1) - XCTAssertTrue(passed2) - } - - func testAddHandler_overload() - { - var passed = false - - let machine = StateMachine(state: .State0) { machine in - - machine.addRoute(.State0 => .State1) - - machine.addHandler(.State0 => .State1) { context in - // empty - } - - machine.addHandler(.State0 => .State1) { context in - passed = true - } - - } - - XCTAssertFalse(passed) - - machine <- .State1 - - XCTAssertTrue(passed) - } - - //-------------------------------------------------- - // MARK: - removeHandler - //-------------------------------------------------- - - func testRemoveHandler() - { - var passed = false - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 - machine.addRoute(.State0 => .State1) - - let handlerDisposable = machine.addHandler(.State0 => .State1) { context in - XCTFail("Should never reach here") - } - - // add 0 => 1 once more - machine.addRoute(.State0 => .State1) - - machine.addHandler(.State0 => .State1) { context in - passed = true - } - - // remove handler - handlerDisposable.dispose() - - } - - XCTAssertFalse(passed) - - // tryState 0 => 1 - machine <- .State1 - - XCTAssertTrue(passed) - } - - func testRemoveHandler_unregistered() - { - let machine = StateMachine(state: .State0) - - // add 0 => 1 - machine.addRoute(.State0 => .State1) - - let handlerDisposable = machine.addHandler(.State0 => .State1) { context in - // empty - } - - XCTAssertFalse(handlerDisposable.disposed) - - // remove handler - handlerDisposable.dispose() - - // remove already unregistered handler - XCTAssertTrue(handlerDisposable.disposed, "removeHandler should fail because handler is already removed.") - } - - func testRemoveErrorHandler() - { - var passed = false - - let machine = StateMachine(state: .State0) { machine in - - // add 2 => 1 - machine.addRoute(.State2 => .State1) - - let handlerDisposable = machine.addErrorHandler { context in - XCTFail("Should never reach here") - } - - // add 2 => 1 once more - machine.addRoute(.State2 => .State1) - - machine.addErrorHandler { context in - passed = true - } - - // remove handler - handlerDisposable.dispose() - - } - - // tryState 0 => 1 - machine <- .State1 - - XCTAssertTrue(passed) - } - - func testRemoveErrorHandler_unregistered() - { - let machine = StateMachine(state: .State0) - - // add 0 => 1 - machine.addRoute(.State0 => .State1) - - let handlerDisposable = machine.addErrorHandler { context in - // empty - } - - XCTAssertFalse(handlerDisposable.disposed) - - // remove handler - handlerDisposable.dispose() - - // remove already unregistered handler - XCTAssertTrue(handlerDisposable.disposed, "removeHandler should fail because handler is already removed.") - } - - //-------------------------------------------------- - // MARK: - addRouteChain - //-------------------------------------------------- - - func testAddRouteChain() - { - var success = false - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 => 2 => 3 - machine.addRouteChain(.State0 => .State1 => .State2 => .State3) { context in - success = true - } - } - - // initial - XCTAssertEqual(machine.state, MyState.State0) - - // 0 => 1 - machine <- .State1 - XCTAssertEqual(machine.state, MyState.State1) - XCTAssertFalse(success, "RouteChain is not completed yet.") - - // 1 => 2 - machine <- .State2 - XCTAssertEqual(machine.state, MyState.State2) - XCTAssertFalse(success, "RouteChain is not completed yet.") - - // 2 => 3 - machine <- .State3 - XCTAssertEqual(machine.state, MyState.State3) - XCTAssertTrue(success) - } - - func testAddChainHandler() - { - var success = false - - let machine = StateMachine(state: .State0) { machine in - - // add all routes - machine.addRoute(.Any => .Any) - - // add 0 => 1 => 2 => 3 - machine.addChainHandler(.State0 => .State1 => .State2 => .State3) { context in - success = true - } - } - - // initial - XCTAssertEqual(machine.state, MyState.State0) - - // 0 => 1 - machine <- .State1 - XCTAssertEqual(machine.state, MyState.State1) - XCTAssertFalse(success, "RouteChain is not completed yet.") - - // 1 => 2 - machine <- .State2 - XCTAssertEqual(machine.state, MyState.State2) - XCTAssertFalse(success, "RouteChain is not completed yet.") - - // 2 => 2 (fails & resets chaining count) - machine <- .State2 - XCTAssertEqual(machine.state, MyState.State2, "State should not change.") - XCTAssertFalse(success, "RouteChain failed and reset count.") - - // 2 => 3 (chaining is failed) - machine <- .State3 - XCTAssertEqual(machine.state, MyState.State3) - XCTAssertFalse(success, "RouteChain is already failed.") - - // go back to 0 & run 0 => 1 => 2 => 3 - machine <- .State0 <- .State1 <- .State2 <- .State3 - XCTAssertEqual(machine.state, MyState.State3) - XCTAssertTrue(success, "RouteChain is resetted & should succeed its chaining.") - } - - //-------------------------------------------------- - // MARK: - Event/StateRouteMapping - //-------------------------------------------------- - - func testAddStateRouteMapping() - { - var routeMappingDisposable: Disposable? - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 & 0 => 2 - routeMappingDisposable = machine.addStateRouteMapping { fromState, userInfo -> [MyState]? in - if fromState == .State0 { - return [.State1, .State2] - } - else { - return nil - } - } - - } - - XCTAssertFalse(machine.hasRoute(.State0 => .State0)) - XCTAssertTrue(machine.hasRoute(.State0 => .State1)) - XCTAssertTrue(machine.hasRoute(.State0 => .State2)) - XCTAssertFalse(machine.hasRoute(.State1 => .State0)) - XCTAssertFalse(machine.hasRoute(.State1 => .State1)) - XCTAssertFalse(machine.hasRoute(.State1 => .State2)) - XCTAssertFalse(machine.hasRoute(.State2 => .State0)) - XCTAssertFalse(machine.hasRoute(.State2 => .State1)) - XCTAssertFalse(machine.hasRoute(.State2 => .State2)) - - // remove routeMapping - routeMappingDisposable?.dispose() - - XCTAssertFalse(machine.hasRoute(.State0 => .State0)) - XCTAssertFalse(machine.hasRoute(.State0 => .State1)) - XCTAssertFalse(machine.hasRoute(.State0 => .State2)) - XCTAssertFalse(machine.hasRoute(.State1 => .State0)) - XCTAssertFalse(machine.hasRoute(.State1 => .State1)) - XCTAssertFalse(machine.hasRoute(.State1 => .State2)) - XCTAssertFalse(machine.hasRoute(.State2 => .State0)) - XCTAssertFalse(machine.hasRoute(.State2 => .State1)) - XCTAssertFalse(machine.hasRoute(.State2 => .State2)) - } - - func testAddStateRouteMapping_handler() - { - var invokedCount = 0 - var routeMappingDisposable: Disposable? - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 & 0 => 2 - routeMappingDisposable = machine.addStateRouteMapping({ fromState, userInfo -> [MyState]? in - - if fromState == .State0 { - return [.State1, .State2] - } - else { - return nil - } - - }, handler: { context in - invokedCount += 1 - }) - - } - - // initial - XCTAssertEqual(machine.state, MyState.State0) - XCTAssertEqual(invokedCount, 0) - - // 0 => 1 - machine <- .State1 - XCTAssertEqual(machine.state, MyState.State1) - XCTAssertEqual(invokedCount, 1) - - // remove routeMapping - routeMappingDisposable?.dispose() - - // 1 => 2 (fails) - machine <- .State1 - XCTAssertEqual(machine.state, MyState.State1) - XCTAssertEqual(invokedCount, 1) - - } - - /// Test `Event/StateRouteMapping`s. - func testAddBothRouteMappings() - { - var routeMappingDisposable: Disposable? - - let machine = StateMachine(state: .State0) { machine in - - // add 0 => 1 & 0 => 2 - routeMappingDisposable = machine.addStateRouteMapping { fromState, userInfo -> [MyState]? in - if fromState == .State0 { - return [.State1, .State2] - } - else { - return nil - } - } - - // add 1 => 0 (can also use `RouteMapping` closure for single-`toState`) - machine.addRouteMapping { event, fromState, userInfo -> MyState? in - guard event == nil else { return nil } - - if fromState == .State1 { - return .State0 - } - else { - return nil - } - } - } - - XCTAssertFalse(machine.hasRoute(.State0 => .State0)) - XCTAssertTrue(machine.hasRoute(.State0 => .State1)) - XCTAssertTrue(machine.hasRoute(.State0 => .State2)) - XCTAssertTrue(machine.hasRoute(.State1 => .State0)) - XCTAssertFalse(machine.hasRoute(.State1 => .State1)) - XCTAssertFalse(machine.hasRoute(.State1 => .State2)) - XCTAssertFalse(machine.hasRoute(.State2 => .State0)) - XCTAssertFalse(machine.hasRoute(.State2 => .State1)) - XCTAssertFalse(machine.hasRoute(.State2 => .State2)) - - // remove routeMapping - routeMappingDisposable?.dispose() - - XCTAssertFalse(machine.hasRoute(.State0 => .State0)) - XCTAssertFalse(machine.hasRoute(.State0 => .State1)) - XCTAssertFalse(machine.hasRoute(.State0 => .State2)) - XCTAssertTrue(machine.hasRoute(.State1 => .State0)) - XCTAssertFalse(machine.hasRoute(.State1 => .State1)) - XCTAssertFalse(machine.hasRoute(.State1 => .State2)) - XCTAssertFalse(machine.hasRoute(.State2 => .State0)) - XCTAssertFalse(machine.hasRoute(.State2 => .State1)) - XCTAssertFalse(machine.hasRoute(.State2 => .State2)) - } -} diff --git a/Tests/SwiftStateTests/BasicTests.swift b/Tests/SwiftStateTests/BasicTests.swift new file mode 100644 index 0000000..7dd88fa --- /dev/null +++ b/Tests/SwiftStateTests/BasicTests.swift @@ -0,0 +1,133 @@ +// +// BasicTests.swift +// SwiftState +// +// Created by Yasuhiro Inami on 2014/08/08. +// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. +// + +import SwiftState +import XCTest + +class BasicTests: _TestCase +{ + func testREADME() + { + // setup state machine + let machine = StateMachine(state: .state0) { machine in + + machine.addRoute(.state0 => .state1) + machine.addRoute(.any => .state2) { context in print("Any => 2, msg=\(String(describing: context.userInfo))") } + machine.addRoute(.state2 => .any) { context in print("2 => Any, msg=\(String(describing: context.userInfo))") } + + // add handler (`context = (event, fromState, toState, userInfo)`) + machine.addHandler(.state0 => .state1) { context in + print("0 => 1") + } + + // add errorHandler + machine.addErrorHandler { context in + print("[ERROR] \(context.fromState) => \(context.toState)") + } + } + + // initial + XCTAssertEqual(machine.state, MyState.state0) + + // tryState 0 => 1 => 2 => 1 => 0 + + machine <- .state1 + XCTAssertEqual(machine.state, MyState.state1) + + machine <- (.state2, "Hello") + XCTAssertEqual(machine.state, MyState.state2) + + machine <- (.state1, "Bye") + XCTAssertEqual(machine.state, MyState.state1) + + machine <- .state0 // fail: no 1 => 0 + XCTAssertEqual(machine.state, MyState.state1) + } + + func testREADME_tryEvent() + { + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + // add event handler + machine.addHandler(event: .event0) { context in + print(".event0 triggered!") + } + } + + // initial + XCTAssertEqual(machine.state, MyState.state0) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2) + + // tryEvent (fails) + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2, "Event0 doesn't have 2 => Any") + } + + func testREADME_routeMapping() + { + let machine = Machine(state: .str("initial")) { machine in + + machine.addRouteMapping { event, fromState, userInfo -> StrState? in + // no route for no-event + guard let event = event else { return nil } + + switch (event, fromState) { + case (.str("gogogo"), .str("initial")): + return .str("Phase 1") + case (.str("gogogo"), .str("Phase 1")): + return .str("Phase 2") + case (.str("finish"), .str("Phase 2")): + return .str("end") + default: + return nil + } + } + + } + + // initial + XCTAssertEqual(machine.state, StrState.str("initial")) + + // tryEvent (fails) + machine <-! .str("go?") + XCTAssertEqual(machine.state, StrState.str("initial"), "No change.") + + // tryEvent + machine <-! .str("gogogo") + XCTAssertEqual(machine.state, StrState.str("Phase 1")) + + // tryEvent (fails) + machine <-! .str("finish") + XCTAssertEqual(machine.state, StrState.str("Phase 1"), "No change.") + + // tryEvent + machine <-! .str("gogogo") + XCTAssertEqual(machine.state, StrState.str("Phase 2")) + + // tryEvent (fails) + machine <-! .str("gogogo") + XCTAssertEqual(machine.state, StrState.str("Phase 2"), "No change.") + + // tryEvent + machine <-! .str("finish") + XCTAssertEqual(machine.state, StrState.str("end")) + } +} diff --git a/Tests/EventTests.swift b/Tests/SwiftStateTests/EventTests.swift similarity index 57% rename from Tests/EventTests.swift rename to Tests/SwiftStateTests/EventTests.swift index 0d1e49a..4626005 100644 --- a/Tests/EventTests.swift +++ b/Tests/SwiftStateTests/EventTests.swift @@ -13,27 +13,27 @@ class EventTests: _TestCase { func testInit_event() { - let event = Event(rawValue: .Event0) - XCTAssertTrue(event == .Event0) - XCTAssertTrue(.Event0 == event) + let event = Event(rawValue: .event0) + XCTAssertTrue(event == .event0) + XCTAssertTrue(.event0 == event) } func testInit_nil() { let event = Event(rawValue: nil) - XCTAssertTrue(event == .Any) - XCTAssertTrue(.Any == event) + XCTAssertTrue(event == .any) + XCTAssertTrue(.any == event) } func testRawValue_event() { - let event = Event.Some(.Event0) - XCTAssertTrue(event.rawValue == .Event0) + let event = Event.some(.event0) + XCTAssertTrue(event.rawValue == .event0) } func testRawValue_any() { - let event = Event.Any + let event = Event.any XCTAssertTrue(event.rawValue == nil) } } diff --git a/Tests/SwiftStateTests/HierarchicalMachineTests.swift b/Tests/SwiftStateTests/HierarchicalMachineTests.swift new file mode 100644 index 0000000..aa18eba --- /dev/null +++ b/Tests/SwiftStateTests/HierarchicalMachineTests.swift @@ -0,0 +1,285 @@ +// +// HierarchicalMachineTests.swift +// SwiftState +// +// Created by Yasuhiro Inami on 2015-11-29. +// Copyright © 2015 Yasuhiro Inami. All rights reserved. +// + +import SwiftState +import XCTest + +private enum _MainState: StateType +{ + case mainState0 + case subMachine1(_SubState) + case subMachine2(_SubState) + + func hash(into hasher: inout Hasher) + { + switch self { + case .mainState0: + hasher.combine("MainState0".hashValue) + case let .subMachine1(state): + hasher.combine("SubMachine1-\(state)".hashValue) + case let .subMachine2(state): + hasher.combine("SubMachine2-\(state)".hashValue) + } + } +} + +private enum _SubState: StateType +{ + case subState0, subState1, subState2 +} + +private func == (lhs: _MainState, rhs: _MainState) -> Bool +{ + switch (lhs, rhs) { + case (.mainState0, .mainState0): + return true + case let (.subMachine1(state1), .subMachine1(state2)): + return state1 == state2 + case let (.subMachine2(state1), .subMachine2(state2)): + return state1 == state2 + default: + return false + } +} + +class HierarchicalMachineTests: _TestCase +{ + /// + /// Hierarchical state machine. + /// + /// - mainMachine + /// - MainState0 (initial) + /// - subMachine1 + /// - subState0 + /// - subState1 + /// - subMachine2 + /// - subState0 + /// - subState1 + /// + /// - Warning: + /// This is a naive implementation and easily lose consistency when `subMachine.state` is changed directly, e.g. `subMachine1 <- .subState1`. + /// + private var mainMachine: StateMachine<_MainState, NoEvent>? + + private var subMachine1: StateMachine<_SubState, NoEvent>? + private var subMachine2: StateMachine<_SubState, NoEvent>? + + override func setUp() + { + super.setUp() + + let subMachine1 = StateMachine<_SubState, NoEvent>(state: .subState0) { subMachine1 in + // add Sub1-0 => Sub1-1 + subMachine1.addRoute(.subState0 => .subState1) + + subMachine1.addHandler(.any => .any) { print("[Sub1] \($0.fromState) => \($0.toState)") } + subMachine1.addErrorHandler { print("[ERROR][Sub1] \($0.fromState) => \($0.toState)") } + } + + let subMachine2 = StateMachine<_SubState, NoEvent>(state: .subState0) { subMachine2 in + // add Sub2-0 => Sub2-1 + subMachine2.addRoute(.subState0 => .subState1) + + subMachine2.addHandler(.any => .any) { print("[Sub2] \($0.fromState) => \($0.toState)") } + subMachine2.addErrorHandler { print("[ERROR][Sub2] \($0.fromState) => \($0.toState)") } + } + + let mainMachine = StateMachine<_MainState, NoEvent>(state: .mainState0) { mainMachine in + + // add routes & handle for same-subMachine internal transitions + mainMachine.addRoute(.any => .any, condition: { context in + + switch (context.fromState, context.toState) { + case let (.subMachine1(state1), .subMachine1(state2)): + return subMachine1.hasRoute(fromState: state1, toState: state2) + case let (.subMachine2(state1), .subMachine2(state2)): + return subMachine2.hasRoute(fromState: state1, toState: state2) + default: + return false + } + + }, handler: { context in + switch (context.fromState, context.toState) { + case let (.subMachine1, .subMachine1(state2)): + subMachine1 <- state2 + case let (.subMachine2, .subMachine2(state2)): + subMachine2 <- state2 + default: + break + } + }) + + // add routes for mainMachine-state transitions (submachine switching) + mainMachine.addRouteMapping { event, fromState, userInfo -> _MainState? in + + // NOTE: use external submachine's states only for evaluating `toState`, but not for `fromState` + switch fromState { + // "Main0" => "Sub1-0" + case .mainState0 where subMachine1.state == .subState0: + return .subMachine1(.subState0) + + // "Sub1-1" => "Sub2-0" + case let .subMachine1(state) where state == .subState1 && subMachine2.state == .subState0: + return .subMachine2(.subState0) + + // "Sub2-1" => "Main0" + case let .subMachine2(state) where state == .subState1: + return .mainState0 + + default: + return nil + } + + } + + mainMachine.addHandler(.any => .any) { print("[Main] \($0.fromState) => \($0.toState)") } + mainMachine.addErrorHandler { print("[ERROR][Main] \($0.fromState) => \($0.toState)") } + } + + self.mainMachine = mainMachine + self.subMachine1 = subMachine1 + self.subMachine2 = subMachine2 + } + + /// `mainMachine.hasRoute()` test to check submachine's internal routes + func testHasRoute_submachine_internal() + { + let mainMachine = self.mainMachine! + let subMachine1 = self.subMachine1! + let subMachine2 = self.subMachine2! + + // initial + XCTAssertTrue(mainMachine.state == .mainState0) + XCTAssertTrue(subMachine1.state == .subState0) + XCTAssertTrue(subMachine2.state == .subState0) + + // subMachine1 internal routes + XCTAssertFalse(mainMachine.hasRoute(.subMachine1(.subState0) => .subMachine1(.subState0))) + XCTAssertTrue(mainMachine.hasRoute(.subMachine1(.subState0) => .subMachine1(.subState1))) + XCTAssertFalse(mainMachine.hasRoute(.subMachine1(.subState1) => .subMachine1(.subState0))) + XCTAssertFalse(mainMachine.hasRoute(.subMachine1(.subState1) => .subMachine1(.subState1))) + + // subMachine2 internal routes + XCTAssertFalse(mainMachine.hasRoute(.subMachine2(.subState0) => .subMachine2(.subState0))) + XCTAssertTrue(mainMachine.hasRoute(.subMachine2(.subState0) => .subMachine2(.subState1))) + XCTAssertFalse(mainMachine.hasRoute(.subMachine2(.subState1) => .subMachine2(.subState0))) + XCTAssertFalse(mainMachine.hasRoute(.subMachine2(.subState1) => .subMachine2(.subState1))) + } + + /// `mainMachine.hasRoute()` test to check switchable submachines + func testHasRoute_submachine_switching() + { + let mainMachine = self.mainMachine! + let subMachine1 = self.subMachine1! + let subMachine2 = self.subMachine2! + + // NOTE: mainMachine can check switchable submachines + // (external routes between submachines = SubState1, SubState2, or nil) + + // initial + XCTAssertTrue(mainMachine.state == .mainState0) + XCTAssertTrue(subMachine1.state == .subState0) + XCTAssertTrue(subMachine2.state == .subState0) + + // from Main0 + XCTAssertTrue(mainMachine.hasRoute(.mainState0 => .subMachine1(.subState0))) + XCTAssertFalse(mainMachine.hasRoute(.mainState0 => .subMachine1(.subState1))) + XCTAssertFalse(mainMachine.hasRoute(.mainState0 => .subMachine2(.subState0))) + XCTAssertFalse(mainMachine.hasRoute(.mainState0 => .subMachine2(.subState1))) + + // from Sub1-0 + XCTAssertFalse(mainMachine.hasRoute(.subMachine1(.subState0) => .subMachine2(.subState0))) + XCTAssertFalse(mainMachine.hasRoute(.subMachine1(.subState0) => .subMachine2(.subState1))) + XCTAssertFalse(mainMachine.hasRoute(.subMachine1(.subState0) => .mainState0)) + + // from Sub1-1 + XCTAssertTrue(mainMachine.hasRoute(.subMachine1(.subState1) => .subMachine2(.subState0))) + XCTAssertFalse(mainMachine.hasRoute(.subMachine1(.subState1) => .subMachine2(.subState1))) + XCTAssertFalse(mainMachine.hasRoute(.subMachine1(.subState1) => .mainState0)) + + // from Sub2-0 + XCTAssertFalse(mainMachine.hasRoute(.subMachine2(.subState0) => .subMachine1(.subState0))) + XCTAssertFalse(mainMachine.hasRoute(.subMachine2(.subState0) => .subMachine1(.subState1))) + XCTAssertFalse(mainMachine.hasRoute(.subMachine2(.subState0) => .mainState0)) + + // from Sub2-1 + XCTAssertFalse(mainMachine.hasRoute(.subMachine2(.subState1) => .subMachine1(.subState0))) + XCTAssertFalse(mainMachine.hasRoute(.subMachine2(.subState1) => .subMachine1(.subState1))) + XCTAssertTrue(mainMachine.hasRoute(.subMachine2(.subState1) => .mainState0)) + + } + + func testTryState() + { + let mainMachine = self.mainMachine! + + // initial + XCTAssertTrue(mainMachine.state == .mainState0) + + // Main0 => Sub1-0 + mainMachine <- .subMachine1(.subState0) + XCTAssertTrue(mainMachine.state == .subMachine1(.subState0)) + + // Sub1-0 => Sub1-1 (Sub1 internal transition) + mainMachine <- .subMachine1(.subState1) + XCTAssertTrue(mainMachine.state == .subMachine1(.subState1)) + + // Sub1-1 => Sub1-2 (Sub1 internal transition, but fails) + mainMachine <- .subMachine1(.subState2) + XCTAssertTrue(mainMachine.state == .subMachine1(.subState1), "No change.") + + // Sub1-1 => Sub2-2 (fails) + mainMachine <- .subMachine2(.subState2) + XCTAssertTrue(mainMachine.state == .subMachine1(.subState1), "No change.") + + // Sub1-1 => Sub2-0 + mainMachine <- .subMachine2(.subState0) + XCTAssertTrue(mainMachine.state == .subMachine2(.subState0)) + + // Sub2-0 => Main0 (fails) + mainMachine <- .mainState0 + XCTAssertTrue(mainMachine.state == .subMachine2(.subState0), "No change.") + + // Sub2-0 => Sub2-1 + mainMachine <- .subMachine2(.subState1) + XCTAssertTrue(mainMachine.state == .subMachine2(.subState1)) + + // Sub2-1 => Main + mainMachine <- .mainState0 + XCTAssertTrue(mainMachine.state == .mainState0) + + } + + func testAddHandler() + { + let mainMachine = self.mainMachine! + + var didPass = false + + // NOTE: this handler is added to mainMachine and doesn't make submachines dirty + mainMachine.addHandler(.mainState0 => .subMachine1(.subState0)) { context in + print("[Main] 1-1 => 1-2 (specific)") + didPass = true + } + + // initial + XCTAssertTrue(mainMachine.state == .mainState0) + XCTAssertFalse(didPass) + + // Main0 => Sub1-1 (fails) + mainMachine <- .subMachine1(.subState1) + XCTAssertTrue(mainMachine.state == .mainState0, "No change.") + XCTAssertFalse(didPass) + + // Main0 => Sub1-0 + mainMachine <- .subMachine1(.subState0) + XCTAssertTrue(mainMachine.state == .subMachine1(.subState0)) + XCTAssertTrue(didPass) + } + +} diff --git a/Tests/Info.plist b/Tests/SwiftStateTests/Info.plist similarity index 100% rename from Tests/Info.plist rename to Tests/SwiftStateTests/Info.plist diff --git a/Tests/SwiftStateTests/MachineTests.swift b/Tests/SwiftStateTests/MachineTests.swift new file mode 100644 index 0000000..3995481 --- /dev/null +++ b/Tests/SwiftStateTests/MachineTests.swift @@ -0,0 +1,540 @@ +// +// MachineTests.swift +// SwiftState +// +// Created by Yasuhiro Inami on 2014/08/05. +// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. +// + +import SwiftState +import XCTest + +class MachineTests: _TestCase +{ + func testConfigure() + { + let machine = Machine(state: .state0) + + machine.configure { + $0.addRoutes(event: .event0, transitions: [ .state0 => .state1 ]) + } + + XCTAssertTrue(machine.canTryEvent(.event0) != nil) + } + + //-------------------------------------------------- + // MARK: - tryEvent a.k.a `<-!` + //-------------------------------------------------- + + func testCanTryEvent() + { + let machine = Machine(state: .state0) + + // add 0 => 1 & 1 => 2 + // (NOTE: this is not chaining e.g. 0 => 1 => 2) + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + XCTAssertTrue(machine.canTryEvent(.event0) != nil) + } + + func testTryEvent() + { + let machine = Machine(state: .state0) { machine in + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + } + + // initial + XCTAssertEqual(machine.state, MyState.state0) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2, "Event0 doesn't have 2 => Any") + } + + func testTryEvent_userInfo() + { + var userInfo: Any? = nil + + let machine = Machine(state: .state0) { machine in + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ], handler: { context in + userInfo = context.userInfo + }) + } + + // initial + XCTAssertEqual(machine.state, MyState.state0) + XCTAssertNil(userInfo) + + // tryEvent + machine <-! (.event0, "gogogo") + XCTAssertEqual(machine.state, MyState.state1) + XCTAssertTrue(userInfo as? String == "gogogo") + + // tryEvent + machine <-! (.event0, "done") + XCTAssertEqual(machine.state, MyState.state2) + XCTAssertTrue(userInfo as? String == "done") + } + + func testTryEvent_twice() + { + let machine = Machine(state: .state0) { machine in + // add 0 => 1 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + ]) + // add 0 => 1 + machine.addRoutes(event: .event1, transitions: [ + .state1 => .state2, + ]) + } + + // tryEvent (twice) + machine <-! .event0 <-! .event1 + XCTAssertEqual(machine.state, MyState.state2) + } + + func testTryEvent_string() + { + let machine = Machine(state: .state0) + + // add 0 => 1 => 2 + machine.addRoutes(event: "Run", transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + // tryEvent + machine <-! "Run" + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! "Run" + XCTAssertEqual(machine.state, MyState.state2) + + // tryEvent + machine <-! "Run" + XCTAssertEqual(machine.state, MyState.state2, "Event=Run doesn't have 2 => Any") + } + + // https://github.com/ReactKit/SwiftState/issues/20 + func testTryEvent_issue20() + { + let machine = Machine(state: MyState.state2) { machine in + machine.addRoutes(event: .event0, transitions: [.any => .state0]) + } + + XCTAssertEqual(machine.state, MyState.state2) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state0) + } + + // Fix for transitioning of routes w/ multiple from-states + // https://github.com/ReactKit/SwiftState/pull/32 + func testTryEvent_issue32() + { + let machine = Machine(state: .state0) { machine in + machine.addRoutes(event: .event0, transitions: [ .state0 => .state1 ]) + machine.addRoutes(event: .event1, routes: [ [ .state1, .state2 ] => .state3 ]) + } + + XCTAssertEqual(machine.state, MyState.state0) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event1 + XCTAssertEqual(machine.state, MyState.state3) + } + + //-------------------------------------------------- + // MARK: - add/removeRoute + //-------------------------------------------------- + + func testAddRoute_multiple() + { + let machine = Machine(state: .state0) { machine in + + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + // add 2 => 1 => 0 + machine.addRoutes(event: .event1, transitions: [ + .state2 => .state1, + .state1 => .state0, + ]) + } + + // initial + XCTAssertEqual(machine.state, MyState.state0) + + // tryEvent + machine <-! .event1 + XCTAssertEqual(machine.state, MyState.state0, "Event1 doesn't have 0 => Any.") + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2, "Event0 doesn't have 2 => Any.") + + // tryEvent + machine <-! .event1 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event1 + XCTAssertEqual(machine.state, MyState.state0) + } + + func testAddRoute_handler() + { + var invokeCount = 0 + + let machine = Machine(state: .state0) { machine in + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ], handler: { context in + invokeCount += 1 + return + }) + } + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2) + + XCTAssertEqual(invokeCount, 2) + } + + func testRemoveRoute() + { + var invokeCount = 0 + + let machine = Machine(state: .state0) { machine in + + // add 0 => 1 => 2 + let routeDisposable = machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + machine.addHandler(event: .event0) { context in + invokeCount += 1 + return + } + + // removeRoute + routeDisposable.dispose() + + } + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state0, "Route should be removed.") + + XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") + } + + func testRemoveRoute_handler() + { + var invokeCount = 0 + + let machine = Machine(state: .state0) { machine in + + // add 0 => 1 => 2 + let routeDisposable = machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ], handler: { context in + invokeCount += 1 + return + }) + + // removeRoute + routeDisposable.dispose() + + } + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state0, "Route should be removed.") + + XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") + } + + //-------------------------------------------------- + // MARK: - add/removeHandler + //-------------------------------------------------- + + func testAddHandler() + { + var invokeCount = 0 + + let machine = Machine(state: .state0) { machine in + + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + machine.addHandler(event: .event0) { context in + invokeCount += 1 + return + } + + } + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2) + + XCTAssertEqual(invokeCount, 2) + } + + func testAddErrorHandler() + { + var invokeCount = 0 + + let machine = Machine(state: .state0) { machine in + machine.addRoutes(event: .event0, transitions: [ .state0 => .state1 ]) + machine.addErrorHandler { _ in + invokeCount += 1 + } + } + + XCTAssertEqual(invokeCount, 0) + + // tryEvent (fails) + machine <-! .event1 + + XCTAssertEqual(invokeCount, 1, "Error handler should be called.") + + } + + func testRemoveHandler() + { + var invokeCount = 0 + + let machine = Machine(state: .state0) { machine in + + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + let handlerDisposable = machine.addHandler(event: .event0) { context in + invokeCount += 1 + return + } + + // remove handler + handlerDisposable.dispose() + + } + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1, "0 => 1 should be succesful") + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2, "1 => 2 should be succesful") + + XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") + } + + //-------------------------------------------------- + // MARK: - RouteMapping + //-------------------------------------------------- + + func testAddRouteMapping() + { + var invokeCount = 0 + + let machine = Machine(state: .str("initial")) { machine in + + machine.addRouteMapping { event, fromState, userInfo -> StrState? in + // no route for no-event + guard let event = event else { return nil } + + switch (event, fromState) { + case (.str("gogogo"), .str("initial")): + return .str("Phase 1") + case (.str("gogogo"), .str("Phase 1")): + return .str("Phase 2") + case (.str("finish"), .str("Phase 2")): + return .str("end") + default: + return nil + } + } + + machine.addHandler(event: .str("gogogo")) { context in + invokeCount += 1 + return + } + + } + + // initial + XCTAssertEqual(machine.state, StrState.str("initial")) + + // tryEvent (fails) + machine <-! .str("go?") + XCTAssertEqual(machine.state, StrState.str("initial"), "No change.") + XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") + + // tryEvent + machine <-! .str("gogogo") + XCTAssertEqual(machine.state, StrState.str("Phase 1")) + XCTAssertEqual(invokeCount, 1) + + // tryEvent (fails) + machine <-! .str("finish") + XCTAssertEqual(machine.state, StrState.str("Phase 1"), "No change.") + XCTAssertEqual(invokeCount, 1, "Handler should NOT be performed") + + // tryEvent + machine <-! .str("gogogo") + XCTAssertEqual(machine.state, StrState.str("Phase 2")) + XCTAssertEqual(invokeCount, 2) + + // tryEvent (fails) + machine <-! .str("gogogo") + XCTAssertEqual(machine.state, StrState.str("Phase 2"), "No change.") + XCTAssertEqual(invokeCount, 2, "Handler should NOT be performed") + + // tryEvent + machine <-! .str("finish") + XCTAssertEqual(machine.state, StrState.str("end")) + XCTAssertEqual(invokeCount, 2, "gogogo-Handler should NOT be performed") + + } + + func testAddRouteMapping_handler() + { + var invokeCount1 = 0 + var invokeCount2 = 0 + var disposables = [Disposable]() + + let machine = Machine(state: .str("initial")) { machine in + + let d = machine.addRouteMapping({ event, fromState, userInfo -> StrState? in + // no route for no-event + guard let event = event else { return nil } + + switch (event, fromState) { + case (.str("gogogo"), .str("initial")): + return .str("Phase 1") + default: + return nil + } + }, handler: { context in + invokeCount1 += 1 + }) + + disposables += [d] + + let d2 = machine.addRouteMapping({ event, fromState, userInfo -> StrState? in + // no route for no-event + guard let event = event else { return nil } + + switch (event, fromState) { + case (.str("finish"), .str("Phase 1")): + return .str("end") + default: + return nil + } + }, handler: { context in + invokeCount2 += 1 + }) + + disposables += [d2] + + } + + // initial + XCTAssertEqual(machine.state, StrState.str("initial")) + + // tryEvent (fails) + machine <-! .str("go?") + XCTAssertEqual(machine.state, StrState.str("initial"), "No change.") + XCTAssertEqual(invokeCount1, 0) + XCTAssertEqual(invokeCount2, 0) + + // tryEvent + machine <-! .str("gogogo") + XCTAssertEqual(machine.state, StrState.str("Phase 1")) + XCTAssertEqual(invokeCount1, 1) + XCTAssertEqual(invokeCount2, 0) + + // tryEvent (fails) + machine <-! .str("gogogo") + XCTAssertEqual(machine.state, StrState.str("Phase 1"), "No change.") + XCTAssertEqual(invokeCount1, 1) + XCTAssertEqual(invokeCount2, 0) + + // tryEvent + machine <-! .str("finish") + XCTAssertEqual(machine.state, StrState.str("end")) + XCTAssertEqual(invokeCount1, 1) + XCTAssertEqual(invokeCount2, 1) + + // hasRoute (before dispose) + XCTAssertEqual(machine.hasRoute(event: .str("gogogo"), transition: .str("initial") => .str("Phase 1")), true) + XCTAssertEqual(machine.hasRoute(event: .str("finish"), transition: .str("Phase 1") => .str("end")), true) + + disposables.forEach { $0.dispose() } + + // hasRoute (after dispose) + XCTAssertEqual(machine.hasRoute(event: .str("gogogo"), transition: .str("initial") => .str("Phase 1")), false, "Routes & handlers should be disposed.") + XCTAssertEqual(machine.hasRoute(event: .str("finish"), transition: .str("Phase 1") => .str("end")), false, "Routes & handlers should be disposed.") + + } + +} diff --git a/Tests/SwiftStateTests/MiscTests.swift b/Tests/SwiftStateTests/MiscTests.swift new file mode 100644 index 0000000..5b1fe90 --- /dev/null +++ b/Tests/SwiftStateTests/MiscTests.swift @@ -0,0 +1,194 @@ +// +// MiscTests.swift +// SwiftState +// +// Created by Yasuhiro Inami on 2015-12-05. +// Copyright © 2015 Yasuhiro Inami. All rights reserved. +// + +import SwiftState +import XCTest + +/// Unarranged tests. +class MiscTests: _TestCase +{ + func testREADME_string() + { + let machine = StateMachine(state: ".State0") { machine in + + machine.addRoute(".State0" => ".State1") + machine.addRoute(.any => ".State2") { context in print("Any => 2, msg=\(String(describing: context.userInfo))") } + machine.addRoute(".State2" => .any) { context in print("2 => Any, msg=\(String(describing: context.userInfo))") } + + // add handler (handlerContext = (event, transition, order, userInfo)) + machine.addHandler(".State0" => ".State1") { context in + print("0 => 1") + } + + // add errorHandler + machine.addErrorHandler { context in + print("[ERROR] \(context.fromState) => \(context.toState)") + } + } + + // tryState 0 => 1 => 2 => 1 => 0 + + machine <- ".State1" + XCTAssertEqual(machine.state, ".State1") + + machine <- (".State2", "Hello") + XCTAssertEqual(machine.state, ".State2") + + machine <- (".State1", "Bye") + XCTAssertEqual(machine.state, ".State1") + + machine <- ".State0" // fail: no 1 => 0 + XCTAssertEqual(machine.state, ".State1") + + print("machine.state = \(machine.state)") + } + + // StateType + associated value + func testREADME_associatedValue() + { + let machine = StateMachine(state: .str("0")) { machine in + + machine.addRoute(.str("0") => .str("1")) + machine.addRoute(.any => .str("2")) { context in print("Any => 2, msg=\(String(describing: context.userInfo))") } + machine.addRoute(.str("2") => .any) { context in print("2 => Any, msg=\(String(describing: context.userInfo))") } + + // add handler (handlerContext = (event, transition, order, userInfo)) + machine.addHandler(.str("0") => .str("1")) { context in + print("0 => 1") + } + + // add errorHandler + machine.addErrorHandler { context in + print("[ERROR] \(context.fromState) => \(context.toState)") + } + } + + // tryState 0 => 1 => 2 => 1 => 0 + + machine <- .str("1") + XCTAssertEqual(machine.state, StrState.str("1")) + + machine <- (.str("2"), "Hello") + XCTAssertEqual(machine.state, StrState.str("2")) + + machine <- (.str("1"), "Bye") + XCTAssertEqual(machine.state, StrState.str("1")) + + machine <- .str("0") // fail: no 1 => 0 + XCTAssertEqual(machine.state, StrState.str("1")) + + print("machine.state = \(machine.state)") + } + + func testExample() + { + let machine = StateMachine(state: .state0) { + + // add 0 => 1 + $0.addRoute(.state0 => .state1) { context in + print("[Transition 0=>1] \(context.fromState) => \(context.toState)") + } + // add 0 => 1 once more + $0.addRoute(.state0 => .state1) { context in + print("[Transition 0=>1b] \(context.fromState) => \(context.toState)") + } + // add 2 => Any + $0.addRoute(.state2 => .any) { context in + print("[Transition exit 2] \(context.fromState) => \(context.toState) (Any)") + } + // add Any => 2 + $0.addRoute(.any => .state2) { context in + print("[Transition Entry 2] \(context.fromState) (Any) => \(context.toState)") + } + // add 1 => 0 (no handler) + $0.addRoute(.state1 => .state0) + + } + + // 0 => 1 + XCTAssertTrue(machine.hasRoute(.state0 => .state1)) + + // 1 => 0 + XCTAssertTrue(machine.hasRoute(.state1 => .state0)) + + // 2 => Any + XCTAssertTrue(machine.hasRoute(.state2 => .state0)) + XCTAssertTrue(machine.hasRoute(.state2 => .state1)) + XCTAssertTrue(machine.hasRoute(.state2 => .state2)) + XCTAssertTrue(machine.hasRoute(.state2 => .state3)) + + // Any => 2 + XCTAssertTrue(machine.hasRoute(.state0 => .state2)) + XCTAssertTrue(machine.hasRoute(.state1 => .state2)) + XCTAssertTrue(machine.hasRoute(.state3 => .state2)) + + // others + XCTAssertFalse(machine.hasRoute(.state0 => .state0)) + XCTAssertFalse(machine.hasRoute(.state0 => .state3)) + XCTAssertFalse(machine.hasRoute(.state1 => .state1)) + XCTAssertFalse(machine.hasRoute(.state1 => .state3)) + XCTAssertFalse(machine.hasRoute(.state3 => .state0)) + XCTAssertFalse(machine.hasRoute(.state3 => .state1)) + XCTAssertFalse(machine.hasRoute(.state3 => .state3)) + + machine.configure { + + // add error handlers + $0.addErrorHandler { context in + print("[ERROR 1] \(context.fromState) => \(context.toState)") + } + + // add entry handlers + $0.addHandler(.any => .state0) { context in + print("[Entry 0] \(context.fromState) => \(context.toState)") // NOTE: this should not be called + } + $0.addHandler(.any => .state1) { context in + print("[Entry 1] \(context.fromState) => \(context.toState)") + } + $0.addHandler(.any => .state2) { context in + print("[Entry 2] \(context.fromState) => \(context.toState), userInfo = \(String(describing: context.userInfo))") + } + $0.addHandler(.any => .state2) { context in + print("[Entry 2b] \(context.fromState) => \(context.toState), userInfo = \(String(describing: context.userInfo))") + } + + // add exit handlers + $0.addHandler(.state0 => .any) { context in + print("[Exit 0] \(context.fromState) => \(context.toState)") + } + $0.addHandler(.state1 => .any) { context in + print("[Exit 1] \(context.fromState) => \(context.toState)") + } + $0.addHandler(.state2 => .any) { context in + print("[Exit 2] \(context.fromState) => \(context.toState), userInfo = \(String(describing: context.userInfo))") + } + $0.addHandler(.state2 => .any) { context in + print("[Exit 2b] \(context.fromState) => \(context.toState), userInfo = \(String(describing: context.userInfo))") + } + } + + XCTAssertEqual(machine.state, MyState.state0) + + // tryState 0 => 1 => 2 => 1 => 0 => 3 + + machine <- .state1 + XCTAssertEqual(machine.state, MyState.state1) + + machine <- (.state2, "State2 activate") + XCTAssertEqual(machine.state, MyState.state2) + + machine <- (.state1, "State2 deactivate") + XCTAssertEqual(machine.state, MyState.state1) + + machine <- .state0 + XCTAssertEqual(machine.state, MyState.state0) + + machine <- .state3 + XCTAssertEqual(machine.state, MyState.state0, "No 0 => 3.") + } +} diff --git a/Tests/MyEvent.swift b/Tests/SwiftStateTests/MyEvent.swift similarity index 69% rename from Tests/MyEvent.swift rename to Tests/SwiftStateTests/MyEvent.swift index 15d16d6..563a78a 100644 --- a/Tests/MyEvent.swift +++ b/Tests/SwiftStateTests/MyEvent.swift @@ -10,17 +10,17 @@ import SwiftState enum MyEvent: EventType { - case Event0, Event1 + case event0, event1 } enum StrEvent: EventType { - case Str(String) + case str(String) - var hashValue: Int + func hash(into hasher: inout Hasher) { switch self { - case .Str(let str): return str.hashValue + case .str(let str): hasher.combine(str.hashValue) } } } @@ -28,7 +28,7 @@ enum StrEvent: EventType func == (lhs: StrEvent, rhs: StrEvent) -> Bool { switch (lhs, rhs) { - case let (.Str(str1), .Str(str2)): + case let (.str(str1), .str(str2)): return str1 == str2 // default: // return false diff --git a/Tests/MyState.swift b/Tests/SwiftStateTests/MyState.swift similarity index 65% rename from Tests/MyState.swift rename to Tests/SwiftStateTests/MyState.swift index 104b37a..7425bb6 100644 --- a/Tests/MyState.swift +++ b/Tests/SwiftStateTests/MyState.swift @@ -10,17 +10,17 @@ import SwiftState enum MyState: StateType { - case State0, State1, State2, State3 + case state0, state1, state2, state3 } enum StrState: StateType { - case Str(String) + case str(String) - var hashValue: Int + func hash(into hasher: inout Hasher) { switch self { - case .Str(let str): return str.hashValue + case .str(let str): hasher.combine(str.hashValue) } } } @@ -28,7 +28,7 @@ enum StrState: StateType func == (lhs: StrState, rhs: StrState) -> Bool { switch (lhs, rhs) { - case let (.Str(str1), .Str(str2)): + case let (.str(str1), .str(str2)): return str1 == str2 } } diff --git a/Tests/QiitaTests.swift b/Tests/SwiftStateTests/QiitaTests.swift similarity index 61% rename from Tests/QiitaTests.swift rename to Tests/SwiftStateTests/QiitaTests.swift index ae16e79..5681945 100644 --- a/Tests/QiitaTests.swift +++ b/Tests/SwiftStateTests/QiitaTests.swift @@ -15,8 +15,8 @@ import XCTest enum InputKey: StateType { - case None - case Key0, Key1, Key2, Key3, Key4, Key5, Key6, Key7, Key8, Key9 + case none + case key0, key1, key2, key3, key4, key5, key6, key7, key8, key9 } class QiitaTests: _TestCase @@ -25,32 +25,32 @@ class QiitaTests: _TestCase { var success = false - let machine = StateMachine(state: .None) { machine in + let machine = StateMachine(state: .none) { machine in // connect all states - machine.addRoute(.Any => .Any) + machine.addRoute(.any => .any) // success = true only when transitionChain 2 => 3 => 5 => 7 is fulfilled - machine.addRouteChain(.Key2 => .Key3 => .Key5 => .Key7) { context in + machine.addRouteChain(.key2 => .key3 => .key5 => .key7) { context in success = true return } } // tryState - machine <- .Key2 - machine <- .Key3 - machine <- .Key4 - machine <- .Key5 - machine <- .Key6 - machine <- .Key7 + machine <- .key2 + machine <- .key3 + machine <- .key4 + machine <- .key5 + machine <- .key6 + machine <- .key7 XCTAssertFalse(success) - machine <- .Key2 - machine <- .Key3 - machine <- .Key5 - machine <- .Key7 + machine <- .key2 + machine <- .key3 + machine <- .key5 + machine <- .key7 XCTAssertTrue(success) } diff --git a/Tests/RouteChainTests.swift b/Tests/SwiftStateTests/RouteChainTests.swift similarity index 64% rename from Tests/RouteChainTests.swift rename to Tests/SwiftStateTests/RouteChainTests.swift index 2029e0f..edd5546 100644 --- a/Tests/RouteChainTests.swift +++ b/Tests/SwiftStateTests/RouteChainTests.swift @@ -15,10 +15,10 @@ class MachineChainTests: _TestCase { var invokeCount = 0 - let machine = StateMachine(state: .State0) { machine in + let machine = StateMachine(state: .state0) { machine in // add 0 => 1 => 2 - machine.addRouteChain(.State0 => .State1 => .State2) { context in + machine.addRouteChain(.state0 => .state1 => .state2) { context in invokeCount += 1 return } @@ -26,20 +26,20 @@ class MachineChainTests: _TestCase } // tryState 0 => 1 => 2 - machine <- .State1 - machine <- .State2 + machine <- .state1 + machine <- .state2 XCTAssertEqual(invokeCount, 1, "Handler should be performed.") // // reset: tryState 2 => 0 // - machine.addRoute(.State2 => .State0) // make sure to add routes - machine <- .State0 + machine.addRoute(.state2 => .state0) // make sure to add routes + machine <- .state0 // tryState 0 => 1 => 2 again - machine <- .State1 - machine <- .State2 + machine <- .state1 + machine <- .state2 XCTAssertEqual(invokeCount, 2, "Handler should be performed again.") } @@ -49,10 +49,10 @@ class MachineChainTests: _TestCase var flag = false var invokeCount = 0 - let machine = StateMachine(state: .State0) { machine in + let machine = StateMachine(state: .state0) { machine in // add 0 => 1 => 2 - machine.addRouteChain(.State0 => .State1 => .State2, condition: { _ in flag }) { context in + machine.addRouteChain(.state0 => .state1 => .state2, condition: { _ in flag }) { context in invokeCount += 1 return } @@ -60,78 +60,78 @@ class MachineChainTests: _TestCase } // tryState 0 => 1 => 2 - machine <- .State1 - machine <- .State2 + machine <- .state1 + machine <- .state2 XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed because flag=false.") // // reset: tryState 2 => 0 // - machine.addRoute(.State2 => .State0) // make sure to add routes - machine <- .State0 + machine.addRoute(.state2 => .state0) // make sure to add routes + machine <- .state0 flag = true // tryState 0 => 1 => 2 - machine <- .State1 - machine <- .State2 + machine <- .state1 + machine <- .state2 XCTAssertEqual(invokeCount, 1, "Handler should be performed.") } func testAddRouteChain_failBySkipping() { - let machine = StateMachine(state: .State0) { machine in + let machine = StateMachine(state: .state0) { machine in // add 0 => 1 => 2 - machine.addRouteChain(.State0 => .State1 => .State2) { context in + machine.addRouteChain(.state0 => .state1 => .state2) { context in XCTFail("Handler should NOT be performed because 0 => 2 is skipping 1.") } } // tryState 0 => 2 directly (skipping 1) - machine <- .State2 + machine <- .state2 } func testAddRouteChain_failByHangingAround() { - let machine = StateMachine(state: .State0) { machine in + let machine = StateMachine(state: .state0) { machine in // add 0 => 1 => 2 - machine.addRouteChain(.State0 => .State1 => .State2) { context in + machine.addRouteChain(.state0 => .state1 => .state2) { context in XCTFail("Handler should NOT be performed because 0 => 1 => 3 => 2 is hanging around 3.") } - machine.addRoute(.State1 => .State3) // add 1 => 3 route for hanging around + machine.addRoute(.state1 => .state3) // add 1 => 3 route for hanging around } // tryState 0 => 1 => 3 => 2 (hanging around 3) - machine <- .State1 - machine <- .State3 - machine <- .State2 + machine <- .state1 + machine <- .state3 + machine <- .state2 } func testAddRouteChain_succeedByFailingHangingAround() { var invokeCount = 0 - let machine = StateMachine(state: .State0) { machine in + let machine = StateMachine(state: .state0) { machine in // add 0 => 1 => 2 - machine.addRouteChain(.State0 => .State1 => .State2) { context in + machine.addRouteChain(.state0 => .state1 => .state2) { context in invokeCount += 1 return } - // machine.addRoute(.State1 => .State3) // comment-out: 1 => 3 is not possible + // machine.addRoute(.state1 => .state3) // comment-out: 1 => 3 is not possible } // tryState 0 => 1 => 3 => 2 (cannot hang around 3) - machine <- .State1 - machine <- .State3 - machine <- .State2 + machine <- .state1 + machine <- .state3 + machine <- .state2 XCTAssertEqual(invokeCount, 1, "Handler should be performed because 1 => 3 is not registered, thus performing 0 => 1 => 2.") } @@ -140,10 +140,10 @@ class MachineChainTests: _TestCase { var invokeCount = 0 - let machine = StateMachine(state: .State0) { machine in + let machine = StateMachine(state: .state0) { machine in // add 0 => 1 => 2 => 0 (back home) => 2 - machine.addRouteChain(.State0 => .State1 => .State2 => .State0 => .State2) { context in + machine.addRouteChain(.state0 => .state1 => .state2 => .state0 => .state2) { context in invokeCount += 1 return } @@ -151,10 +151,10 @@ class MachineChainTests: _TestCase } // tryState 0 => 1 => 2 => 0 => 2 - machine <- .State1 - machine <- .State2 - machine <- .State0 - machine <- .State2 + machine <- .state1 + machine <- .state2 + machine <- .state0 + machine <- .state2 XCTAssertEqual(invokeCount, 1) } @@ -164,12 +164,12 @@ class MachineChainTests: _TestCase { var invokeCount = 0 - let machine = StateMachine(state: .State0) { machine in + let machine = StateMachine(state: .state0) { machine in - machine.addRoute(.Any => .Any) // connect all states + machine.addRoute(.any => .any) // connect all states // add 0 => 1 => 2 => 0 (back home) => 1 => 2 - machine.addRouteChain(.State0 => .State1 => .State2 => .State0 => .State1 => .State2) { context in + machine.addRouteChain(.state0 => .state1 => .state2 => .state0 => .state1 => .state2) { context in invokeCount += 1 return } @@ -177,24 +177,24 @@ class MachineChainTests: _TestCase } // tryState 0 => 1 => 2 => 0 => 1 => 0 => 2 - machine <- .State1 - machine <- .State2 - machine <- .State0 - machine <- .State1 - machine <- .State0 - machine <- .State2 + machine <- .state1 + machine <- .state2 + machine <- .state0 + machine <- .state1 + machine <- .state0 + machine <- .state2 XCTAssertEqual(invokeCount, 0) // reset to 0 - machine <- .State0 + machine <- .state0 // tryState 0 => 1 => 2 => 0 => 1 => 2 - machine <- .State1 - machine <- .State2 - machine <- .State0 - machine <- .State1 - machine <- .State2 + machine <- .state1 + machine <- .state2 + machine <- .state0 + machine <- .state1 + machine <- .state2 XCTAssertEqual(invokeCount, 1) } @@ -203,10 +203,10 @@ class MachineChainTests: _TestCase { var invokeCount = 0 - let machine = StateMachine(state: .State0) { machine in + let machine = StateMachine(state: .state0) { machine in // add 0 => 1 => 2 - let chainDisposable = machine.addRouteChain(.State0 => .State1 => .State2) { context in + let chainDisposable = machine.addRouteChain(.state0 => .state1 => .state2) { context in invokeCount += 1 return } @@ -218,10 +218,10 @@ class MachineChainTests: _TestCase // tryState 0 => 1 => 2 - machine <- .State1 + machine <- .state1 XCTAssertEqual(invokeCount, 0, "ChainHandler should NOT be performed.") - machine <- .State2 + machine <- .state2 XCTAssertEqual(invokeCount, 0, "ChainHandler should NOT be performed.") } @@ -229,11 +229,11 @@ class MachineChainTests: _TestCase { var errorCount = 0 - let machine = StateMachine(state: .State0) { machine in + let machine = StateMachine(state: .state0) { machine in - let transitionChain = MyState.State0 => .State1 => .State2 + let transitionChain = MyState.state0 => .state1 => .state2 - machine.addRoute(.Any => .Any) // connect all states + machine.addRoute(.any => .any) // connect all states // add 0 => 1 => 2 machine.addRouteChain(transitionChain) { context in @@ -250,13 +250,13 @@ class MachineChainTests: _TestCase } // tryState 0 (starting state) => 1 => 0 - machine <- .State1 + machine <- .state1 XCTAssertEqual(errorCount, 0, "0 => 1 is successful (still chaining), so chainErrorHandler should NOT be performed at this point.") - machine <- .State0 + machine <- .state0 XCTAssertEqual(errorCount, 1, "chainErrorHandler should be performed.") // tryState 0 (starting state) => 2 - machine <- .State2 + machine <- .state2 XCTAssertEqual(errorCount, 2, "chainErrorHandler should be performed again.") } @@ -264,11 +264,11 @@ class MachineChainTests: _TestCase { var errorCount = 0 - let machine = StateMachine(state: .State0) { machine in + let machine = StateMachine(state: .state0) { machine in - let transitionChain = MyState.State0 => .State1 => .State2 + let transitionChain = MyState.state0 => .state1 => .state2 - machine.addRoute(.Any => .Any) // connect all states + machine.addRoute(.any => .any) // connect all states // add 0 => 1 => 2 chainErrorHandler let chainErrorHandlerDisposable = machine.addChainErrorHandler(transitionChain) { context in @@ -282,12 +282,12 @@ class MachineChainTests: _TestCase } // tryState 0 (starting state) => 1 => 0 - machine <- .State1 - machine <- .State0 + machine <- .state1 + machine <- .state0 XCTAssertEqual(errorCount, 0, "Chain error, but chainErrorHandler should NOT be performed.") // tryState 0 (starting state) => 2 - machine <- .State2 + machine <- .state2 XCTAssertEqual(errorCount, 0, "Chain error, but chainErrorHandler should NOT be performed.") } diff --git a/Tests/SwiftStateTests/RouteMappingTests.swift b/Tests/SwiftStateTests/RouteMappingTests.swift new file mode 100644 index 0000000..a92ed06 --- /dev/null +++ b/Tests/SwiftStateTests/RouteMappingTests.swift @@ -0,0 +1,189 @@ +// +// RouteMappingTests.swift +// SwiftState +// +// Created by Yasuhiro Inami on 2015-11-02. +// Copyright © 2015 Yasuhiro Inami. All rights reserved. +// + +import SwiftState +import XCTest + +// +// RouteMapping for StateType & EventType using associated values +// +// https://github.com/ReactKit/SwiftState/issues/34 +// https://github.com/ReactKit/SwiftState/pull/36 +// + +private enum _State: StateType, Hashable +{ + case pending + case loading(Int) + + func hash(into hasher: inout Hasher) + { + switch self { + case .pending: + hasher.combine("Pending".hashValue) + case let .loading(x): + hasher.combine("Loading\(x)".hashValue) + } + } +} + +private func == (lhs: _State, rhs: _State) -> Bool +{ + switch (lhs, rhs) { + case (.pending, .pending): + return true + case let (.loading(x1), .loading(x2)): + return x1 == x2 + default: + return false + } +} + +private enum _Event: SwiftState.EventType, Hashable +{ + case cancelAction + case loadAction(Int) + + func hash(into hasher: inout Hasher) + { + switch self { + case .cancelAction: + hasher.combine("CancelAction".hashValue) + case let .loadAction(x): + hasher.combine("LoadAction\(x)".hashValue) + } + } +} + +private func == (lhs: _Event, rhs: _Event) -> Bool +{ + switch (lhs, rhs) { + case (.cancelAction, .cancelAction): + return true + case let (.loadAction(x1), .loadAction(x2)): + return x1 == x2 + default: + return false + } +} + +class RouteMappingTests: _TestCase +{ + /// Test for state & event with associated values + func testAddRouteMapping() + { + var count = 0 + + let machine = StateMachine<_State, _Event>(state: .pending) { machine in + + machine.addRouteMapping { event, fromState, userInfo in + // no routes for no event + guard let event = event else { + return nil + } + + switch event { + case .cancelAction: + // can transit to `.pending` if current state is not the same + return fromState == .pending ? nil : .pending + case .loadAction(let actionId): + // can transit to `.loading(actionId)` if current state is not the same + return fromState == .loading(actionId) ? nil : .loading(actionId) + } + } + + // increment `count` when any events i.e. `.cancelAction` and `.loadAction(x)` succeed. + machine.addHandler(event: .any) { _ in + count += 1 + } + + } + + // initial + XCTAssertEqual(machine.state, _State.pending) + XCTAssertEqual(count, 0) + + // CancelAction (to .pending state, same as before) + machine <-! .cancelAction + XCTAssertEqual(machine.state, _State.pending) + XCTAssertEqual(count, 0, "`tryEvent()` failed, and `count` should not be incremented.") + + // LoadAction(1) (to .loading(1) state) + machine <-! .loadAction(1) + XCTAssertEqual(machine.state, _State.loading(1)) + XCTAssertEqual(count, 1) + + // LoadAction(1) (same as before) + machine <-! .loadAction(1) + XCTAssertEqual(machine.state, _State.loading(1)) + XCTAssertEqual(count, 1, "`tryEvent()` failed, and `count` should not be incremented.") + + machine <-! .loadAction(2) + XCTAssertEqual(machine.state, _State.loading(2)) + XCTAssertEqual(count, 2) + + machine <-! .cancelAction + XCTAssertEqual(machine.state, _State.pending) + XCTAssertEqual(count, 3) + } + + /// Test for state with associated values + func testAddStateRouteMapping() + { + var count = 0 + + let machine = StateMachine<_State, _Event>(state: .pending) { machine in + + // Add following routes: + // - `.pending => .loading(1)` + // - `.loading(x) => .loading(x+10)` + // - `.loading(x) => .loading(x+100)` + machine.addStateRouteMapping { fromState, userInfo in + switch fromState { + case .pending: + return [.loading(1)] + case .loading(let actionId): + return [.loading(actionId+10), .loading(actionId+100)] + } + } + + // increment `count` when any events i.e. `.cancelAction` and `.loadAction(x)` succeed. + machine.addHandler(.any => .any) { _ in + count += 1 + } + + } + + // initial + XCTAssertEqual(machine.state, _State.pending) + XCTAssertEqual(count, 0) + + // .loading(999) (fails) + machine <- .loading(999) + XCTAssertEqual(machine.state, _State.pending) + XCTAssertEqual(count, 0, "`tryState()` failed, and `count` should not be incremented.") + + // .loading(1) + machine <- .loading(1) + XCTAssertEqual(machine.state, _State.loading(1)) + XCTAssertEqual(count, 1) + + // .loading(999) (fails) + machine <- .loading(999) + XCTAssertEqual(machine.state, _State.loading(1)) + XCTAssertEqual(count, 1, "`tryState()` failed, and `count` should not be incremented.") + + machine <- .loading(11) + XCTAssertEqual(machine.state, _State.loading(11)) + XCTAssertEqual(count, 2) + + machine <- .loading(111) + XCTAssertEqual(machine.state, _State.loading(111)) + XCTAssertEqual(count, 3) + } +} diff --git a/Tests/RouteTests.swift b/Tests/SwiftStateTests/RouteTests.swift similarity index 67% rename from Tests/RouteTests.swift rename to Tests/SwiftStateTests/RouteTests.swift index b0cb499..b02fa7f 100644 --- a/Tests/RouteTests.swift +++ b/Tests/SwiftStateTests/RouteTests.swift @@ -13,19 +13,19 @@ class RouteTests: _TestCase { func testInit() { - let route = Route(transition: .State0 => .State1, condition: nil) - XCTAssertEqual(route.transition.fromState.rawValue, MyState.State0) - XCTAssertEqual(route.transition.toState.rawValue, MyState.State1) + let route = Route(transition: .state0 => .state1, condition: nil) + XCTAssertEqual(route.transition.fromState.rawValue, MyState.state0) + XCTAssertEqual(route.transition.toState.rawValue, MyState.state1) XCTAssertTrue(route.condition == nil) - let route2 = Route(transition: .State1 => .State2, condition: { _ in false }) - XCTAssertEqual(route2.transition.fromState.rawValue, MyState.State1) - XCTAssertEqual(route2.transition.toState.rawValue, MyState.State2) + let route2 = Route(transition: .state1 => .state2, condition: { _ in false }) + XCTAssertEqual(route2.transition.fromState.rawValue, MyState.state1) + XCTAssertEqual(route2.transition.toState.rawValue, MyState.state2) XCTAssertTrue(route2.condition != nil) - let route3 = Route(transition: .State2 => .State3, condition: { context in false }) - XCTAssertEqual(route3.transition.fromState.rawValue, MyState.State2) - XCTAssertEqual(route3.transition.toState.rawValue, MyState.State3) + let route3 = Route(transition: .state2 => .state3, condition: { context in false }) + XCTAssertEqual(route3.transition.fromState.rawValue, MyState.state2) + XCTAssertEqual(route3.transition.toState.rawValue, MyState.state3) XCTAssertTrue(route3.condition != nil) } } diff --git a/Tests/SwiftStateTests/StateMachineEventTests.swift b/Tests/SwiftStateTests/StateMachineEventTests.swift new file mode 100644 index 0000000..278a1df --- /dev/null +++ b/Tests/SwiftStateTests/StateMachineEventTests.swift @@ -0,0 +1,448 @@ +// +// StateMachineEventTests.swift +// SwiftState +// +// Created by Yasuhiro Inami on 2014/08/05. +// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. +// + +import SwiftState +import XCTest + +class StateMachineEventTests: _TestCase +{ + func testCanTryEvent() + { + let machine = StateMachine(state: .state0) + + // add 0 => 1 & 1 => 2 + // (NOTE: this is not chaining e.g. 0 => 1 => 2) + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + XCTAssertTrue(machine.canTryEvent(.event0) != nil) + } + + //-------------------------------------------------- + // MARK: - tryEvent a.k.a `<-!` + //-------------------------------------------------- + + func testTryEvent() + { + let machine = StateMachine(state: .state0) + + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2, "Event0 doesn't have 2 => Any") + } + + func testTryEvent_string() + { + let machine = StateMachine(state: .state0) + + // add 0 => 1 => 2 + machine.addRoutes(event: "Run", transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + // tryEvent + machine <-! "Run" + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! "Run" + XCTAssertEqual(machine.state, MyState.state2) + + // tryEvent + machine <-! "Run" + XCTAssertEqual(machine.state, MyState.state2, "Event=Run doesn't have 2 => Any") + } + + // https://github.com/ReactKit/SwiftState/issues/20 + func testTryEvent_issue20() + { + let machine = StateMachine(state: MyState.state2) { machine in + machine.addRoutes(event: .event0, transitions: [.any => .state0]) + } + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state0) + } + + // https://github.com/ReactKit/SwiftState/issues/28 + func testTryEvent_issue28() + { + var eventCount = 0 + + let machine = StateMachine(state: .state0) { machine in + machine.addRoute(.state0 => .state1) + machine.addRoutes(event: .event0, transitions: [.any => .any]) { _ in + eventCount += 1 + } + } + + XCTAssertEqual(eventCount, 0) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(eventCount, 1) + XCTAssertEqual(machine.state, MyState.state0, "State should NOT be changed") + + // tryEvent + machine <- .state1 + XCTAssertEqual(machine.state, MyState.state1, "State should be changed") + + // tryEvent + machine <-! .event0 + XCTAssertEqual(eventCount, 2) + XCTAssertEqual(machine.state, MyState.state1, "State should NOT be changed") + } + + // Fix for transitioning of routes w/ multiple from-states + // https://github.com/ReactKit/SwiftState/pull/32 + func testTryEvent_issue32() + { + let machine = StateMachine(state: .state0) { machine in + machine.addRoutes(event: .event0, transitions: [ .state0 => .state1 ]) + machine.addRoutes(event: .event1, routes: [ [ .state1, .state2 ] => .state3 ]) + } + + XCTAssertEqual(machine.state, MyState.state0) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event1 + XCTAssertEqual(machine.state, MyState.state3) + } + + // MARK: hasRoute + event + + func testHasRoute_anyEvent() + { + ({ + let machine = StateMachine(state: .state0) { machine in + machine.addRoute(.state0 => .state1) + machine.addRoutes(event: .any, transitions: [.state0 => .state1]) + } + + let hasRoute = machine.hasRoute(event: .event0, transition: .state0 => .state1) + XCTAssertTrue(hasRoute) + })() + + ({ + let machine = StateMachine(state: .state0) { machine in + machine.addRoute(.state0 => .state1) + machine.addRoutes(event: .any, transitions: [.state2 => .state3]) + } + + let hasRoute = machine.hasRoute(event: .event0, transition: .state0 => .state1) + XCTAssertFalse(hasRoute) + })() + } + + // Fix hasRoute() bug when there are routes for no-event & with-event. + // https://github.com/ReactKit/SwiftState/pull/19 + func testHasRoute_issue19() + { + let machine = StateMachine(state: .state0) { machine in + machine.addRoute(.state0 => .state1) // no-event + machine.addRoutes(event: .event0, transitions: [.state1 => .state2]) // with-event + } + + let hasRoute = machine.hasRoute(event: .event0, transition: .state1 => .state2) + XCTAssertTrue(hasRoute) + } + + //-------------------------------------------------- + // MARK: - add/removeRoute + //-------------------------------------------------- + + func testAddRoute_tryState() + { + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 & 1 => 2 + // (NOTE: this is not chaining e.g. 0 => 1 => 2) + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + } + + // tryState 0 => 1 + machine <- .state1 + XCTAssertEqual(machine.state, MyState.state1) + + // tryState 1 => 2 + machine <- .state2 + XCTAssertEqual(machine.state, MyState.state2) + + // tryState 2 => 3 + machine <- .state3 + XCTAssertEqual(machine.state, MyState.state2, "2 => 3 is not registered.") + } + + func testAddRoute_multiple() + { + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + // add 2 => 1 => 0 + machine.addRoutes(event: .event1, transitions: [ + .state2 => .state1, + .state1 => .state0, + ]) + } + + // initial + XCTAssertEqual(machine.state, MyState.state0) + + // tryEvent + machine <-! .event1 + XCTAssertEqual(machine.state, MyState.state0, "Event1 doesn't have 0 => Any.") + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2, "Event0 doesn't have 2 => Any.") + + // tryEvent + machine <-! .event1 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event1 + XCTAssertEqual(machine.state, MyState.state0) + } + + func testAddRoute_handler() + { + var invokeCount = 0 + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ], handler: { context in + invokeCount += 1 + return + }) + } + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2) + + XCTAssertEqual(invokeCount, 2) + } + + func testRemoveRoute() + { + var invokeCount = 0 + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 => 2 + let routeDisposable = machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + machine.addHandler(event: .event0) { context in + invokeCount += 1 + return + } + + // removeRoute + routeDisposable.dispose() + + } + + // initial + XCTAssertEqual(machine.state, MyState.state0) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state0, "Route should be removed.") + + XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") + } + + //-------------------------------------------------- + // MARK: - add/removeHandler + //-------------------------------------------------- + + func testAddHandler() + { + var invokeCount = 0 + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + machine.addHandler(event: .event0) { context in + invokeCount += 1 + return + } + + } + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2) + + XCTAssertEqual(invokeCount, 2) + } + + func testRemoveHandler() + { + var invokeCount = 0 + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 => 2 + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + let handlerDisposable = machine.addHandler(event: .event0) { context in + invokeCount += 1 + return + } + + // remove handler + handlerDisposable.dispose() + + } + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1, "0 => 1 should be succesful") + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2, "1 => 2 should be succesful") + + XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed") + } + + //-------------------------------------------------- + // MARK: - addAnyHandler + //-------------------------------------------------- + + func testAddAnyHandler() + { + var invokeCounts = [0, 0, 0, 0, 0, 0] + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 => 2 (event-based) + machine.addRoutes(event: .event0, transitions: [ + .state0 => .state1, + .state1 => .state2, + ]) + + // add 2 => 3 (state-based) + machine.addRoute(.state2 => .state3) + + // + // addAnyHandler (for both event-based & state-based) + // + + machine.addAnyHandler(.state0 => .state1) { context in + invokeCounts[0] += 1 + } + + machine.addAnyHandler(.state1 => .state2) { context in + invokeCounts[1] += 1 + } + + machine.addAnyHandler(.state2 => .state3) { context in + invokeCounts[2] += 1 + } + + machine.addAnyHandler(.any => .state3) { context in + invokeCounts[3] += 1 + } + + machine.addAnyHandler(.state0 => .any) { context in + invokeCounts[4] += 1 + } + + machine.addAnyHandler(.any => .any) { context in + invokeCounts[5] += 1 + } + + } + + // initial + XCTAssertEqual(machine.state, MyState.state0) + XCTAssertEqual(invokeCounts, [0, 0, 0, 0, 0, 0]) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state1) + XCTAssertEqual(invokeCounts, [1, 0, 0, 0, 1, 1]) + + // tryEvent + machine <-! .event0 + XCTAssertEqual(machine.state, MyState.state2) + XCTAssertEqual(invokeCounts, [1, 1, 0, 0, 1, 2]) + + // tryState + machine <- .state3 + XCTAssertEqual(machine.state, MyState.state3) + XCTAssertEqual(invokeCounts, [1, 1, 1, 1, 1, 3]) + + } + +} diff --git a/Tests/SwiftStateTests/StateMachineTests.swift b/Tests/SwiftStateTests/StateMachineTests.swift new file mode 100644 index 0000000..83b6595 --- /dev/null +++ b/Tests/SwiftStateTests/StateMachineTests.swift @@ -0,0 +1,780 @@ +// +// StateMachineTests.swift +// SwiftState +// +// Created by Yasuhiro Inami on 2014/08/03. +// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. +// + +import SwiftState +import XCTest + +class StateMachineTests: _TestCase +{ + func testInit() + { + let machine = StateMachine(state: .state0) + + XCTAssertEqual(machine.state, MyState.state0) + } + + //-------------------------------------------------- + // MARK: - tryState a.k.a `<-` + //-------------------------------------------------- + + // machine <- state + func testTryState() + { + let machine = StateMachine(state: .state0) + + // tryState 0 => 1, without registering any transitions + machine <- .state1 + + XCTAssertEqual(machine.state, MyState.state0, "0 => 1 should fail because transition is not added yet.") + + // add 0 => 1 + machine.addRoute(.state0 => .state1) + + // tryState 0 => 1 + machine <- .state1 + XCTAssertEqual(machine.state, MyState.state1) + } + + func testTryState_string() + { + let machine = StateMachine(state: "0") + + // tryState 0 => 1, without registering any transitions + machine <- "1" + + XCTAssertEqual(machine.state, "0", "0 => 1 should fail because transition is not added yet.") + + // add 0 => 1 + machine.addRoute("0" => "1") + + // tryState 0 => 1 + machine <- "1" + XCTAssertEqual(machine.state, "1") + } + + //-------------------------------------------------- + // MARK: - addRoute + //-------------------------------------------------- + + // add state1 => state2 + func testAddRoute() + { + let machine = StateMachine(state: .state0) { machine in + machine.addRoute(.state0 => .state1) + } + + XCTAssertFalse(machine.hasRoute(.state0 => .state0)) + XCTAssertTrue(machine.hasRoute(.state0 => .state1)) // true + XCTAssertFalse(machine.hasRoute(.state0 => .state2)) + XCTAssertFalse(machine.hasRoute(.state1 => .state0)) + XCTAssertFalse(machine.hasRoute(.state1 => .state1)) + XCTAssertFalse(machine.hasRoute(.state1 => .state2)) + } + + // add .any => state + func testAddRoute_fromAnyState() + { + let machine = StateMachine(state: .state0) { machine in + machine.addRoute(.any => .state1) // Any => State1 + } + + XCTAssertFalse(machine.hasRoute(.state0 => .state0)) + XCTAssertTrue(machine.hasRoute(.state0 => .state1)) // true + XCTAssertFalse(machine.hasRoute(.state0 => .state2)) + XCTAssertFalse(machine.hasRoute(.state1 => .state0)) + XCTAssertTrue(machine.hasRoute(.state1 => .state1)) // true + XCTAssertFalse(machine.hasRoute(.state1 => .state2)) + } + + // add state => .any + func testAddRoute_toAnyState() + { + let machine = StateMachine(state: .state0) { machine in + machine.addRoute(.state1 => .any) // State1 => Any + } + + XCTAssertFalse(machine.hasRoute(.state0 => .state0)) + XCTAssertFalse(machine.hasRoute(.state0 => .state1)) + XCTAssertFalse(machine.hasRoute(.state0 => .state2)) + XCTAssertTrue(machine.hasRoute(.state1 => .state0)) // true + XCTAssertTrue(machine.hasRoute(.state1 => .state1)) // true + XCTAssertTrue(machine.hasRoute(.state1 => .state2)) // true + } + + // add .any => .any + func testAddRoute_bothAnyState() + { + let machine = StateMachine(state: .state0) { machine in + machine.addRoute(.any => .any) // Any => Any + } + + XCTAssertTrue(machine.hasRoute(.state0 => .state0)) // true + XCTAssertTrue(machine.hasRoute(.state0 => .state1)) // true + XCTAssertTrue(machine.hasRoute(.state0 => .state2)) // true + XCTAssertTrue(machine.hasRoute(.state1 => .state0)) // true + XCTAssertTrue(machine.hasRoute(.state1 => .state1)) // true + XCTAssertTrue(machine.hasRoute(.state1 => .state2)) // true + } + + // add state0 => state0 + func testAddRoute_sameState() + { + let machine = StateMachine(state: .state0) { machine in + machine.addRoute(.state0 => .state0) + } + + XCTAssertTrue(machine.hasRoute(.state0 => .state0)) + } + + // add route + condition + func testAddRoute_condition() + { + var flag = false + + let machine = StateMachine(state: .state0) { machine in + // add 0 => 1 + machine.addRoute(.state0 => .state1, condition: { _ in flag }) + + } + + XCTAssertFalse(machine.hasRoute(.state0 => .state1)) + + flag = true + + XCTAssertTrue(machine.hasRoute(.state0 => .state1)) + } + + // add route + condition + blacklist + func testAddRoute_condition_blacklist() + { + let machine = StateMachine(state: .state0) { machine in + // add 0 => Any, except 0 => 2 + machine.addRoute(.state0 => .any, condition: { context in + return context.toState != .state2 + }) + } + + XCTAssertTrue(machine.hasRoute(.state0 => .state0)) + XCTAssertTrue(machine.hasRoute(.state0 => .state1)) + XCTAssertFalse(machine.hasRoute(.state0 => .state2)) + XCTAssertTrue(machine.hasRoute(.state0 => .state3)) + } + + // add route + handler + func testAddRoute_handler() + { + var invokedCount = 0 + + let machine = StateMachine(state: .state0) { machine in + + machine.addRoute(.state0 => .state1) { context in + XCTAssertEqual(context.fromState, MyState.state0) + XCTAssertEqual(context.toState, MyState.state1) + + invokedCount += 1 + } + + } + + XCTAssertEqual(invokedCount, 0, "Transition has not started yet.") + + // tryState 0 => 1 + machine <- .state1 + + XCTAssertEqual(invokedCount, 1) + } + + // add route + conditional handler + func testAddRoute_conditionalHandler() + { + var invokedCount = 0 + var flag = false + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 without condition to guarantee 0 => 1 transition + machine.addRoute(.state0 => .state1) + + // add 0 => 1 with condition + conditionalHandler + machine.addRoute(.state0 => .state1, condition: { _ in flag }) { context in + XCTAssertEqual(context.fromState, MyState.state0) + XCTAssertEqual(context.toState, MyState.state1) + + invokedCount += 1 + } + + // add 1 => 0 for resetting state + machine.addRoute(.state1 => .state0) + + } + + // tryState 0 => 1 + machine <- .state1 + + XCTAssertEqual(machine.state, MyState.state1) + XCTAssertEqual(invokedCount, 0, "Conditional handler should NOT be performed because flag=false.") + + // tryState 1 => 0 (resetting to 0) + machine <- .state0 + + XCTAssertEqual(machine.state, MyState.state0) + + flag = true + + // tryState 0 => 1 + machine <- .state1 + + XCTAssertEqual(machine.state, MyState.state1) + XCTAssertEqual(invokedCount, 1) + + } + + // MARK: addRoute using array + + func testAddRoute_array_left() + { + let machine = StateMachine(state: .state0) { machine in + // add 0 => 2 or 1 => 2 + machine.addRoute([.state0, .state1] => .state2) + } + + XCTAssertFalse(machine.hasRoute(.state0 => .state0)) + XCTAssertFalse(machine.hasRoute(.state0 => .state1)) + XCTAssertTrue(machine.hasRoute(.state0 => .state2)) + XCTAssertFalse(machine.hasRoute(.state1 => .state0)) + XCTAssertFalse(machine.hasRoute(.state1 => .state1)) + XCTAssertTrue(machine.hasRoute(.state1 => .state2)) + } + + func testAddRoute_array_right() + { + let machine = StateMachine(state: .state0) { machine in + // add 0 => 1 or 0 => 2 + machine.addRoute(.state0 => [.state1, .state2]) + } + + XCTAssertFalse(machine.hasRoute(.state0 => .state0)) + XCTAssertTrue(machine.hasRoute(.state0 => .state1)) + XCTAssertTrue(machine.hasRoute(.state0 => .state2)) + XCTAssertFalse(machine.hasRoute(.state1 => .state0)) + XCTAssertFalse(machine.hasRoute(.state1 => .state1)) + XCTAssertFalse(machine.hasRoute(.state1 => .state2)) + } + + func testAddRoute_array_both() + { + let machine = StateMachine(state: .state0) { machine in + // add 0 => 2 or 0 => 3 or 1 => 2 or 1 => 3 + machine.addRoute([MyState.state0, MyState.state1] => [MyState.state2, MyState.state3]) + } + + XCTAssertFalse(machine.hasRoute(.state0 => .state0)) + XCTAssertFalse(machine.hasRoute(.state0 => .state1)) + XCTAssertTrue(machine.hasRoute(.state0 => .state2)) + XCTAssertTrue(machine.hasRoute(.state0 => .state3)) + XCTAssertFalse(machine.hasRoute(.state1 => .state0)) + XCTAssertFalse(machine.hasRoute(.state1 => .state1)) + XCTAssertTrue(machine.hasRoute(.state1 => .state2)) + XCTAssertTrue(machine.hasRoute(.state1 => .state3)) + XCTAssertFalse(machine.hasRoute(.state2 => .state0)) + XCTAssertFalse(machine.hasRoute(.state2 => .state1)) + XCTAssertFalse(machine.hasRoute(.state2 => .state2)) + XCTAssertFalse(machine.hasRoute(.state2 => .state3)) + XCTAssertFalse(machine.hasRoute(.state3 => .state0)) + XCTAssertFalse(machine.hasRoute(.state3 => .state1)) + XCTAssertFalse(machine.hasRoute(.state3 => .state2)) + XCTAssertFalse(machine.hasRoute(.state3 => .state3)) + } + + //-------------------------------------------------- + // MARK: - removeRoute + //-------------------------------------------------- + + func testRemoveRoute() + { + let machine = StateMachine(state: .state0) + + let routeDisposable = machine.addRoute(.state0 => .state1) + + XCTAssertTrue(machine.hasRoute(.state0 => .state1)) + + // remove route + routeDisposable.dispose() + + XCTAssertFalse(machine.hasRoute(.state0 => .state1)) + } + + func testRemoveRoute_handler() + { + let machine = StateMachine(state: .state0) + + let routeDisposable = machine.addRoute(.state0 => .state1, handler: { _ in }) + + XCTAssertTrue(machine.hasRoute(.state0 => .state1)) + + // remove route + routeDisposable.dispose() + + XCTAssertFalse(machine.hasRoute(.state0 => .state1)) + } + + //-------------------------------------------------- + // MARK: - addHandler + //-------------------------------------------------- + + func testAddHandler() + { + var invokedCount = 0 + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 + machine.addRoute(.state0 => .state1) + + machine.addHandler(.state0 => .state1) { context in + XCTAssertEqual(context.fromState, MyState.state0) + XCTAssertEqual(context.toState, MyState.state1) + + invokedCount += 1 + } + + } + + // not tried yet + XCTAssertEqual(invokedCount, 0, "Transition has not started yet.") + + // tryState 0 => 1 + machine <- .state1 + + XCTAssertEqual(invokedCount, 1) + } + + func testAddHandler_order() + { + var invokedCount = 0 + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 + machine.addRoute(.state0 => .state1) + + // order = 100 (default) + machine.addHandler(.state0 => .state1) { context in + XCTAssertEqual(invokedCount, 1) + + XCTAssertEqual(context.fromState, MyState.state0) + XCTAssertEqual(context.toState, MyState.state1) + + invokedCount += 1 + } + + // order = 99 + machine.addHandler(.state0 => .state1, order: 99) { context in + XCTAssertEqual(invokedCount, 0) + + XCTAssertEqual(context.fromState, MyState.state0) + XCTAssertEqual(context.toState, MyState.state1) + + invokedCount += 1 + } + + } + + XCTAssertEqual(invokedCount, 0) + + // tryState 0 => 1 + machine <- .state1 + + XCTAssertEqual(invokedCount, 2) + } + + + func testAddHandler_multiple() + { + var passed1 = false + var passed2 = false + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 + machine.addRoute(.state0 => .state1) + + machine.addHandler(.state0 => .state1) { context in + passed1 = true + } + + // add 0 => 1 once more + machine.addRoute(.state0 => .state1) + + machine.addHandler(.state0 => .state1) { context in + passed2 = true + } + + } + + // tryState 0 => 1 + machine <- .state1 + + XCTAssertTrue(passed1) + XCTAssertTrue(passed2) + } + + func testAddHandler_overload() + { + var passed = false + + let machine = StateMachine(state: .state0) { machine in + + machine.addRoute(.state0 => .state1) + + machine.addHandler(.state0 => .state1) { context in + // empty + } + + machine.addHandler(.state0 => .state1) { context in + passed = true + } + + } + + XCTAssertFalse(passed) + + machine <- .state1 + + XCTAssertTrue(passed) + } + + //-------------------------------------------------- + // MARK: - removeHandler + //-------------------------------------------------- + + func testRemoveHandler() + { + var passed = false + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 + machine.addRoute(.state0 => .state1) + + let handlerDisposable = machine.addHandler(.state0 => .state1) { context in + XCTFail("Should never reach here") + } + + // add 0 => 1 once more + machine.addRoute(.state0 => .state1) + + machine.addHandler(.state0 => .state1) { context in + passed = true + } + + // remove handler + handlerDisposable.dispose() + + } + + XCTAssertFalse(passed) + + // tryState 0 => 1 + machine <- .state1 + + XCTAssertTrue(passed) + } + + func testRemoveHandler_unregistered() + { + let machine = StateMachine(state: .state0) + + // add 0 => 1 + machine.addRoute(.state0 => .state1) + + let handlerDisposable = machine.addHandler(.state0 => .state1) { context in + // empty + } + + XCTAssertFalse(handlerDisposable.disposed) + + // remove handler + handlerDisposable.dispose() + + // remove already unregistered handler + XCTAssertTrue(handlerDisposable.disposed, "removeHandler should fail because handler is already removed.") + } + + func testRemoveErrorHandler() + { + var passed = false + + let machine = StateMachine(state: .state0) { machine in + + // add 2 => 1 + machine.addRoute(.state2 => .state1) + + let handlerDisposable = machine.addErrorHandler { context in + XCTFail("Should never reach here") + } + + // add 2 => 1 once more + machine.addRoute(.state2 => .state1) + + machine.addErrorHandler { context in + passed = true + } + + // remove handler + handlerDisposable.dispose() + + } + + // tryState 0 => 1 + machine <- .state1 + + XCTAssertTrue(passed) + } + + func testRemoveErrorHandler_unregistered() + { + let machine = StateMachine(state: .state0) + + // add 0 => 1 + machine.addRoute(.state0 => .state1) + + let handlerDisposable = machine.addErrorHandler { context in + // empty + } + + XCTAssertFalse(handlerDisposable.disposed) + + // remove handler + handlerDisposable.dispose() + + // remove already unregistered handler + XCTAssertTrue(handlerDisposable.disposed, "removeHandler should fail because handler is already removed.") + } + + //-------------------------------------------------- + // MARK: - addRouteChain + //-------------------------------------------------- + + func testAddRouteChain() + { + var success = false + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 => 2 => 3 + machine.addRouteChain(.state0 => .state1 => .state2 => .state3) { context in + success = true + } + } + + // initial + XCTAssertEqual(machine.state, MyState.state0) + + // 0 => 1 + machine <- .state1 + XCTAssertEqual(machine.state, MyState.state1) + XCTAssertFalse(success, "RouteChain is not completed yet.") + + // 1 => 2 + machine <- .state2 + XCTAssertEqual(machine.state, MyState.state2) + XCTAssertFalse(success, "RouteChain is not completed yet.") + + // 2 => 3 + machine <- .state3 + XCTAssertEqual(machine.state, MyState.state3) + XCTAssertTrue(success) + } + + func testAddChainHandler() + { + var success = false + + let machine = StateMachine(state: .state0) { machine in + + // add all routes + machine.addRoute(.any => .any) + + // add 0 => 1 => 2 => 3 + machine.addChainHandler(.state0 => .state1 => .state2 => .state3) { context in + success = true + } + } + + // initial + XCTAssertEqual(machine.state, MyState.state0) + + // 0 => 1 + machine <- .state1 + XCTAssertEqual(machine.state, MyState.state1) + XCTAssertFalse(success, "RouteChain is not completed yet.") + + // 1 => 2 + machine <- .state2 + XCTAssertEqual(machine.state, MyState.state2) + XCTAssertFalse(success, "RouteChain is not completed yet.") + + // 2 => 2 (fails & resets chaining count) + machine <- .state2 + XCTAssertEqual(machine.state, MyState.state2, "State should not change.") + XCTAssertFalse(success, "RouteChain failed and reset count.") + + // 2 => 3 (chaining is failed) + machine <- .state3 + XCTAssertEqual(machine.state, MyState.state3) + XCTAssertFalse(success, "RouteChain is already failed.") + + // go back to 0 & run 0 => 1 => 2 => 3 + machine <- .state0 <- .state1 <- .state2 <- .state3 + XCTAssertEqual(machine.state, MyState.state3) + XCTAssertTrue(success, "RouteChain is resetted & should succeed its chaining.") + } + + //-------------------------------------------------- + // MARK: - Event/StateRouteMapping + //-------------------------------------------------- + + func testAddStateRouteMapping() + { + var routeMappingDisposable: Disposable? + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 & 0 => 2 + routeMappingDisposable = machine.addStateRouteMapping { fromState, userInfo -> [MyState]? in + if fromState == .state0 { + return [.state1, .state2] + } + else { + return nil + } + } + + } + + XCTAssertFalse(machine.hasRoute(.state0 => .state0)) + XCTAssertTrue(machine.hasRoute(.state0 => .state1)) + XCTAssertTrue(machine.hasRoute(.state0 => .state2)) + XCTAssertFalse(machine.hasRoute(.state1 => .state0)) + XCTAssertFalse(machine.hasRoute(.state1 => .state1)) + XCTAssertFalse(machine.hasRoute(.state1 => .state2)) + XCTAssertFalse(machine.hasRoute(.state2 => .state0)) + XCTAssertFalse(machine.hasRoute(.state2 => .state1)) + XCTAssertFalse(machine.hasRoute(.state2 => .state2)) + + // remove routeMapping + routeMappingDisposable?.dispose() + + XCTAssertFalse(machine.hasRoute(.state0 => .state0)) + XCTAssertFalse(machine.hasRoute(.state0 => .state1)) + XCTAssertFalse(machine.hasRoute(.state0 => .state2)) + XCTAssertFalse(machine.hasRoute(.state1 => .state0)) + XCTAssertFalse(machine.hasRoute(.state1 => .state1)) + XCTAssertFalse(machine.hasRoute(.state1 => .state2)) + XCTAssertFalse(machine.hasRoute(.state2 => .state0)) + XCTAssertFalse(machine.hasRoute(.state2 => .state1)) + XCTAssertFalse(machine.hasRoute(.state2 => .state2)) + } + + func testAddStateRouteMapping_handler() + { + var invokedCount = 0 + var routeMappingDisposable: Disposable? + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 & 0 => 2 + routeMappingDisposable = machine.addStateRouteMapping({ fromState, userInfo -> [MyState]? in + + if fromState == .state0 { + return [.state1, .state2] + } + else { + return nil + } + + }, handler: { context in + invokedCount += 1 + }) + + } + + // initial + XCTAssertEqual(machine.state, MyState.state0) + XCTAssertEqual(invokedCount, 0) + + // 0 => 1 + machine <- .state1 + XCTAssertEqual(machine.state, MyState.state1) + XCTAssertEqual(invokedCount, 1) + + // remove routeMapping + routeMappingDisposable?.dispose() + + // 1 => 2 (fails) + machine <- .state1 + XCTAssertEqual(machine.state, MyState.state1) + XCTAssertEqual(invokedCount, 1) + + } + + /// Test `Event/StateRouteMapping`s. + func testAddBothRouteMappings() + { + var routeMappingDisposable: Disposable? + + let machine = StateMachine(state: .state0) { machine in + + // add 0 => 1 & 0 => 2 + routeMappingDisposable = machine.addStateRouteMapping { fromState, userInfo -> [MyState]? in + if fromState == .state0 { + return [.state1, .state2] + } + else { + return nil + } + } + + // add 1 => 0 (can also use `RouteMapping` closure for single-`toState`) + machine.addRouteMapping { event, fromState, userInfo -> MyState? in + guard event == nil else { return nil } + + if fromState == .state1 { + return .state0 + } + else { + return nil + } + } + } + + XCTAssertFalse(machine.hasRoute(.state0 => .state0)) + XCTAssertTrue(machine.hasRoute(.state0 => .state1)) + XCTAssertTrue(machine.hasRoute(.state0 => .state2)) + XCTAssertTrue(machine.hasRoute(.state1 => .state0)) + XCTAssertFalse(machine.hasRoute(.state1 => .state1)) + XCTAssertFalse(machine.hasRoute(.state1 => .state2)) + XCTAssertFalse(machine.hasRoute(.state2 => .state0)) + XCTAssertFalse(machine.hasRoute(.state2 => .state1)) + XCTAssertFalse(machine.hasRoute(.state2 => .state2)) + + // remove routeMapping + routeMappingDisposable?.dispose() + + XCTAssertFalse(machine.hasRoute(.state0 => .state0)) + XCTAssertFalse(machine.hasRoute(.state0 => .state1)) + XCTAssertFalse(machine.hasRoute(.state0 => .state2)) + XCTAssertTrue(machine.hasRoute(.state1 => .state0)) + XCTAssertFalse(machine.hasRoute(.state1 => .state1)) + XCTAssertFalse(machine.hasRoute(.state1 => .state2)) + XCTAssertFalse(machine.hasRoute(.state2 => .state0)) + XCTAssertFalse(machine.hasRoute(.state2 => .state1)) + XCTAssertFalse(machine.hasRoute(.state2 => .state2)) + } +} diff --git a/Tests/StateTests.swift b/Tests/SwiftStateTests/StateTests.swift similarity index 57% rename from Tests/StateTests.swift rename to Tests/SwiftStateTests/StateTests.swift index 855eb3e..d98e9a7 100644 --- a/Tests/StateTests.swift +++ b/Tests/SwiftStateTests/StateTests.swift @@ -13,27 +13,27 @@ class StateTests: _TestCase { func testInit_state() { - let state = State(rawValue: .State0) - XCTAssertTrue(state == .State0) - XCTAssertTrue(.State0 == state) + let state = State(rawValue: .state0) + XCTAssertTrue(state == .state0) + XCTAssertTrue(.state0 == state) } func testInit_nil() { let state = State(rawValue: nil) - XCTAssertTrue(state == .Any) - XCTAssertTrue(.Any == state) + XCTAssertTrue(state == .any) + XCTAssertTrue(.any == state) } func testRawValue_state() { - let state = State.Some(.State0) - XCTAssertTrue(state.rawValue == .State0) + let state = State.some(.state0) + XCTAssertTrue(state.rawValue == .state0) } func testRawValue_any() { - let state = State.Any + let state = State.any XCTAssertTrue(state.rawValue == nil) } } diff --git a/Tests/String+TestExt.swift b/Tests/SwiftStateTests/String+TestExt.swift similarity index 100% rename from Tests/String+TestExt.swift rename to Tests/SwiftStateTests/String+TestExt.swift diff --git a/Tests/SwiftStateTests/TransitionChainTests.swift b/Tests/SwiftStateTests/TransitionChainTests.swift new file mode 100644 index 0000000..523e838 --- /dev/null +++ b/Tests/SwiftStateTests/TransitionChainTests.swift @@ -0,0 +1,92 @@ +// +// TransitionChainTests.swift +// SwiftState +// +// Created by Yasuhiro Inami on 2014/08/04. +// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. +// + +import SwiftState +import XCTest + +class TransitionChainTests: _TestCase +{ + func testInit() + { + // 0 => 1 => 2 + var chain = MyState.state0 => .state1 => .state2 + + XCTAssertEqual(chain.states.count, 3) + XCTAssertTrue(chain.states[0] == .state0) + XCTAssertTrue(chain.states[1] == .state1) + XCTAssertTrue(chain.states[2] == .state2) + + // (1 => 2) => 3 + chain = MyState.state1 => .state2 => .state3 + + XCTAssertEqual(chain.states.count, 3) + XCTAssertTrue(chain.states[0] == .state1) + XCTAssertTrue(chain.states[1] == .state2) + XCTAssertTrue(chain.states[2] == .state3) + + // 2 => (3 => 0) + chain = MyState.state2 => (.state3 => .state0) + + XCTAssertEqual(chain.states.count, 3) + XCTAssertTrue(chain.states[0] == .state2) + XCTAssertTrue(chain.states[1] == .state3) + XCTAssertTrue(chain.states[2] == .state0) + } + + func testAppend() + { + // 0 => 1 + let transition = MyState.state0 => .state1 + var chain = TransitionChain(transition: transition) + + XCTAssertEqual(chain.states.count, 2) + XCTAssertTrue(chain.states[0] == .state0) + XCTAssertTrue(chain.states[1] == .state1) + + // 0 => 1 => 2 + chain = chain => .state2 + XCTAssertEqual(chain.states.count, 3) + XCTAssertTrue(chain.states[0] == .state0) + XCTAssertTrue(chain.states[1] == .state1) + XCTAssertTrue(chain.states[2] == .state2) + + // 0 => 1 => 2 => 3 + chain = chain => .state3 + XCTAssertEqual(chain.states.count, 4) + XCTAssertTrue(chain.states[0] == .state0) + XCTAssertTrue(chain.states[1] == .state1) + XCTAssertTrue(chain.states[2] == .state2) + XCTAssertTrue(chain.states[3] == .state3) + } + + func testPrepend() + { + // 0 => 1 + let transition = MyState.state0 => .state1 + var chain = TransitionChain(transition: transition) + + XCTAssertEqual(chain.states.count, 2) + XCTAssertTrue(chain.states[0] == .state0) + XCTAssertTrue(chain.states[1] == .state1) + + // 2 => 0 => 1 + chain = .state2 => chain // same as prepend + XCTAssertEqual(chain.states.count, 3) + XCTAssertTrue(chain.states[0] == .state2) + XCTAssertTrue(chain.states[1] == .state0) + XCTAssertTrue(chain.states[2] == .state1) + + // 3 => 2 => 0 => 1 + chain = .state3 => chain + XCTAssertEqual(chain.states.count, 4) + XCTAssertTrue(chain.states[0] == .state3) + XCTAssertTrue(chain.states[1] == .state2) + XCTAssertTrue(chain.states[2] == .state0) + XCTAssertTrue(chain.states[3] == .state1) + } +} diff --git a/Tests/SwiftStateTests/TransitionTests.swift b/Tests/SwiftStateTests/TransitionTests.swift new file mode 100644 index 0000000..e4e05af --- /dev/null +++ b/Tests/SwiftStateTests/TransitionTests.swift @@ -0,0 +1,67 @@ +// +// TransitionTests.swift +// SwiftState +// +// Created by Yasuhiro Inami on 2014/08/03. +// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. +// + +import SwiftState +import XCTest + +class TransitionTests: _TestCase +{ + func testInit() + { + let transition = Transition(fromState: .state0, toState: .state1) + XCTAssertEqual(transition.fromState.rawValue, MyState.state0) + XCTAssertEqual(transition.toState.rawValue, MyState.state1) + + // shorthand + let transition2 = MyState.state1 => .state0 + XCTAssertEqual(transition2.fromState.rawValue, MyState.state1) + XCTAssertEqual(transition2.toState.rawValue, MyState.state0) + } + + func testInit_fromAny() + { + let transition = Transition(fromState: .any, toState: .state1) + XCTAssertNil(transition.fromState.rawValue) + XCTAssertEqual(transition.toState.rawValue, MyState.state1) + + // shorthand + let transition2 = .any => MyState.state0 + XCTAssertNil(transition2.fromState.rawValue) + XCTAssertEqual(transition2.toState.rawValue, MyState.state0) + } + + func testInit_toAny() + { + let transition = Transition(fromState: .state0, toState: .any) + XCTAssertEqual(transition.fromState.rawValue, MyState.state0) + XCTAssertNil(transition.toState.rawValue) + + // shorthand + let transition2 = MyState.state1 => .any + XCTAssertEqual(transition2.fromState.rawValue, MyState.state1) + XCTAssertNil(transition2.toState.rawValue) + } + + func testNil() + { + // .any => state + let transition = .any => MyState.state0 + XCTAssertTrue(transition.fromState == .any) + XCTAssertTrue(transition.toState == .state0) + + // state => .any + let transition2 = MyState.state0 => .any + XCTAssertTrue(transition2.fromState == .state0) + XCTAssertTrue(transition2.toState == .any) + + // .any => .any + let transition3: Transition = .any => .any + XCTAssertTrue(transition3.fromState == .any) + XCTAssertTrue(transition3.toState == .any) + } +} diff --git a/Tests/_TestCase.swift b/Tests/SwiftStateTests/_TestCase.swift similarity index 100% rename from Tests/_TestCase.swift rename to Tests/SwiftStateTests/_TestCase.swift diff --git a/Tests/TransitionChainTests.swift b/Tests/TransitionChainTests.swift deleted file mode 100644 index 333f077..0000000 --- a/Tests/TransitionChainTests.swift +++ /dev/null @@ -1,92 +0,0 @@ -// -// TransitionChainTests.swift -// SwiftState -// -// Created by Yasuhiro Inami on 2014/08/04. -// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. -// - -import SwiftState -import XCTest - -class TransitionChainTests: _TestCase -{ - func testInit() - { - // 0 => 1 => 2 - var chain = MyState.State0 => .State1 => .State2 - - XCTAssertEqual(chain.states.count, 3) - XCTAssertTrue(chain.states[0] == .State0) - XCTAssertTrue(chain.states[1] == .State1) - XCTAssertTrue(chain.states[2] == .State2) - - // (1 => 2) => 3 - chain = MyState.State1 => .State2 => .State3 - - XCTAssertEqual(chain.states.count, 3) - XCTAssertTrue(chain.states[0] == .State1) - XCTAssertTrue(chain.states[1] == .State2) - XCTAssertTrue(chain.states[2] == .State3) - - // 2 => (3 => 0) - chain = MyState.State2 => (.State3 => .State0) - - XCTAssertEqual(chain.states.count, 3) - XCTAssertTrue(chain.states[0] == .State2) - XCTAssertTrue(chain.states[1] == .State3) - XCTAssertTrue(chain.states[2] == .State0) - } - - func testAppend() - { - // 0 => 1 - let transition = MyState.State0 => .State1 - var chain = TransitionChain(transition: transition) - - XCTAssertEqual(chain.states.count, 2) - XCTAssertTrue(chain.states[0] == .State0) - XCTAssertTrue(chain.states[1] == .State1) - - // 0 => 1 => 2 - chain = chain => .State2 - XCTAssertEqual(chain.states.count, 3) - XCTAssertTrue(chain.states[0] == .State0) - XCTAssertTrue(chain.states[1] == .State1) - XCTAssertTrue(chain.states[2] == .State2) - - // 0 => 1 => 2 => 3 - chain = chain => .State3 - XCTAssertEqual(chain.states.count, 4) - XCTAssertTrue(chain.states[0] == .State0) - XCTAssertTrue(chain.states[1] == .State1) - XCTAssertTrue(chain.states[2] == .State2) - XCTAssertTrue(chain.states[3] == .State3) - } - - func testPrepend() - { - // 0 => 1 - let transition = MyState.State0 => .State1 - var chain = TransitionChain(transition: transition) - - XCTAssertEqual(chain.states.count, 2) - XCTAssertTrue(chain.states[0] == .State0) - XCTAssertTrue(chain.states[1] == .State1) - - // 2 => 0 => 1 - chain = .State2 => chain // same as prepend - XCTAssertEqual(chain.states.count, 3) - XCTAssertTrue(chain.states[0] == .State2) - XCTAssertTrue(chain.states[1] == .State0) - XCTAssertTrue(chain.states[2] == .State1) - - // 3 => 2 => 0 => 1 - chain = .State3 => chain - XCTAssertEqual(chain.states.count, 4) - XCTAssertTrue(chain.states[0] == .State3) - XCTAssertTrue(chain.states[1] == .State2) - XCTAssertTrue(chain.states[2] == .State0) - XCTAssertTrue(chain.states[3] == .State1) - } -} diff --git a/Tests/TransitionTests.swift b/Tests/TransitionTests.swift deleted file mode 100644 index f9f4834..0000000 --- a/Tests/TransitionTests.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// TransitionTests.swift -// SwiftState -// -// Created by Yasuhiro Inami on 2014/08/03. -// Copyright (c) 2014年 Yasuhiro Inami. All rights reserved. -// - -import SwiftState -import XCTest - -class TransitionTests: _TestCase -{ - func testInit() - { - let transition = Transition(fromState: .State0, toState: .State1) - XCTAssertEqual(transition.fromState.rawValue, MyState.State0) - XCTAssertEqual(transition.toState.rawValue, MyState.State1) - - // shorthand - let transition2 = MyState.State1 => .State0 - XCTAssertEqual(transition2.fromState.rawValue, MyState.State1) - XCTAssertEqual(transition2.toState.rawValue, MyState.State0) - } - - func testInit_fromAny() - { - let transition = Transition(fromState: .Any, toState: .State1) - XCTAssertNil(transition.fromState.rawValue) - XCTAssertEqual(transition.toState.rawValue, MyState.State1) - - // shorthand - let transition2 = .Any => MyState.State0 - XCTAssertNil(transition2.fromState.rawValue) - XCTAssertEqual(transition2.toState.rawValue, MyState.State0) - } - - func testInit_toAny() - { - let transition = Transition(fromState: .State0, toState: .Any) - XCTAssertEqual(transition.fromState.rawValue, MyState.State0) - XCTAssertNil(transition.toState.rawValue) - - // shorthand - let transition2 = MyState.State1 => .Any - XCTAssertEqual(transition2.fromState.rawValue, MyState.State1) - XCTAssertNil(transition2.toState.rawValue) - } - - func testNil() - { - // .Any => state - let transition = .Any => MyState.State0 - XCTAssertTrue(transition.fromState == .Any) - XCTAssertTrue(transition.toState == .State0) - - // state => .Any - let transition2 = MyState.State0 => .Any - XCTAssertTrue(transition2.fromState == .State0) - XCTAssertTrue(transition2.toState == .Any) - - // .Any => .Any - let transition3: Transition = .Any => .Any - XCTAssertTrue(transition3.fromState == .Any) - XCTAssertTrue(transition3.toState == .Any) - } -}