hsume2-state_machine 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. data/CHANGELOG.rdoc +413 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +717 -0
  4. data/Rakefile +77 -0
  5. data/examples/AutoShop_state.png +0 -0
  6. data/examples/Car_state.png +0 -0
  7. data/examples/TrafficLight_state.png +0 -0
  8. data/examples/Vehicle_state.png +0 -0
  9. data/examples/auto_shop.rb +11 -0
  10. data/examples/car.rb +19 -0
  11. data/examples/merb-rest/controller.rb +51 -0
  12. data/examples/merb-rest/model.rb +28 -0
  13. data/examples/merb-rest/view_edit.html.erb +24 -0
  14. data/examples/merb-rest/view_index.html.erb +23 -0
  15. data/examples/merb-rest/view_new.html.erb +13 -0
  16. data/examples/merb-rest/view_show.html.erb +17 -0
  17. data/examples/rails-rest/controller.rb +43 -0
  18. data/examples/rails-rest/migration.rb +11 -0
  19. data/examples/rails-rest/model.rb +23 -0
  20. data/examples/rails-rest/view_edit.html.erb +25 -0
  21. data/examples/rails-rest/view_index.html.erb +23 -0
  22. data/examples/rails-rest/view_new.html.erb +14 -0
  23. data/examples/rails-rest/view_show.html.erb +17 -0
  24. data/examples/traffic_light.rb +7 -0
  25. data/examples/vehicle.rb +31 -0
  26. data/init.rb +1 -0
  27. data/lib/state_machine.rb +448 -0
  28. data/lib/state_machine/alternate_machine.rb +79 -0
  29. data/lib/state_machine/assertions.rb +36 -0
  30. data/lib/state_machine/branch.rb +224 -0
  31. data/lib/state_machine/callback.rb +236 -0
  32. data/lib/state_machine/condition_proxy.rb +94 -0
  33. data/lib/state_machine/error.rb +13 -0
  34. data/lib/state_machine/eval_helpers.rb +86 -0
  35. data/lib/state_machine/event.rb +304 -0
  36. data/lib/state_machine/event_collection.rb +139 -0
  37. data/lib/state_machine/extensions.rb +149 -0
  38. data/lib/state_machine/initializers.rb +4 -0
  39. data/lib/state_machine/initializers/merb.rb +1 -0
  40. data/lib/state_machine/initializers/rails.rb +25 -0
  41. data/lib/state_machine/integrations.rb +110 -0
  42. data/lib/state_machine/integrations/active_model.rb +502 -0
  43. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  44. data/lib/state_machine/integrations/active_model/observer.rb +45 -0
  45. data/lib/state_machine/integrations/active_model/versions.rb +31 -0
  46. data/lib/state_machine/integrations/active_record.rb +424 -0
  47. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  48. data/lib/state_machine/integrations/active_record/versions.rb +143 -0
  49. data/lib/state_machine/integrations/base.rb +91 -0
  50. data/lib/state_machine/integrations/data_mapper.rb +392 -0
  51. data/lib/state_machine/integrations/data_mapper/observer.rb +210 -0
  52. data/lib/state_machine/integrations/data_mapper/versions.rb +62 -0
  53. data/lib/state_machine/integrations/mongo_mapper.rb +272 -0
  54. data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
  55. data/lib/state_machine/integrations/mongo_mapper/versions.rb +110 -0
  56. data/lib/state_machine/integrations/mongoid.rb +357 -0
  57. data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
  58. data/lib/state_machine/integrations/mongoid/versions.rb +18 -0
  59. data/lib/state_machine/integrations/sequel.rb +428 -0
  60. data/lib/state_machine/integrations/sequel/versions.rb +36 -0
  61. data/lib/state_machine/machine.rb +1873 -0
  62. data/lib/state_machine/machine_collection.rb +87 -0
  63. data/lib/state_machine/matcher.rb +123 -0
  64. data/lib/state_machine/matcher_helpers.rb +54 -0
  65. data/lib/state_machine/node_collection.rb +157 -0
  66. data/lib/state_machine/path.rb +120 -0
  67. data/lib/state_machine/path_collection.rb +90 -0
  68. data/lib/state_machine/state.rb +271 -0
  69. data/lib/state_machine/state_collection.rb +112 -0
  70. data/lib/state_machine/transition.rb +458 -0
  71. data/lib/state_machine/transition_collection.rb +244 -0
  72. data/lib/tasks/state_machine.rake +1 -0
  73. data/lib/tasks/state_machine.rb +27 -0
  74. data/test/files/en.yml +17 -0
  75. data/test/files/switch.rb +11 -0
  76. data/test/functional/alternate_state_machine_test.rb +122 -0
  77. data/test/functional/state_machine_test.rb +993 -0
  78. data/test/test_helper.rb +4 -0
  79. data/test/unit/assertions_test.rb +40 -0
  80. data/test/unit/branch_test.rb +890 -0
  81. data/test/unit/callback_test.rb +701 -0
  82. data/test/unit/condition_proxy_test.rb +328 -0
  83. data/test/unit/error_test.rb +43 -0
  84. data/test/unit/eval_helpers_test.rb +222 -0
  85. data/test/unit/event_collection_test.rb +358 -0
  86. data/test/unit/event_test.rb +985 -0
  87. data/test/unit/integrations/active_model_test.rb +1097 -0
  88. data/test/unit/integrations/active_record_test.rb +2021 -0
  89. data/test/unit/integrations/base_test.rb +99 -0
  90. data/test/unit/integrations/data_mapper_test.rb +1909 -0
  91. data/test/unit/integrations/mongo_mapper_test.rb +1611 -0
  92. data/test/unit/integrations/mongoid_test.rb +1591 -0
  93. data/test/unit/integrations/sequel_test.rb +1523 -0
  94. data/test/unit/integrations_test.rb +61 -0
  95. data/test/unit/invalid_event_test.rb +20 -0
  96. data/test/unit/invalid_parallel_transition_test.rb +18 -0
  97. data/test/unit/invalid_transition_test.rb +77 -0
  98. data/test/unit/machine_collection_test.rb +599 -0
  99. data/test/unit/machine_test.rb +3043 -0
  100. data/test/unit/matcher_helpers_test.rb +37 -0
  101. data/test/unit/matcher_test.rb +155 -0
  102. data/test/unit/node_collection_test.rb +217 -0
  103. data/test/unit/path_collection_test.rb +266 -0
  104. data/test/unit/path_test.rb +485 -0
  105. data/test/unit/state_collection_test.rb +310 -0
  106. data/test/unit/state_machine_test.rb +31 -0
  107. data/test/unit/state_test.rb +924 -0
  108. data/test/unit/transition_collection_test.rb +2102 -0
  109. data/test/unit/transition_test.rb +1541 -0
  110. metadata +207 -0
@@ -0,0 +1,1541 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class TransitionTest < Test::Unit::TestCase
4
+ def setup
5
+ @klass = Class.new
6
+ @machine = StateMachine::Machine.new(@klass)
7
+ @machine.state :parked, :idling
8
+ @machine.event :ignite
9
+
10
+ @object = @klass.new
11
+ @object.state = 'parked'
12
+
13
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
14
+ end
15
+
16
+ def test_should_have_an_object
17
+ assert_equal @object, @transition.object
18
+ end
19
+
20
+ def test_should_have_a_machine
21
+ assert_equal @machine, @transition.machine
22
+ end
23
+
24
+ def test_should_have_an_event
25
+ assert_equal :ignite, @transition.event
26
+ end
27
+
28
+ def test_should_have_a_qualified_event
29
+ assert_equal :ignite, @transition.qualified_event
30
+ end
31
+
32
+ def test_should_have_a_human_event
33
+ assert_equal 'ignite', @transition.human_event
34
+ end
35
+
36
+ def test_should_have_a_from_value
37
+ assert_equal 'parked', @transition.from
38
+ end
39
+
40
+ def test_should_have_a_from_name
41
+ assert_equal :parked, @transition.from_name
42
+ end
43
+
44
+ def test_should_have_a_qualified_from_name
45
+ assert_equal :parked, @transition.qualified_from_name
46
+ end
47
+
48
+ def test_should_have_a_human_from_name
49
+ assert_equal 'parked', @transition.human_from_name
50
+ end
51
+
52
+ def test_should_have_a_to_value
53
+ assert_equal 'idling', @transition.to
54
+ end
55
+
56
+ def test_should_have_a_to_name
57
+ assert_equal :idling, @transition.to_name
58
+ end
59
+
60
+ def test_should_have_a_qualified_to_name
61
+ assert_equal :idling, @transition.qualified_to_name
62
+ end
63
+
64
+ def test_should_have_a_human_to_name
65
+ assert_equal 'idling', @transition.human_to_name
66
+ end
67
+
68
+ def test_should_have_an_attribute
69
+ assert_equal :state, @transition.attribute
70
+ end
71
+
72
+ def test_should_not_have_an_action
73
+ assert_nil @transition.action
74
+ end
75
+
76
+ def test_should_not_be_transient
77
+ assert_equal false, @transition.transient?
78
+ end
79
+
80
+ def test_should_generate_attributes
81
+ expected = {:object => @object, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}
82
+ assert_equal expected, @transition.attributes
83
+ end
84
+
85
+ def test_should_have_empty_args
86
+ assert_equal [], @transition.args
87
+ end
88
+
89
+ def test_should_not_have_a_result
90
+ assert_nil @transition.result
91
+ end
92
+
93
+ def test_should_use_pretty_inspect
94
+ assert_equal '#<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>', @transition.inspect
95
+ end
96
+ end
97
+
98
+ class TransitionWithInvalidNodesTest < Test::Unit::TestCase
99
+ def setup
100
+ @klass = Class.new
101
+ @machine = StateMachine::Machine.new(@klass)
102
+ @machine.state :parked, :idling
103
+ @machine.event :ignite
104
+
105
+ @object = @klass.new
106
+ @object.state = 'parked'
107
+ end
108
+
109
+ def test_should_raise_exception_without_event
110
+ assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, nil, :parked, :idling) }
111
+ end
112
+
113
+ def test_should_raise_exception_with_invalid_event
114
+ assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :invalid, :parked, :idling) }
115
+ end
116
+
117
+ def test_should_raise_exception_with_invalid_from_state
118
+ assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :ignite, :invalid, :idling) }
119
+ end
120
+
121
+ def test_should_raise_exception_with_invalid_to_state
122
+ assert_raise(IndexError) { StateMachine::Transition.new(@object, @machine, :ignite, :parked, :invalid) }
123
+ end
124
+ end
125
+
126
+ class TransitionWithDynamicToValueTest < Test::Unit::TestCase
127
+ def setup
128
+ @klass = Class.new
129
+ @machine = StateMachine::Machine.new(@klass)
130
+ @machine.state :parked
131
+ @machine.state :idling, :value => lambda {1}
132
+ @machine.event :ignite
133
+
134
+ @object = @klass.new
135
+ @object.state = 'parked'
136
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
137
+ end
138
+
139
+ def test_should_evaluate_to_value
140
+ assert_equal 1, @transition.to
141
+ end
142
+ end
143
+
144
+ class TransitionLoopbackTest < Test::Unit::TestCase
145
+ def setup
146
+ @klass = Class.new
147
+ @machine = StateMachine::Machine.new(@klass)
148
+ @machine.state :parked
149
+ @machine.event :park
150
+
151
+ @object = @klass.new
152
+ @object.state = 'parked'
153
+ @transition = StateMachine::Transition.new(@object, @machine, :park, :parked, :parked)
154
+ end
155
+
156
+ def test_should_be_loopback
157
+ assert @transition.loopback?
158
+ end
159
+ end
160
+
161
+ class TransitionWithDifferentStatesTest < Test::Unit::TestCase
162
+ def setup
163
+ @klass = Class.new
164
+ @machine = StateMachine::Machine.new(@klass)
165
+ @machine.state :parked, :idling
166
+ @machine.event :ignite
167
+
168
+ @object = @klass.new
169
+ @object.state = 'parked'
170
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
171
+ end
172
+
173
+ def test_should_not_be_loopback
174
+ assert !@transition.loopback?
175
+ end
176
+ end
177
+
178
+ class TransitionWithNamespaceTest < Test::Unit::TestCase
179
+ def setup
180
+ @klass = Class.new
181
+ @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
182
+ @machine.state :off, :active
183
+ @machine.event :activate
184
+
185
+ @object = @klass.new
186
+ @object.state = 'off'
187
+
188
+ @transition = StateMachine::Transition.new(@object, @machine, :activate, :off, :active)
189
+ end
190
+
191
+ def test_should_have_an_event
192
+ assert_equal :activate, @transition.event
193
+ end
194
+
195
+ def test_should_have_a_qualified_event
196
+ assert_equal :activate_alarm, @transition.qualified_event
197
+ end
198
+
199
+ def test_should_have_a_from_name
200
+ assert_equal :off, @transition.from_name
201
+ end
202
+
203
+ def test_should_have_a_qualified_from_name
204
+ assert_equal :alarm_off, @transition.qualified_from_name
205
+ end
206
+
207
+ def test_should_have_a_human_from_name
208
+ assert_equal 'off', @transition.human_from_name
209
+ end
210
+
211
+ def test_should_have_a_to_name
212
+ assert_equal :active, @transition.to_name
213
+ end
214
+
215
+ def test_should_have_a_qualified_to_name
216
+ assert_equal :alarm_active, @transition.qualified_to_name
217
+ end
218
+
219
+ def test_should_have_a_human_to_name
220
+ assert_equal 'active', @transition.human_to_name
221
+ end
222
+ end
223
+
224
+ class TransitionWithCustomMachineAttributeTest < Test::Unit::TestCase
225
+ def setup
226
+ @klass = Class.new
227
+ @machine = StateMachine::Machine.new(@klass, :state, :attribute => :state_id)
228
+ @machine.state :off, :value => 1
229
+ @machine.state :active, :value => 2
230
+ @machine.event :activate
231
+
232
+ @object = @klass.new
233
+ @object.state_id = 1
234
+
235
+ @transition = StateMachine::Transition.new(@object, @machine, :activate, :off, :active)
236
+ end
237
+
238
+ def test_should_persist
239
+ @transition.persist
240
+ assert_equal 2, @object.state_id
241
+ end
242
+
243
+ def test_should_rollback
244
+ @object.state_id = 2
245
+ @transition.rollback
246
+
247
+ assert_equal 1, @object.state_id
248
+ end
249
+ end
250
+
251
+ class TransitionWithoutReadingStateTest < Test::Unit::TestCase
252
+ def setup
253
+ @klass = Class.new
254
+ @machine = StateMachine::Machine.new(@klass)
255
+ @machine.state :parked, :idling
256
+ @machine.event :ignite
257
+
258
+ @object = @klass.new
259
+ @object.state = 'idling'
260
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling, false)
261
+ end
262
+
263
+ def test_should_not_read_from_value_from_object
264
+ assert_equal 'parked', @transition.from
265
+ end
266
+
267
+ def test_should_have_to_value
268
+ assert_equal 'idling', @transition.to
269
+ end
270
+ end
271
+
272
+ class TransitionWithActionTest < Test::Unit::TestCase
273
+ def setup
274
+ @klass = Class.new do
275
+ def save
276
+ end
277
+ end
278
+
279
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
280
+ @machine.state :parked, :idling
281
+ @machine.event :ignite
282
+
283
+ @object = @klass.new
284
+ @object.state = 'parked'
285
+
286
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
287
+ end
288
+
289
+ def test_should_have_an_action
290
+ assert_equal :save, @transition.action
291
+ end
292
+
293
+ def test_should_not_have_a_result
294
+ assert_nil @transition.result
295
+ end
296
+ end
297
+
298
+ class TransitionAfterBeingPersistedTest < Test::Unit::TestCase
299
+ def setup
300
+ @klass = Class.new
301
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
302
+ @machine.state :parked, :idling
303
+ @machine.event :ignite
304
+
305
+ @object = @klass.new
306
+ @object.state = 'parked'
307
+
308
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
309
+ @transition.persist
310
+ end
311
+
312
+ def test_should_update_state_value
313
+ assert_equal 'idling', @object.state
314
+ end
315
+
316
+ def test_should_not_change_from_state
317
+ assert_equal 'parked', @transition.from
318
+ end
319
+
320
+ def test_should_not_change_to_state
321
+ assert_equal 'idling', @transition.to
322
+ end
323
+
324
+ def test_should_not_be_able_to_persist_twice
325
+ @object.state = 'parked'
326
+ @transition.persist
327
+ assert_equal 'parked', @object.state
328
+ end
329
+
330
+ def test_should_be_able_to_persist_again_after_resetting
331
+ @object.state = 'parked'
332
+ @transition.reset
333
+ @transition.persist
334
+ assert_equal 'idling', @object.state
335
+ end
336
+
337
+ def test_should_revert_to_from_state_on_rollback
338
+ @transition.rollback
339
+ assert_equal 'parked', @object.state
340
+ end
341
+ end
342
+
343
+ class TransitionAfterBeingRolledBackTest < Test::Unit::TestCase
344
+ def setup
345
+ @klass = Class.new
346
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
347
+ @machine.state :parked, :idling
348
+ @machine.event :ignite
349
+
350
+ @object = @klass.new
351
+ @object.state = 'parked'
352
+
353
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
354
+ @object.state = 'idling'
355
+
356
+ @transition.rollback
357
+ end
358
+
359
+ def test_should_update_state_value_to_from_state
360
+ assert_equal 'parked', @object.state
361
+ end
362
+
363
+ def test_should_not_change_from_state
364
+ assert_equal 'parked', @transition.from
365
+ end
366
+
367
+ def test_should_not_change_to_state
368
+ assert_equal 'idling', @transition.to
369
+ end
370
+
371
+ def test_should_still_be_able_to_persist
372
+ @transition.persist
373
+ assert_equal 'idling', @object.state
374
+ end
375
+ end
376
+
377
+ class TransitionWithoutCallbacksTest < Test::Unit::TestCase
378
+ def setup
379
+ @klass = Class.new
380
+
381
+ @machine = StateMachine::Machine.new(@klass)
382
+ @machine.state :parked, :idling
383
+ @machine.event :ignite
384
+
385
+ @object = @klass.new
386
+ @object.state = 'parked'
387
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
388
+ end
389
+
390
+ def test_should_succeed
391
+ assert_equal true, @transition.run_callbacks
392
+ end
393
+
394
+ def test_should_succeed_if_after_callbacks_skipped
395
+ assert_equal true, @transition.run_callbacks(:after => false)
396
+ end
397
+
398
+ def test_should_call_block_if_provided
399
+ @transition.run_callbacks { @ran_block = true; {} }
400
+ assert @ran_block
401
+ end
402
+
403
+ def test_should_track_block_result
404
+ @transition.run_callbacks {{:result => 1}}
405
+ assert_equal 1, @transition.result
406
+ end
407
+ end
408
+
409
+ class TransitionWithBeforeCallbacksTest < Test::Unit::TestCase
410
+ def setup
411
+ @klass = Class.new
412
+
413
+ @machine = StateMachine::Machine.new(@klass)
414
+ @machine.state :parked, :idling
415
+ @machine.event :ignite
416
+
417
+ @object = @klass.new
418
+ @object.state = 'parked'
419
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
420
+ end
421
+
422
+ def test_should_run_before_callbacks
423
+ @machine.before_transition {@run = true}
424
+ result = @transition.run_callbacks
425
+
426
+ assert_equal true, result
427
+ assert_equal true, @run
428
+ end
429
+
430
+ def test_should_only_run_those_that_match_transition_context
431
+ @count = 0
432
+ callback = lambda {@count += 1}
433
+
434
+ @machine.before_transition :from => :parked, :to => :idling, :on => :park, :do => callback
435
+ @machine.before_transition :from => :parked, :to => :parked, :on => :park, :do => callback
436
+ @machine.before_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
437
+ @machine.before_transition :from => :idling, :to => :idling, :on => :park, :do => callback
438
+ @transition.run_callbacks
439
+
440
+ assert_equal 1, @count
441
+ end
442
+
443
+ def test_should_pass_transition_as_argument
444
+ @machine.before_transition {|*args| @args = args}
445
+ @transition.run_callbacks
446
+
447
+ assert_equal [@object, @transition], @args
448
+ end
449
+
450
+ def test_should_catch_halts
451
+ @machine.before_transition {throw :halt}
452
+
453
+ result = nil
454
+ assert_nothing_thrown { result = @transition.run_callbacks }
455
+ assert_equal false, result
456
+ end
457
+
458
+ def test_should_not_catch_exceptions
459
+ @machine.before_transition {raise ArgumentError}
460
+ assert_raise(ArgumentError) { @transition.run_callbacks }
461
+ end
462
+
463
+ def test_should_not_be_able_to_run_twice
464
+ @count = 0
465
+ @machine.before_transition {@count += 1}
466
+ @transition.run_callbacks
467
+ @transition.run_callbacks
468
+ assert_equal 1, @count
469
+ end
470
+
471
+ def test_should_be_able_to_run_again_after_halt
472
+ @count = 0
473
+ @machine.before_transition {@count += 1; throw :halt}
474
+ @transition.run_callbacks
475
+ @transition.run_callbacks
476
+ assert_equal 2, @count
477
+ end
478
+
479
+ def test_should_be_able_to_run_again_after_resetting
480
+ @count = 0
481
+ @machine.before_transition {@count += 1}
482
+ @transition.run_callbacks
483
+ @transition.reset
484
+ @transition.run_callbacks
485
+ assert_equal 2, @count
486
+ end
487
+
488
+ def test_should_succeed_if_block_result_is_false
489
+ @machine.before_transition {@run = true}
490
+ assert_equal true, @transition.run_callbacks {{:result => false}}
491
+ assert @run
492
+ end
493
+
494
+ def test_should_succeed_if_block_result_is_true
495
+ @machine.before_transition {@run = true}
496
+ assert_equal true, @transition.run_callbacks {{:result => true}}
497
+ assert @run
498
+ end
499
+
500
+ def test_should_succeed_if_block_success_is_false
501
+ @machine.before_transition {@run = true}
502
+ assert_equal true, @transition.run_callbacks {{:success => false}}
503
+ assert @run
504
+ end
505
+
506
+ def test_should_succeed_if_block_success_is_false
507
+ @machine.before_transition {@run = true}
508
+ assert_equal true, @transition.run_callbacks {{:success => true}}
509
+ assert @run
510
+ end
511
+ end
512
+
513
+ class TransitionWithMultipleBeforeCallbacksTest < Test::Unit::TestCase
514
+ def setup
515
+ @klass = Class.new
516
+
517
+ @machine = StateMachine::Machine.new(@klass)
518
+ @machine.state :parked, :idling
519
+ @machine.event :ignite
520
+
521
+ @object = @klass.new
522
+ @object.state = 'parked'
523
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
524
+ end
525
+
526
+ def test_should_run_in_the_order_they_were_defined
527
+ @callbacks = []
528
+ @machine.before_transition {@callbacks << 1}
529
+ @machine.before_transition {@callbacks << 2}
530
+ @transition.run_callbacks
531
+
532
+ assert_equal [1, 2], @callbacks
533
+ end
534
+
535
+ def test_should_not_run_further_callbacks_if_halted
536
+ @callbacks = []
537
+ @machine.before_transition {@callbacks << 1; throw :halt}
538
+ @machine.before_transition {@callbacks << 2}
539
+
540
+ assert_equal false, @transition.run_callbacks
541
+ assert_equal [1], @callbacks
542
+ end
543
+
544
+ def test_should_fail_if_any_callback_halted
545
+ @machine.before_transition {true}
546
+ @machine.before_transition {throw :halt}
547
+
548
+ assert_equal false, @transition.run_callbacks
549
+ end
550
+ end
551
+
552
+ class TransitionWithAfterCallbacksTest < Test::Unit::TestCase
553
+ def setup
554
+ @klass = Class.new
555
+
556
+ @machine = StateMachine::Machine.new(@klass)
557
+ @machine.state :parked, :idling
558
+ @machine.event :ignite
559
+
560
+ @object = @klass.new
561
+ @object.state = 'parked'
562
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
563
+ end
564
+
565
+ def test_should_run_after_callbacks
566
+ @machine.after_transition {|object| @run = true}
567
+ result = @transition.run_callbacks
568
+
569
+ assert_equal true, result
570
+ assert_equal true, @run
571
+ end
572
+
573
+ def test_should_only_run_those_that_match_transition_context
574
+ @count = 0
575
+ callback = lambda {@count += 1}
576
+
577
+ @machine.after_transition :from => :parked, :to => :idling, :on => :park, :do => callback
578
+ @machine.after_transition :from => :parked, :to => :parked, :on => :park, :do => callback
579
+ @machine.after_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
580
+ @machine.after_transition :from => :idling, :to => :idling, :on => :park, :do => callback
581
+ @transition.run_callbacks
582
+
583
+ assert_equal 1, @count
584
+ end
585
+
586
+ def test_should_not_run_if_not_successful
587
+ @machine.after_transition {|object| @run = true}
588
+ @transition.run_callbacks {{:success => false}}
589
+ assert !@run
590
+ end
591
+
592
+ def test_should_run_if_successful
593
+ @machine.after_transition {|object| @run = true}
594
+ @transition.run_callbacks {{:success => true}}
595
+ assert @run
596
+ end
597
+
598
+ def test_should_pass_transition_as_argument
599
+ @machine.after_transition {|*args| @args = args}
600
+
601
+ @transition.run_callbacks
602
+ assert_equal [@object, @transition], @args
603
+ end
604
+
605
+ def test_should_catch_halts
606
+ @machine.after_transition {throw :halt}
607
+
608
+ result = nil
609
+ assert_nothing_thrown { result = @transition.run_callbacks }
610
+ assert_equal true, result
611
+ end
612
+
613
+ def test_should_not_catch_exceptions
614
+ @machine.after_transition {raise ArgumentError}
615
+ assert_raise(ArgumentError) { @transition.run_callbacks }
616
+ end
617
+
618
+ def test_should_not_be_able_to_run_twice
619
+ @count = 0
620
+ @machine.after_transition {@count += 1}
621
+ @transition.run_callbacks
622
+ @transition.run_callbacks
623
+ assert_equal 1, @count
624
+ end
625
+
626
+ def test_should_not_be_able_to_run_twice_if_halted
627
+ @count = 0
628
+ @machine.after_transition {@count += 1; throw :halt}
629
+ @transition.run_callbacks
630
+ @transition.run_callbacks
631
+ assert_equal 1, @count
632
+ end
633
+
634
+ def test_should_be_able_to_run_again_after_resetting
635
+ @count = 0
636
+ @machine.after_transition {@count += 1}
637
+ @transition.run_callbacks
638
+ @transition.reset
639
+ @transition.run_callbacks
640
+ assert_equal 2, @count
641
+ end
642
+ end
643
+
644
+ class TransitionWithMultipleAfterCallbacksTest < Test::Unit::TestCase
645
+ def setup
646
+ @klass = Class.new
647
+
648
+ @machine = StateMachine::Machine.new(@klass)
649
+ @machine.state :parked, :idling
650
+ @machine.event :ignite
651
+
652
+ @object = @klass.new
653
+ @object.state = 'parked'
654
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
655
+ end
656
+
657
+ def test_should_run_in_the_order_they_were_defined
658
+ @callbacks = []
659
+ @machine.after_transition {@callbacks << 1}
660
+ @machine.after_transition {@callbacks << 2}
661
+ @transition.run_callbacks
662
+
663
+ assert_equal [1, 2], @callbacks
664
+ end
665
+
666
+ def test_should_not_run_further_callbacks_if_halted
667
+ @callbacks = []
668
+ @machine.after_transition {@callbacks << 1; throw :halt}
669
+ @machine.after_transition {@callbacks << 2}
670
+
671
+ assert_equal true, @transition.run_callbacks
672
+ assert_equal [1], @callbacks
673
+ end
674
+
675
+ def test_should_fail_if_any_callback_halted
676
+ @machine.after_transition {true}
677
+ @machine.after_transition {throw :halt}
678
+
679
+ assert_equal true, @transition.run_callbacks
680
+ end
681
+ end
682
+
683
+ class TransitionWithAroundCallbacksTest < Test::Unit::TestCase
684
+ def setup
685
+ @klass = Class.new
686
+
687
+ @machine = StateMachine::Machine.new(@klass)
688
+ @machine.state :parked, :idling
689
+ @machine.event :ignite
690
+
691
+ @object = @klass.new
692
+ @object.state = 'parked'
693
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
694
+ end
695
+
696
+ def test_should_run_around_callbacks
697
+ @machine.around_transition {|object, transition, block| @run_before = true; block.call; @run_after = true}
698
+ result = @transition.run_callbacks
699
+
700
+ assert_equal true, result
701
+ assert_equal true, @run_before
702
+ assert_equal true, @run_after
703
+ end
704
+
705
+ def test_should_only_run_those_that_match_transition_context
706
+ @count = 0
707
+ callback = lambda {|object, transition, block| @count += 1; block.call}
708
+
709
+ @machine.around_transition :from => :parked, :to => :idling, :on => :park, :do => callback
710
+ @machine.around_transition :from => :parked, :to => :parked, :on => :park, :do => callback
711
+ @machine.around_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
712
+ @machine.around_transition :from => :idling, :to => :idling, :on => :park, :do => callback
713
+ @transition.run_callbacks
714
+
715
+ assert_equal 1, @count
716
+ end
717
+
718
+ def test_should_pass_transition_as_argument
719
+ @machine.around_transition {|*args| block = args.pop; @args = args; block.call}
720
+ @transition.run_callbacks
721
+
722
+ assert_equal [@object, @transition], @args
723
+ end
724
+
725
+ def test_should_run_block_between_callback
726
+ @callbacks = []
727
+ @machine.around_transition {|block| @callbacks << :before; block.call; @callbacks << :after}
728
+ @transition.run_callbacks { @callbacks << :within; {:success => true} }
729
+
730
+ assert_equal [:before, :within, :after], @callbacks
731
+ end
732
+
733
+ def test_should_have_access_to_result_after_yield
734
+ @machine.around_transition {|block| @before_result = @transition.result; block.call; @after_result = @transition.result}
735
+ @transition.run_callbacks {{:result => 1, :success => true}}
736
+
737
+ assert_nil @before_result
738
+ assert_equal 1, @after_result
739
+ end
740
+
741
+ def test_should_catch_before_yield_halts
742
+ @machine.around_transition {throw :halt}
743
+
744
+ result = nil
745
+ assert_nothing_thrown { result = @transition.run_callbacks }
746
+ assert_equal false, result
747
+ end
748
+
749
+ def test_should_catch_after_yield_halts
750
+ @machine.around_transition {|block| block.call; throw :halt}
751
+
752
+ result = nil
753
+ assert_nothing_thrown { result = @transition.run_callbacks }
754
+ assert_equal true, result
755
+ end
756
+
757
+ def test_should_not_catch_before_yield
758
+ @machine.around_transition {raise ArgumentError}
759
+ assert_raise(ArgumentError) { @transition.run_callbacks }
760
+ end
761
+
762
+ def test_should_not_catch_after_yield
763
+ @machine.around_transition {|block| block.call; raise ArgumentError}
764
+ assert_raise(ArgumentError) { @transition.run_callbacks }
765
+ end
766
+
767
+ def test_should_fail_if_not_yielded
768
+ @machine.around_transition {}
769
+
770
+ result = nil
771
+ assert_nothing_thrown { result = @transition.run_callbacks }
772
+ assert_equal false, result
773
+ end
774
+
775
+ def test_should_not_be_able_to_run_twice
776
+ @before_count = 0
777
+ @after_count = 0
778
+ @machine.around_transition {|block| @before_count += 1; block.call; @after_count += 1}
779
+ @transition.run_callbacks
780
+ @transition.run_callbacks
781
+ assert_equal 1, @before_count
782
+ assert_equal 1, @after_count
783
+ end
784
+
785
+ def test_should_be_able_to_run_again_after_resetting
786
+ @before_count = 0
787
+ @after_count = 0
788
+ @machine.around_transition {|block| @before_count += 1; block.call; @after_count += 1}
789
+ @transition.run_callbacks
790
+ @transition.reset
791
+ @transition.run_callbacks
792
+ assert_equal 2, @before_count
793
+ assert_equal 2, @after_count
794
+ end
795
+
796
+ def test_should_succeed_if_block_result_is_false
797
+ @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
798
+ assert_equal true, @transition.run_callbacks {{:success => true, :result => false}}
799
+ assert @before_run
800
+ assert @after_run
801
+ end
802
+
803
+ def test_should_succeed_if_block_result_is_true
804
+ @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
805
+ assert_equal true, @transition.run_callbacks {{:success => true, :result => true}}
806
+ assert @before_run
807
+ assert @after_run
808
+ end
809
+
810
+ def test_should_only_run_before_if_block_success_is_false
811
+ @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
812
+ assert_equal true, @transition.run_callbacks {{:success => false}}
813
+ assert @before_run
814
+ assert !@after_run
815
+ end
816
+
817
+ def test_should_succeed_if_block_success_is_false
818
+ @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
819
+ assert_equal true, @transition.run_callbacks {{:success => true}}
820
+ assert @before_run
821
+ assert @after_run
822
+ end
823
+ end
824
+
825
+ class TransitionWithMultipleAroundCallbacksTest < Test::Unit::TestCase
826
+ def setup
827
+ @klass = Class.new
828
+
829
+ @machine = StateMachine::Machine.new(@klass)
830
+ @machine.state :parked, :idling
831
+ @machine.event :ignite
832
+
833
+ @object = @klass.new
834
+ @object.state = 'parked'
835
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
836
+ end
837
+
838
+ def test_should_before_yield_in_the_order_they_were_defined
839
+ @callbacks = []
840
+ @machine.around_transition {|block| @callbacks << 1; block.call}
841
+ @machine.around_transition {|block| @callbacks << 2; block.call}
842
+ @transition.run_callbacks
843
+
844
+ assert_equal [1, 2], @callbacks
845
+ end
846
+
847
+ def test_should_before_yield_multiple_methods_in_the_order_they_were_defined
848
+ @callbacks = []
849
+ @machine.around_transition(lambda {|block| @callbacks << 1; block.call}, lambda {|block| @callbacks << 2; block.call})
850
+ @machine.around_transition(lambda {|block| @callbacks << 3; block.call}, lambda {|block| @callbacks << 4; block.call})
851
+ @transition.run_callbacks
852
+
853
+ assert_equal [1, 2, 3, 4], @callbacks
854
+ end
855
+
856
+ def test_should_after_yield_in_the_reverse_order_they_were_defined
857
+ @callbacks = []
858
+ @machine.around_transition {|block| block.call; @callbacks << 1}
859
+ @machine.around_transition {|block| block.call; @callbacks << 2}
860
+ @transition.run_callbacks
861
+
862
+ assert_equal [2, 1], @callbacks
863
+ end
864
+
865
+ def test_should_after_yield_multiple_methods_in_the_reverse_order_they_were_defined
866
+ @callbacks = []
867
+ @machine.around_transition(lambda {|block| block.call; @callbacks << 1}) {|block| block.call; @callbacks << 2}
868
+ @machine.around_transition(lambda {|block| block.call; @callbacks << 3}) {|block| block.call; @callbacks << 4}
869
+ @transition.run_callbacks
870
+
871
+ assert_equal [4, 3, 2, 1], @callbacks
872
+ end
873
+
874
+ def test_should_run_block_between_callback
875
+ @callbacks = []
876
+ @machine.around_transition {|block| @callbacks << :before_1; block.call; @callbacks << :after_1}
877
+ @machine.around_transition {|block| @callbacks << :before_2; block.call; @callbacks << :after_2}
878
+ @transition.run_callbacks { @callbacks << :within; {:success => true} }
879
+
880
+ assert_equal [:before_1, :before_2, :within, :after_2, :after_1], @callbacks
881
+ end
882
+
883
+ def test_should_have_access_to_result_after_yield
884
+ @machine.around_transition {|block| @before_result_1 = @transition.result; block.call; @after_result_1 = @transition.result}
885
+ @machine.around_transition {|block| @before_result_2 = @transition.result; block.call; @after_result_2 = @transition.result}
886
+ @transition.run_callbacks {{:result => 1, :success => true}}
887
+
888
+ assert_nil @before_result_1
889
+ assert_nil @before_result_2
890
+ assert_equal 1, @after_result_1
891
+ assert_equal 1, @after_result_2
892
+ end
893
+
894
+ def test_should_fail_if_any_before_yield_halted
895
+ @machine.around_transition {|block| block.call}
896
+ @machine.around_transition {throw :halt}
897
+
898
+ assert_equal false, @transition.run_callbacks
899
+ end
900
+
901
+ def test_should_not_continue_around_callbacks_if_before_yield_halted
902
+ @callbacks = []
903
+ @machine.around_transition {@callbacks << 1; throw :halt}
904
+ @machine.around_transition {|block| @callbacks << 2; block.call; @callbacks << 3}
905
+
906
+ assert_equal false, @transition.run_callbacks
907
+ assert_equal [1], @callbacks
908
+ end
909
+
910
+ def test_should_not_continue_around_callbacks_if_later_before_yield_halted
911
+ @callbacks = []
912
+ @machine.around_transition {|block| block.call; @callbacks << 1}
913
+ @machine.around_transition {throw :halt}
914
+
915
+ @transition.run_callbacks
916
+ assert_equal [], @callbacks
917
+ end
918
+
919
+ def test_should_not_run_further_callbacks_if_after_yield_halted
920
+ @callbacks = []
921
+ @machine.around_transition {|block| block.call; @callbacks << 1}
922
+ @machine.around_transition {|block| block.call; throw :halt}
923
+
924
+ assert_equal true, @transition.run_callbacks
925
+ assert_equal [], @callbacks
926
+ end
927
+
928
+ def test_should_fail_if_any_fail_to_yield
929
+ @callbacks = []
930
+ @machine.around_transition {@callbacks << 1}
931
+ @machine.around_transition {|block| @callbacks << 2; block.call; @callbacks << 3}
932
+
933
+ assert_equal false, @transition.run_callbacks
934
+ assert_equal [1], @callbacks
935
+ end
936
+ end
937
+
938
+ class TransitionWithFailureCallbacksTest < Test::Unit::TestCase
939
+ def setup
940
+ @klass = Class.new
941
+
942
+ @machine = StateMachine::Machine.new(@klass)
943
+ @machine.state :parked, :idling
944
+ @machine.event :ignite
945
+
946
+ @object = @klass.new
947
+ @object.state = 'parked'
948
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
949
+ end
950
+
951
+ def test_should_only_run_those_that_match_transition_context
952
+ @count = 0
953
+ callback = lambda {@count += 1}
954
+
955
+ @machine.after_failure :do => callback
956
+ @machine.after_failure :on => :park, :do => callback
957
+ @machine.after_failure :on => :ignite, :do => callback
958
+ @transition.run_callbacks {{:success => false}}
959
+
960
+ assert_equal 2, @count
961
+ end
962
+
963
+ def test_should_run_if_not_successful
964
+ @machine.after_failure {|object| @run = true}
965
+ @transition.run_callbacks {{:success => false}}
966
+ assert @run
967
+ end
968
+
969
+ def test_should_not_run_if_successful
970
+ @machine.after_failure {|object| @run = true}
971
+ @transition.run_callbacks {{:success => true}}
972
+ assert !@run
973
+ end
974
+
975
+ def test_should_pass_transition_as_argument
976
+ @machine.after_failure {|*args| @args = args}
977
+
978
+ @transition.run_callbacks {{:success => false}}
979
+ assert_equal [@object, @transition], @args
980
+ end
981
+
982
+ def test_should_catch_halts
983
+ @machine.after_failure {throw :halt}
984
+
985
+ result = nil
986
+ assert_nothing_thrown { result = @transition.run_callbacks {{:success => false}} }
987
+ assert_equal true, result
988
+ end
989
+
990
+ def test_should_not_catch_exceptions
991
+ @machine.after_failure {raise ArgumentError}
992
+ assert_raise(ArgumentError) { @transition.run_callbacks {{:success => false}} }
993
+ end
994
+
995
+ def test_should_not_be_able_to_run_twice
996
+ @count = 0
997
+ @machine.after_failure {@count += 1}
998
+ @transition.run_callbacks {{:success => false}}
999
+ @transition.run_callbacks {{:success => false}}
1000
+ assert_equal 1, @count
1001
+ end
1002
+
1003
+ def test_should_not_be_able_to_run_twice_if_halted
1004
+ @count = 0
1005
+ @machine.after_failure {@count += 1; throw :halt}
1006
+ @transition.run_callbacks {{:success => false}}
1007
+ @transition.run_callbacks {{:success => false}}
1008
+ assert_equal 1, @count
1009
+ end
1010
+
1011
+ def test_should_be_able_to_run_again_after_resetting
1012
+ @count = 0
1013
+ @machine.after_failure {@count += 1}
1014
+ @transition.run_callbacks {{:success => false}}
1015
+ @transition.reset
1016
+ @transition.run_callbacks {{:success => false}}
1017
+ assert_equal 2, @count
1018
+ end
1019
+ end
1020
+
1021
+ class TransitionWithMultipleFailureCallbacksTest < Test::Unit::TestCase
1022
+ def setup
1023
+ @klass = Class.new
1024
+
1025
+ @machine = StateMachine::Machine.new(@klass)
1026
+ @machine.state :parked, :idling
1027
+ @machine.event :ignite
1028
+
1029
+ @object = @klass.new
1030
+ @object.state = 'parked'
1031
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1032
+ end
1033
+
1034
+ def test_should_run_in_the_order_they_were_defined
1035
+ @callbacks = []
1036
+ @machine.after_failure {@callbacks << 1}
1037
+ @machine.after_failure {@callbacks << 2}
1038
+ @transition.run_callbacks {{:success => false}}
1039
+
1040
+ assert_equal [1, 2], @callbacks
1041
+ end
1042
+
1043
+ def test_should_not_run_further_callbacks_if_halted
1044
+ @callbacks = []
1045
+ @machine.after_failure {@callbacks << 1; throw :halt}
1046
+ @machine.after_failure {@callbacks << 2}
1047
+
1048
+ assert_equal true, @transition.run_callbacks {{:success => false}}
1049
+ assert_equal [1], @callbacks
1050
+ end
1051
+
1052
+ def test_should_fail_if_any_callback_halted
1053
+ @machine.after_failure {true}
1054
+ @machine.after_failure {throw :halt}
1055
+
1056
+ assert_equal true, @transition.run_callbacks {{:success => false}}
1057
+ end
1058
+ end
1059
+
1060
+ class TransitionWithMixedCallbacksTest < Test::Unit::TestCase
1061
+ def setup
1062
+ @klass = Class.new
1063
+
1064
+ @machine = StateMachine::Machine.new(@klass)
1065
+ @machine.state :parked, :idling
1066
+ @machine.event :ignite
1067
+
1068
+ @object = @klass.new
1069
+ @object.state = 'parked'
1070
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1071
+ end
1072
+
1073
+ def test_should_before_and_around_callbacks_in_order_defined
1074
+ @callbacks = []
1075
+ @machine.before_transition {@callbacks << :before_1}
1076
+ @machine.around_transition {|block| @callbacks << :around; block.call}
1077
+ @machine.before_transition {@callbacks << :before_2}
1078
+
1079
+ assert_equal true, @transition.run_callbacks
1080
+ assert_equal [:before_1, :around, :before_2], @callbacks
1081
+ end
1082
+
1083
+ def test_should_run_around_callbacks_before_after_callbacks
1084
+ @callbacks = []
1085
+ @machine.after_transition {@callbacks << :after_1}
1086
+ @machine.around_transition {|block| block.call; @callbacks << :after_2}
1087
+ @machine.after_transition {@callbacks << :after_3}
1088
+
1089
+ assert_equal true, @transition.run_callbacks
1090
+ assert_equal [:after_2, :after_1, :after_3], @callbacks
1091
+ end
1092
+
1093
+ def test_should_have_access_to_result_for_both_after_and_around_callbacks
1094
+ @machine.after_transition {@after_result = @transition.result}
1095
+ @machine.around_transition {|block| block.call; @around_result = @transition.result}
1096
+
1097
+ @transition.run_callbacks {{:result => 1, :success => true}}
1098
+ assert_equal 1, @after_result
1099
+ assert_equal 1, @around_result
1100
+ end
1101
+
1102
+ def test_should_not_run_further_callbacks_if_before_callback_halts
1103
+ @callbacks = []
1104
+ @machine.before_transition {@callbacks << :before_1}
1105
+ @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
1106
+ @machine.before_transition {@callbacks << :before_2; throw :halt}
1107
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
1108
+ @machine.after_transition {@callbacks << :after}
1109
+
1110
+ assert_equal false, @transition.run_callbacks
1111
+ assert_equal [:before_1, :before_around_1, :before_2], @callbacks
1112
+ end
1113
+
1114
+ def test_should_not_run_further_callbacks_if_before_yield_halts
1115
+ @callbacks = []
1116
+ @machine.before_transition {@callbacks << :before_1}
1117
+ @machine.around_transition {|block| @callbacks << :before_around_1; throw :halt}
1118
+ @machine.before_transition {@callbacks << :before_2; throw :halt}
1119
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
1120
+ @machine.after_transition {@callbacks << :after}
1121
+
1122
+ assert_equal false, @transition.run_callbacks
1123
+ assert_equal [:before_1, :before_around_1], @callbacks
1124
+ end
1125
+
1126
+ def test_should_not_run_further_callbacks_if_around_callback_fails_to_yield
1127
+ @callbacks = []
1128
+ @machine.before_transition {@callbacks << :before_1}
1129
+ @machine.around_transition {|block| @callbacks << :before_around_1}
1130
+ @machine.before_transition {@callbacks << :before_2; throw :halt}
1131
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
1132
+ @machine.after_transition {@callbacks << :after}
1133
+
1134
+ assert_equal false, @transition.run_callbacks
1135
+ assert_equal [:before_1, :before_around_1], @callbacks
1136
+ end
1137
+
1138
+ def test_should_not_run_further_callbacks_if_after_yield_halts
1139
+ @callbacks = []
1140
+ @machine.before_transition {@callbacks << :before_1}
1141
+ @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1; throw :halt}
1142
+ @machine.before_transition {@callbacks << :before_2}
1143
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
1144
+ @machine.after_transition {@callbacks << :after}
1145
+
1146
+ assert_equal true, @transition.run_callbacks
1147
+ assert_equal [:before_1, :before_around_1, :before_2, :before_around_2, :after_around_2, :after_around_1], @callbacks
1148
+ end
1149
+
1150
+ def test_should_not_run_further_callbacks_if_after_callback_halts
1151
+ @callbacks = []
1152
+ @machine.before_transition {@callbacks << :before_1}
1153
+ @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
1154
+ @machine.before_transition {@callbacks << :before_2}
1155
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
1156
+ @machine.after_transition {@callbacks << :after_1; throw :halt}
1157
+ @machine.after_transition {@callbacks << :after_2}
1158
+
1159
+ assert_equal true, @transition.run_callbacks
1160
+ assert_equal [:before_1, :before_around_1, :before_2, :before_around_2, :after_around_2, :after_around_1, :after_1], @callbacks
1161
+ end
1162
+ end
1163
+
1164
+ class TransitionWithBeforeCallbacksSkippedTest < Test::Unit::TestCase
1165
+ def setup
1166
+ @klass = Class.new
1167
+
1168
+ @machine = StateMachine::Machine.new(@klass)
1169
+ @machine.state :parked, :idling
1170
+ @machine.event :ignite
1171
+
1172
+ @object = @klass.new
1173
+ @object.state = 'parked'
1174
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1175
+ end
1176
+
1177
+ def test_should_not_run_before_callbacks
1178
+ @machine.before_transition {@run = true}
1179
+
1180
+ assert_equal false, @transition.run_callbacks(:before => false)
1181
+ assert !@run
1182
+ end
1183
+
1184
+ def test_should_run_failure_callbacks
1185
+ @machine.after_failure {@run = true}
1186
+
1187
+ assert_equal false, @transition.run_callbacks(:before => false)
1188
+ assert @run
1189
+ end
1190
+ end
1191
+
1192
+ class TransitionWithAfterCallbacksSkippedTest < Test::Unit::TestCase
1193
+ def setup
1194
+ @klass = Class.new
1195
+
1196
+ @machine = StateMachine::Machine.new(@klass)
1197
+ @machine.state :parked, :idling
1198
+ @machine.event :ignite
1199
+
1200
+ @object = @klass.new
1201
+ @object.state = 'parked'
1202
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1203
+ end
1204
+
1205
+ def test_should_run_before_callbacks
1206
+ @machine.before_transition {@run = true}
1207
+
1208
+ assert_equal true, @transition.run_callbacks(:after => false)
1209
+ assert @run
1210
+ end
1211
+
1212
+ def test_should_run_around_callbacks_before_yield
1213
+ @machine.around_transition {|block| @run = true; block.call}
1214
+
1215
+ assert_equal true, @transition.run_callbacks(:after => false)
1216
+ assert @run
1217
+ end
1218
+
1219
+ def test_should_not_run_after_callbacks
1220
+ @machine.after_transition {@run = true}
1221
+
1222
+ assert_equal true, @transition.run_callbacks(:after => false)
1223
+ assert !@run
1224
+ end
1225
+
1226
+ def test_should_not_run_around_callbacks_after_yield
1227
+ @machine.around_transition {|block| block.call; @run = true}
1228
+
1229
+ assert_equal true, @transition.run_callbacks(:after => false)
1230
+ assert !@run
1231
+ end
1232
+
1233
+ def test_should_continue_around_transition_execution_on_second_call
1234
+ @callbacks = []
1235
+ @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
1236
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
1237
+ @machine.after_transition {@callbacks << :after}
1238
+
1239
+ assert_equal true, @transition.run_callbacks(:after => false)
1240
+ assert_equal [:before_around_1, :before_around_2], @callbacks
1241
+
1242
+ assert_equal true, @transition.run_callbacks
1243
+ assert_equal [:before_around_1, :before_around_2, :after_around_2, :after_around_1, :after], @callbacks
1244
+ end
1245
+
1246
+ def test_should_not_run_further_callbacks_if_halted_during_continue_around_transition
1247
+ @callbacks = []
1248
+ @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
1249
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2; throw :halt}
1250
+ @machine.after_transition {@callbacks << :after}
1251
+
1252
+ assert_equal true, @transition.run_callbacks(:after => false)
1253
+ assert_equal [:before_around_1, :before_around_2], @callbacks
1254
+
1255
+ assert_equal true, @transition.run_callbacks
1256
+ assert_equal [:before_around_1, :before_around_2, :after_around_2], @callbacks
1257
+ end
1258
+
1259
+ def test_should_not_be_able_to_continue_twice
1260
+ @count = 0
1261
+ @machine.around_transition {|block| block.call; @count += 1}
1262
+ @machine.after_transition {@count += 1}
1263
+
1264
+ @transition.run_callbacks(:after => false)
1265
+
1266
+ 2.times do
1267
+ assert_equal true, @transition.run_callbacks
1268
+ assert_equal 2, @count
1269
+ end
1270
+ end
1271
+
1272
+ def test_should_not_be_able_to_continue_again_after_halted
1273
+ @count = 0
1274
+ @machine.around_transition {|block| block.call; @count += 1; throw :halt}
1275
+ @machine.after_transition {@count += 1}
1276
+
1277
+ @transition.run_callbacks(:after => false)
1278
+
1279
+ 2.times do
1280
+ assert_equal true, @transition.run_callbacks
1281
+ assert_equal 1, @count
1282
+ end
1283
+ end
1284
+
1285
+ def test_should_have_access_to_result_after_continued
1286
+ @machine.around_transition {|block| @around_before_result = @transition.result; block.call; @around_after_result = @transition.result}
1287
+ @machine.after_transition {@after_result = @transition.result}
1288
+
1289
+ @transition.run_callbacks(:after => false)
1290
+ @transition.run_callbacks {{:result => 1}}
1291
+
1292
+ assert_nil @around_before_result
1293
+ assert_equal 1, @around_after_result
1294
+ assert_equal 1, @after_result
1295
+ end
1296
+
1297
+ def test_should_raise_exceptions_during_around_callbacks_after_yield_in_second_execution
1298
+ @machine.around_transition {|block| block.call; raise ArgumentError}
1299
+
1300
+ assert_nothing_raised { @transition.run_callbacks(:after => false) }
1301
+ assert_raise(ArgumentError) { @transition.run_callbacks }
1302
+ end
1303
+ end
1304
+
1305
+ class TransitionAfterBeingPerformedTest < Test::Unit::TestCase
1306
+ def setup
1307
+ @klass = Class.new do
1308
+ attr_reader :saved, :save_state
1309
+
1310
+ def save
1311
+ @save_state = state
1312
+ @saved = true
1313
+ 1
1314
+ end
1315
+ end
1316
+
1317
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
1318
+ @machine.state :parked, :idling
1319
+ @machine.event :ignite
1320
+
1321
+ @object = @klass.new
1322
+ @object.state = 'parked'
1323
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1324
+ @result = @transition.perform
1325
+ end
1326
+
1327
+ def test_should_have_empty_args
1328
+ assert_equal [], @transition.args
1329
+ end
1330
+
1331
+ def test_should_have_a_result
1332
+ assert_equal 1, @transition.result
1333
+ end
1334
+
1335
+ def test_should_be_successful
1336
+ assert_equal true, @result
1337
+ end
1338
+
1339
+ def test_should_change_the_current_state
1340
+ assert_equal 'idling', @object.state
1341
+ end
1342
+
1343
+ def test_should_run_the_action
1344
+ assert @object.saved
1345
+ end
1346
+
1347
+ def test_should_run_the_action_after_saving_the_state
1348
+ assert_equal 'idling', @object.save_state
1349
+ end
1350
+ end
1351
+
1352
+ class TransitionWithPerformArgumentsTest < Test::Unit::TestCase
1353
+ def setup
1354
+ @klass = Class.new do
1355
+ attr_reader :saved
1356
+
1357
+ def save
1358
+ @saved = true
1359
+ end
1360
+ end
1361
+
1362
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
1363
+ @machine.state :parked, :idling
1364
+ @machine.event :ignite
1365
+
1366
+ @object = @klass.new
1367
+ @object.state = 'parked'
1368
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1369
+ end
1370
+
1371
+ def test_should_have_arguments
1372
+ @transition.perform(1, 2)
1373
+
1374
+ assert_equal [1, 2], @transition.args
1375
+ assert @object.saved
1376
+ end
1377
+
1378
+ def test_should_not_include_run_action_in_arguments
1379
+ @transition.perform(1, 2, false)
1380
+
1381
+ assert_equal [1, 2], @transition.args
1382
+ assert !@object.saved
1383
+ end
1384
+ end
1385
+
1386
+ class TransitionWithoutRunningActionTest < Test::Unit::TestCase
1387
+ def setup
1388
+ @klass = Class.new do
1389
+ attr_reader :saved
1390
+
1391
+ def save
1392
+ @saved = true
1393
+ end
1394
+ end
1395
+
1396
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
1397
+ @machine.state :parked, :idling
1398
+ @machine.event :ignite
1399
+ @machine.after_transition {|object| @run_after = true}
1400
+
1401
+ @object = @klass.new
1402
+ @object.state = 'parked'
1403
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1404
+ @result = @transition.perform(false)
1405
+ end
1406
+
1407
+ def test_should_have_empty_args
1408
+ assert_equal [], @transition.args
1409
+ end
1410
+
1411
+ def test_should_not_have_a_result
1412
+ assert_nil @transition.result
1413
+ end
1414
+
1415
+ def test_should_be_successful
1416
+ assert_equal true, @result
1417
+ end
1418
+
1419
+ def test_should_change_the_current_state
1420
+ assert_equal 'idling', @object.state
1421
+ end
1422
+
1423
+ def test_should_not_run_the_action
1424
+ assert !@object.saved
1425
+ end
1426
+
1427
+ def test_should_run_after_callbacks
1428
+ assert @run_after
1429
+ end
1430
+ end
1431
+
1432
+ class TransitionWithTransactionsTest < Test::Unit::TestCase
1433
+ def setup
1434
+ @klass = Class.new do
1435
+ class << self
1436
+ attr_accessor :running_transaction
1437
+ end
1438
+
1439
+ attr_accessor :result
1440
+
1441
+ def save
1442
+ @result = self.class.running_transaction
1443
+ true
1444
+ end
1445
+ end
1446
+
1447
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
1448
+ @machine.state :parked, :idling
1449
+ @machine.event :ignite
1450
+
1451
+ @object = @klass.new
1452
+ @object.state = 'parked'
1453
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1454
+
1455
+ class << @machine
1456
+ def within_transaction(object)
1457
+ owner_class.running_transaction = object
1458
+ yield
1459
+ owner_class.running_transaction = false
1460
+ end
1461
+ end
1462
+ end
1463
+
1464
+ def test_should_run_blocks_within_transaction_for_object
1465
+ @transition.within_transaction do
1466
+ @result = @klass.running_transaction
1467
+ end
1468
+
1469
+ assert_equal @object, @result
1470
+ end
1471
+ end
1472
+
1473
+ class TransitionTransientTest < Test::Unit::TestCase
1474
+ def setup
1475
+ @klass = Class.new
1476
+
1477
+ @machine = StateMachine::Machine.new(@klass)
1478
+ @machine.state :parked, :idling
1479
+ @machine.event :ignite
1480
+
1481
+ @object = @klass.new
1482
+ @object.state = 'parked'
1483
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1484
+ @transition.transient = true
1485
+ end
1486
+
1487
+ def test_should_be_transient
1488
+ assert @transition.transient?
1489
+ end
1490
+ end
1491
+
1492
+ class TransitionEqualityTest < Test::Unit::TestCase
1493
+ def setup
1494
+ @klass = Class.new
1495
+
1496
+ @machine = StateMachine::Machine.new(@klass)
1497
+ @machine.state :parked, :idling
1498
+ @machine.event :ignite
1499
+
1500
+ @object = @klass.new
1501
+ @object.state = 'parked'
1502
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1503
+ end
1504
+
1505
+ def test_should_be_equal_with_same_properties
1506
+ transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1507
+ assert_equal transition, @transition
1508
+ end
1509
+
1510
+ def test_should_not_be_equal_with_different_machines
1511
+ machine = StateMachine::Machine.new(@klass, :status, :namespace => :other)
1512
+ machine.state :parked, :idling
1513
+ machine.event :ignite
1514
+ transition = StateMachine::Transition.new(@object, machine, :ignite, :parked, :idling)
1515
+
1516
+ assert_not_equal transition, @transition
1517
+ end
1518
+
1519
+ def test_should_not_be_equal_with_different_objects
1520
+ transition = StateMachine::Transition.new(@klass.new, @machine, :ignite, :parked, :idling)
1521
+ assert_not_equal transition, @transition
1522
+ end
1523
+
1524
+ def test_should_not_be_equal_with_different_event_names
1525
+ @machine.event :park
1526
+ transition = StateMachine::Transition.new(@object, @machine, :park, :parked, :idling)
1527
+ assert_not_equal transition, @transition
1528
+ end
1529
+
1530
+ def test_should_not_be_equal_with_different_from_state_names
1531
+ @machine.state :first_gear
1532
+ transition = StateMachine::Transition.new(@object, @machine, :ignite, :first_gear, :idling)
1533
+ assert_not_equal transition, @transition
1534
+ end
1535
+
1536
+ def test_should_not_be_equal_with_different_to_state_names
1537
+ @machine.state :first_gear
1538
+ transition = StateMachine::Transition.new(@object, @machine, :ignite, :idling, :first_gear)
1539
+ assert_not_equal transition, @transition
1540
+ end
1541
+ end