verborghs-state_machine 0.9.4

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 (89) hide show
  1. data/CHANGELOG.rdoc +360 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +635 -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/assertions.rb +36 -0
  28. data/lib/state_machine/callback.rb +241 -0
  29. data/lib/state_machine/condition_proxy.rb +106 -0
  30. data/lib/state_machine/eval_helpers.rb +83 -0
  31. data/lib/state_machine/event.rb +267 -0
  32. data/lib/state_machine/event_collection.rb +122 -0
  33. data/lib/state_machine/extensions.rb +149 -0
  34. data/lib/state_machine/guard.rb +230 -0
  35. data/lib/state_machine/initializers/merb.rb +1 -0
  36. data/lib/state_machine/initializers/rails.rb +5 -0
  37. data/lib/state_machine/initializers.rb +4 -0
  38. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  39. data/lib/state_machine/integrations/active_model/observer.rb +45 -0
  40. data/lib/state_machine/integrations/active_model.rb +445 -0
  41. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  42. data/lib/state_machine/integrations/active_record.rb +522 -0
  43. data/lib/state_machine/integrations/data_mapper/observer.rb +175 -0
  44. data/lib/state_machine/integrations/data_mapper.rb +379 -0
  45. data/lib/state_machine/integrations/mongo_mapper.rb +309 -0
  46. data/lib/state_machine/integrations/sequel.rb +356 -0
  47. data/lib/state_machine/integrations.rb +83 -0
  48. data/lib/state_machine/machine.rb +1645 -0
  49. data/lib/state_machine/machine_collection.rb +64 -0
  50. data/lib/state_machine/matcher.rb +123 -0
  51. data/lib/state_machine/matcher_helpers.rb +54 -0
  52. data/lib/state_machine/node_collection.rb +152 -0
  53. data/lib/state_machine/state.rb +260 -0
  54. data/lib/state_machine/state_collection.rb +112 -0
  55. data/lib/state_machine/transition.rb +399 -0
  56. data/lib/state_machine/transition_collection.rb +244 -0
  57. data/lib/state_machine.rb +421 -0
  58. data/lib/tasks/state_machine.rake +1 -0
  59. data/lib/tasks/state_machine.rb +27 -0
  60. data/test/files/en.yml +9 -0
  61. data/test/files/switch.rb +11 -0
  62. data/test/functional/state_machine_test.rb +980 -0
  63. data/test/test_helper.rb +4 -0
  64. data/test/unit/assertions_test.rb +40 -0
  65. data/test/unit/callback_test.rb +728 -0
  66. data/test/unit/condition_proxy_test.rb +328 -0
  67. data/test/unit/eval_helpers_test.rb +222 -0
  68. data/test/unit/event_collection_test.rb +324 -0
  69. data/test/unit/event_test.rb +795 -0
  70. data/test/unit/guard_test.rb +909 -0
  71. data/test/unit/integrations/active_model_test.rb +956 -0
  72. data/test/unit/integrations/active_record_test.rb +1918 -0
  73. data/test/unit/integrations/data_mapper_test.rb +1814 -0
  74. data/test/unit/integrations/mongo_mapper_test.rb +1382 -0
  75. data/test/unit/integrations/sequel_test.rb +1492 -0
  76. data/test/unit/integrations_test.rb +50 -0
  77. data/test/unit/invalid_event_test.rb +7 -0
  78. data/test/unit/invalid_transition_test.rb +7 -0
  79. data/test/unit/machine_collection_test.rb +565 -0
  80. data/test/unit/machine_test.rb +2349 -0
  81. data/test/unit/matcher_helpers_test.rb +37 -0
  82. data/test/unit/matcher_test.rb +155 -0
  83. data/test/unit/node_collection_test.rb +207 -0
  84. data/test/unit/state_collection_test.rb +280 -0
  85. data/test/unit/state_machine_test.rb +31 -0
  86. data/test/unit/state_test.rb +848 -0
  87. data/test/unit/transition_collection_test.rb +2098 -0
  88. data/test/unit/transition_test.rb +1384 -0
  89. metadata +176 -0
@@ -0,0 +1,980 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class AutoShop
4
+ attr_accessor :num_customers
5
+
6
+ def initialize
7
+ @num_customers = 0
8
+ super
9
+ end
10
+
11
+ state_machine :initial => :available do
12
+ after_transition :available => any, :do => :increment_customers
13
+ after_transition :busy => any, :do => :decrement_customers
14
+
15
+ event :tow_vehicle do
16
+ transition :available => :busy
17
+ end
18
+
19
+ event :fix_vehicle do
20
+ transition :busy => :available
21
+ end
22
+ end
23
+
24
+ # Increments the number of customers in service
25
+ def increment_customers
26
+ self.num_customers += 1
27
+ end
28
+
29
+ # Decrements the number of customers in service
30
+ def decrement_customers
31
+ self.num_customers -= 1
32
+ end
33
+ end
34
+
35
+ class ModelBase
36
+ def save
37
+ @saved = true
38
+ self
39
+ end
40
+ end
41
+
42
+ class Vehicle < ModelBase
43
+ attr_accessor :auto_shop, :seatbelt_on, :insurance_premium, :force_idle, :callbacks, :saved, :time_elapsed
44
+
45
+ def initialize(attributes = {})
46
+ attributes = {
47
+ :auto_shop => AutoShop.new,
48
+ :seatbelt_on => false,
49
+ :insurance_premium => 50,
50
+ :force_idle => false,
51
+ :callbacks => [],
52
+ :saved => false
53
+ }.merge(attributes)
54
+
55
+ attributes.each {|attr, value| send("#{attr}=", value)}
56
+ super()
57
+ end
58
+
59
+ # Defines the state machine for the state of the vehicled
60
+ state_machine :initial => lambda {|vehicle| vehicle.force_idle ? :idling : :parked}, :action => :save do
61
+ before_transition :parked => any, :do => :put_on_seatbelt
62
+ before_transition any => :stalled, :do => :increase_insurance_premium
63
+ after_transition any => :parked, :do => lambda {|vehicle| vehicle.seatbelt_on = false}
64
+ after_transition :on => :crash, :do => :tow
65
+ after_transition :on => :repair, :do => :fix
66
+
67
+ # Callback tracking for initial state callbacks
68
+ after_transition any => :parked, :do => lambda {|vehicle| vehicle.callbacks << 'before_enter_parked'}
69
+ before_transition any => :idling, :do => lambda {|vehicle| vehicle.callbacks << 'before_enter_idling'}
70
+
71
+ around_transition do |vehicle, transition, block|
72
+ time = Time.now
73
+ block.call
74
+ vehicle.time_elapsed = Time.now - time
75
+ end
76
+
77
+ event :park do
78
+ transition [:idling, :first_gear] => :parked
79
+ end
80
+
81
+ event :ignite do
82
+ transition :stalled => :stalled
83
+ transition :parked => :idling
84
+ end
85
+
86
+ event :idle do
87
+ transition :first_gear => :idling
88
+ end
89
+
90
+ event :shift_up do
91
+ transition :idling => :first_gear, :first_gear => :second_gear, :second_gear => :third_gear
92
+ end
93
+
94
+ event :shift_down do
95
+ transition :third_gear => :second_gear
96
+ transition :second_gear => :first_gear
97
+ end
98
+
99
+ event :crash do
100
+ transition [:first_gear, :second_gear, :third_gear] => :stalled, :if => lambda {|vehicle| vehicle.auto_shop.available?}
101
+ end
102
+
103
+ event :repair do
104
+ transition :stalled => :parked, :if => :auto_shop_busy?
105
+ end
106
+ end
107
+
108
+ state_machine :insurance_state, :initial => :inactive, :namespace => 'insurance' do
109
+ event :buy do
110
+ transition :inactive => :active
111
+ end
112
+
113
+ event :cancel do
114
+ transition :active => :inactive
115
+ end
116
+ end
117
+
118
+ def save
119
+ super
120
+ end
121
+
122
+ def new_record?
123
+ @saved == false
124
+ end
125
+
126
+ def park
127
+ super
128
+ end
129
+
130
+ # Tows the vehicle to the auto shop
131
+ def tow
132
+ auto_shop.tow_vehicle
133
+ end
134
+
135
+ # Fixes the vehicle; it will no longer be in the auto shop
136
+ def fix
137
+ auto_shop.fix_vehicle
138
+ end
139
+
140
+ def decibels
141
+ 0.0
142
+ end
143
+
144
+ private
145
+ # Safety first! Puts on our seatbelt
146
+ def put_on_seatbelt
147
+ self.seatbelt_on = true
148
+ end
149
+
150
+ # We crashed! Increase the insurance premium on the vehicle
151
+ def increase_insurance_premium
152
+ self.insurance_premium += 100
153
+ end
154
+
155
+ # Is the auto shop currently servicing another customer?
156
+ def auto_shop_busy?
157
+ auto_shop.busy?
158
+ end
159
+ end
160
+
161
+ class Car < Vehicle
162
+ state_machine do
163
+ event :reverse do
164
+ transition [:parked, :idling, :first_gear] => :backing_up
165
+ end
166
+
167
+ event :park do
168
+ transition :backing_up => :parked
169
+ end
170
+
171
+ event :idle do
172
+ transition :backing_up => :idling
173
+ end
174
+
175
+ event :shift_up do
176
+ transition :backing_up => :first_gear
177
+ end
178
+ end
179
+ end
180
+
181
+ class Motorcycle < Vehicle
182
+ state_machine :initial => :idling do
183
+ state :first_gear do
184
+ def decibels
185
+ 1.0
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ class TrafficLight
192
+ state_machine :initial => :stop do
193
+ event :cycle do
194
+ transition :stop => :proceed, :proceed=> :caution, :caution => :stop
195
+ end
196
+
197
+ state :stop do
198
+ def color(transform)
199
+ value = 'red'
200
+
201
+ if block_given?
202
+ yield value
203
+ else
204
+ value.send(transform)
205
+ end
206
+
207
+ value
208
+ end
209
+ end
210
+
211
+ state :proceed do
212
+ def color(transform)
213
+ 'green'
214
+ end
215
+ end
216
+
217
+ state :caution do
218
+ def color(transform)
219
+ 'yellow'
220
+ end
221
+ end
222
+ end
223
+
224
+ def color(transform = :to_s)
225
+ super
226
+ end
227
+ end
228
+
229
+ class VehicleTest < Test::Unit::TestCase
230
+ def setup
231
+ @vehicle = Vehicle.new
232
+ end
233
+
234
+ def test_should_not_allow_access_to_subclass_events
235
+ assert !@vehicle.respond_to?(:reverse)
236
+ end
237
+
238
+ def test_should_have_human_state_names
239
+ assert_equal 'parked', Vehicle.human_state_name(:parked)
240
+ end
241
+
242
+ def test_should_have_human_state_event_names
243
+ assert_equal 'park', Vehicle.human_state_event_name(:park)
244
+ end
245
+ end
246
+
247
+ class VehicleUnsavedTest < Test::Unit::TestCase
248
+ def setup
249
+ @vehicle = Vehicle.new
250
+ end
251
+
252
+ def test_should_be_in_parked_state
253
+ assert_equal 'parked', @vehicle.state
254
+ end
255
+
256
+ def test_should_raise_exception_if_checking_invalid_state
257
+ assert_raise(IndexError) { @vehicle.state?(:invalid) }
258
+ end
259
+
260
+ def test_should_raise_exception_if_getting_name_of_invalid_state
261
+ @vehicle.state = 'invalid'
262
+ assert_raise(ArgumentError) { @vehicle.state_name }
263
+ end
264
+
265
+ def test_should_be_parked
266
+ assert @vehicle.parked?
267
+ assert @vehicle.state?(:parked)
268
+ assert_equal :parked, @vehicle.state_name
269
+ assert_equal 'parked', @vehicle.human_state_name
270
+ end
271
+
272
+ def test_should_not_be_idling
273
+ assert !@vehicle.idling?
274
+ end
275
+
276
+ def test_should_not_be_first_gear
277
+ assert !@vehicle.first_gear?
278
+ end
279
+
280
+ def test_should_not_be_second_gear
281
+ assert !@vehicle.second_gear?
282
+ end
283
+
284
+ def test_should_not_be_stalled
285
+ assert !@vehicle.stalled?
286
+ end
287
+
288
+ def test_should_not_be_able_to_park
289
+ assert !@vehicle.can_park?
290
+ end
291
+
292
+ def test_should_not_have_a_transition_for_park
293
+ assert_nil @vehicle.park_transition
294
+ end
295
+
296
+ def test_should_not_allow_park
297
+ assert !@vehicle.park
298
+ end
299
+
300
+ def test_should_be_able_to_ignite
301
+ assert @vehicle.can_ignite?
302
+ end
303
+
304
+ def test_should_have_a_transition_for_ignite
305
+ transition = @vehicle.ignite_transition
306
+ assert_not_nil transition
307
+ assert_equal 'parked', transition.from
308
+ assert_equal 'idling', transition.to
309
+ assert_equal :ignite, transition.event
310
+ assert_equal :state, transition.attribute
311
+ assert_equal @vehicle, transition.object
312
+ end
313
+
314
+ def test_should_have_a_list_of_possible_events
315
+ assert_equal [:ignite], @vehicle.state_events
316
+ end
317
+
318
+ def test_should_have_a_list_of_possible_transitions
319
+ assert_equal [{:object => @vehicle, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}], @vehicle.state_transitions.map {|transition| transition.attributes}
320
+ end
321
+
322
+ def test_should_allow_ignite
323
+ assert @vehicle.ignite
324
+ assert_equal 'idling', @vehicle.state
325
+ end
326
+
327
+ def test_should_allow_ignite_with_skipped_action
328
+ assert @vehicle.ignite(false)
329
+ assert @vehicle.new_record?
330
+ end
331
+
332
+ def test_should_allow_ignite_bang
333
+ assert @vehicle.ignite!
334
+ end
335
+
336
+ def test_should_allow_ignite_bang_with_skipped_action
337
+ assert @vehicle.ignite!(false)
338
+ assert @vehicle.new_record?
339
+ end
340
+
341
+ def test_should_be_saved_after_successful_event
342
+ @vehicle.ignite
343
+ assert !@vehicle.new_record?
344
+ end
345
+
346
+ def test_should_not_allow_idle
347
+ assert !@vehicle.idle
348
+ end
349
+
350
+ def test_should_not_allow_shift_up
351
+ assert !@vehicle.shift_up
352
+ end
353
+
354
+ def test_should_not_allow_shift_down
355
+ assert !@vehicle.shift_down
356
+ end
357
+
358
+ def test_should_not_allow_crash
359
+ assert !@vehicle.crash
360
+ end
361
+
362
+ def test_should_not_allow_repair
363
+ assert !@vehicle.repair
364
+ end
365
+
366
+ def test_should_be_insurance_inactive
367
+ assert @vehicle.insurance_inactive?
368
+ end
369
+
370
+ def test_should_be_able_to_buy
371
+ assert @vehicle.can_buy_insurance?
372
+ end
373
+
374
+ def test_should_allow_buying_insurance
375
+ assert @vehicle.buy_insurance
376
+ end
377
+
378
+ def test_should_allow_buying_insurance_bang
379
+ assert @vehicle.buy_insurance!
380
+ end
381
+
382
+ def test_should_allow_ignite_buying_insurance_with_skipped_action
383
+ assert @vehicle.buy_insurance!(false)
384
+ assert @vehicle.new_record?
385
+ end
386
+
387
+ def test_should_not_be_insurance_active
388
+ assert !@vehicle.insurance_active?
389
+ end
390
+
391
+ def test_should_not_be_able_to_cancel
392
+ assert !@vehicle.can_cancel_insurance?
393
+ end
394
+
395
+ def test_should_not_allow_cancelling_insurance
396
+ assert !@vehicle.cancel_insurance
397
+ end
398
+ end
399
+
400
+ class VehicleParkedTest < Test::Unit::TestCase
401
+ def setup
402
+ @vehicle = Vehicle.new
403
+ end
404
+
405
+ def test_should_be_in_parked_state
406
+ assert_equal 'parked', @vehicle.state
407
+ end
408
+
409
+ def test_should_not_have_the_seatbelt_on
410
+ assert !@vehicle.seatbelt_on
411
+ end
412
+
413
+ def test_should_not_allow_park
414
+ assert !@vehicle.park
415
+ end
416
+
417
+ def test_should_allow_ignite
418
+ assert @vehicle.ignite
419
+ assert_equal 'idling', @vehicle.state
420
+ end
421
+
422
+ def test_should_not_allow_idle
423
+ assert !@vehicle.idle
424
+ end
425
+
426
+ def test_should_not_allow_shift_up
427
+ assert !@vehicle.shift_up
428
+ end
429
+
430
+ def test_should_not_allow_shift_down
431
+ assert !@vehicle.shift_down
432
+ end
433
+
434
+ def test_should_not_allow_crash
435
+ assert !@vehicle.crash
436
+ end
437
+
438
+ def test_should_not_allow_repair
439
+ assert !@vehicle.repair
440
+ end
441
+
442
+ def test_should_raise_exception_if_repair_not_allowed!
443
+ assert_raise(StateMachine::InvalidTransition) {@vehicle.repair!}
444
+ end
445
+ end
446
+
447
+ class VehicleIdlingTest < Test::Unit::TestCase
448
+ def setup
449
+ @vehicle = Vehicle.new
450
+ @vehicle.ignite
451
+ end
452
+
453
+ def test_should_be_in_idling_state
454
+ assert_equal 'idling', @vehicle.state
455
+ end
456
+
457
+ def test_should_be_idling
458
+ assert @vehicle.idling?
459
+ end
460
+
461
+ def test_should_have_seatbelt_on
462
+ assert @vehicle.seatbelt_on
463
+ end
464
+
465
+ def test_should_track_time_elapsed
466
+ assert_not_nil @vehicle.time_elapsed
467
+ end
468
+
469
+ def test_should_allow_park
470
+ assert @vehicle.park
471
+ end
472
+
473
+ def test_should_call_park_with_bang_action
474
+ class << @vehicle
475
+ def park
476
+ super && 1
477
+ end
478
+ end
479
+
480
+ assert_equal 1, @vehicle.park!
481
+ end
482
+
483
+ def test_should_not_allow_idle
484
+ assert !@vehicle.idle
485
+ end
486
+
487
+ def test_should_allow_shift_up
488
+ assert @vehicle.shift_up
489
+ end
490
+
491
+ def test_should_not_allow_shift_down
492
+ assert !@vehicle.shift_down
493
+ end
494
+
495
+ def test_should_not_allow_crash
496
+ assert !@vehicle.crash
497
+ end
498
+
499
+ def test_should_not_allow_repair
500
+ assert !@vehicle.repair
501
+ end
502
+ end
503
+
504
+ class VehicleFirstGearTest < Test::Unit::TestCase
505
+ def setup
506
+ @vehicle = Vehicle.new
507
+ @vehicle.ignite
508
+ @vehicle.shift_up
509
+ end
510
+
511
+ def test_should_be_in_first_gear_state
512
+ assert_equal 'first_gear', @vehicle.state
513
+ end
514
+
515
+ def test_should_be_first_gear
516
+ assert @vehicle.first_gear?
517
+ end
518
+
519
+ def test_should_allow_park
520
+ assert @vehicle.park
521
+ end
522
+
523
+ def test_should_allow_idle
524
+ assert @vehicle.idle
525
+ end
526
+
527
+ def test_should_allow_shift_up
528
+ assert @vehicle.shift_up
529
+ end
530
+
531
+ def test_should_not_allow_shift_down
532
+ assert !@vehicle.shift_down
533
+ end
534
+
535
+ def test_should_allow_crash
536
+ assert @vehicle.crash
537
+ end
538
+
539
+ def test_should_not_allow_repair
540
+ assert !@vehicle.repair
541
+ end
542
+ end
543
+
544
+ class VehicleSecondGearTest < Test::Unit::TestCase
545
+ def setup
546
+ @vehicle = Vehicle.new
547
+ @vehicle.ignite
548
+ 2.times {@vehicle.shift_up}
549
+ end
550
+
551
+ def test_should_be_in_second_gear_state
552
+ assert_equal 'second_gear', @vehicle.state
553
+ end
554
+
555
+ def test_should_be_second_gear
556
+ assert @vehicle.second_gear?
557
+ end
558
+
559
+ def test_should_not_allow_park
560
+ assert !@vehicle.park
561
+ end
562
+
563
+ def test_should_not_allow_idle
564
+ assert !@vehicle.idle
565
+ end
566
+
567
+ def test_should_allow_shift_up
568
+ assert @vehicle.shift_up
569
+ end
570
+
571
+ def test_should_allow_shift_down
572
+ assert @vehicle.shift_down
573
+ end
574
+
575
+ def test_should_allow_crash
576
+ assert @vehicle.crash
577
+ end
578
+
579
+ def test_should_not_allow_repair
580
+ assert !@vehicle.repair
581
+ end
582
+ end
583
+
584
+ class VehicleThirdGearTest < Test::Unit::TestCase
585
+ def setup
586
+ @vehicle = Vehicle.new
587
+ @vehicle.ignite
588
+ 3.times {@vehicle.shift_up}
589
+ end
590
+
591
+ def test_should_be_in_third_gear_state
592
+ assert_equal 'third_gear', @vehicle.state
593
+ end
594
+
595
+ def test_should_be_third_gear
596
+ assert @vehicle.third_gear?
597
+ end
598
+
599
+ def test_should_not_allow_park
600
+ assert !@vehicle.park
601
+ end
602
+
603
+ def test_should_not_allow_idle
604
+ assert !@vehicle.idle
605
+ end
606
+
607
+ def test_should_not_allow_shift_up
608
+ assert !@vehicle.shift_up
609
+ end
610
+
611
+ def test_should_allow_shift_down
612
+ assert @vehicle.shift_down
613
+ end
614
+
615
+ def test_should_allow_crash
616
+ assert @vehicle.crash
617
+ end
618
+
619
+ def test_should_not_allow_repair
620
+ assert !@vehicle.repair
621
+ end
622
+ end
623
+
624
+ class VehicleStalledTest < Test::Unit::TestCase
625
+ def setup
626
+ @vehicle = Vehicle.new
627
+ @vehicle.ignite
628
+ @vehicle.shift_up
629
+ @vehicle.crash
630
+ end
631
+
632
+ def test_should_be_in_stalled_state
633
+ assert_equal 'stalled', @vehicle.state
634
+ end
635
+
636
+ def test_should_be_stalled
637
+ assert @vehicle.stalled?
638
+ end
639
+
640
+ def test_should_be_towed
641
+ assert @vehicle.auto_shop.busy?
642
+ assert_equal 1, @vehicle.auto_shop.num_customers
643
+ end
644
+
645
+ def test_should_have_an_increased_insurance_premium
646
+ assert_equal 150, @vehicle.insurance_premium
647
+ end
648
+
649
+ def test_should_not_allow_park
650
+ assert !@vehicle.park
651
+ end
652
+
653
+ def test_should_allow_ignite
654
+ assert @vehicle.ignite
655
+ end
656
+
657
+ def test_should_not_change_state_when_ignited
658
+ assert_equal 'stalled', @vehicle.state
659
+ end
660
+
661
+ def test_should_not_allow_idle
662
+ assert !@vehicle.idle
663
+ end
664
+
665
+ def test_should_now_allow_shift_up
666
+ assert !@vehicle.shift_up
667
+ end
668
+
669
+ def test_should_not_allow_shift_down
670
+ assert !@vehicle.shift_down
671
+ end
672
+
673
+ def test_should_not_allow_crash
674
+ assert !@vehicle.crash
675
+ end
676
+
677
+ def test_should_allow_repair_if_auto_shop_is_busy
678
+ assert @vehicle.repair
679
+ end
680
+
681
+ def test_should_not_allow_repair_if_auto_shop_is_available
682
+ @vehicle.auto_shop.fix_vehicle
683
+ assert !@vehicle.repair
684
+ end
685
+ end
686
+
687
+ class VehicleRepairedTest < Test::Unit::TestCase
688
+ def setup
689
+ @vehicle = Vehicle.new
690
+ @vehicle.ignite
691
+ @vehicle.shift_up
692
+ @vehicle.crash
693
+ @vehicle.repair
694
+ end
695
+
696
+ def test_should_be_in_parked_state
697
+ assert_equal 'parked', @vehicle.state
698
+ end
699
+
700
+ def test_should_not_have_a_busy_auto_shop
701
+ assert @vehicle.auto_shop.available?
702
+ end
703
+ end
704
+
705
+ class VehicleWithParallelEventsTest < Test::Unit::TestCase
706
+ def setup
707
+ @vehicle = Vehicle.new
708
+ end
709
+
710
+ def test_should_fail_if_any_event_cannot_transition
711
+ assert !@vehicle.fire_events(:ignite, :cancel_insurance)
712
+ end
713
+
714
+ def test_should_be_successful_if_all_events_transition
715
+ assert @vehicle.fire_events(:ignite, :buy_insurance)
716
+ end
717
+
718
+ def test_should_not_save_if_skipping_action
719
+ assert @vehicle.fire_events(:ignite, :buy_insurance, false)
720
+ assert !@vehicle.saved
721
+ end
722
+
723
+ def test_should_raise_exception_if_any_event_cannot_transition_on_bang
724
+ assert_raise(StateMachine::InvalidTransition) { @vehicle.fire_events!(:ignite, :cancel_insurance) }
725
+ end
726
+
727
+ def test_should_not_raise_exception_if_all_events_transition_on_bang
728
+ assert @vehicle.fire_events!(:ignite, :buy_insurance)
729
+ end
730
+
731
+ def test_should_not_save_if_skipping_action_on_bang
732
+ assert @vehicle.fire_events!(:ignite, :buy_insurance, false)
733
+ assert !@vehicle.saved
734
+ end
735
+ end
736
+
737
+ class VehicleWithEventAttributesTest < Test::Unit::TestCase
738
+ def setup
739
+ @vehicle = Vehicle.new
740
+ @vehicle.state_event = 'ignite'
741
+ end
742
+
743
+ def test_should_fail_if_event_is_invalid
744
+ @vehicle.state_event = 'invalid'
745
+ assert !@vehicle.save
746
+ assert_equal 'parked', @vehicle.state
747
+ end
748
+
749
+ def test_should_fail_if_event_has_no_transition
750
+ @vehicle.state_event = 'park'
751
+ assert !@vehicle.save
752
+ assert_equal 'parked', @vehicle.state
753
+ end
754
+
755
+ def test_should_return_original_action_value_on_success
756
+ assert_equal @vehicle, @vehicle.save
757
+ end
758
+
759
+ def test_should_transition_state_on_success
760
+ @vehicle.save
761
+ assert_equal 'idling', @vehicle.state
762
+ end
763
+ end
764
+
765
+ class MotorcycleTest < Test::Unit::TestCase
766
+ def setup
767
+ @motorcycle = Motorcycle.new
768
+ end
769
+
770
+ def test_should_be_in_idling_state
771
+ assert_equal 'idling', @motorcycle.state
772
+ end
773
+
774
+ def test_should_allow_park
775
+ assert @motorcycle.park
776
+ end
777
+
778
+ def test_should_not_allow_ignite
779
+ assert !@motorcycle.ignite
780
+ end
781
+
782
+ def test_should_allow_shift_up
783
+ assert @motorcycle.shift_up
784
+ end
785
+
786
+ def test_should_not_allow_shift_down
787
+ assert !@motorcycle.shift_down
788
+ end
789
+
790
+ def test_should_not_allow_crash
791
+ assert !@motorcycle.crash
792
+ end
793
+
794
+ def test_should_not_allow_repair
795
+ assert !@motorcycle.repair
796
+ end
797
+
798
+ def test_should_inherit_decibels_from_superclass
799
+ @motorcycle.park
800
+ assert_equal 0.0, @motorcycle.decibels
801
+ end
802
+
803
+ def test_should_use_decibels_defined_in_state
804
+ @motorcycle.shift_up
805
+ assert_equal 1.0, @motorcycle.decibels
806
+ end
807
+ end
808
+
809
+ class CarTest < Test::Unit::TestCase
810
+ def setup
811
+ @car = Car.new
812
+ end
813
+
814
+ def test_should_be_in_parked_state
815
+ assert_equal 'parked', @car.state
816
+ end
817
+
818
+ def test_should_not_have_the_seatbelt_on
819
+ assert !@car.seatbelt_on
820
+ end
821
+
822
+ def test_should_not_allow_park
823
+ assert !@car.park
824
+ end
825
+
826
+ def test_should_allow_ignite
827
+ assert @car.ignite
828
+ assert_equal 'idling', @car.state
829
+ end
830
+
831
+ def test_should_not_allow_idle
832
+ assert !@car.idle
833
+ end
834
+
835
+ def test_should_not_allow_shift_up
836
+ assert !@car.shift_up
837
+ end
838
+
839
+ def test_should_not_allow_shift_down
840
+ assert !@car.shift_down
841
+ end
842
+
843
+ def test_should_not_allow_crash
844
+ assert !@car.crash
845
+ end
846
+
847
+ def test_should_not_allow_repair
848
+ assert !@car.repair
849
+ end
850
+
851
+ def test_should_allow_reverse
852
+ assert @car.reverse
853
+ end
854
+ end
855
+
856
+ class CarBackingUpTest < Test::Unit::TestCase
857
+ def setup
858
+ @car = Car.new
859
+ @car.reverse
860
+ end
861
+
862
+ def test_should_be_in_backing_up_state
863
+ assert_equal 'backing_up', @car.state
864
+ end
865
+
866
+ def test_should_allow_park
867
+ assert @car.park
868
+ end
869
+
870
+ def test_should_not_allow_ignite
871
+ assert !@car.ignite
872
+ end
873
+
874
+ def test_should_allow_idle
875
+ assert @car.idle
876
+ end
877
+
878
+ def test_should_allow_shift_up
879
+ assert @car.shift_up
880
+ end
881
+
882
+ def test_should_not_allow_shift_down
883
+ assert !@car.shift_down
884
+ end
885
+
886
+ def test_should_not_allow_crash
887
+ assert !@car.crash
888
+ end
889
+
890
+ def test_should_not_allow_repair
891
+ assert !@car.repair
892
+ end
893
+
894
+ def test_should_not_allow_reverse
895
+ assert !@car.reverse
896
+ end
897
+ end
898
+
899
+ class AutoShopAvailableTest < Test::Unit::TestCase
900
+ def setup
901
+ @auto_shop = AutoShop.new
902
+ end
903
+
904
+ def test_should_be_in_available_state
905
+ assert_equal 'available', @auto_shop.state
906
+ end
907
+
908
+ def test_should_allow_tow_vehicle
909
+ assert @auto_shop.tow_vehicle
910
+ end
911
+
912
+ def test_should_not_allow_fix_vehicle
913
+ assert !@auto_shop.fix_vehicle
914
+ end
915
+ end
916
+
917
+ class AutoShopBusyTest < Test::Unit::TestCase
918
+ def setup
919
+ @auto_shop = AutoShop.new
920
+ @auto_shop.tow_vehicle
921
+ end
922
+
923
+ def test_should_be_in_busy_state
924
+ assert_equal 'busy', @auto_shop.state
925
+ end
926
+
927
+ def test_should_have_incremented_number_of_customers
928
+ assert_equal 1, @auto_shop.num_customers
929
+ end
930
+
931
+ def test_should_not_allow_tow_vehicle
932
+ assert !@auto_shop.tow_vehicle
933
+ end
934
+
935
+ def test_should_allow_fix_vehicle
936
+ assert @auto_shop.fix_vehicle
937
+ end
938
+ end
939
+
940
+ class TrafficLightStopTest < Test::Unit::TestCase
941
+ def setup
942
+ @light = TrafficLight.new
943
+ @light.state = 'stop'
944
+ end
945
+
946
+ def test_should_use_stop_color
947
+ assert_equal 'red', @light.color
948
+ end
949
+
950
+ def test_should_pass_arguments_through
951
+ assert_equal 'RED', @light.color(:upcase!)
952
+ end
953
+
954
+ def test_should_pass_block_through
955
+ color = @light.color {|value| value.upcase!}
956
+ assert_equal 'RED', color
957
+ end
958
+ end
959
+
960
+ class TrafficLightProceedTest < Test::Unit::TestCase
961
+ def setup
962
+ @light = TrafficLight.new
963
+ @light.state = 'proceed'
964
+ end
965
+
966
+ def test_should_use_proceed_color
967
+ assert_equal 'green', @light.color
968
+ end
969
+ end
970
+
971
+ class TrafficLightCautionTest < Test::Unit::TestCase
972
+ def setup
973
+ @light = TrafficLight.new
974
+ @light.state = 'caution'
975
+ end
976
+
977
+ def test_should_use_caution_color
978
+ assert_equal 'yellow', @light.color
979
+ end
980
+ end