hsume2-state_machine 1.0.1

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