MINT-statemachine 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGES +135 -0
  2. data/LICENSE +16 -0
  3. data/MINT-statemachine.gemspec +27 -0
  4. data/README.rdoc +69 -0
  5. data/Rakefile +88 -0
  6. data/TODO +2 -0
  7. data/lib/statemachine.rb +26 -0
  8. data/lib/statemachine/action_invokation.rb +83 -0
  9. data/lib/statemachine/builder.rb +383 -0
  10. data/lib/statemachine/generate/dot_graph.rb +1 -0
  11. data/lib/statemachine/generate/dot_graph/dot_graph_statemachine.rb +127 -0
  12. data/lib/statemachine/generate/java.rb +1 -0
  13. data/lib/statemachine/generate/java/java_statemachine.rb +265 -0
  14. data/lib/statemachine/generate/src_builder.rb +48 -0
  15. data/lib/statemachine/generate/util.rb +50 -0
  16. data/lib/statemachine/parallelstate.rb +196 -0
  17. data/lib/statemachine/state.rb +102 -0
  18. data/lib/statemachine/statemachine.rb +279 -0
  19. data/lib/statemachine/stub_context.rb +26 -0
  20. data/lib/statemachine/superstate.rb +53 -0
  21. data/lib/statemachine/transition.rb +76 -0
  22. data/lib/statemachine/version.rb +17 -0
  23. data/spec/action_invokation_spec.rb +101 -0
  24. data/spec/builder_spec.rb +243 -0
  25. data/spec/default_transition_spec.rb +111 -0
  26. data/spec/generate/dot_graph/dot_graph_stagemachine_spec.rb +27 -0
  27. data/spec/generate/java/java_statemachine_spec.rb +349 -0
  28. data/spec/history_spec.rb +107 -0
  29. data/spec/noodle.rb +23 -0
  30. data/spec/sm_action_parameterization_spec.rb +99 -0
  31. data/spec/sm_activation_spec.rb +116 -0
  32. data/spec/sm_entry_exit_actions_spec.rb +99 -0
  33. data/spec/sm_odds_n_ends_spec.rb +67 -0
  34. data/spec/sm_parallel_state_spec.rb +207 -0
  35. data/spec/sm_simple_spec.rb +26 -0
  36. data/spec/sm_super_state_spec.rb +55 -0
  37. data/spec/sm_turnstile_spec.rb +76 -0
  38. data/spec/spec_helper.rb +121 -0
  39. data/spec/transition_spec.rb +107 -0
  40. metadata +115 -0
data/spec/noodle.rb ADDED
@@ -0,0 +1,23 @@
1
+ class Noodle
2
+
3
+ attr_accessor :shape, :cooked, :tasty
4
+
5
+ def initialize
6
+ @shape = "farfalla"
7
+ @cooked = false
8
+ @tasty = false
9
+ end
10
+
11
+ def cook
12
+ @cooked = true
13
+ end
14
+
15
+ def good
16
+ @tasty = true
17
+ end
18
+
19
+ def transform(shape)
20
+ @shape = shape
21
+ end
22
+
23
+ end
@@ -0,0 +1,99 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "State Machine Odds And Ends" do
4
+ include SwitchStatemachine
5
+
6
+ before(:each) do
7
+ create_switch
8
+ end
9
+
10
+ it "action with one parameter" do
11
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |value| @status = value } }
12
+ @sm.set "blue"
13
+ @status.should eql("blue")
14
+ @sm.state.should equal(:on)
15
+ end
16
+
17
+ it "action with two parameters" do
18
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b| @status = [a, b].join(",") } }
19
+ @sm.set "blue", "green"
20
+ @status.should eql("blue,green")
21
+ @sm.state.should equal(:on)
22
+ end
23
+
24
+ it "action with three parameters" do
25
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c| @status = [a, b, c].join(",") } }
26
+ @sm.set "blue", "green", "red"
27
+ @status.should eql("blue,green,red")
28
+ @sm.state.should equal(:on)
29
+ end
30
+
31
+ it "action with four parameters" do
32
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c, d| @status = [a, b, c, d].join(",") } }
33
+ @sm.set "blue", "green", "red", "orange"
34
+ @status.should eql("blue,green,red,orange")
35
+ @sm.state.should equal(:on)
36
+ end
37
+
38
+ it "action with five parameters" do
39
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c, d, e| @status = [a, b, c, d, e].join(",") } }
40
+ @sm.set "blue", "green", "red", "orange", "yellow"
41
+ @status.should eql("blue,green,red,orange,yellow")
42
+ @sm.state.should equal(:on)
43
+ end
44
+
45
+ it "action with six parameters" do
46
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c, d, e, f| @status = [a, b, c, d, e, f].join(",") } }
47
+ @sm.set "blue", "green", "red", "orange", "yellow", "indigo"
48
+ @status.should eql("blue,green,red,orange,yellow,indigo")
49
+ @sm.state.should equal(:on)
50
+ end
51
+
52
+ it "action with seven parameters" do
53
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c, d, e, f, g| @status = [a, b, c, d, e, f, g].join(",") } }
54
+ @sm.set "blue", "green", "red", "orange", "yellow", "indigo", "violet"
55
+ @status.should eql("blue,green,red,orange,yellow,indigo,violet")
56
+ @sm.state.should equal(:on)
57
+ end
58
+
59
+ it "action with eight parameters" do
60
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c, d, e, f, g, h| @status = [a, b, c, d, e, f, g, h].join(",") } }
61
+ @sm.set "blue", "green", "red", "orange", "yellow", "indigo", "violet", "ultra-violet"
62
+ @status.should eql("blue,green,red,orange,yellow,indigo,violet,ultra-violet")
63
+ @sm.state.should equal(:on)
64
+ end
65
+
66
+ it "calling process_event with parameters" do
67
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c| @status = [a, b, c].join(",") } }
68
+ @sm.process_event(:set, "blue", "green", "red")
69
+ @status.should eql("blue,green,red")
70
+ @sm.state.should equal(:on)
71
+ end
72
+
73
+ it "Insufficient params" do
74
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, b, c| @status = [a, b, c].join(",") } }
75
+ lambda { @sm.set "blue", "green" }.should raise_error(Statemachine::StatemachineException,
76
+ "Insufficient parameters. (transition action from 'off' state invoked by 'set' event)")
77
+ end
78
+
79
+ it "infinate args" do
80
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |*a| @status = a.join(",") } }
81
+ @sm.set(1, 2, 3)
82
+ @status.should eql("1,2,3")
83
+
84
+ @sm.state = :off
85
+ @sm.set(1, 2, 3, 4, 5, 6)
86
+ @status.should eql("1,2,3,4,5,6")
87
+ end
88
+
89
+ it "Insufficient params when params are infinate" do
90
+ Statemachine.build(@sm) { |s| s.trans :off, :set, :on, Proc.new { |a, *b| @status = a.to_s + ":" + b.join(",") } }
91
+ @sm.set(1, 2, 3)
92
+ @status.should eql("1:2,3")
93
+
94
+ @sm.state = :off
95
+
96
+ lambda { @sm.set }.should raise_error(Statemachine::StatemachineException,
97
+ "Insufficient parameters. (transition action from 'off' state invoked by 'set' event)")
98
+ end
99
+ end
@@ -0,0 +1,116 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "State Activation Callback" do
4
+ include SwitchStatemachine
5
+ include ParallelStatemachine
6
+
7
+ before(:each) do
8
+ class ActivationCallback
9
+ attr_reader :called
10
+ attr_reader :state
11
+ attr_reader :abstract_states
12
+ attr_reader :atomic_states
13
+
14
+ def initialize
15
+ @called = []
16
+ @state = []
17
+ @abstract_states = []
18
+ @atomic_states =[]
19
+
20
+ end
21
+ def activate(state,abstract_states, atomic_states)
22
+ @called << true
23
+ @state << state
24
+ @abstract_states << abstract_states
25
+ @atomic_states << atomic_states
26
+ puts "activate #{@state.last} #{@abstract_states.last} #{@atomic_states.last}"
27
+ end
28
+ end
29
+
30
+ @callback = ActivationCallback.new
31
+ end
32
+
33
+ it "should fire on successful state change" do
34
+ create_switch
35
+ @sm.activation=@callback.method(:activate)
36
+ @callback.called.length.should == 0
37
+ @sm.toggle
38
+ @callback.called.length.should == 1
39
+ end
40
+
41
+ it "should deliver new active state on state change" do
42
+ create_switch
43
+ @sm.activation=@callback.method(:activate)
44
+ @sm.toggle
45
+ @callback.state.last.should == :on
46
+ @callback.atomic_states.last.should == [:on]
47
+ @callback.abstract_states.last.should == [:root]
48
+ @sm.toggle
49
+ @callback.state.last.should == :off
50
+ end
51
+
52
+ it "should deliver new active state on state change of parallel state machine" do
53
+ create_parallel
54
+
55
+ @sm.activation=@callback.method(:activate)
56
+ @sm.go
57
+ @callback.called.length.should == 2
58
+ @callback.state.last.should == :on
59
+ @callback.abstract_states.last.should.eql? [:operative, :root, :onoff]
60
+ @callback.atomic_states.last.should == [:locked, :on]
61
+ @sm.toggle
62
+ @callback.state.last.should == :off
63
+ @callback.abstract_states.last.should.eql? [:onoff,:operative,:root]
64
+ @callback.atomic_states.last.should.eql? [:off,:locked]
65
+
66
+ end
67
+
68
+ it "activation works for on_entry ticks as well" do
69
+ create_tick
70
+ @sm.activation=@callback.method(:activate)
71
+ @sm.toggle
72
+ @callback.called.length.should == 2
73
+ @callback.state.last.should == :off
74
+ @callback.state.first.should == :on
75
+ @callback.atomic_states.last.should == [:off]
76
+ @callback.atomic_states.first.should == [:on]
77
+ @callback.abstract_states.last.should == [:root]
78
+ end
79
+
80
+ it "activation works for self-transitions as well" do
81
+ create_tome
82
+ @sm.activation=@callback.method(:activate)
83
+ @sm.toggle
84
+ @callback.called.length.should == 1
85
+ @callback.state.last.should == :me
86
+ @callback.atomic_states.last.should == [:me]
87
+ @callback.abstract_states.last.should == [:root]
88
+ end
89
+
90
+ it "should activate corretly on direct entry to parallel state" do
91
+ @sm = Statemachine.build do
92
+ trans :start,:go, :unlocked
93
+ parallel :p do
94
+ statemachine :s1 do
95
+ superstate :operative do
96
+ trans :locked, :coin, :unlocked, Proc.new { @cooked = true }
97
+ trans :unlocked, :coin, :locked
98
+ end
99
+ end
100
+ statemachine :s2 do
101
+ superstate :onoff do
102
+ trans :on, :toggle, :off
103
+ trans :off, :toggle, :on
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ @sm.activation=@callback.method(:activate)
110
+ @sm.go
111
+ @callback.state.should.eql? [:unlocked,:on]
112
+ @callback.called.length.should == 2
113
+ end
114
+
115
+
116
+ end
@@ -0,0 +1,99 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "State Machine Entry and Exit Actions" do
4
+
5
+ before(:each) do
6
+ @log = []
7
+ @sm = Statemachine.build do
8
+ trans :off, :toggle, :on, Proc.new { @log << "on" }
9
+ trans :on, :toggle, :off, Proc.new { @log << "off" }
10
+ end
11
+ @sm.context = self
12
+ end
13
+
14
+ it "entry action" do
15
+ @sm.get_state(:on).entry_action = Proc.new { @log << "entered_on" }
16
+
17
+ @sm.toggle
18
+
19
+ @log.join(",").should eql("on,entered_on")
20
+ end
21
+
22
+ it "exit action" do
23
+ @sm.get_state(:off).exit_action = Proc.new { @log << "exited_off" }
24
+
25
+ @sm.toggle
26
+
27
+ @log.join(",").should eql("exited_off,on")
28
+ end
29
+
30
+ it "exit and entry" do
31
+ @sm.get_state(:off).exit_action = Proc.new { @log << "exited_off" }
32
+ @sm.get_state(:on).entry_action = Proc.new { @log << "entered_on" }
33
+
34
+ @sm.toggle
35
+
36
+ @log.join(",").should eql("exited_off,on,entered_on")
37
+ end
38
+
39
+ it "entry and exit actions may be parameterized" do
40
+ @sm.get_state(:off).exit_action = Proc.new { |a| @log << "exited_off(#{a})" }
41
+ @sm.get_state(:on).entry_action = Proc.new { |a, b| @log << "entered_on(#{a},#{b})" }
42
+
43
+ @sm.toggle "one", "two"
44
+
45
+ @log.join(",").should eql("exited_off(one),on,entered_on(one,two)")
46
+ end
47
+
48
+ it "current state is set prior to exit and entry actions" do
49
+ @sm.get_state(:off).exit_action = Proc.new { @log << @sm.state }
50
+ @sm.get_state(:on).entry_action = Proc.new { @log << @sm.state }
51
+
52
+ @sm.toggle
53
+
54
+ @log.join(",").should eql("off,on,on")
55
+ end
56
+
57
+ it "current state is set prior to exit and entry actions even with super states" do
58
+ @sm = Statemachine::Statemachine.new
59
+ Statemachine.build(@sm) do
60
+ superstate :off_super do
61
+ on_exit Proc.new {@log << @sm.state}
62
+ state :off
63
+ event :toggle, :on, Proc.new { @log << "super_on" }
64
+ end
65
+ superstate :on_super do
66
+ on_entry Proc.new { @log << @sm.state }
67
+ state :on
68
+ event :toggle, :off, Proc.new { @log << "super_off" }
69
+ end
70
+ startstate :off
71
+ end
72
+ @sm.context = self
73
+
74
+ @sm.toggle
75
+ @log.join(",").should eql("off,super_on,on")
76
+ end
77
+
78
+ it "entry actions invokes another event" do
79
+ @sm.get_state(:on).entry_action = Proc.new { @sm.toggle }
80
+
81
+ @sm.toggle
82
+ @log.join(",").should eql("on,off")
83
+ @sm.state.should equal(:off)
84
+ end
85
+
86
+ it "startstate's entry action should be called when the statemachine starts" do
87
+ the_context = self
88
+ @sm = Statemachine.build do
89
+ trans :a, :b, :c
90
+ on_entry_of :a, Proc.new { @log << "entering a" }
91
+ context the_context
92
+ end
93
+
94
+ @log.join(",").should eql("entering a")
95
+ end
96
+
97
+
98
+
99
+ end
@@ -0,0 +1,67 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "State Machine Odds And Ends" do
4
+ include SwitchStatemachine
5
+
6
+ before(:each) do
7
+ create_switch
8
+ end
9
+
10
+ it "method missing delegates to super in case of no event" do
11
+ $method_missing_called = false
12
+ module Blah
13
+ def method_missing(message, *args)
14
+ $method_missing_called = true
15
+ end
16
+ end
17
+ @sm.extend(Blah)
18
+ @sm.blah
19
+ $method_missing_called.should eql(true)
20
+ end
21
+
22
+ it "should raise TransistionMissingException when the state doesn't respond to the event" do
23
+ lambda { @sm.blah }.should raise_error(Statemachine::TransitionMissingException, "'off' state does not respond to the 'blah' event.")
24
+ end
25
+
26
+ it "should respond to valid events" do
27
+ @sm.respond_to?(:toggle).should eql(true)
28
+ @sm.respond_to?(:blah).should eql(false)
29
+ end
30
+
31
+ it "should not crash when respond_to? called when the statemachine is not in a state" do
32
+ @sm.instance_eval { @state = nil }
33
+ lambda { @sm.respond_to?(:toggle) }.should_not raise_error
34
+ @sm.respond_to?(:toggle).should eql(false)
35
+ end
36
+
37
+ it "set state with string" do
38
+ @sm.state.should equal(:off)
39
+ @sm.state = "on"
40
+ @sm.state.should equal(:on)
41
+ end
42
+
43
+ it "set state with symbol" do
44
+ @sm.state.should equal(:off)
45
+ @sm.state = :on
46
+ @sm.state.should equal(:on)
47
+ end
48
+
49
+ it "process event accepts strings" do
50
+ @sm.process_event("toggle")
51
+ @sm.state.should equal(:on)
52
+ end
53
+
54
+ it "states without transitions are valid" do
55
+ @sm = Statemachine.build do
56
+ trans :middle, :push, :stuck
57
+ startstate :middle
58
+ end
59
+
60
+ @sm.push
61
+ @sm.state.should equal(:stuck)
62
+ end
63
+
64
+ end
65
+
66
+
67
+
@@ -0,0 +1,207 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require "noodle"
3
+
4
+ describe "Nested parallel" do
5
+ before(:each) do
6
+ @out_out_order = false
7
+ @locked = true
8
+ @noodle = Noodle.new
9
+
10
+ @sm = Statemachine.build do
11
+ trans :start,:go,:p
12
+ state :maintenance
13
+ parallel :p do
14
+ statemachine :s1 do
15
+ superstate :operative do
16
+ trans :locked, :coin, :unlocked, Proc.new { @cooked = true }
17
+ trans :unlocked, :coin, :locked
18
+ event :maintain, :maintenance, Proc.new { @out_of_order = true }
19
+ end
20
+ end
21
+ statemachine :s2 do
22
+ superstate :onoff do
23
+ trans :on, :toggle, :off
24
+ trans :off, :toggle, :on
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ @sm.context = @noodle
31
+ end
32
+ # @TODO add tests that set a certain state that is part of a parallel state machine
33
+ # to check if
34
+ # the other sub statemachine is set to the initial state
35
+ # the other sub state machines states doe not change if already in this parallel state machine
36
+ it "supports entering a parallel state" do
37
+ @sm.state.should eql :start
38
+ @sm.go
39
+ @sm.state.should eql :p
40
+ @sm.states_id.should == [:locked,:on]
41
+ @sm.coin
42
+ @sm.state.should eql :p
43
+ @sm.states_id.should == [:unlocked,:on]
44
+ @sm.toggle
45
+ @sm.state.should eql :p
46
+ @sm.states_id.should == [:unlocked,:off]
47
+ end
48
+
49
+ it "supports leaving a parallel state" do
50
+ @sm.states_id.should == [:start]
51
+ @sm.go
52
+ @sm.states_id.should == [:locked,:on]
53
+ @sm.maintain
54
+ @sm.state.should == :maintenance
55
+
56
+ end
57
+
58
+ it "support testing with 'in' condition for superstates " do
59
+ @sm.go
60
+ @sm.process_event(:coin)
61
+ @sm.In(:unlocked).should == true
62
+ end
63
+
64
+ it "support testing with 'in' condition for parallel superstates " do
65
+ @sm.go
66
+ @sm.coin
67
+ @sm.In(:onoff).should == true
68
+ @sm.In(:operative).should == true
69
+ @sm.In(:on).should == true
70
+
71
+ # @sm.is_in_state?(:second).should == true
72
+
73
+ @sm.maintain # TODO not working
74
+ @sm.In(:maintenance).should == true
75
+ end
76
+
77
+ it "supports process_event for parallel states" do
78
+ @sm.go
79
+ @sm.process_event(:coin)
80
+ @sm.In(:onoff).should == true
81
+ @sm.In(:operative).should == true
82
+ @sm.In(:on).should == true
83
+ end
84
+
85
+ it "supports calling transition actions inside parallel state changes" do
86
+ @noodle.cooked.should equal(false)
87
+ @sm.go
88
+ @sm.process_event(:coin)
89
+ @noodle.cooked.should equal(true)
90
+ end
91
+
92
+ it "supports calling transition actions inside parallel state changes from instant context set by process_event" do
93
+ @noodle2 = Noodle.new
94
+ @noodle2.cooked.should equal(false)
95
+ @sm.go
96
+ @sm.context = @noodle2
97
+ @sm.process_event(:coin)
98
+ @noodle2.cooked.should equal(true)
99
+ end
100
+
101
+ it "should support state recovery" do
102
+ @sm.states=[:locked,:off]
103
+ @sm.toggle
104
+ puts @sm.abstract_states
105
+ end
106
+
107
+ it "should support parallel states inside superstates" do
108
+ @sm = Statemachine.build do
109
+ trans :start,:go,:s
110
+ state :maintenance
111
+ superstate :s do
112
+ parallel :p do
113
+ statemachine :s1 do
114
+ superstate :operative do
115
+ trans :locked, :coin, :unlocked, Proc.new { @cooked = true }
116
+ trans :unlocked, :coin, :locked
117
+ event :maintain, :maintenance, Proc.new { @out_of_order = true }
118
+ end
119
+ end
120
+ statemachine :s2 do
121
+ superstate :onoff do
122
+ trans :on, :toggle, :off
123
+ trans :off, :toggle, :on
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ @sm.go
131
+ @sm.states.should.eql? [:locked,:off]
132
+
133
+ end
134
+
135
+ it "should support direct transitions into an atomic state of a parallel state set" do
136
+ @sm = Statemachine.build do
137
+ trans :start,:go, :unlocked
138
+ state :maintenance
139
+ superstate :s do
140
+ parallel :p do
141
+ statemachine :s1 do
142
+ superstate :operative do
143
+ trans :locked, :coin, :unlocked, Proc.new { @cooked = true }
144
+ trans :unlocked, :coin, :locked
145
+ event :maintain, :maintenance, Proc.new { @out_of_order = true }
146
+ end
147
+ end
148
+ statemachine :s2 do
149
+ superstate :onoff do
150
+ trans :on, :toggle, :off
151
+ trans :off, :toggle, :on
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ @sm.go
159
+ @sm.state.should eql :p
160
+ @sm.states_id.should == [:unlocked,:on]
161
+ @sm.maintain
162
+ @sm.state.should eql :maintenance
163
+ @sm.states_id.should == [:maintenance]
164
+ end
165
+
166
+ it "should support leaving a parallel state by an event from a super state of the parallel state" do
167
+ pending ("superstates have problems with late defined events ")
168
+ @sm = Statemachine.build do
169
+ trans :start,:go, :unlocked
170
+ state :maintenance
171
+ superstate :test do
172
+ superstate :s do
173
+ event :m, :maintenance
174
+ parallel :p do
175
+ statemachine :s1 do
176
+ trans :locked, :coin, :unlocked, Proc.new { @cooked = true }
177
+ trans :unlocked, :coin, :locked
178
+ end
179
+ statemachine :s2 do
180
+ superstate :onoff do
181
+ trans :on, :toggle, :off
182
+ trans :off, :toggle, :on
183
+ end
184
+ end
185
+ end
186
+ end
187
+ event :repair, :maintenance # this one does not work, event has to be defined directly after superstate definition!
188
+ end
189
+ end
190
+
191
+ @sm.go
192
+ @sm.state.should eql :p
193
+ @sm.states_id.should == [:unlocked,:on]
194
+ @sm.toggle
195
+ @sm.repair
196
+ @sm.state.should eql :maintenance
197
+ @sm.states_id.should == [:maintenance]
198
+ end
199
+
200
+ it "should fail for undefined events if actual state is inside a parallel state" do
201
+ @sm.go
202
+ lambda {@sm.unknown}.should raise_error
203
+ end
204
+
205
+
206
+
207
+ end