Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cc6bc8f
[Test] Add FrogcjnTest (event + associated value).
inamiy Nov 1, 2015
ef07416
Refactor code for better typing, naming, and routeMapping support.
inamiy Nov 14, 2015
465146a
[Test] Remove unnecessary `case Any`.
inamiy Nov 14, 2015
da9b9b4
Remove unnecessary methods.
inamiy Nov 24, 2015
c9f6db9
Add comment.
inamiy Nov 24, 2015
32630e2
Rename `Mapping` to `RouteMapping`.
inamiy Nov 24, 2015
93f184b
[Test] Organize tests.
inamiy Nov 24, 2015
bfd9ed2
Remove verbose methods in `Transition` & `Route`.
inamiy Nov 24, 2015
439d5a8
Organize code.
inamiy Nov 28, 2015
12cb3b8
[Test] Add testHasRoute_anyEvent()
inamiy Nov 28, 2015
2eeff1f
Add `final` modifiers.
inamiy Nov 28, 2015
1be6782
[Test] Re-add HierarchicalMachineTests which was deleted in ef07416.
inamiy Nov 29, 2015
8e093da
Update README.md & BasicTests.
inamiy Nov 30, 2015
2986012
Resize logo.png
inamiy Nov 30, 2015
f7ce748
Refactor code by separating event-only-driven `Machine` and `StateMac…
inamiy Dec 5, 2015
8ade68e
[Test] Add more RouteMapping tests.
inamiy Dec 5, 2015
e9c1bf3
[Test] Improve 8ade68e & update README.md
inamiy Dec 5, 2015
c1c18ec
Add `addStateRouteMapping()` & rename `EventRouteMapping` to `RouteMa…
inamiy Dec 7, 2015
b42ac6d
Update README.md
inamiy Dec 7, 2015
4c15f86
Conform State<S> & Event<E> to RawRepresentable.
inamiy Dec 8, 2015
edc0f3e
Set codeCoverageEnabled=YES.
inamiy Dec 8, 2015
2c9844f
[Test] Add StateTests & EventTests.
inamiy Dec 8, 2015
ce2ef2a
Fix RouteMapping + handler.
inamiy Dec 8, 2015
56b8c76
Simplify `machine.addRoutes()`.
inamiy Dec 8, 2015
e49b1c6
[Test] Improve code coverage.
inamiy Dec 8, 2015
c1751bf
Create universal framework to support watchOS & tvOS by using xcconfigs.
inamiy Dec 8, 2015
c6b59e4
Merge pull request #38 from ReactKit/universal-framework
inamiy Dec 8, 2015
83af536
Remove unnecessary xcodeproj settings.
inamiy Dec 9, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
[Test] Improve 8ade68e & update README.md
  • Loading branch information
inamiy committed Dec 5, 2015
commit e9c1bf3e044b83825cad0f9fbde3124bde12ec28
77 changes: 74 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Any => 2, msg=Optional("Hello")

### Transition by Event

Use `<-!` operator to try transition by `Event` rather than specifying target `State` ([Test Case](https://github.com/ReactKit/SwiftState/blob/1be67826b3cc9187dfaac85c2e70613f3129fad6/SwiftStateTests/TryEventTests.swift#L32-L54)).
Use `<-!` operator to try transition by `Event` rather than specifying target `State`.

```swift
enum MyEvent: EventType {
Expand All @@ -79,7 +79,10 @@ let machine = StateMachine<MyState, MyEvent>(state: .State0) { machine in
.State1 => .State2,
])
}


// initial
XCTAssertEqual(machine.state, MyState.State0)

// tryEvent
machine <-! .Event0
XCTAssertEqual(machine.state, MyState.State1)
Expand All @@ -88,13 +91,81 @@ XCTAssertEqual(machine.state, MyState.State1)
machine <-! .Event0
XCTAssertEqual(machine.state, MyState.State2)

// tryEvent
// tryEvent (fails)
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.

### State & Event enums with associated values

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 `machine.addRouteMapping()` and pass either of the following closure types (_closure-style routing_):

- `EventRouteMapping`: `(event: E?, fromState: S, userInfo: Any?) -> S?`
- `StateRouteMapping`: `(fromState: S, userInfo: Any?) -> [S]?`

For example:

```swift
enum StrState: StateType {
case Str(String) ...
}
enum StrEvent: EventType {
case Str(String) ...
}

let machine = Machine<StrState, StrEvent>(state: .Str("initial")) { machine in

// add EventRouteMapping
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"))
```

This behaves very similar to JavaScript's safe state-container [rackt/Redux](https://github.com/rackt/redux), where `EventRouteMapping` can be interpretted as `Redux.Reducer`.

For more examples, please see XCTest cases.


Expand Down
56 changes: 55 additions & 1 deletion Tests/BasicTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class BasicTests: _TestCase
])
}

// initial
XCTAssertEqual(machine.state, MyState.State0)

// tryEvent
machine <-! .Event0
XCTAssertEqual(machine.state, MyState.State1)
Expand All @@ -68,8 +71,59 @@ class BasicTests: _TestCase
machine <-! .Event0
XCTAssertEqual(machine.state, MyState.State2)

// tryEvent
// tryEvent (fails)
machine <-! .Event0
XCTAssertEqual(machine.state, MyState.State2, "Event0 doesn't have 2 => Any")
}

func testREADME_routeMapping()
{
let machine = Machine<StrState, StrEvent>(state: .Str("initial")) { machine in

// add EventRouteMapping
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"))
}
}
44 changes: 22 additions & 22 deletions Tests/MachineTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -306,63 +306,63 @@ class MachineTests: _TestCase
{
var invokeCount = 0

let machine = Machine<MyState2, MyEvent2>(state: .State0("initial")) { machine in
let machine = Machine<StrState, StrEvent>(state: .Str("initial")) { machine in

// add EventRouteMapping
machine.addRouteMapping { event, fromState, userInfo -> MyState2? in
machine.addRouteMapping { event, fromState, userInfo -> StrState? in
// no route for no-event
guard let event = event else { return nil }

switch (event, fromState) {
case (.Event0("gogogo"), .State0("initial")):
return .State0("Phase 1")
case (.Event0("gogogo"), .State0("Phase 1")):
return .State0("Phase 2")
case (.Event0("finish"), .State0("Phase 2")):
return .State0("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
}
}

machine.addHandler(event: .Event0("gogogo")) { context in
machine.addHandler(event: .Str("gogogo")) { context in
invokeCount++
return
}

}

// initial
XCTAssertEqual(machine.state, MyState2.State0("initial"))
XCTAssertEqual(machine.state, StrState.Str("initial"))

// tryEvent (fails)
machine <-! .Event0("go?")
XCTAssertEqual(machine.state, MyState2.State0("initial"), "No change.")
machine <-! .Str("go?")
XCTAssertEqual(machine.state, StrState.Str("initial"), "No change.")
XCTAssertEqual(invokeCount, 0, "Handler should NOT be performed")

// tryEvent
machine <-! .Event0("gogogo")
XCTAssertEqual(machine.state, MyState2.State0("Phase 1"))
machine <-! .Str("gogogo")
XCTAssertEqual(machine.state, StrState.Str("Phase 1"))
XCTAssertEqual(invokeCount, 1)

// tryEvent (fails)
machine <-! .Event0("finish")
XCTAssertEqual(machine.state, MyState2.State0("Phase 1"), "No change.")
machine <-! .Str("finish")
XCTAssertEqual(machine.state, StrState.Str("Phase 1"), "No change.")
XCTAssertEqual(invokeCount, 1, "Handler should NOT be performed")

// tryEvent
machine <-! .Event0("gogogo")
XCTAssertEqual(machine.state, MyState2.State0("Phase 2"))
machine <-! .Str("gogogo")
XCTAssertEqual(machine.state, StrState.Str("Phase 2"))
XCTAssertEqual(invokeCount, 2)

// tryEvent (fails)
machine <-! .Event0("gogogo")
XCTAssertEqual(machine.state, MyState2.State0("Phase 2"), "No change.")
machine <-! .Str("gogogo")
XCTAssertEqual(machine.state, StrState.Str("Phase 2"), "No change.")
XCTAssertEqual(invokeCount, 2, "Handler should NOT be performed")

// tryEvent
machine <-! .Event0("finish")
XCTAssertEqual(machine.state, MyState2.State0("end"))
machine <-! .Str("finish")
XCTAssertEqual(machine.state, StrState.Str("end"))
XCTAssertEqual(invokeCount, 2, "gogogo-Handler should NOT be performed")

}
Expand Down
26 changes: 13 additions & 13 deletions Tests/MiscTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ class MiscTests: _TestCase
// StateType + associated value
func testREADME_associatedValue()
{
let machine = StateMachine<MyState2, MyEvent2>(state: .State0("0")) { machine in
let machine = StateMachine<StrState, StrEvent>(state: .Str("0")) { machine in

machine.addRoute(.State0("0") => .State0("1"))
machine.addRoute(.Any => .State0("2")) { context in print("Any => 2, msg=\(context.userInfo)") }
machine.addRoute(.State0("2") => .Any) { context in print("2 => Any, msg=\(context.userInfo)") }
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(.State0("0") => .State0("1")) { context in
machine.addHandler(.Str("0") => .Str("1")) { context in
print("0 => 1")
}

Expand All @@ -70,17 +70,17 @@ class MiscTests: _TestCase

// tryState 0 => 1 => 2 => 1 => 0

machine <- .State0("1")
XCTAssertEqual(machine.state, MyState2.State0("1"))
machine <- .Str("1")
XCTAssertEqual(machine.state, StrState.Str("1"))

machine <- (.State0("2"), "Hello")
XCTAssertEqual(machine.state, MyState2.State0("2"))
machine <- (.Str("2"), "Hello")
XCTAssertEqual(machine.state, StrState.Str("2"))

machine <- (.State0("1"), "Bye")
XCTAssertEqual(machine.state, MyState2.State0("1"))
machine <- (.Str("1"), "Bye")
XCTAssertEqual(machine.state, StrState.Str("1"))

machine <- .State0("0") // fail: no 1 => 0
XCTAssertEqual(machine.state, MyState2.State0("1"))
machine <- .Str("0") // fail: no 1 => 0
XCTAssertEqual(machine.state, StrState.Str("1"))

print("machine.state = \(machine.state)")
}
Expand Down
10 changes: 5 additions & 5 deletions Tests/MyEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@ enum MyEvent: EventType
case Event0, Event1
}

enum MyEvent2: EventType
enum StrEvent: EventType
{
case Event0(String)
case Str(String)

var hashValue: Int
{
switch self {
case .Event0(let str): return str.hashValue
case .Str(let str): return str.hashValue
}
}
}

func == (lhs: MyEvent2, rhs: MyEvent2) -> Bool
func == (lhs: StrEvent, rhs: StrEvent) -> Bool
{
switch (lhs, rhs) {
case let (.Event0(str1), .Event0(str2)):
case let (.Str(str1), .Str(str2)):
return str1 == str2
// default:
// return false
Expand Down
10 changes: 5 additions & 5 deletions Tests/MyState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@ enum MyState: StateType
case State0, State1, State2, State3
}

enum MyState2: StateType
enum StrState: StateType
{
case State0(String)
case Str(String)

var hashValue: Int
{
switch self {
case .State0(let str): return str.hashValue
case .Str(let str): return str.hashValue
}
}
}

func == (lhs: MyState2, rhs: MyState2) -> Bool
func == (lhs: StrState, rhs: StrState) -> Bool
{
switch (lhs, rhs) {
case let (.State0(str1), .State0(str2)):
case let (.Str(str1), .Str(str2)):
return str1 == str2
}
}