pluginaweek-state_machine 0.7.6
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.
- data/CHANGELOG.rdoc +273 -0
- data/LICENSE +20 -0
- data/README.rdoc +466 -0
- data/Rakefile +98 -0
- data/examples/AutoShop_state.png +0 -0
- data/examples/Car_state.png +0 -0
- data/examples/TrafficLight_state.png +0 -0
- data/examples/Vehicle_state.png +0 -0
- data/examples/auto_shop.rb +11 -0
- data/examples/car.rb +19 -0
- data/examples/merb-rest/controller.rb +51 -0
- data/examples/merb-rest/model.rb +28 -0
- data/examples/merb-rest/view_edit.html.erb +24 -0
- data/examples/merb-rest/view_index.html.erb +23 -0
- data/examples/merb-rest/view_new.html.erb +13 -0
- data/examples/merb-rest/view_show.html.erb +17 -0
- data/examples/rails-rest/controller.rb +43 -0
- data/examples/rails-rest/migration.rb +11 -0
- data/examples/rails-rest/model.rb +23 -0
- data/examples/rails-rest/view_edit.html.erb +25 -0
- data/examples/rails-rest/view_index.html.erb +23 -0
- data/examples/rails-rest/view_new.html.erb +14 -0
- data/examples/rails-rest/view_show.html.erb +17 -0
- data/examples/traffic_light.rb +7 -0
- data/examples/vehicle.rb +31 -0
- data/init.rb +1 -0
- data/lib/state_machine.rb +429 -0
- data/lib/state_machine/assertions.rb +36 -0
- data/lib/state_machine/callback.rb +189 -0
- data/lib/state_machine/condition_proxy.rb +94 -0
- data/lib/state_machine/eval_helpers.rb +67 -0
- data/lib/state_machine/event.rb +251 -0
- data/lib/state_machine/event_collection.rb +113 -0
- data/lib/state_machine/extensions.rb +158 -0
- data/lib/state_machine/guard.rb +219 -0
- data/lib/state_machine/integrations.rb +68 -0
- data/lib/state_machine/integrations/active_record.rb +444 -0
- data/lib/state_machine/integrations/active_record/locale.rb +10 -0
- data/lib/state_machine/integrations/active_record/observer.rb +41 -0
- data/lib/state_machine/integrations/data_mapper.rb +325 -0
- data/lib/state_machine/integrations/data_mapper/observer.rb +139 -0
- data/lib/state_machine/integrations/sequel.rb +292 -0
- data/lib/state_machine/machine.rb +1431 -0
- data/lib/state_machine/machine_collection.rb +146 -0
- data/lib/state_machine/matcher.rb +123 -0
- data/lib/state_machine/matcher_helpers.rb +54 -0
- data/lib/state_machine/node_collection.rb +152 -0
- data/lib/state_machine/state.rb +249 -0
- data/lib/state_machine/state_collection.rb +112 -0
- data/lib/state_machine/transition.rb +367 -0
- data/tasks/state_machine.rake +1 -0
- data/tasks/state_machine.rb +30 -0
- data/test/classes/switch.rb +11 -0
- data/test/functional/state_machine_test.rb +941 -0
- data/test/test_helper.rb +4 -0
- data/test/unit/assertions_test.rb +40 -0
- data/test/unit/callback_test.rb +455 -0
- data/test/unit/condition_proxy_test.rb +328 -0
- data/test/unit/eval_helpers_test.rb +129 -0
- data/test/unit/event_collection_test.rb +293 -0
- data/test/unit/event_test.rb +605 -0
- data/test/unit/guard_test.rb +862 -0
- data/test/unit/integrations/active_record_test.rb +1001 -0
- data/test/unit/integrations/data_mapper_test.rb +694 -0
- data/test/unit/integrations/sequel_test.rb +486 -0
- data/test/unit/integrations_test.rb +42 -0
- data/test/unit/invalid_event_test.rb +7 -0
- data/test/unit/invalid_transition_test.rb +7 -0
- data/test/unit/machine_collection_test.rb +710 -0
- data/test/unit/machine_test.rb +1910 -0
- data/test/unit/matcher_helpers_test.rb +37 -0
- data/test/unit/matcher_test.rb +155 -0
- data/test/unit/node_collection_test.rb +207 -0
- data/test/unit/state_collection_test.rb +280 -0
- data/test/unit/state_machine_test.rb +31 -0
- data/test/unit/state_test.rb +795 -0
- data/test/unit/transition_test.rb +1113 -0
- metadata +161 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
class StateMachineByDefaultTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@klass = Class.new
|
6
|
+
@machine = @klass.state_machine
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_should_use_state_attribute
|
10
|
+
assert_equal :state, @machine.attribute
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class StateMachineTest < Test::Unit::TestCase
|
15
|
+
def setup
|
16
|
+
@klass = Class.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_should_allow_state_machines_on_any_class
|
20
|
+
assert @klass.respond_to?(:state_machine)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_should_evaluate_block_within_machine_context
|
24
|
+
responded = false
|
25
|
+
@klass.state_machine(:state) do
|
26
|
+
responded = respond_to?(:event)
|
27
|
+
end
|
28
|
+
|
29
|
+
assert responded
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,795 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
class StateByDefaultTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@machine = StateMachine::Machine.new(Class.new)
|
6
|
+
@state = StateMachine::State.new(@machine, :parked)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_should_have_a_machine
|
10
|
+
assert_equal @machine, @state.machine
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_should_have_a_name
|
14
|
+
assert_equal :parked, @state.name
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_should_have_a_qualified_name
|
18
|
+
assert_equal :parked, @state.name
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_should_use_stringify_the_name_as_the_value
|
22
|
+
assert_equal 'parked', @state.value
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_should_not_be_initial
|
26
|
+
assert !@state.initial
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_should_not_have_a_matcher
|
30
|
+
assert_nil @state.matcher
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_should_not_have_any_methods
|
34
|
+
expected = {}
|
35
|
+
assert_equal expected, @state.methods
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class StateTest < Test::Unit::TestCase
|
40
|
+
def setup
|
41
|
+
@machine = StateMachine::Machine.new(Class.new)
|
42
|
+
@state = StateMachine::State.new(@machine, :parked)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_should_raise_exception_if_invalid_option_specified
|
46
|
+
exception = assert_raise(ArgumentError) {StateMachine::State.new(@machine, :parked, :invalid => true)}
|
47
|
+
assert_equal 'Invalid key(s): invalid', exception.message
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_should_allow_changing_machine
|
51
|
+
new_machine = StateMachine::Machine.new(Class.new)
|
52
|
+
@state.machine = new_machine
|
53
|
+
assert_equal new_machine, @state.machine
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_should_allow_changing_value
|
57
|
+
@state.value = 1
|
58
|
+
assert_equal 1, @state.value
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_should_allow_changing_initial
|
62
|
+
@state.initial = true
|
63
|
+
assert @state.initial
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_should_allow_changing_matcher
|
67
|
+
matcher = lambda {}
|
68
|
+
@state.matcher = matcher
|
69
|
+
assert_equal matcher, @state.matcher
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_should_use_pretty_inspect
|
73
|
+
assert_equal '#<StateMachine::State name=:parked value="parked" initial=false context=[]>', @state.inspect
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class StateWithoutNameTest < Test::Unit::TestCase
|
78
|
+
def setup
|
79
|
+
@klass = Class.new
|
80
|
+
@machine = StateMachine::Machine.new(@klass)
|
81
|
+
@state = StateMachine::State.new(@machine, nil)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_should_have_a_nil_name
|
85
|
+
assert_nil @state.name
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_should_have_a_nil_qualified_name
|
89
|
+
assert_nil @state.qualified_name
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_should_have_a_nil_value
|
93
|
+
assert_nil @state.value
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_should_not_redefine_nil_predicate
|
97
|
+
object = @klass.new
|
98
|
+
assert !object.nil?
|
99
|
+
assert !object.respond_to?('?')
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_should_have_a_description
|
103
|
+
assert_equal 'nil', @state.description
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class StateWithNameTest < Test::Unit::TestCase
|
108
|
+
def setup
|
109
|
+
@klass = Class.new
|
110
|
+
@machine = StateMachine::Machine.new(@klass)
|
111
|
+
@state = StateMachine::State.new(@machine, :parked)
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_should_have_a_name
|
115
|
+
assert_equal :parked, @state.name
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_should_have_a_qualified_name
|
119
|
+
assert_equal :parked, @state.name
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_should_use_stringify_the_name_as_the_value
|
123
|
+
assert_equal 'parked', @state.value
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_should_match_stringified_name
|
127
|
+
assert @state.matches?('parked')
|
128
|
+
assert !@state.matches?('idling')
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_should_not_include_value_in_description
|
132
|
+
assert_equal 'parked', @state.description
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_should_define_predicate
|
136
|
+
assert @klass.new.respond_to?(:parked?)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class StateWithNilValueTest < Test::Unit::TestCase
|
141
|
+
def setup
|
142
|
+
@klass = Class.new
|
143
|
+
@machine = StateMachine::Machine.new(@klass)
|
144
|
+
@state = StateMachine::State.new(@machine, :parked, :value => nil)
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_should_have_a_name
|
148
|
+
assert_equal :parked, @state.name
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_should_have_a_nil_value
|
152
|
+
assert_nil @state.value
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_should_match_nil_values
|
156
|
+
assert @state.matches?(nil)
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_should_have_a_description
|
160
|
+
assert_equal 'parked (nil)', @state.description
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_should_define_predicate
|
164
|
+
object = @klass.new
|
165
|
+
assert object.respond_to?(:parked?)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class StateWithSymbolicValueTest < Test::Unit::TestCase
|
170
|
+
def setup
|
171
|
+
@klass = Class.new
|
172
|
+
@machine = StateMachine::Machine.new(@klass)
|
173
|
+
@state = StateMachine::State.new(@machine, :parked, :value => :parked)
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_should_use_custom_value
|
177
|
+
assert_equal :parked, @state.value
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_should_not_include_value_in_description
|
181
|
+
assert_equal 'parked', @state.description
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_should_match_symbolic_value
|
185
|
+
assert @state.matches?(:parked)
|
186
|
+
assert !@state.matches?('parked')
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_should_define_predicate
|
190
|
+
object = @klass.new
|
191
|
+
assert object.respond_to?(:parked?)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class StateWithIntegerValueTest < Test::Unit::TestCase
|
196
|
+
def setup
|
197
|
+
@klass = Class.new
|
198
|
+
@machine = StateMachine::Machine.new(@klass)
|
199
|
+
@state = StateMachine::State.new(@machine, :parked, :value => 1)
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_should_use_custom_value
|
203
|
+
assert_equal 1, @state.value
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_should_include_value_in_description
|
207
|
+
assert_equal 'parked (1)', @state.description
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_should_match_integer_value
|
211
|
+
assert @state.matches?(1)
|
212
|
+
assert !@state.matches?(2)
|
213
|
+
end
|
214
|
+
|
215
|
+
def test_should_define_predicate
|
216
|
+
object = @klass.new
|
217
|
+
assert object.respond_to?(:parked?)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
class StateWithLambdaValueTest < Test::Unit::TestCase
|
222
|
+
def setup
|
223
|
+
@klass = Class.new
|
224
|
+
@args = nil
|
225
|
+
@machine = StateMachine::Machine.new(@klass)
|
226
|
+
@value = lambda {|*args| @args = args; :parked}
|
227
|
+
@state = StateMachine::State.new(@machine, :parked, :value => @value)
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_should_use_evaluated_value_by_default
|
231
|
+
assert_equal :parked, @state.value
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_should_allow_access_to_original_value
|
235
|
+
assert_equal @value, @state.value(false)
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_should_include_masked_value_in_description
|
239
|
+
assert_equal 'parked (*)', @state.description
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_should_not_pass_in_any_arguments
|
243
|
+
@state.value
|
244
|
+
assert_equal [], @args
|
245
|
+
end
|
246
|
+
|
247
|
+
def test_should_define_predicate
|
248
|
+
object = @klass.new
|
249
|
+
assert object.respond_to?(:parked?)
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_should_match_evaluated_value
|
253
|
+
assert @state.matches?(:parked)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class StateWithCachedLambdaValueTest < Test::Unit::TestCase
|
258
|
+
def setup
|
259
|
+
@klass = Class.new
|
260
|
+
@machine = StateMachine::Machine.new(@klass)
|
261
|
+
@dynamic_value = lambda {'value'}
|
262
|
+
@machine.states << @state = StateMachine::State.new(@machine, :parked, :value => @dynamic_value, :cache => true)
|
263
|
+
end
|
264
|
+
|
265
|
+
def test_should_be_caching
|
266
|
+
assert @state.cache
|
267
|
+
end
|
268
|
+
|
269
|
+
def test_should_evaluate_value
|
270
|
+
assert_equal 'value', @state.value
|
271
|
+
end
|
272
|
+
|
273
|
+
def test_should_only_evaluate_value_once
|
274
|
+
value = @state.value
|
275
|
+
assert_same value, @state.value
|
276
|
+
end
|
277
|
+
|
278
|
+
def test_should_update_value_index_for_state_collection
|
279
|
+
@state.value
|
280
|
+
assert_equal @state, @machine.states['value', :value]
|
281
|
+
assert_nil @machine.states[@dynamic_value, :value]
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
class StateWithoutCachedLambdaValueTest < Test::Unit::TestCase
|
286
|
+
def setup
|
287
|
+
@klass = Class.new
|
288
|
+
@machine = StateMachine::Machine.new(@klass)
|
289
|
+
@dynamic_value = lambda {'value'}
|
290
|
+
@machine.states << @state = StateMachine::State.new(@machine, :parked, :value => @dynamic_value)
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_should_not_be_caching
|
294
|
+
assert !@state.cache
|
295
|
+
end
|
296
|
+
|
297
|
+
def test_should_evaluate_value_each_time
|
298
|
+
value = @state.value
|
299
|
+
assert_not_same value, @state.value
|
300
|
+
end
|
301
|
+
|
302
|
+
def test_should_not_update_value_index_for_state_collection
|
303
|
+
@state.value
|
304
|
+
assert_nil @machine.states['value', :value]
|
305
|
+
assert_equal @state, @machine.states[@dynamic_value, :value]
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
class StateWithMatcherTest < Test::Unit::TestCase
|
310
|
+
def setup
|
311
|
+
@klass = Class.new
|
312
|
+
@args = nil
|
313
|
+
@machine = StateMachine::Machine.new(@klass)
|
314
|
+
@state = StateMachine::State.new(@machine, :parked, :if => lambda {|value| value == 1})
|
315
|
+
end
|
316
|
+
|
317
|
+
def test_should_not_match_actual_value
|
318
|
+
assert !@state.matches?('parked')
|
319
|
+
end
|
320
|
+
|
321
|
+
def test_should_match_evaluated_block
|
322
|
+
assert @state.matches?(1)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
class StateInitialTest < Test::Unit::TestCase
|
327
|
+
def setup
|
328
|
+
@machine = StateMachine::Machine.new(Class.new)
|
329
|
+
@state = StateMachine::State.new(@machine, :parked, :initial => true)
|
330
|
+
end
|
331
|
+
|
332
|
+
def test_should_be_initial
|
333
|
+
assert @state.initial
|
334
|
+
assert @state.initial?
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
class StateNotInitialTest < Test::Unit::TestCase
|
339
|
+
def setup
|
340
|
+
@machine = StateMachine::Machine.new(Class.new)
|
341
|
+
@state = StateMachine::State.new(@machine, :parked, :initial => false)
|
342
|
+
end
|
343
|
+
|
344
|
+
def test_should_not_be_initial
|
345
|
+
assert !@state.initial
|
346
|
+
assert !@state.initial?
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
class StateFinalTest < Test::Unit::TestCase
|
351
|
+
def setup
|
352
|
+
@machine = StateMachine::Machine.new(Class.new)
|
353
|
+
@state = StateMachine::State.new(@machine, :parked)
|
354
|
+
end
|
355
|
+
|
356
|
+
def test_should_be_final_without_input_transitions
|
357
|
+
assert @state.final?
|
358
|
+
end
|
359
|
+
|
360
|
+
def test_should_be_final_with_input_transitions
|
361
|
+
@machine.event :park do
|
362
|
+
transition :idling => :parked
|
363
|
+
end
|
364
|
+
|
365
|
+
assert @state.final?
|
366
|
+
end
|
367
|
+
|
368
|
+
def test_should_be_final_with_loopback
|
369
|
+
@machine.event :ignite do
|
370
|
+
transition :parked => same
|
371
|
+
end
|
372
|
+
|
373
|
+
assert @state.final?
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
class StateNotFinalTest < Test::Unit::TestCase
|
378
|
+
def setup
|
379
|
+
@machine = StateMachine::Machine.new(Class.new)
|
380
|
+
@state = StateMachine::State.new(@machine, :parked)
|
381
|
+
end
|
382
|
+
|
383
|
+
def test_should_not_be_final_with_outgoing_whitelist_transitions
|
384
|
+
@machine.event :ignite do
|
385
|
+
transition :parked => :idling
|
386
|
+
end
|
387
|
+
|
388
|
+
assert !@state.final?
|
389
|
+
end
|
390
|
+
|
391
|
+
def test_should_not_be_final_with_outgoing_all_transitions
|
392
|
+
@machine.event :ignite do
|
393
|
+
transition all => :idling
|
394
|
+
end
|
395
|
+
|
396
|
+
assert !@state.final?
|
397
|
+
end
|
398
|
+
|
399
|
+
def test_should_not_be_final_with_outgoing_blacklist_transitions
|
400
|
+
@machine.event :ignite do
|
401
|
+
transition all - :first_gear => :idling
|
402
|
+
end
|
403
|
+
|
404
|
+
assert !@state.final?
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
class StateWithConflictingHelpersTest < Test::Unit::TestCase
|
409
|
+
def setup
|
410
|
+
@klass = Class.new do
|
411
|
+
def parked?
|
412
|
+
0
|
413
|
+
end
|
414
|
+
end
|
415
|
+
@machine = StateMachine::Machine.new(@klass)
|
416
|
+
@machine.state :parked
|
417
|
+
@object = @klass.new
|
418
|
+
end
|
419
|
+
|
420
|
+
def test_should_not_redefine_state_predicate
|
421
|
+
assert_equal 0, @object.parked?
|
422
|
+
end
|
423
|
+
|
424
|
+
def test_should_allow_super_chaining
|
425
|
+
@klass.class_eval do
|
426
|
+
def parked?
|
427
|
+
super ? 1 : 0
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
assert_equal 0, @object.parked?
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
class StateWithNamespaceTest < Test::Unit::TestCase
|
436
|
+
def setup
|
437
|
+
@klass = Class.new
|
438
|
+
@machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
|
439
|
+
@state = StateMachine::State.new(@machine, :active)
|
440
|
+
@object = @klass.new
|
441
|
+
end
|
442
|
+
|
443
|
+
def test_should_have_a_name
|
444
|
+
assert_equal :active, @state.name
|
445
|
+
end
|
446
|
+
|
447
|
+
def test_should_have_a_qualified_name
|
448
|
+
assert_equal :alarm_active, @state.qualified_name
|
449
|
+
end
|
450
|
+
|
451
|
+
def test_should_namespace_predicate
|
452
|
+
assert @object.respond_to?(:alarm_active?)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
class StateAfterBeingCopiedTest < Test::Unit::TestCase
|
457
|
+
def setup
|
458
|
+
@machine = StateMachine::Machine.new(Class.new)
|
459
|
+
@state = StateMachine::State.new(@machine, :parked)
|
460
|
+
@copied_state = @state.dup
|
461
|
+
end
|
462
|
+
|
463
|
+
def test_should_not_have_the_same_collection_of_methods
|
464
|
+
assert_not_same @state.methods, @copied_state.methods
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
class StateWithContextTest < Test::Unit::TestCase
|
469
|
+
def setup
|
470
|
+
@klass = Class.new
|
471
|
+
@machine = StateMachine::Machine.new(@klass)
|
472
|
+
@ancestors = @klass.ancestors
|
473
|
+
@state = StateMachine::State.new(@machine, :idling)
|
474
|
+
|
475
|
+
speed_method = nil
|
476
|
+
rpm_method = nil
|
477
|
+
@state.context do
|
478
|
+
def speed
|
479
|
+
0
|
480
|
+
end
|
481
|
+
speed_method = instance_method(:speed)
|
482
|
+
|
483
|
+
def rpm
|
484
|
+
1000
|
485
|
+
end
|
486
|
+
rpm_method = instance_method(:rpm)
|
487
|
+
end
|
488
|
+
|
489
|
+
@speed_method = speed_method
|
490
|
+
@rpm_method = rpm_method
|
491
|
+
end
|
492
|
+
|
493
|
+
def test_should_include_new_module_in_owner_class
|
494
|
+
assert_not_equal @ancestors, @klass.ancestors
|
495
|
+
assert_equal 1, @klass.ancestors.size - @ancestors.size
|
496
|
+
end
|
497
|
+
|
498
|
+
def test_should_define_each_context_method_in_owner_class
|
499
|
+
%w(speed rpm).each {|method| assert @klass.method_defined?(method)}
|
500
|
+
end
|
501
|
+
|
502
|
+
def test_should_not_use_context_methods_as_owner_class_methods
|
503
|
+
assert_not_equal @speed_method, @klass.instance_method(:speed)
|
504
|
+
assert_not_equal @rpm_method, @klass.instance_method(:rpm)
|
505
|
+
end
|
506
|
+
|
507
|
+
def test_should_include_context_methods_in_state_methods
|
508
|
+
assert_equal @speed_method, @state.methods[:speed]
|
509
|
+
assert_equal @rpm_method, @state.methods[:rpm]
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
class StateWithMultipleContextsTest < Test::Unit::TestCase
|
514
|
+
def setup
|
515
|
+
@klass = Class.new
|
516
|
+
@machine = StateMachine::Machine.new(@klass)
|
517
|
+
@ancestors = @klass.ancestors
|
518
|
+
@state = StateMachine::State.new(@machine, :idling)
|
519
|
+
|
520
|
+
speed_method = nil
|
521
|
+
@state.context do
|
522
|
+
def speed
|
523
|
+
0
|
524
|
+
end
|
525
|
+
|
526
|
+
speed_method = instance_method(:speed)
|
527
|
+
end
|
528
|
+
@speed_method = speed_method
|
529
|
+
|
530
|
+
rpm_method = nil
|
531
|
+
@state.context do
|
532
|
+
def rpm
|
533
|
+
1000
|
534
|
+
end
|
535
|
+
|
536
|
+
rpm_method = instance_method(:rpm)
|
537
|
+
end
|
538
|
+
@rpm_method = rpm_method
|
539
|
+
end
|
540
|
+
|
541
|
+
def test_should_include_new_module_in_owner_class
|
542
|
+
assert_not_equal @ancestors, @klass.ancestors
|
543
|
+
assert_equal 2, @klass.ancestors.size - @ancestors.size
|
544
|
+
end
|
545
|
+
|
546
|
+
def test_should_define_each_context_method_in_owner_class
|
547
|
+
%w(speed rpm).each {|method| assert @klass.method_defined?(method)}
|
548
|
+
end
|
549
|
+
|
550
|
+
def test_should_not_use_context_methods_as_owner_class_methods
|
551
|
+
assert_not_equal @speed_method, @klass.instance_method(:speed)
|
552
|
+
assert_not_equal @rpm_method, @klass.instance_method(:rpm)
|
553
|
+
end
|
554
|
+
|
555
|
+
def test_should_include_context_methods_in_state_methods
|
556
|
+
assert_equal @speed_method, @state.methods[:speed]
|
557
|
+
assert_equal @rpm_method, @state.methods[:rpm]
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
class StateWithExistingContextMethodTest < Test::Unit::TestCase
|
562
|
+
def setup
|
563
|
+
@klass = Class.new do
|
564
|
+
def speed
|
565
|
+
60
|
566
|
+
end
|
567
|
+
end
|
568
|
+
@original_speed_method = @klass.instance_method(:speed)
|
569
|
+
|
570
|
+
@machine = StateMachine::Machine.new(@klass)
|
571
|
+
@state = StateMachine::State.new(@machine, :idling)
|
572
|
+
@state.context do
|
573
|
+
def speed
|
574
|
+
0
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
def test_should_not_override_method
|
580
|
+
assert_equal @original_speed_method, @klass.instance_method(:speed)
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
class StateWithRedefinedContextMethodTest < Test::Unit::TestCase
|
585
|
+
def setup
|
586
|
+
@klass = Class.new
|
587
|
+
@machine = StateMachine::Machine.new(@klass)
|
588
|
+
@state = StateMachine::State.new(@machine, 'on')
|
589
|
+
|
590
|
+
old_speed_method = nil
|
591
|
+
@state.context do
|
592
|
+
def speed
|
593
|
+
0
|
594
|
+
end
|
595
|
+
old_speed_method = instance_method(:speed)
|
596
|
+
end
|
597
|
+
@old_speed_method = old_speed_method
|
598
|
+
|
599
|
+
current_speed_method = nil
|
600
|
+
@state.context do
|
601
|
+
def speed
|
602
|
+
'green'
|
603
|
+
end
|
604
|
+
current_speed_method = instance_method(:speed)
|
605
|
+
end
|
606
|
+
@current_speed_method = current_speed_method
|
607
|
+
end
|
608
|
+
|
609
|
+
def test_should_track_latest_defined_method
|
610
|
+
assert_equal @current_speed_method, @state.methods[:speed]
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
class StateWithInvalidMethodCallTest < Test::Unit::TestCase
|
615
|
+
def setup
|
616
|
+
@klass = Class.new
|
617
|
+
@machine = StateMachine::Machine.new(@klass)
|
618
|
+
@ancestors = @klass.ancestors
|
619
|
+
@state = StateMachine::State.new(@machine, :idling)
|
620
|
+
@state.context do
|
621
|
+
def speed
|
622
|
+
0
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
626
|
+
@object = @klass.new
|
627
|
+
end
|
628
|
+
|
629
|
+
def test_should_raise_an_exception
|
630
|
+
exception = assert_raise(NoMethodError) { @state.call(@object, :invalid) }
|
631
|
+
assert_equal "undefined method 'invalid' for #{@object} in state nil", exception.message
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
class StateWithValidMethodCallTest < Test::Unit::TestCase
|
636
|
+
def setup
|
637
|
+
@klass = Class.new
|
638
|
+
@machine = StateMachine::Machine.new(@klass)
|
639
|
+
@ancestors = @klass.ancestors
|
640
|
+
@state = StateMachine::State.new(@machine, :idling)
|
641
|
+
@state.context do
|
642
|
+
def speed(arg = nil)
|
643
|
+
block_given? ? [arg, yield] : arg
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
@object = @klass.new
|
648
|
+
end
|
649
|
+
|
650
|
+
def test_should_not_raise_an_exception
|
651
|
+
assert_nothing_raised { @state.call(@object, :speed) }
|
652
|
+
end
|
653
|
+
|
654
|
+
def test_should_pass_arguments_through
|
655
|
+
assert_equal 1, @state.call(@object, :speed, 1)
|
656
|
+
end
|
657
|
+
|
658
|
+
def test_should_pass_blocks_through
|
659
|
+
assert_equal [nil, 1], @state.call(@object, :speed) {1}
|
660
|
+
end
|
661
|
+
|
662
|
+
def test_should_pass_both_arguments_and_blocks_through
|
663
|
+
assert_equal [1, 2], @state.call(@object, :speed, 1) {2}
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
begin
|
668
|
+
# Load library
|
669
|
+
require 'rubygems'
|
670
|
+
require 'graphviz'
|
671
|
+
|
672
|
+
class StateDrawingTest < Test::Unit::TestCase
|
673
|
+
def setup
|
674
|
+
@machine = StateMachine::Machine.new(Class.new)
|
675
|
+
@machine.event :ignite do
|
676
|
+
transition :parked => :idling
|
677
|
+
end
|
678
|
+
@state = StateMachine::State.new(@machine, :parked, :value => 1)
|
679
|
+
|
680
|
+
graph = GraphViz.new('G')
|
681
|
+
@node = @state.draw(graph)
|
682
|
+
end
|
683
|
+
|
684
|
+
def test_should_use_ellipse_shape
|
685
|
+
assert_equal 'ellipse', @node['shape']
|
686
|
+
end
|
687
|
+
|
688
|
+
def test_should_set_width_to_one
|
689
|
+
assert_equal '1', @node['width']
|
690
|
+
end
|
691
|
+
|
692
|
+
def test_should_set_height_to_one
|
693
|
+
assert_equal '1', @node['height']
|
694
|
+
end
|
695
|
+
|
696
|
+
def test_should_use_stringified_name_as_name
|
697
|
+
assert_equal 'parked', @node.name
|
698
|
+
end
|
699
|
+
|
700
|
+
def test_should_use_description_as_label
|
701
|
+
assert_equal 'parked (1)', @node['label']
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
705
|
+
class StateDrawingInitialTest < Test::Unit::TestCase
|
706
|
+
def setup
|
707
|
+
@machine = StateMachine::Machine.new(Class.new)
|
708
|
+
@machine.event :ignite do
|
709
|
+
transition :parked => :idling
|
710
|
+
end
|
711
|
+
@state = StateMachine::State.new(@machine, :parked, :initial => true)
|
712
|
+
|
713
|
+
@graph = GraphViz.new('G')
|
714
|
+
@node = @state.draw(@graph)
|
715
|
+
end
|
716
|
+
|
717
|
+
def test_should_use_ellipse_as_shape
|
718
|
+
assert_equal 'ellipse', @node['shape']
|
719
|
+
end
|
720
|
+
|
721
|
+
def test_should_draw_edge_between_point_and_state
|
722
|
+
assert_equal 2, @graph.node_count
|
723
|
+
assert_equal 1, @graph.edge_count
|
724
|
+
end
|
725
|
+
end
|
726
|
+
|
727
|
+
class StateDrawingNilNameTest < Test::Unit::TestCase
|
728
|
+
def setup
|
729
|
+
@machine = StateMachine::Machine.new(Class.new)
|
730
|
+
@state = StateMachine::State.new(@machine, nil)
|
731
|
+
|
732
|
+
graph = GraphViz.new('G')
|
733
|
+
@node = @state.draw(graph)
|
734
|
+
end
|
735
|
+
|
736
|
+
def test_should_use_stringified_nil_as_name
|
737
|
+
assert_equal 'nil', @node.name
|
738
|
+
end
|
739
|
+
|
740
|
+
def test_should_use_description_as_label
|
741
|
+
assert_equal 'nil', @node['label']
|
742
|
+
end
|
743
|
+
end
|
744
|
+
|
745
|
+
class StateDrawingLambdaValueTest < Test::Unit::TestCase
|
746
|
+
def setup
|
747
|
+
@machine = StateMachine::Machine.new(Class.new)
|
748
|
+
@state = StateMachine::State.new(@machine, :parked, :value => lambda {})
|
749
|
+
|
750
|
+
graph = GraphViz.new('G')
|
751
|
+
@node = @state.draw(graph)
|
752
|
+
end
|
753
|
+
|
754
|
+
def test_should_use_stringified_name_as_name
|
755
|
+
assert_equal 'parked', @node.name
|
756
|
+
end
|
757
|
+
|
758
|
+
def test_should_use_description_as_label
|
759
|
+
assert_equal 'parked (*)', @node['label']
|
760
|
+
end
|
761
|
+
end
|
762
|
+
|
763
|
+
class StateDrawingNonFinalTest < Test::Unit::TestCase
|
764
|
+
def setup
|
765
|
+
@machine = StateMachine::Machine.new(Class.new)
|
766
|
+
@machine.event :ignite do
|
767
|
+
transition :parked => :idling
|
768
|
+
end
|
769
|
+
@state = StateMachine::State.new(@machine, :parked)
|
770
|
+
|
771
|
+
graph = GraphViz.new('G')
|
772
|
+
@node = @state.draw(graph)
|
773
|
+
end
|
774
|
+
|
775
|
+
def test_should_use_ellipse_as_shape
|
776
|
+
assert_equal 'ellipse', @node['shape']
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
class StateDrawingFinalTest < Test::Unit::TestCase
|
781
|
+
def setup
|
782
|
+
@machine = StateMachine::Machine.new(Class.new)
|
783
|
+
@state = StateMachine::State.new(@machine, :parked)
|
784
|
+
|
785
|
+
graph = GraphViz.new('G')
|
786
|
+
@node = @state.draw(graph)
|
787
|
+
end
|
788
|
+
|
789
|
+
def test_should_use_doublecircle_as_shape
|
790
|
+
assert_equal 'doublecircle', @node['shape']
|
791
|
+
end
|
792
|
+
end
|
793
|
+
rescue LoadError
|
794
|
+
$stderr.puts 'Skipping GraphViz StateMachine::State tests. `gem install ruby-graphviz` and try again.'
|
795
|
+
end
|