MINT-statemachine 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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