state_machine 0.1.0

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.
@@ -0,0 +1,234 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class EventTest < Test::Unit::TestCase
4
+ def setup
5
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
6
+ @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
7
+ end
8
+
9
+ def test_should_have_a_machine
10
+ assert_equal @machine, @event.machine
11
+ end
12
+
13
+ def test_should_have_a_name
14
+ assert_equal 'turn_on', @event.name
15
+ end
16
+
17
+ def test_should_define_an_event_action_on_the_owner_class
18
+ switch = new_switch
19
+ assert switch.respond_to?(:turn_on!)
20
+ end
21
+
22
+ def test_should_define_transition_callbacks
23
+ assert Switch.respond_to?(:transition_on_turn_on)
24
+ end
25
+
26
+ def test_should_define_before_event_callbacks
27
+ assert Switch.respond_to?(:before_turn_on)
28
+ end
29
+
30
+ def test_should_define_after_event_callbacks
31
+ assert Switch.respond_to?(:after_turn_on)
32
+ end
33
+ end
34
+
35
+ class EventWithInvalidOptionsTest < Test::Unit::TestCase
36
+ def setup
37
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
38
+ end
39
+
40
+ def test_should_raise_exception
41
+ assert_raise(ArgumentError) {PluginAWeek::StateMachine::Event.new(@machine, 'turn_on', :invalid => true)}
42
+ end
43
+ end
44
+
45
+ class EventWithTransitionsTest < Test::Unit::TestCase
46
+ def setup
47
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
48
+ @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
49
+ end
50
+
51
+ def test_should_raise_exception_if_invalid_option_specified
52
+ assert_raise(ArgumentError) {@event.transition(:invalid => true)}
53
+ end
54
+
55
+ def test_should_raise_exception_if_to_option_not_specified
56
+ assert_raise(ArgumentError) {@event.transition(:from => 'off')}
57
+ end
58
+
59
+ def test_should_not_raise_exception_if_from_option_not_specified
60
+ assert_nothing_raised {@event.transition(:to => 'on')}
61
+ end
62
+
63
+ def test_should_allow_transitioning_from_a_single_state
64
+ assert_equal [%w(off on)], @event.transition(:to => 'on', :from => 'off').map {|t| [t.from_state, t.to_state]}
65
+ end
66
+
67
+ def test_should_allow_transitioning_from_multiple_states
68
+ assert_equal [%w(off on), %w(on on)], @event.transition(:to => 'on', :from => %w(off on)).map {|t| [t.from_state, t.to_state]}
69
+ end
70
+
71
+ def teardown
72
+ Switch.class_eval do
73
+ @transition_on_turn_on_callbacks = nil
74
+ end
75
+ end
76
+ end
77
+
78
+ class EventAfterBeingFiredWithNoTransitionsTest < Test::Unit::TestCase
79
+ def setup
80
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
81
+ @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
82
+ @switch = create_switch(:state => 'off')
83
+ end
84
+
85
+ def test_should_not_fire
86
+ assert !@event.fire!(@switch)
87
+ end
88
+
89
+ def test_should_not_change_the_current_state
90
+ @event.fire!(@switch)
91
+ assert_equal 'off', @switch.state
92
+ end
93
+ end
94
+
95
+ class EventAfterBeingFiredWithTransitionsTest < Test::Unit::TestCase
96
+ def setup
97
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
98
+ @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
99
+ @event.transition :to => 'error', :from => 'on'
100
+ @switch = create_switch(:state => 'off')
101
+ end
102
+
103
+ def test_should_not_fire_if_no_transitions_are_matched
104
+ assert !@event.fire!(@switch)
105
+ assert_equal 'off', @switch.state
106
+ end
107
+
108
+ def test_should_fire_if_transition_is_matched
109
+ @event.transition :to => 'on', :from => 'off'
110
+ assert @event.fire!(@switch)
111
+ assert_equal 'on', @switch.state
112
+ end
113
+
114
+ def teardown
115
+ Switch.class_eval do
116
+ @transition_on_turn_on_callbacks = nil
117
+ end
118
+ end
119
+ end
120
+
121
+ class EventAfterBeingFiredWithConditionalTransitionsTest < Test::Unit::TestCase
122
+ def setup
123
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
124
+ @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
125
+ @event.transition :to => 'if_not_evaluated', :from => 'off', :if => Proc.new {false}
126
+ @event.transition :to => 'unless_not_evaluated', :from => 'off', :unless => Proc.new {true}
127
+ @event.transition :to => 'no_record', :from => 'off', :if => Proc.new {|record, value| record.nil?}
128
+ @event.transition :to => 'no_arguments', :from => 'off', :if => Proc.new {|record, value| value.nil?}
129
+
130
+ @switch = create_switch(:state => 'off')
131
+ end
132
+
133
+ def test_should_not_fire_of_no_transitions_are_matched
134
+ assert !@event.fire!(@switch, 1)
135
+ assert_equal 'off', @switch.state
136
+ end
137
+
138
+ def test_should_fire_if_transition_is_matched
139
+ @event.transition :to => 'on', :from => 'off', :if => Proc.new {|record, value| value == 1}
140
+ assert @event.fire!(@switch, 1)
141
+ assert_equal 'on', @switch.state
142
+ end
143
+
144
+ def teardown
145
+ Switch.class_eval do
146
+ @transition_on_turn_on_callbacks = nil
147
+ end
148
+ end
149
+ end
150
+
151
+ class EventWithinTransactionTest < Test::Unit::TestCase
152
+ def setup
153
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
154
+ @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
155
+ @event.transition :to => 'on', :from => 'off'
156
+ @switch = create_switch(:state => 'off')
157
+
158
+ Switch.define_callbacks :after_enter_state_on
159
+ end
160
+
161
+ def test_should_save_all_records_within_transaction_if_performed
162
+ Switch.after_enter_state_on Proc.new {|record| Switch.create(:state => 'pending'); true}
163
+ assert @event.fire!(@switch)
164
+ assert_equal 'on', @switch.state
165
+ assert_equal 'pending', Switch.find(:all).last.state
166
+ end
167
+
168
+ uses_transaction :test_should_rollback_all_records_within_transaction_if_not_performed
169
+ def test_should_rollback_all_records_within_transaction_if_not_performed
170
+ Switch.after_enter_state_on Proc.new {|record| Switch.create(:state => 'pending'); false}
171
+ assert !@event.fire!(@switch)
172
+ assert_equal 1, Switch.count
173
+ ensure
174
+ Switch.destroy_all
175
+ end
176
+
177
+ def teardown
178
+ Switch.class_eval do
179
+ @transition_on_turn_on_callbacks = nil
180
+ @after_enter_state_on_callbacks = nil
181
+ end
182
+ end
183
+ end
184
+
185
+ class EventWithCallbacksTest < Test::Unit::TestCase
186
+ def setup
187
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
188
+ @event = PluginAWeek::StateMachine::Event.new(@machine, 'turn_on')
189
+ @event.transition :from => 'off', :to => 'on'
190
+ @record = create_switch(:state => 'off')
191
+
192
+ Switch.define_callbacks :before_turn_on, :after_turn_on
193
+ end
194
+
195
+ def test_should_not_perform_if_before_callback_fails
196
+ Switch.before_turn_on Proc.new {|record| false}
197
+ Switch.after_turn_on Proc.new {|record| record.callbacks << 'after'; true}
198
+
199
+ assert !@event.fire!(@record)
200
+ assert_equal %w(), @record.callbacks
201
+ end
202
+
203
+ def test_should_not_perform_if_after_callback_fails
204
+ Switch.before_turn_on Proc.new {|record| record.callbacks << 'before'; true}
205
+ Switch.after_turn_on Proc.new {|record| false}
206
+
207
+ assert !@event.fire!(@record)
208
+ assert_equal %w(before), @record.callbacks
209
+ end
210
+
211
+ def test_should_perform_if_all_callbacks_are_successful
212
+ Switch.before_turn_on Proc.new {|record| record.callbacks << 'before'; true}
213
+ Switch.after_turn_on Proc.new {|record| record.callbacks << 'after'; true}
214
+
215
+ assert @event.fire!(@record)
216
+ assert_equal %w(before after), @record.callbacks
217
+ end
218
+
219
+ def test_should_pass_additional_arguments_to_callbacks
220
+ Switch.before_turn_on Proc.new {|record, value| record.callbacks << "before-#{value}"; true}
221
+ Switch.after_turn_on Proc.new {|record, value| record.callbacks << "after-#{value}"; true}
222
+
223
+ assert @event.fire!(@record, 'light')
224
+ assert_equal %w(before-light after-light), @record.callbacks
225
+ end
226
+
227
+ def teardown
228
+ Switch.class_eval do
229
+ @before_turn_on_callbacks = nil
230
+ @after_turn_on_callbacks = nil
231
+ @transition_on_turn_on_callbacks = nil
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,152 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class MachineByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
6
+ end
7
+
8
+ def test_should_have_an_attribute
9
+ assert_equal 'state', @machine.attribute
10
+ end
11
+
12
+ def test_should_not_have_an_initial_state
13
+ assert_nil @machine.initial_state(new_switch)
14
+ end
15
+
16
+ def test_should_have_an_owner_class
17
+ assert_equal Switch, @machine.owner_class
18
+ end
19
+
20
+ def test_should_not_have_any_events
21
+ assert @machine.events.empty?
22
+ end
23
+ end
24
+
25
+ class MachineWithInvalidOptionsTest < Test::Unit::TestCase
26
+ def test_should_throw_an_exception
27
+ assert_raise(ArgumentError) {PluginAWeek::StateMachine::Machine.new(Switch, 'state', :invalid => true)}
28
+ end
29
+ end
30
+
31
+ class MachineWithInitialStateTest < Test::Unit::TestCase
32
+ def setup
33
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
34
+ end
35
+
36
+ def test_should_have_an_initial_state
37
+ assert_equal 'off', @machine.initial_state(new_switch)
38
+ end
39
+ end
40
+
41
+ class MachineWithDynamicInitialStateTest < Test::Unit::TestCase
42
+ def setup
43
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => Proc.new {|switch| switch.initial_state})
44
+ end
45
+
46
+ def test_should_use_the_record_for_determining_the_initial_state
47
+ assert_equal 'off', @machine.initial_state(new_switch(:initial_state => 'off'))
48
+ assert_equal 'on', @machine.initial_state(new_switch(:initial_state => 'on'))
49
+ end
50
+ end
51
+
52
+ class MachineTest < Test::Unit::TestCase
53
+ def setup
54
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
55
+ end
56
+
57
+ def test_should_define_a_named_scope_for_the_attribute
58
+ on = create_switch(:state => 'on')
59
+ off = create_switch(:state => 'off')
60
+
61
+ assert_equal [on], Switch.with_state('on')
62
+ end
63
+
64
+ def test_should_define_a_pluralized_named_scope_for_the_attribute
65
+ on = create_switch(:state => 'on')
66
+ off = create_switch(:state => 'off')
67
+
68
+ assert_equal [on, off], Switch.with_state('on', 'off')
69
+ end
70
+ end
71
+
72
+ class MachineWithConflictingNamedScopesTest < Test::Unit::TestCase
73
+ class Switch < ActiveRecord::Base
74
+ def self.with_state
75
+ :custom
76
+ end
77
+
78
+ def self.with_states
79
+ :custom
80
+ end
81
+ end
82
+
83
+ def setup
84
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
85
+ end
86
+
87
+ def test_should_not_define_a_named_scope_for_the_attribute
88
+ assert_equal :custom, Switch.with_state
89
+ end
90
+
91
+ def test_should_not_define_a_pluralized_named_scope_for_the_attribute
92
+ assert_equal :custom, Switch.with_states
93
+ end
94
+ end
95
+
96
+ class MachineWithEventsTest < Test::Unit::TestCase
97
+ def setup
98
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
99
+ end
100
+
101
+ def test_should_create_event_with_given_name
102
+ event = @machine.event :turn_on do
103
+ end
104
+ assert_equal 'turn_on', event.name
105
+ end
106
+
107
+ def test_should_evaluate_block_within_event_context
108
+ responded = false
109
+ @machine.event :turn_on do
110
+ responded = respond_to?(:transition)
111
+ end
112
+
113
+ assert responded
114
+ end
115
+
116
+ def test_should_store_the_event
117
+ @machine.event :turn_on do
118
+ end
119
+ assert_equal 1, @machine.events.size
120
+ end
121
+ end
122
+
123
+ class MachineWithStateCallbacksTest < Test::Unit::TestCase
124
+ def setup
125
+ @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
126
+ @machine.before_exit 'off', Proc.new {|switch, value| switch.callbacks << 'before_exit'; true}
127
+ @machine.before_enter 'on', Proc.new {|switch, value| switch.callbacks << 'before_enter'; true}
128
+ @machine.after_exit 'off', Proc.new {|switch, value| switch.callbacks << 'after_exit'; true}
129
+ @machine.after_enter 'on', Proc.new {|switch, value| switch.callbacks << 'after_enter'; true}
130
+
131
+ @event = @machine.event :turn_on do
132
+ transition :to => 'on', :from => 'off'
133
+ end
134
+
135
+ @switch = create_switch(:state => 'off')
136
+ end
137
+
138
+ def test_should_invoke_callbacks_during_transition
139
+ @event.fire!(@switch)
140
+ assert_equal %w(before_exit before_enter after_exit after_enter), @switch.callbacks
141
+ end
142
+
143
+ def teardown
144
+ Switch.class_eval do
145
+ @transition_on_turn_on_callbacks = nil
146
+ @before_exit_state_off_callbacks = nil
147
+ @before_enter_state_on_callbacks = nil
148
+ @after_exit_state_off_callbacks = nil
149
+ @after_enter_state_on_callbacks = nil
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,102 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class StateMachineTest < Test::Unit::TestCase
4
+ def test_should_track_all_state_machines
5
+ @machine = Switch.state_machine(:state)
6
+ assert_equal @machine, Switch.state_machines['state']
7
+ end
8
+
9
+ def test_should_allow_multiple_state_machines
10
+ @machine = Switch.state_machine(:state)
11
+ @second_machine = Switch.state_machine(:kind)
12
+ assert_equal 2, Switch.state_machines.size
13
+ end
14
+
15
+ def test_should_evaluate_block_within_event_context
16
+ responded = false
17
+ Switch.state_machine(:state) do
18
+ responded = respond_to?(:event)
19
+ end
20
+
21
+ assert responded
22
+ end
23
+
24
+ def teardown
25
+ Switch.write_inheritable_attribute(:state_machines, {})
26
+ end
27
+ end
28
+
29
+ class StateMachineAfterInitializedTest < Test::Unit::TestCase
30
+ def setup
31
+ Switch.state_machine(:state, :initial => 'off')
32
+ end
33
+
34
+ def test_should_set_the_initial_state
35
+ assert_equal 'off', Switch.new.state
36
+ end
37
+
38
+ def teardown
39
+ Switch.write_inheritable_attribute(:state_machines, {})
40
+ end
41
+ end
42
+
43
+ class StateMachineAfterInitializedWithDynamicInitialStateTest < Test::Unit::TestCase
44
+ def setup
45
+ Switch.state_machine(:state, :initial => Proc.new {|record| record.initial_state})
46
+ end
47
+
48
+ def test_should_set_the_initial_state_based_on_the_record
49
+ assert_equal 'off', Switch.new(:initial_state => 'off').state
50
+ assert_equal 'on', Switch.new(:initial_state => 'on').state
51
+ end
52
+
53
+ def teardown
54
+ Switch.write_inheritable_attribute(:state_machines, {})
55
+ end
56
+ end
57
+
58
+ class StateMachineAfterCreatedTest < Test::Unit::TestCase
59
+ def setup
60
+ machine = Switch.state_machine(:state, :initial => 'off')
61
+
62
+ machine.before_exit 'off', Proc.new {|switch, value| switch.callbacks << 'before_exit'; true}
63
+ machine.before_enter 'off', Proc.new {|switch, value| switch.callbacks << 'before_enter'; true}
64
+ machine.after_exit 'off', Proc.new {|switch, value| switch.callbacks << 'after_exit'; true}
65
+ machine.after_enter 'off', Proc.new {|switch, value| switch.callbacks << 'after_enter'; true}
66
+
67
+ @switch = create_switch
68
+ end
69
+
70
+ def test_should_invoke_after_enter_callbacks_for_initial_state
71
+ assert_equal %w(after_enter), @switch.callbacks
72
+ end
73
+
74
+ def teardown
75
+ Switch.write_inheritable_attribute(:state_machines, {})
76
+
77
+ Switch.class_eval do
78
+ @transition_on_turn_on_callbacks = nil
79
+ @before_exit_state_off_callbacks = nil
80
+ @before_enter_state_on_callbacks = nil
81
+ @after_exit_state_off_callbacks = nil
82
+ @after_enter_state_on_callbacks = nil
83
+ end
84
+ end
85
+ end
86
+
87
+ class StateMachineWithSubclassTest < Test::Unit::TestCase
88
+ def setup
89
+ Switch.state_machine(:state, :initial => 'on')
90
+ ToggleSwitch.state_machine(:state, :initial => 'off')
91
+ end
92
+
93
+ def test_should_be_able_to_override_initial_state
94
+ assert_equal 'on', Switch.new.state
95
+ assert_equal 'off', ToggleSwitch.new.state
96
+ end
97
+
98
+ def teardown
99
+ Switch.write_inheritable_attribute(:state_machines, {})
100
+ ToggleSwitch.write_inheritable_attribute(:state_machines, {})
101
+ end
102
+ end