joelind-state_machine 0.8.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 (78) hide show
  1. data/CHANGELOG.rdoc +297 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +466 -0
  4. data/Rakefile +98 -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 +388 -0
  28. data/lib/state_machine/assertions.rb +36 -0
  29. data/lib/state_machine/callback.rb +189 -0
  30. data/lib/state_machine/condition_proxy.rb +94 -0
  31. data/lib/state_machine/eval_helpers.rb +67 -0
  32. data/lib/state_machine/event.rb +252 -0
  33. data/lib/state_machine/event_collection.rb +122 -0
  34. data/lib/state_machine/extensions.rb +149 -0
  35. data/lib/state_machine/guard.rb +230 -0
  36. data/lib/state_machine/integrations.rb +68 -0
  37. data/lib/state_machine/integrations/active_record.rb +492 -0
  38. data/lib/state_machine/integrations/active_record/locale.rb +11 -0
  39. data/lib/state_machine/integrations/active_record/observer.rb +41 -0
  40. data/lib/state_machine/integrations/data_mapper.rb +351 -0
  41. data/lib/state_machine/integrations/data_mapper/observer.rb +139 -0
  42. data/lib/state_machine/integrations/sequel.rb +322 -0
  43. data/lib/state_machine/machine.rb +1467 -0
  44. data/lib/state_machine/machine_collection.rb +155 -0
  45. data/lib/state_machine/matcher.rb +123 -0
  46. data/lib/state_machine/matcher_helpers.rb +54 -0
  47. data/lib/state_machine/node_collection.rb +152 -0
  48. data/lib/state_machine/state.rb +249 -0
  49. data/lib/state_machine/state_collection.rb +112 -0
  50. data/lib/state_machine/transition.rb +394 -0
  51. data/tasks/state_machine.rake +1 -0
  52. data/tasks/state_machine.rb +30 -0
  53. data/test/classes/switch.rb +11 -0
  54. data/test/functional/state_machine_test.rb +941 -0
  55. data/test/test_helper.rb +4 -0
  56. data/test/unit/assertions_test.rb +40 -0
  57. data/test/unit/callback_test.rb +455 -0
  58. data/test/unit/condition_proxy_test.rb +328 -0
  59. data/test/unit/eval_helpers_test.rb +120 -0
  60. data/test/unit/event_collection_test.rb +326 -0
  61. data/test/unit/event_test.rb +743 -0
  62. data/test/unit/guard_test.rb +908 -0
  63. data/test/unit/integrations/active_record_test.rb +1374 -0
  64. data/test/unit/integrations/data_mapper_test.rb +962 -0
  65. data/test/unit/integrations/sequel_test.rb +859 -0
  66. data/test/unit/integrations_test.rb +42 -0
  67. data/test/unit/invalid_event_test.rb +7 -0
  68. data/test/unit/invalid_transition_test.rb +7 -0
  69. data/test/unit/machine_collection_test.rb +938 -0
  70. data/test/unit/machine_test.rb +2004 -0
  71. data/test/unit/matcher_helpers_test.rb +37 -0
  72. data/test/unit/matcher_test.rb +155 -0
  73. data/test/unit/node_collection_test.rb +207 -0
  74. data/test/unit/state_collection_test.rb +280 -0
  75. data/test/unit/state_machine_test.rb +31 -0
  76. data/test/unit/state_test.rb +795 -0
  77. data/test/unit/transition_test.rb +1212 -0
  78. metadata +163 -0
@@ -0,0 +1,2004 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class MachineByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @klass = Class.new
6
+ @machine = StateMachine::Machine.new(@klass)
7
+ @object = @klass.new
8
+ end
9
+
10
+ def test_should_have_an_owner_class
11
+ assert_equal @klass, @machine.owner_class
12
+ end
13
+
14
+ def test_should_have_a_name
15
+ assert_equal :state, @machine.name
16
+ end
17
+
18
+ def test_should_have_an_attribute
19
+ assert_equal :state, @machine.attribute
20
+ end
21
+
22
+ def test_should_prefix_custom_attributes_with_attribute
23
+ assert_equal :state_event, @machine.attribute(:event)
24
+ end
25
+
26
+ def test_should_have_an_initial_state
27
+ assert_not_nil @machine.initial_state(@object)
28
+ end
29
+
30
+ def test_should_have_a_nil_initial_state
31
+ assert_nil @machine.initial_state(@object).value
32
+ end
33
+
34
+ def test_should_not_have_any_events
35
+ assert !@machine.events.any?
36
+ end
37
+
38
+ def test_should_not_have_any_before_callbacks
39
+ assert @machine.callbacks[:before].empty?
40
+ end
41
+
42
+ def test_should_not_have_any_after_callbacks
43
+ assert @machine.callbacks[:after].empty?
44
+ end
45
+
46
+ def test_should_not_have_an_action
47
+ assert_nil @machine.action
48
+ end
49
+
50
+ def test_should_use_tranactions
51
+ assert_equal true, @machine.use_transactions
52
+ end
53
+
54
+ def test_should_not_have_a_namespace
55
+ assert_nil @machine.namespace
56
+ end
57
+
58
+ def test_should_have_a_nil_state
59
+ assert_equal [nil], @machine.states.keys
60
+ end
61
+
62
+ def test_should_set_initial_on_nil_state
63
+ assert @machine.state(nil).initial
64
+ end
65
+
66
+ def test_should_generate_default_messages
67
+ assert_equal 'is invalid', @machine.generate_message(:invalid)
68
+ assert_equal 'cannot transition when parked', @machine.generate_message(:invalid_event, [[:state, :parked]])
69
+ assert_equal 'cannot transition via "park"', @machine.generate_message(:invalid_transition, [[:event, :park]])
70
+ end
71
+
72
+ def test_should_not_be_extended_by_the_active_record_integration
73
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::ActiveRecord)
74
+ end
75
+
76
+ def test_should_not_be_extended_by_the_datamapper_integration
77
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::DataMapper)
78
+ end
79
+
80
+ def test_should_not_be_extended_by_the_sequel_integration
81
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Sequel)
82
+ end
83
+
84
+ def test_should_define_a_reader_attribute_for_the_attribute
85
+ assert @object.respond_to?(:state)
86
+ end
87
+
88
+ def test_should_define_a_writer_attribute_for_the_attribute
89
+ assert @object.respond_to?(:state=)
90
+ end
91
+
92
+ def test_should_define_a_predicate_for_the_attribute
93
+ assert @object.respond_to?(:state?)
94
+ end
95
+
96
+ def test_should_define_a_name_reader_for_the_attribute
97
+ assert @object.respond_to?(:state_name)
98
+ end
99
+
100
+ def test_should_define_an_event_reader_for_the_attribute
101
+ assert @object.respond_to?(:state_events)
102
+ end
103
+
104
+ def test_should_define_a_transition_reader_for_the_attribute
105
+ assert @object.respond_to?(:state_transitions)
106
+ end
107
+
108
+ def test_should_not_define_an_event_attribute_reader
109
+ assert !@object.respond_to?(:state_event)
110
+ end
111
+
112
+ def test_should_not_define_an_event_attribute_writer
113
+ assert !@object.respond_to?(:state_event=)
114
+ end
115
+
116
+ def test_should_not_define_an_event_transition_attribute_reader
117
+ assert !@object.respond_to?(:state_event_transition)
118
+ end
119
+
120
+ def test_should_not_define_an_event_transition_attribute_writer
121
+ assert !@object.respond_to?(:state_event_transition=)
122
+ end
123
+
124
+ def test_should_not_define_singular_with_scope
125
+ assert !@klass.respond_to?(:with_state)
126
+ end
127
+
128
+ def test_should_not_define_singular_without_scope
129
+ assert !@klass.respond_to?(:without_state)
130
+ end
131
+
132
+ def test_should_not_define_plural_with_scope
133
+ assert !@klass.respond_to?(:with_states)
134
+ end
135
+
136
+ def test_should_not_define_plural_without_scope
137
+ assert !@klass.respond_to?(:without_states)
138
+ end
139
+
140
+ def test_should_extend_owner_class_with_class_methods
141
+ assert (class << @klass; ancestors; end).include?(StateMachine::ClassMethods)
142
+ end
143
+
144
+ def test_should_include_instance_methods_in_owner_class
145
+ assert @klass.included_modules.include?(StateMachine::InstanceMethods)
146
+ end
147
+
148
+ def test_should_define_state_machines_reader
149
+ expected = {:state => @machine}
150
+ assert_equal expected, @klass.state_machines
151
+ end
152
+ end
153
+
154
+ class MachineWithCustomNameTest < Test::Unit::TestCase
155
+ def setup
156
+ @klass = Class.new
157
+ @machine = StateMachine::Machine.new(@klass, :status)
158
+ @object = @klass.new
159
+ end
160
+
161
+ def test_should_use_custom_name
162
+ assert_equal :status, @machine.name
163
+ end
164
+
165
+ def test_should_use_custom_name_for_attribute
166
+ assert_equal :status, @machine.attribute
167
+ end
168
+
169
+ def test_should_prefix_custom_attributes_with_custom_name
170
+ assert_equal :status_event, @machine.attribute(:event)
171
+ end
172
+
173
+ def test_should_define_a_reader_attribute_for_the_attribute
174
+ assert @object.respond_to?(:status)
175
+ end
176
+
177
+ def test_should_define_a_writer_attribute_for_the_attribute
178
+ assert @object.respond_to?(:status=)
179
+ end
180
+
181
+ def test_should_define_a_predicate_for_the_attribute
182
+ assert @object.respond_to?(:status?)
183
+ end
184
+
185
+ def test_should_define_a_name_reader_for_the_attribute
186
+ assert @object.respond_to?(:status_name)
187
+ end
188
+
189
+ def test_should_define_an_event_reader_for_the_attribute
190
+ assert @object.respond_to?(:status_events)
191
+ end
192
+
193
+ def test_should_define_a_transition_reader_for_the_attribute
194
+ assert @object.respond_to?(:status_transitions)
195
+ end
196
+ end
197
+
198
+ class MachineWithStaticInitialStateTest < Test::Unit::TestCase
199
+ def setup
200
+ @klass = Class.new do
201
+ def initialize(attributes = {})
202
+ attributes.each {|attr, value| send("#{attr}=", value)}
203
+ super()
204
+ end
205
+ end
206
+
207
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
208
+ end
209
+
210
+ def test_should_not_have_dynamic_initial_state
211
+ assert !@machine.dynamic_initial_state?
212
+ end
213
+
214
+ def test_should_have_an_initial_state
215
+ object = @klass.new
216
+ assert_equal 'parked', @machine.initial_state(object).value
217
+ end
218
+
219
+ def test_should_set_initial_on_state_object
220
+ assert @machine.state(:parked).initial
221
+ end
222
+
223
+ def test_should_set_initial_state_if_existing_is_nil
224
+ object = @klass.new(:state => nil)
225
+ assert_equal 'parked', object.state
226
+ end
227
+
228
+ def test_should_set_initial_state_if_existing_is_empty
229
+ object = @klass.new(:state => '')
230
+ assert_equal 'parked', object.state
231
+ end
232
+
233
+ def test_should_not_set_initial_state_if_existing_is_not_empty
234
+ object = @klass.new(:state => 'idling')
235
+ assert_equal 'idling', object.state
236
+ end
237
+
238
+ def test_should_set_initial_state_prior_to_initialization
239
+ base = Class.new do
240
+ attr_accessor :state_on_init
241
+
242
+ def initialize
243
+ self.state_on_init = state
244
+ end
245
+ end
246
+ klass = Class.new(base)
247
+ machine = StateMachine::Machine.new(klass, :initial => :parked)
248
+
249
+ assert_equal 'parked', klass.new.state_on_init
250
+ end
251
+
252
+ def test_should_be_included_in_known_states
253
+ assert_equal [:parked], @machine.states.keys
254
+ end
255
+ end
256
+
257
+ class MachineWithDynamicInitialStateTest < Test::Unit::TestCase
258
+ def setup
259
+ @klass = Class.new do
260
+ attr_accessor :initial_state
261
+ end
262
+ @machine = StateMachine::Machine.new(@klass, :initial => lambda {|object| object.initial_state || :default})
263
+ @machine.state :parked, :idling, :default
264
+ @object = @klass.new
265
+ end
266
+
267
+ def test_should_have_dynamic_initial_state
268
+ assert @machine.dynamic_initial_state?
269
+ end
270
+
271
+ def test_should_use_the_record_for_determining_the_initial_state
272
+ @object.initial_state = :parked
273
+ assert_equal :parked, @machine.initial_state(@object).name
274
+
275
+ @object.initial_state = :idling
276
+ assert_equal :idling, @machine.initial_state(@object).name
277
+ end
278
+
279
+ def test_should_set_initial_state_on_created_object
280
+ assert_equal 'default', @object.state
281
+ end
282
+
283
+ def test_should_set_initial_state_after_initialization
284
+ base = Class.new do
285
+ attr_accessor :state_on_init
286
+
287
+ def initialize
288
+ self.state_on_init = state
289
+ end
290
+ end
291
+ klass = Class.new(base)
292
+ machine = StateMachine::Machine.new(klass, :initial => lambda {|object| :parked})
293
+ machine.state :parked
294
+
295
+ assert_nil klass.new.state_on_init
296
+ end
297
+
298
+ def test_should_not_be_included_in_known_states
299
+ assert_equal [:parked, :idling, :default], @machine.states.map {|state| state.name}
300
+ end
301
+ end
302
+
303
+ class MachineWithCustomActionTest < Test::Unit::TestCase
304
+ def setup
305
+ @machine = StateMachine::Machine.new(Class.new, :action => :save)
306
+ end
307
+
308
+ def test_should_use_the_custom_action
309
+ assert_equal :save, @machine.action
310
+ end
311
+ end
312
+
313
+ class MachineWithNilActionTest < Test::Unit::TestCase
314
+ def setup
315
+ integration = Module.new do
316
+ class << self; attr_reader :defaults; end
317
+ @defaults = {:action => :save}
318
+ end
319
+ StateMachine::Integrations.const_set('Custom', integration)
320
+ @machine = StateMachine::Machine.new(Class.new, :action => nil, :integration => :custom)
321
+ end
322
+
323
+ def test_should_have_a_nil_action
324
+ assert_nil @machine.action
325
+ end
326
+
327
+ def teardown
328
+ StateMachine::Integrations.send(:remove_const, 'Custom')
329
+ end
330
+ end
331
+
332
+ class MachineWithoutIntegrationTest < Test::Unit::TestCase
333
+ def setup
334
+ @klass = Class.new
335
+ @machine = StateMachine::Machine.new(@klass)
336
+ @object = @klass.new
337
+ end
338
+
339
+ def test_transaction_should_yield
340
+ @yielded = false
341
+ @machine.within_transaction(@object) do
342
+ @yielded = true
343
+ end
344
+
345
+ assert @yielded
346
+ end
347
+
348
+ def test_invalidation_should_do_nothing
349
+ assert_nil @machine.invalidate(@object, :state, :invalid_transition, [[:event, :park]])
350
+ end
351
+
352
+ def test_reset_should_do_nothing
353
+ assert_nil @machine.reset(@object)
354
+ end
355
+ end
356
+
357
+ class MachineWithCustomIntegrationTest < Test::Unit::TestCase
358
+ def setup
359
+ StateMachine::Integrations.const_set('Custom', Module.new)
360
+ @machine = StateMachine::Machine.new(Class.new, :integration => :custom)
361
+ end
362
+
363
+ def test_should_be_extended_by_the_integration
364
+ assert (class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom)
365
+ end
366
+
367
+ def teardown
368
+ StateMachine::Integrations.send(:remove_const, 'Custom')
369
+ end
370
+ end
371
+
372
+ class MachineWithIntegrationTest < Test::Unit::TestCase
373
+ def setup
374
+ StateMachine::Integrations.const_set('Custom', Module.new do
375
+ class << self; attr_reader :defaults; end
376
+ @defaults = {:action => :save, :use_transactions => false}
377
+
378
+ attr_reader :initialized, :with_scopes, :without_scopes, :ran_transaction
379
+
380
+ def after_initialize
381
+ @initialized = true
382
+ end
383
+
384
+ def create_with_scope(name)
385
+ (@with_scopes ||= []) << name
386
+ lambda {}
387
+ end
388
+
389
+ def create_without_scope(name)
390
+ (@without_scopes ||= []) << name
391
+ lambda {}
392
+ end
393
+
394
+ def transaction(object)
395
+ @ran_transaction = true
396
+ yield
397
+ end
398
+ end)
399
+
400
+ @machine = StateMachine::Machine.new(Class.new, :integration => :custom)
401
+ end
402
+
403
+ def test_should_call_after_initialize_hook
404
+ assert @machine.initialized
405
+ end
406
+
407
+ def test_should_use_the_default_action
408
+ assert_equal :save, @machine.action
409
+ end
410
+
411
+ def test_should_use_the_custom_action_if_specified
412
+ machine = StateMachine::Machine.new(Class.new, :integration => :custom, :action => :save!)
413
+ assert_equal :save!, machine.action
414
+ end
415
+
416
+ def test_should_use_the_default_use_transactions
417
+ assert_equal false, @machine.use_transactions
418
+ end
419
+
420
+ def test_should_use_the_custom_use_transactions_if_specified
421
+ machine = StateMachine::Machine.new(Class.new, :integration => :custom, :use_transactions => true)
422
+ assert_equal true, machine.use_transactions
423
+ end
424
+
425
+ def test_should_define_a_singular_and_plural_with_scope
426
+ assert_equal %w(with_state with_states), @machine.with_scopes
427
+ end
428
+
429
+ def test_should_define_a_singular_and_plural_without_scope
430
+ assert_equal %w(without_state without_states), @machine.without_scopes
431
+ end
432
+
433
+ def teardown
434
+ StateMachine::Integrations.send(:remove_const, 'Custom')
435
+ end
436
+ end
437
+
438
+ class MachineWithActionTest < Test::Unit::TestCase
439
+ def setup
440
+ @klass = Class.new do
441
+ def save
442
+ end
443
+ end
444
+
445
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
446
+ @object = @klass.new
447
+ end
448
+
449
+ def test_should_define_an_event_attribute_reader
450
+ assert @object.respond_to?(:state_event)
451
+ end
452
+
453
+ def test_should_define_an_event_attribute_writer
454
+ assert @object.respond_to?(:state_event=)
455
+ end
456
+
457
+ def test_should_define_an_event_transition_attribute_reader
458
+ assert @object.respond_to?(:state_event_transition)
459
+ end
460
+
461
+ def test_should_define_an_event_transition_attribute_writer
462
+ assert @object.respond_to?(:state_event_transition=)
463
+ end
464
+ end
465
+
466
+ class MachineWithActionUndefinedTest < Test::Unit::TestCase
467
+ def setup
468
+ @klass = Class.new
469
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
470
+ @object = @klass.new
471
+ end
472
+
473
+ def test_should_define_an_event_attribute_reader
474
+ assert @object.respond_to?(:state_event)
475
+ end
476
+
477
+ def test_should_define_an_event_attribute_writer
478
+ assert @object.respond_to?(:state_event=)
479
+ end
480
+
481
+ def test_should_define_an_event_transition_attribute_reader
482
+ assert @object.respond_to?(:state_event_transition)
483
+ end
484
+
485
+ def test_should_define_an_event_transition_attribute_writer
486
+ assert @object.respond_to?(:state_event_transition=)
487
+ end
488
+
489
+ def test_should_not_define_action
490
+ assert !@object.respond_to?(:save)
491
+ end
492
+ end
493
+
494
+ class MachineWithCustomPluralTest < Test::Unit::TestCase
495
+ def setup
496
+ @integration = Module.new do
497
+ class << self; attr_accessor :with_scopes, :without_scopes; end
498
+ @with_scopes = []
499
+ @without_scopes = []
500
+
501
+ def create_with_scope(name)
502
+ StateMachine::Integrations::Custom.with_scopes << name
503
+ lambda {}
504
+ end
505
+
506
+ def create_without_scope(name)
507
+ StateMachine::Integrations::Custom.without_scopes << name
508
+ lambda {}
509
+ end
510
+ end
511
+
512
+ StateMachine::Integrations.const_set('Custom', @integration)
513
+ @machine = StateMachine::Machine.new(Class.new, :integration => :custom, :plural => 'staties')
514
+ end
515
+
516
+ def test_should_define_a_singular_and_plural_with_scope
517
+ assert_equal %w(with_state with_staties), @integration.with_scopes
518
+ end
519
+
520
+ def test_should_define_a_singular_and_plural_without_scope
521
+ assert_equal %w(without_state without_staties), @integration.without_scopes
522
+ end
523
+
524
+ def teardown
525
+ StateMachine::Integrations.send(:remove_const, 'Custom')
526
+ end
527
+ end
528
+
529
+ class MachineWithCustomInvalidationTest < Test::Unit::TestCase
530
+ def setup
531
+ @integration = Module.new do
532
+ def invalidate(object, attribute, message, values = [])
533
+ object.error = generate_message(message, values)
534
+ end
535
+ end
536
+ StateMachine::Integrations.const_set('Custom', @integration)
537
+
538
+ @klass = Class.new do
539
+ attr_accessor :error
540
+ end
541
+
542
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom, :messages => {:invalid_transition => 'cannot %s'})
543
+ @machine.state :parked
544
+
545
+ @object = @klass.new
546
+ @object.state = 'parked'
547
+ end
548
+
549
+ def test_generate_custom_message
550
+ assert_equal 'cannot park', @machine.generate_message(:invalid_transition, [[:event, :park]])
551
+ end
552
+
553
+ def test_use_custom_message
554
+ @machine.invalidate(@object, :state, :invalid_transition, [[:event, :park]])
555
+ assert_equal 'cannot park', @object.error
556
+ end
557
+
558
+ def teardown
559
+ StateMachine::Integrations.send(:remove_const, 'Custom')
560
+ end
561
+ end
562
+
563
+ class MachineTest < Test::Unit::TestCase
564
+ def test_should_raise_exception_if_invalid_option_specified
565
+ assert_raise(ArgumentError) {StateMachine::Machine.new(Class.new, :invalid => true)}
566
+ end
567
+
568
+ def test_should_not_raise_exception_if_custom_messages_specified
569
+ assert_nothing_raised {StateMachine::Machine.new(Class.new, :messages => {:invalid_transition => 'custom'})}
570
+ end
571
+
572
+ def test_should_evaluate_a_block_during_initialization
573
+ called = true
574
+ StateMachine::Machine.new(Class.new) do
575
+ called = respond_to?(:event)
576
+ end
577
+
578
+ assert called
579
+ end
580
+
581
+ def test_should_provide_matcher_helpers_during_initialization
582
+ matchers = []
583
+
584
+ StateMachine::Machine.new(Class.new) do
585
+ matchers = [all, any, same]
586
+ end
587
+
588
+ assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
589
+ end
590
+ end
591
+
592
+ class MachineAfterBeingCopiedTest < Test::Unit::TestCase
593
+ def setup
594
+ @machine = StateMachine::Machine.new(Class.new, :state, :initial => :parked)
595
+ @machine.event(:ignite) {}
596
+ @machine.before_transition(lambda {})
597
+ @machine.after_transition(lambda {})
598
+
599
+ @copied_machine = @machine.clone
600
+ end
601
+
602
+ def test_should_not_have_the_same_collection_of_states
603
+ assert_not_same @copied_machine.states, @machine.states
604
+ end
605
+
606
+ def test_should_copy_each_state
607
+ assert_not_same @copied_machine.states[:parked], @machine.states[:parked]
608
+ end
609
+
610
+ def test_should_update_machine_for_each_state
611
+ assert_equal @copied_machine, @copied_machine.states[:parked].machine
612
+ end
613
+
614
+ def test_should_not_update_machine_for_original_state
615
+ assert_equal @machine, @machine.states[:parked].machine
616
+ end
617
+
618
+ def test_should_not_have_the_same_collection_of_events
619
+ assert_not_same @copied_machine.events, @machine.events
620
+ end
621
+
622
+ def test_should_copy_each_event
623
+ assert_not_same @copied_machine.events[:ignite], @machine.events[:ignite]
624
+ end
625
+
626
+ def test_should_update_machine_for_each_event
627
+ assert_equal @copied_machine, @copied_machine.events[:ignite].machine
628
+ end
629
+
630
+ def test_should_not_update_machine_for_original_event
631
+ assert_equal @machine, @machine.events[:ignite].machine
632
+ end
633
+
634
+ def test_should_not_have_the_same_callbacks
635
+ assert_not_same @copied_machine.callbacks, @machine.callbacks
636
+ end
637
+
638
+ def test_should_not_have_the_same_before_callbacks
639
+ assert_not_same @copied_machine.callbacks[:before], @machine.callbacks[:before]
640
+ end
641
+
642
+ def test_should_not_have_the_same_after_callbacks
643
+ assert_not_same @copied_machine.callbacks[:after], @machine.callbacks[:after]
644
+ end
645
+ end
646
+
647
+ class MachineAfterChangingOwnerClassTest < Test::Unit::TestCase
648
+ def setup
649
+ @original_class = Class.new
650
+ @machine = StateMachine::Machine.new(@original_class)
651
+
652
+ @new_class = Class.new(@original_class)
653
+ @new_machine = @machine.clone
654
+ @new_machine.owner_class = @new_class
655
+
656
+ @object = @new_class.new
657
+ end
658
+
659
+ def test_should_update_owner_class
660
+ assert_equal @new_class, @new_machine.owner_class
661
+ end
662
+
663
+ def test_should_not_change_original_owner_class
664
+ assert_equal @original_class, @machine.owner_class
665
+ end
666
+
667
+ def test_should_change_the_associated_machine_in_the_new_class
668
+ assert_equal @new_machine, @new_class.state_machines[:state]
669
+ end
670
+
671
+ def test_should_not_change_the_associated_machine_in_the_original_class
672
+ assert_equal @machine, @original_class.state_machines[:state]
673
+ end
674
+ end
675
+
676
+ class MachineAfterChangingInitialState < Test::Unit::TestCase
677
+ def setup
678
+ @klass = Class.new
679
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
680
+ @machine.initial_state = :idling
681
+
682
+ @object = @klass.new
683
+ end
684
+
685
+ def test_should_change_the_initial_state
686
+ assert_equal :idling, @machine.initial_state(@object).name
687
+ end
688
+
689
+ def test_should_include_in_known_states
690
+ assert_equal [:parked, :idling], @machine.states.map {|state| state.name}
691
+ end
692
+
693
+ def test_should_reset_original_initial_state
694
+ assert !@machine.state(:parked).initial
695
+ end
696
+
697
+ def test_should_set_new_state_to_initial
698
+ assert @machine.state(:idling).initial
699
+ end
700
+ end
701
+
702
+ class MachineWithInstanceHelpersTest < Test::Unit::TestCase
703
+ def setup
704
+ @klass = Class.new
705
+ @machine = StateMachine::Machine.new(@klass)
706
+ @object = @klass.new
707
+ end
708
+
709
+ def test_should_not_redefine_existing_public_methods
710
+ @klass.class_eval do
711
+ def state
712
+ 'parked'
713
+ end
714
+ end
715
+
716
+ @machine.define_instance_method(:state) {}
717
+ assert_equal 'parked', @object.state
718
+ end
719
+
720
+ def test_should_not_redefine_existing_protected_methods
721
+ @klass.class_eval do
722
+ protected
723
+ def state
724
+ 'parked'
725
+ end
726
+ end
727
+
728
+ @machine.define_instance_method(:state) {}
729
+ assert_equal 'parked', @object.send(:state)
730
+ end
731
+
732
+ def test_should_not_redefine_existing_private_methods
733
+ @klass.class_eval do
734
+ private
735
+ def state
736
+ 'parked'
737
+ end
738
+ end
739
+
740
+ @machine.define_instance_method(:state) {}
741
+ assert_equal 'parked', @object.send(:state)
742
+ end
743
+
744
+ def test_should_define_nonexistent_methods
745
+ @machine.define_instance_method(:state) {'parked'}
746
+ assert_equal 'parked', @object.state
747
+ end
748
+ end
749
+
750
+ class MachineWithClassHelpersTest < Test::Unit::TestCase
751
+ def setup
752
+ @klass = Class.new
753
+ @machine = StateMachine::Machine.new(@klass)
754
+ end
755
+
756
+ def test_should_not_redefine_existing_public_methods
757
+ class << @klass
758
+ def states
759
+ []
760
+ end
761
+ end
762
+
763
+ @machine.define_class_method(:states) {}
764
+ assert_equal [], @klass.states
765
+ end
766
+
767
+ def test_should_not_redefine_existing_protected_methods
768
+ class << @klass
769
+ protected
770
+ def states
771
+ []
772
+ end
773
+ end
774
+
775
+ @machine.define_class_method(:states) {}
776
+ assert_equal [], @klass.send(:states)
777
+ end
778
+
779
+ def test_should_not_redefine_existing_private_methods
780
+ class << @klass
781
+ private
782
+ def states
783
+ []
784
+ end
785
+ end
786
+
787
+ @machine.define_class_method(:states) {}
788
+ assert_equal [], @klass.send(:states)
789
+ end
790
+
791
+ def test_should_define_nonexistent_methods
792
+ @machine.define_class_method(:states) {[]}
793
+ assert_equal [], @klass.states
794
+ end
795
+ end
796
+
797
+ class MachineWithConflictingHelpersTest < Test::Unit::TestCase
798
+ def setup
799
+ @klass = Class.new do
800
+ def self.with_state
801
+ :with_state
802
+ end
803
+
804
+ def self.with_states
805
+ :with_states
806
+ end
807
+
808
+ def self.without_state
809
+ :without_state
810
+ end
811
+
812
+ def self.without_states
813
+ :without_states
814
+ end
815
+
816
+ attr_accessor :status
817
+
818
+ def state
819
+ 'parked'
820
+ end
821
+
822
+ def state=(value)
823
+ self.status = value
824
+ end
825
+
826
+ def state?
827
+ true
828
+ end
829
+
830
+ def state_name
831
+ :parked
832
+ end
833
+
834
+ def state_events
835
+ [:ignite]
836
+ end
837
+
838
+ def state_transitions
839
+ [{:parked => :idling}]
840
+ end
841
+ end
842
+
843
+ StateMachine::Integrations.const_set('Custom', Module.new do
844
+ def create_with_scope(name)
845
+ lambda {|klass, values| []}
846
+ end
847
+
848
+ def create_without_scope(name)
849
+ lambda {|klass, values| []}
850
+ end
851
+ end)
852
+
853
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
854
+ @machine.state :parked, :idling
855
+ @object = @klass.new
856
+ end
857
+
858
+ def test_should_not_redefine_singular_with_scope
859
+ assert_equal :with_state, @klass.with_state
860
+ end
861
+
862
+ def test_should_not_redefine_plural_with_scope
863
+ assert_equal :with_states, @klass.with_states
864
+ end
865
+
866
+ def test_should_not_redefine_singular_without_scope
867
+ assert_equal :without_state, @klass.without_state
868
+ end
869
+
870
+ def test_should_not_redefine_plural_without_scope
871
+ assert_equal :without_states, @klass.without_states
872
+ end
873
+
874
+ def test_should_not_redefine_attribute_writer
875
+ assert_equal 'parked', @object.state
876
+ end
877
+
878
+ def test_should_not_redefine_attribute_writer
879
+ @object.state = 'parked'
880
+ assert_equal 'parked', @object.status
881
+ end
882
+
883
+ def test_should_not_define_attribute_predicate
884
+ assert @object.state?
885
+ end
886
+
887
+ def test_should_not_redefine_attribute_name_reader
888
+ assert_equal :parked, @object.state_name
889
+ end
890
+
891
+ def test_should_not_redefine_attribute_events_reader
892
+ assert_equal [:ignite], @object.state_events
893
+ end
894
+
895
+ def test_should_not_redefine_attribute_transitions_reader
896
+ assert_equal [{:parked => :idling}], @object.state_transitions
897
+ end
898
+
899
+ def test_should_allow_super_chaining
900
+ @klass.class_eval do
901
+ def self.with_state(*states)
902
+ super == []
903
+ end
904
+
905
+ def self.with_states(*states)
906
+ super == []
907
+ end
908
+
909
+ def self.without_state(*states)
910
+ super == []
911
+ end
912
+
913
+ def self.without_states(*states)
914
+ super == []
915
+ end
916
+
917
+ attr_accessor :status
918
+
919
+ def state
920
+ super || 'parked'
921
+ end
922
+
923
+ def state=(value)
924
+ super
925
+ self.status = value
926
+ end
927
+
928
+ def state?(state)
929
+ super ? 1 : 0
930
+ end
931
+
932
+ def state_name
933
+ super == :parked ? 1 : 0
934
+ end
935
+
936
+ def state_events
937
+ super == []
938
+ end
939
+
940
+ def state_transitions
941
+ super == []
942
+ end
943
+ end
944
+
945
+ assert_equal true, @klass.with_state
946
+ assert_equal true, @klass.with_states
947
+ assert_equal true, @klass.without_state
948
+ assert_equal true, @klass.without_states
949
+
950
+ assert_equal 'parked', @object.state
951
+ @object.state = 'idling'
952
+ assert_equal 'idling', @object.status
953
+ assert_equal 0, @object.state?(:parked)
954
+ assert_equal 0, @object.state_name
955
+ assert_equal true, @object.state_events
956
+ assert_equal true, @object.state_transitions
957
+ end
958
+
959
+ def teardown
960
+ StateMachine::Integrations.send(:remove_const, 'Custom')
961
+ end
962
+ end
963
+
964
+ class MachineWithoutInitializeTest < Test::Unit::TestCase
965
+ def setup
966
+ @klass = Class.new
967
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
968
+ @object = @klass.new
969
+ end
970
+
971
+ def test_should_initialize_state
972
+ assert_equal 'parked', @object.state
973
+ end
974
+ end
975
+
976
+ class MachineWithInitializeWithoutSuperTest < Test::Unit::TestCase
977
+ def setup
978
+ @klass = Class.new do
979
+ def initialize
980
+ end
981
+ end
982
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
983
+ @object = @klass.new
984
+ end
985
+
986
+ def test_should_not_initialize_state
987
+ assert_nil @object.state
988
+ end
989
+ end
990
+
991
+ class MachineWithInitializeAndSuperTest < Test::Unit::TestCase
992
+ def setup
993
+ @klass = Class.new do
994
+ def initialize
995
+ super()
996
+ end
997
+ end
998
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
999
+ @object = @klass.new
1000
+ end
1001
+
1002
+ def test_should_initialize_state
1003
+ assert_equal 'parked', @object.state
1004
+ end
1005
+ end
1006
+
1007
+ class MachineWithInitializeArgumentsAndBlockTest < Test::Unit::TestCase
1008
+ def setup
1009
+ @superclass = Class.new do
1010
+ attr_reader :args
1011
+ attr_reader :block_given
1012
+
1013
+ def initialize(*args)
1014
+ @args = args
1015
+ @block_given = block_given?
1016
+ end
1017
+ end
1018
+ @klass = Class.new(@superclass)
1019
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1020
+ @object = @klass.new(1, 2, 3) {}
1021
+ end
1022
+
1023
+ def test_should_initialize_state
1024
+ assert_equal 'parked', @object.state
1025
+ end
1026
+
1027
+ def test_should_preserve_arguments
1028
+ assert_equal [1, 2, 3], @object.args
1029
+ end
1030
+
1031
+ def test_should_preserve_block
1032
+ assert @object.block_given
1033
+ end
1034
+ end
1035
+
1036
+ class MachineWithCustomInitializeTest < Test::Unit::TestCase
1037
+ def setup
1038
+ @klass = Class.new do
1039
+ def initialize
1040
+ initialize_state_machines
1041
+ end
1042
+ end
1043
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1044
+ @object = @klass.new
1045
+ end
1046
+
1047
+ def test_should_initialize_state
1048
+ assert_equal 'parked', @object.state
1049
+ end
1050
+ end
1051
+
1052
+ class MachinePersistenceTest < Test::Unit::TestCase
1053
+ def setup
1054
+ @klass = Class.new do
1055
+ attr_accessor :state_event
1056
+ end
1057
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1058
+ @object = @klass.new
1059
+ end
1060
+
1061
+ def test_should_allow_reading_state
1062
+ assert_equal 'parked', @machine.read(@object, :state)
1063
+ end
1064
+
1065
+ def test_should_allow_reading_custom_attributes
1066
+ assert_nil @machine.read(@object, :event)
1067
+
1068
+ @object.state_event = 'ignite'
1069
+ assert_equal 'ignite', @machine.read(@object, :event)
1070
+ end
1071
+
1072
+ def test_should_allow_reading_custom_instance_variables
1073
+ @klass.class_eval do
1074
+ attr_writer :state_value
1075
+ end
1076
+
1077
+ @object.state_value = 1
1078
+ assert_raise(NoMethodError) { @machine.read(@object, :value) }
1079
+ assert_equal 1, @machine.read(@object, :value, true)
1080
+ end
1081
+
1082
+ def test_should_allow_writing_state
1083
+ @machine.write(@object, :state, 'idling')
1084
+ assert_equal 'idling', @object.state
1085
+ end
1086
+
1087
+ def test_should_allow_writing_custom_attributes
1088
+ @machine.write(@object, :event, 'ignite')
1089
+ assert_equal 'ignite', @object.state_event
1090
+ end
1091
+ end
1092
+
1093
+
1094
+ class MachineWithStatesTest < Test::Unit::TestCase
1095
+ def setup
1096
+ @klass = Class.new
1097
+ @machine = StateMachine::Machine.new(@klass)
1098
+ @parked, @idling = @machine.state :parked, :idling
1099
+
1100
+ @object = @klass.new
1101
+ end
1102
+
1103
+ def test_should_have_states
1104
+ assert_equal [nil, :parked, :idling], @machine.states.map {|state| state.name}
1105
+ end
1106
+
1107
+ def test_should_allow_state_lookup_by_name
1108
+ assert_equal @parked, @machine.states[:parked]
1109
+ end
1110
+
1111
+ def test_should_allow_state_lookup_by_value
1112
+ assert_equal @parked, @machine.states['parked', :value]
1113
+ end
1114
+
1115
+ def test_should_use_stringified_name_for_value
1116
+ assert_equal 'parked', @parked.value
1117
+ end
1118
+
1119
+ def test_should_not_use_custom_matcher
1120
+ assert_nil @parked.matcher
1121
+ end
1122
+
1123
+ def test_should_raise_exception_if_invalid_option_specified
1124
+ exception = assert_raise(ArgumentError) {@machine.state(:first_gear, :invalid => true)}
1125
+ assert_equal 'Invalid key(s): invalid', exception.message
1126
+ end
1127
+ end
1128
+
1129
+ class MachineWithStatesWithCustomValuesTest < Test::Unit::TestCase
1130
+ def setup
1131
+ @klass = Class.new
1132
+ @machine = StateMachine::Machine.new(@klass)
1133
+ @state = @machine.state :parked, :value => 1
1134
+
1135
+ @object = @klass.new
1136
+ @object.state = 1
1137
+ end
1138
+
1139
+ def test_should_use_custom_value
1140
+ assert_equal 1, @state.value
1141
+ end
1142
+
1143
+ def test_should_allow_lookup_by_custom_value
1144
+ assert_equal @state, @machine.states[1, :value]
1145
+ end
1146
+ end
1147
+
1148
+ class MachineWithStatesWithRuntimeDependenciesTest < Test::Unit::TestCase
1149
+ def setup
1150
+ @klass = Class.new
1151
+ @machine = StateMachine::Machine.new(@klass)
1152
+ @machine.state :parked
1153
+ end
1154
+
1155
+ def test_should_not_evaluate_value_during_definition
1156
+ assert_nothing_raised { @machine.state :parked, :value => lambda {raise ArgumentError} }
1157
+ end
1158
+
1159
+ def test_should_not_evaluate_if_not_initial_state
1160
+ @machine.state :parked, :value => lambda {raise ArgumentError}
1161
+ assert_nothing_raised { @klass.new }
1162
+ end
1163
+ end
1164
+
1165
+ class MachineWithStateWithMatchersTest < Test::Unit::TestCase
1166
+ def setup
1167
+ @klass = Class.new
1168
+ @machine = StateMachine::Machine.new(@klass)
1169
+ @state = @machine.state :parked, :if => lambda {|value| !value.nil?}
1170
+
1171
+ @object = @klass.new
1172
+ @object.state = 1
1173
+ end
1174
+
1175
+ def test_should_use_custom_matcher
1176
+ assert_not_nil @state.matcher
1177
+ assert @state.matches?(1)
1178
+ assert !@state.matches?(nil)
1179
+ end
1180
+ end
1181
+
1182
+ class MachineWithCachedStateTest < Test::Unit::TestCase
1183
+ def setup
1184
+ @klass = Class.new
1185
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1186
+ @state = @machine.state :parked, :value => lambda {Object.new}, :cache => true
1187
+
1188
+ @object = @klass.new
1189
+ end
1190
+
1191
+ def test_should_use_evaluated_value
1192
+ assert_instance_of Object, @object.state
1193
+ end
1194
+
1195
+ def test_use_same_value_across_multiple_objects
1196
+ assert_equal @object.state, @klass.new.state
1197
+ end
1198
+ end
1199
+
1200
+ class MachineWithStatesWithBehaviorsTest < Test::Unit::TestCase
1201
+ def setup
1202
+ @klass = Class.new
1203
+ @machine = StateMachine::Machine.new(@klass)
1204
+
1205
+ @parked, @idling = @machine.state :parked, :idling do
1206
+ def speed
1207
+ 0
1208
+ end
1209
+ end
1210
+ end
1211
+
1212
+ def test_should_define_behaviors_for_each_state
1213
+ assert_not_nil @parked.methods[:speed]
1214
+ assert_not_nil @idling.methods[:speed]
1215
+ end
1216
+
1217
+ def test_should_define_different_behaviors_for_each_state
1218
+ assert_not_equal @parked.methods[:speed], @idling.methods[:speed]
1219
+ end
1220
+ end
1221
+
1222
+ class MachineWithExistingStateTest < Test::Unit::TestCase
1223
+ def setup
1224
+ @klass = Class.new
1225
+ @machine = StateMachine::Machine.new(@klass)
1226
+ @state = @machine.state :parked
1227
+ @same_state = @machine.state :parked, :value => 1
1228
+ end
1229
+
1230
+ def test_should_not_create_a_new_state
1231
+ assert_same @state, @same_state
1232
+ end
1233
+
1234
+ def test_should_update_attributes
1235
+ assert_equal 1, @state.value
1236
+ end
1237
+
1238
+ def test_should_no_longer_be_able_to_look_up_state_by_original_value
1239
+ assert_nil @machine.states['parked', :value]
1240
+ end
1241
+
1242
+ def test_should_be_able_to_look_up_state_by_new_value
1243
+ assert_equal @state, @machine.states[1, :value]
1244
+ end
1245
+ end
1246
+
1247
+ class MachineWithOtherStates < Test::Unit::TestCase
1248
+ def setup
1249
+ @klass = Class.new
1250
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1251
+ @parked, @idling = @machine.other_states(:parked, :idling)
1252
+ end
1253
+
1254
+ def test_should_include_other_states_in_known_states
1255
+ assert_equal [@parked, @idling], @machine.states.to_a
1256
+ end
1257
+
1258
+ def test_should_use_default_value
1259
+ assert_equal 'idling', @idling.value
1260
+ end
1261
+
1262
+ def test_should_not_create_matcher
1263
+ assert_nil @idling.matcher
1264
+ end
1265
+ end
1266
+
1267
+ class MachineWithEventsTest < Test::Unit::TestCase
1268
+ def setup
1269
+ @klass = Class.new
1270
+ @machine = StateMachine::Machine.new(@klass)
1271
+ end
1272
+
1273
+ def test_should_return_the_created_event
1274
+ assert_instance_of StateMachine::Event, @machine.event(:ignite)
1275
+ end
1276
+
1277
+ def test_should_create_event_with_given_name
1278
+ event = @machine.event(:ignite) {}
1279
+ assert_equal :ignite, event.name
1280
+ end
1281
+
1282
+ def test_should_evaluate_block_within_event_context
1283
+ responded = false
1284
+ @machine.event :ignite do
1285
+ responded = respond_to?(:transition)
1286
+ end
1287
+
1288
+ assert responded
1289
+ end
1290
+
1291
+ def test_should_be_aliased_as_on
1292
+ event = @machine.on(:ignite) {}
1293
+ assert_equal :ignite, event.name
1294
+ end
1295
+
1296
+ def test_should_have_events
1297
+ event = @machine.event(:ignite)
1298
+ assert_equal [event], @machine.events.to_a
1299
+ end
1300
+ end
1301
+
1302
+ class MachineWithExistingEventTest < Test::Unit::TestCase
1303
+ def setup
1304
+ @machine = StateMachine::Machine.new(Class.new)
1305
+ @event = @machine.event(:ignite)
1306
+ @same_event = @machine.event(:ignite)
1307
+ end
1308
+
1309
+ def test_should_not_create_new_event
1310
+ assert_same @event, @same_event
1311
+ end
1312
+
1313
+ def test_should_allow_accessing_event_without_block
1314
+ assert_equal @event, @machine.event(:ignite)
1315
+ end
1316
+ end
1317
+
1318
+ class MachineWithEventsWithTransitionsTest < Test::Unit::TestCase
1319
+ def setup
1320
+ @klass = Class.new
1321
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1322
+ @event = @machine.event(:ignite) do
1323
+ transition :parked => :idling
1324
+ transition :stalled => :idling
1325
+ end
1326
+ end
1327
+
1328
+ def test_should_have_events
1329
+ assert_equal [@event], @machine.events.to_a
1330
+ end
1331
+
1332
+ def test_should_track_states_defined_in_event_transitions
1333
+ assert_equal [:parked, :idling, :stalled], @machine.states.map {|state| state.name}
1334
+ end
1335
+
1336
+ def test_should_not_duplicate_states_defined_in_multiple_event_transitions
1337
+ @machine.event :park do
1338
+ transition :idling => :parked
1339
+ end
1340
+
1341
+ assert_equal [:parked, :idling, :stalled], @machine.states.map {|state| state.name}
1342
+ end
1343
+
1344
+ def test_should_track_state_from_new_events
1345
+ @machine.event :shift_up do
1346
+ transition :idling => :first_gear
1347
+ end
1348
+
1349
+ assert_equal [:parked, :idling, :stalled, :first_gear], @machine.states.map {|state| state.name}
1350
+ end
1351
+ end
1352
+
1353
+ class MachineWithMultipleEventsTest < Test::Unit::TestCase
1354
+ def setup
1355
+ @klass = Class.new
1356
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1357
+ @park, @shift_down = @machine.event(:park, :shift_down) do
1358
+ transition :first_gear => :parked
1359
+ end
1360
+ end
1361
+
1362
+ def test_should_have_events
1363
+ assert_equal [@park, @shift_down], @machine.events.to_a
1364
+ end
1365
+
1366
+ def test_should_define_transitions_for_each_event
1367
+ [@park, @shift_down].each {|event| assert_equal 1, event.guards.size}
1368
+ end
1369
+
1370
+ def test_should_transition_the_same_for_each_event
1371
+ object = @klass.new
1372
+ object.state = 'first_gear'
1373
+ object.park
1374
+ assert_equal 'parked', object.state
1375
+
1376
+ object = @klass.new
1377
+ object.state = 'first_gear'
1378
+ object.shift_down
1379
+ assert_equal 'parked', object.state
1380
+ end
1381
+ end
1382
+
1383
+ class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
1384
+ def setup
1385
+ @klass = Class.new do
1386
+ attr_accessor :callbacks
1387
+ end
1388
+
1389
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1390
+ @event = @machine.event :ignite do
1391
+ transition :parked => :idling
1392
+ end
1393
+
1394
+ @object = @klass.new
1395
+ @object.callbacks = []
1396
+ end
1397
+
1398
+ def test_should_not_raise_exception_if_implicit_option_specified
1399
+ assert_nothing_raised {@machine.before_transition :invalid => :valid, :do => lambda {}}
1400
+ end
1401
+
1402
+ def test_should_raise_exception_if_method_not_specified
1403
+ exception = assert_raise(ArgumentError) {@machine.before_transition :to => :idling}
1404
+ assert_equal 'Method(s) for callback must be specified', exception.message
1405
+ end
1406
+
1407
+ def test_should_invoke_callbacks_during_transition
1408
+ @machine.before_transition lambda {|object| object.callbacks << 'before'}
1409
+ @machine.after_transition lambda {|object| object.callbacks << 'after'}
1410
+
1411
+ @event.fire(@object)
1412
+ assert_equal %w(before after), @object.callbacks
1413
+ end
1414
+
1415
+ def test_should_allow_multiple_callbacks
1416
+ @machine.before_transition lambda {|object| object.callbacks << 'before1'}, lambda {|object| object.callbacks << 'before2'}
1417
+ @machine.after_transition lambda {|object| object.callbacks << 'after1'}, lambda {|object| object.callbacks << 'after2'}
1418
+
1419
+ @event.fire(@object)
1420
+ assert_equal %w(before1 before2 after1 after2), @object.callbacks
1421
+ end
1422
+
1423
+ def test_should_allow_multiple_callbacks_with_requirements
1424
+ @machine.before_transition lambda {|object| object.callbacks << 'before_parked1'}, lambda {|object| object.callbacks << 'before_parked2'}, :from => :parked
1425
+ @machine.before_transition lambda {|object| object.callbacks << 'before_idling1'}, lambda {|object| object.callbacks << 'before_idling2'}, :from => :idling
1426
+ @machine.after_transition lambda {|object| object.callbacks << 'after_parked1'}, lambda {|object| object.callbacks << 'after_parked2'}, :from => :parked
1427
+ @machine.after_transition lambda {|object| object.callbacks << 'after_idling1'}, lambda {|object| object.callbacks << 'after_idling2'}, :from => :idling
1428
+
1429
+ @event.fire(@object)
1430
+ assert_equal %w(before_parked1 before_parked2 after_parked1 after_parked2), @object.callbacks
1431
+ end
1432
+
1433
+ def test_should_support_from_requirement
1434
+ @machine.before_transition :from => :parked, :do => lambda {|object| object.callbacks << :parked}
1435
+ @machine.before_transition :from => :idling, :do => lambda {|object| object.callbacks << :idling}
1436
+
1437
+ @event.fire(@object)
1438
+ assert_equal [:parked], @object.callbacks
1439
+ end
1440
+
1441
+ def test_should_support_except_from_requirement
1442
+ @machine.before_transition :except_from => :parked, :do => lambda {|object| object.callbacks << :parked}
1443
+ @machine.before_transition :except_from => :idling, :do => lambda {|object| object.callbacks << :idling}
1444
+
1445
+ @event.fire(@object)
1446
+ assert_equal [:idling], @object.callbacks
1447
+ end
1448
+
1449
+ def test_should_support_to_requirement
1450
+ @machine.before_transition :to => :parked, :do => lambda {|object| object.callbacks << :parked}
1451
+ @machine.before_transition :to => :idling, :do => lambda {|object| object.callbacks << :idling}
1452
+
1453
+ @event.fire(@object)
1454
+ assert_equal [:idling], @object.callbacks
1455
+ end
1456
+
1457
+ def test_should_support_except_to_requirement
1458
+ @machine.before_transition :except_to => :parked, :do => lambda {|object| object.callbacks << :parked}
1459
+ @machine.before_transition :except_to => :idling, :do => lambda {|object| object.callbacks << :idling}
1460
+
1461
+ @event.fire(@object)
1462
+ assert_equal [:parked], @object.callbacks
1463
+ end
1464
+
1465
+ def test_should_support_on_requirement
1466
+ @machine.before_transition :on => :park, :do => lambda {|object| object.callbacks << :park}
1467
+ @machine.before_transition :on => :ignite, :do => lambda {|object| object.callbacks << :ignite}
1468
+
1469
+ @event.fire(@object)
1470
+ assert_equal [:ignite], @object.callbacks
1471
+ end
1472
+
1473
+ def test_should_support_except_on_requirement
1474
+ @machine.before_transition :except_on => :park, :do => lambda {|object| object.callbacks << :park}
1475
+ @machine.before_transition :except_on => :ignite, :do => lambda {|object| object.callbacks << :ignite}
1476
+
1477
+ @event.fire(@object)
1478
+ assert_equal [:park], @object.callbacks
1479
+ end
1480
+
1481
+ def test_should_support_implicit_requirement
1482
+ @machine.before_transition :parked => :idling, :do => lambda {|object| object.callbacks << :parked}
1483
+ @machine.before_transition :idling => :parked, :do => lambda {|object| object.callbacks << :idling}
1484
+
1485
+ @event.fire(@object)
1486
+ assert_equal [:parked], @object.callbacks
1487
+ end
1488
+
1489
+ def test_should_track_states_defined_in_transition_callbacks
1490
+ @machine.before_transition :parked => :idling, :do => lambda {}
1491
+ @machine.after_transition :first_gear => :second_gear, :do => lambda {}
1492
+
1493
+ assert_equal [:parked, :idling, :first_gear, :second_gear], @machine.states.map {|state| state.name}
1494
+ end
1495
+
1496
+ def test_should_not_duplicate_states_defined_in_multiple_event_transitions
1497
+ @machine.before_transition :parked => :idling, :do => lambda {}
1498
+ @machine.after_transition :first_gear => :second_gear, :do => lambda {}
1499
+ @machine.after_transition :parked => :idling, :do => lambda {}
1500
+
1501
+ assert_equal [:parked, :idling, :first_gear, :second_gear], @machine.states.map {|state| state.name}
1502
+ end
1503
+
1504
+ def test_should_define_predicates_for_each_state
1505
+ [:parked?, :idling?].each {|predicate| assert @object.respond_to?(predicate)}
1506
+ end
1507
+ end
1508
+
1509
+ class MachineWithOwnerSubclassTest < Test::Unit::TestCase
1510
+ def setup
1511
+ @klass = Class.new
1512
+ @machine = StateMachine::Machine.new(@klass)
1513
+ @subclass = Class.new(@klass)
1514
+ end
1515
+
1516
+ def test_should_have_a_different_collection_of_state_machines
1517
+ assert_not_same @klass.state_machines, @subclass.state_machines
1518
+ end
1519
+
1520
+ def test_should_have_the_same_attribute_associated_state_machines
1521
+ assert_equal @klass.state_machines, @subclass.state_machines
1522
+ end
1523
+ end
1524
+
1525
+ class MachineWithExistingMachinesOnOwnerClassTest < Test::Unit::TestCase
1526
+ def setup
1527
+ @klass = Class.new
1528
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1529
+ @second_machine = StateMachine::Machine.new(@klass, :status, :initial => :idling)
1530
+ @object = @klass.new
1531
+ end
1532
+
1533
+ def test_should_track_each_state_machine
1534
+ expected = {:state => @machine, :status => @second_machine}
1535
+ assert_equal expected, @klass.state_machines
1536
+ end
1537
+
1538
+ def test_should_initialize_state_for_both_machines
1539
+ assert_equal 'parked', @object.state
1540
+ assert_equal 'idling', @object.status
1541
+ end
1542
+ end
1543
+
1544
+ class MachineWithExistingMachinesWithSameAttributesOnOwnerClassTest < Test::Unit::TestCase
1545
+ def setup
1546
+ @klass = Class.new
1547
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1548
+ @second_machine = StateMachine::Machine.new(@klass, :public_state, :attribute => :state)
1549
+ @object = @klass.new
1550
+ end
1551
+
1552
+ def test_should_track_each_state_machine
1553
+ expected = {:state => @machine, :public_state => @second_machine}
1554
+ assert_equal expected, @klass.state_machines
1555
+ end
1556
+
1557
+ def test_should_initialize_based_on_first_available_initial_state
1558
+ assert_equal 'parked', @object.state
1559
+ end
1560
+
1561
+ def test_should_allow_transitions_on_both_machines
1562
+ @machine.event :ignite do
1563
+ transition :parked => :idling
1564
+ end
1565
+
1566
+ @second_machine.event :park do
1567
+ transition :idling => :parked
1568
+ end
1569
+
1570
+ @object.ignite
1571
+ assert_equal 'idling', @object.state
1572
+
1573
+ @object.park
1574
+ assert_equal 'parked', @object.state
1575
+ end
1576
+ end
1577
+
1578
+ class MachineWithNamespaceTest < Test::Unit::TestCase
1579
+ def setup
1580
+ @klass = Class.new
1581
+ @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm', :initial => :active) do
1582
+ event :enable do
1583
+ transition :off => :active
1584
+ end
1585
+
1586
+ event :disable do
1587
+ transition :active => :off
1588
+ end
1589
+ end
1590
+ @object = @klass.new
1591
+ end
1592
+
1593
+ def test_should_namespace_state_predicates
1594
+ [:alarm_active?, :alarm_off?].each do |name|
1595
+ assert @object.respond_to?(name)
1596
+ end
1597
+ end
1598
+
1599
+ def test_should_namespace_event_checks
1600
+ [:can_enable_alarm?, :can_disable_alarm?].each do |name|
1601
+ assert @object.respond_to?(name)
1602
+ end
1603
+ end
1604
+
1605
+ def test_should_namespace_event_transition_readers
1606
+ [:enable_alarm_transition, :disable_alarm_transition].each do |name|
1607
+ assert @object.respond_to?(name)
1608
+ end
1609
+ end
1610
+
1611
+ def test_should_namespace_events
1612
+ [:enable_alarm, :disable_alarm].each do |name|
1613
+ assert @object.respond_to?(name)
1614
+ end
1615
+ end
1616
+
1617
+ def test_should_namespace_bang_events
1618
+ [:enable_alarm!, :disable_alarm!].each do |name|
1619
+ assert @object.respond_to?(name)
1620
+ end
1621
+ end
1622
+ end
1623
+
1624
+ class MachineWithCustomAttributeTest < Test::Unit::TestCase
1625
+ def setup
1626
+ StateMachine::Integrations.const_set('Custom', Module.new do
1627
+ class << self; attr_reader :defaults; end
1628
+ @defaults = {:action => :save, :use_transactions => false}
1629
+
1630
+ def create_with_scope(name)
1631
+ lambda {}
1632
+ end
1633
+
1634
+ def create_without_scope(name)
1635
+ lambda {}
1636
+ end
1637
+ end)
1638
+
1639
+ @klass = Class.new
1640
+ @machine = StateMachine::Machine.new(@klass, :state, :attribute => :state_id, :initial => :active, :integration => :custom) do
1641
+ event :ignite do
1642
+ transition :parked => :idling
1643
+ end
1644
+ end
1645
+ @object = @klass.new
1646
+ end
1647
+
1648
+ def test_should_define_a_reader_attribute_for_the_attribute
1649
+ assert @object.respond_to?(:state_id)
1650
+ end
1651
+
1652
+ def test_should_define_a_writer_attribute_for_the_attribute
1653
+ assert @object.respond_to?(:state_id=)
1654
+ end
1655
+
1656
+ def test_should_define_a_predicate_for_the_attribute
1657
+ assert @object.respond_to?(:state?)
1658
+ end
1659
+
1660
+ def test_should_define_a_name_reader_for_the_attribute
1661
+ assert @object.respond_to?(:state_name)
1662
+ end
1663
+
1664
+ def test_should_define_an_event_reader_for_the_attribute
1665
+ assert @object.respond_to?(:state_events)
1666
+ end
1667
+
1668
+ def test_should_define_a_transition_reader_for_the_attribute
1669
+ assert @object.respond_to?(:state_transitions)
1670
+ end
1671
+
1672
+ def test_should_define_singular_with_scope
1673
+ assert @klass.respond_to?(:with_state)
1674
+ end
1675
+
1676
+ def test_should_define_singular_without_scope
1677
+ assert @klass.respond_to?(:without_state)
1678
+ end
1679
+
1680
+ def test_should_define_plural_with_scope
1681
+ assert @klass.respond_to?(:with_states)
1682
+ end
1683
+
1684
+ def test_should_define_plural_without_scope
1685
+ assert @klass.respond_to?(:without_states)
1686
+ end
1687
+
1688
+ def test_should_define_state_machines_reader
1689
+ expected = {:state => @machine}
1690
+ assert_equal expected, @klass.state_machines
1691
+ end
1692
+
1693
+ def teardown
1694
+ StateMachine::Integrations.send(:remove_const, 'Custom')
1695
+ end
1696
+ end
1697
+
1698
+ class MachineFinderWithoutExistingMachineTest < Test::Unit::TestCase
1699
+ def setup
1700
+ @klass = Class.new
1701
+ @machine = StateMachine::Machine.find_or_create(@klass)
1702
+ end
1703
+
1704
+ def test_should_accept_a_block
1705
+ called = false
1706
+ StateMachine::Machine.find_or_create(Class.new) do
1707
+ called = respond_to?(:event)
1708
+ end
1709
+
1710
+ assert called
1711
+ end
1712
+
1713
+ def test_should_create_a_new_machine
1714
+ assert_not_nil @machine
1715
+ end
1716
+
1717
+ def test_should_use_default_state
1718
+ assert_equal :state, @machine.attribute
1719
+ end
1720
+ end
1721
+
1722
+ class MachineFinderWithExistingOnSameClassTest < Test::Unit::TestCase
1723
+ def setup
1724
+ @klass = Class.new
1725
+ @existing_machine = StateMachine::Machine.new(@klass)
1726
+ @machine = StateMachine::Machine.find_or_create(@klass)
1727
+ end
1728
+
1729
+ def test_should_accept_a_block
1730
+ called = false
1731
+ StateMachine::Machine.find_or_create(@klass) do
1732
+ called = respond_to?(:event)
1733
+ end
1734
+
1735
+ assert called
1736
+ end
1737
+
1738
+ def test_should_not_create_a_new_machine
1739
+ assert_same @machine, @existing_machine
1740
+ end
1741
+ end
1742
+
1743
+ class MachineFinderWithExistingMachineOnSuperclassTest < Test::Unit::TestCase
1744
+ def setup
1745
+ integration = Module.new do
1746
+ def self.matches?(klass)
1747
+ false
1748
+ end
1749
+ end
1750
+ StateMachine::Integrations.const_set('Custom', integration)
1751
+
1752
+ @base_class = Class.new
1753
+ @base_machine = StateMachine::Machine.new(@base_class, :status, :action => :save, :integration => :custom)
1754
+ @base_machine.event(:ignite) {}
1755
+ @base_machine.before_transition(lambda {})
1756
+ @base_machine.after_transition(lambda {})
1757
+
1758
+ @klass = Class.new(@base_class)
1759
+ @machine = StateMachine::Machine.find_or_create(@klass, :status) {}
1760
+ end
1761
+
1762
+ def test_should_accept_a_block
1763
+ called = false
1764
+ StateMachine::Machine.find_or_create(Class.new(@base_class)) do
1765
+ called = respond_to?(:event)
1766
+ end
1767
+
1768
+ assert called
1769
+ end
1770
+
1771
+ def test_should_not_create_a_new_machine_if_no_block_or_options
1772
+ machine = StateMachine::Machine.find_or_create(Class.new(@base_class), :status)
1773
+
1774
+ assert_same machine, @base_machine
1775
+ end
1776
+
1777
+ def test_should_create_a_new_machine_if_given_options
1778
+ machine = StateMachine::Machine.find_or_create(@klass, :status, :initial => :parked)
1779
+
1780
+ assert_not_nil machine
1781
+ assert_not_same machine, @base_machine
1782
+ end
1783
+
1784
+ def test_should_create_a_new_machine_if_given_block
1785
+ assert_not_nil @machine
1786
+ assert_not_same @machine, @base_machine
1787
+ end
1788
+
1789
+ def test_should_copy_the_base_attribute
1790
+ assert_equal :status, @machine.attribute
1791
+ end
1792
+
1793
+ def test_should_copy_the_base_configuration
1794
+ assert_equal :save, @machine.action
1795
+ end
1796
+
1797
+ def test_should_copy_events
1798
+ # Can't assert equal arrays since their machines change
1799
+ assert_equal 1, @machine.events.length
1800
+ end
1801
+
1802
+ def test_should_copy_before_callbacks
1803
+ assert_equal @base_machine.callbacks[:before], @machine.callbacks[:before]
1804
+ end
1805
+
1806
+ def test_should_copy_after_transitions
1807
+ assert_equal @base_machine.callbacks[:after], @machine.callbacks[:after]
1808
+ end
1809
+
1810
+ def test_should_use_the_same_integration
1811
+ assert (class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom)
1812
+ end
1813
+
1814
+ def teardown
1815
+ StateMachine::Integrations.send(:remove_const, 'Custom')
1816
+ end
1817
+ end
1818
+
1819
+ class MachineFinderCustomOptionsTest < Test::Unit::TestCase
1820
+ def setup
1821
+ @klass = Class.new
1822
+ @machine = StateMachine::Machine.find_or_create(@klass, :status, :initial => :parked)
1823
+ @object = @klass.new
1824
+ end
1825
+
1826
+ def test_should_use_custom_attribute
1827
+ assert_equal :status, @machine.attribute
1828
+ end
1829
+
1830
+ def test_should_set_custom_initial_state
1831
+ assert_equal :parked, @machine.initial_state(@object).name
1832
+ end
1833
+ end
1834
+
1835
+ begin
1836
+ # Load library
1837
+ require 'rubygems'
1838
+ require 'graphviz'
1839
+
1840
+ class MachineDrawingTest < Test::Unit::TestCase
1841
+ def setup
1842
+ @klass = Class.new do
1843
+ def self.name; 'Vehicle'; end
1844
+ end
1845
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1846
+ @machine.event :ignite do
1847
+ transition :parked => :idling
1848
+ end
1849
+ end
1850
+
1851
+ def test_should_raise_exception_if_invalid_option_specified
1852
+ assert_raise(ArgumentError) {@machine.draw(:invalid => true)}
1853
+ end
1854
+
1855
+ def test_should_save_file_with_class_name_by_default
1856
+ graph = @machine.draw(:output => false)
1857
+ assert_equal './Vehicle_state.png', graph.instance_variable_get('@filename')
1858
+ end
1859
+
1860
+ def test_should_allow_base_name_to_be_customized
1861
+ graph = @machine.draw(:name => 'machine', :output => false)
1862
+ assert_equal './machine.png', graph.instance_variable_get('@filename')
1863
+ end
1864
+
1865
+ def test_should_allow_format_to_be_customized
1866
+ graph = @machine.draw(:format => 'jpg', :output => false)
1867
+ assert_equal './Vehicle_state.jpg', graph.instance_variable_get('@filename')
1868
+ assert_equal 'jpg', graph.instance_variable_get('@format')
1869
+ end
1870
+
1871
+ def test_should_allow_path_to_be_customized
1872
+ graph = @machine.draw(:path => "#{File.dirname(__FILE__)}/", :output => false)
1873
+ assert_equal "#{File.dirname(__FILE__)}/Vehicle_state.png", graph.instance_variable_get('@filename')
1874
+ end
1875
+
1876
+ def test_should_allow_orientation_to_be_landscape
1877
+ graph = @machine.draw(:orientation => 'landscape', :output => false)
1878
+ assert_equal 'LR', graph['rankdir']
1879
+ end
1880
+
1881
+ def test_should_allow_orientation_to_be_portrait
1882
+ graph = @machine.draw(:orientation => 'portrait', :output => false)
1883
+ assert_equal 'TB', graph['rankdir']
1884
+ end
1885
+ end
1886
+
1887
+ class MachineDrawingWithIntegerStatesTest < Test::Unit::TestCase
1888
+ def setup
1889
+ @klass = Class.new do
1890
+ def self.name; 'Vehicle'; end
1891
+ end
1892
+ @machine = StateMachine::Machine.new(@klass, :state_id, :initial => :parked)
1893
+ @machine.event :ignite do
1894
+ transition :parked => :idling
1895
+ end
1896
+ @machine.state :parked, :value => 1
1897
+ @machine.state :idling, :value => 2
1898
+ @graph = @machine.draw
1899
+ end
1900
+
1901
+ def test_should_draw_all_states
1902
+ assert_equal 3, @graph.node_count
1903
+ end
1904
+
1905
+ def test_should_draw_all_events
1906
+ assert_equal 2, @graph.edge_count
1907
+ end
1908
+
1909
+ def test_should_draw_machine
1910
+ assert File.exist?('./Vehicle_state_id.png')
1911
+ ensure
1912
+ FileUtils.rm('./Vehicle_state_id.png')
1913
+ end
1914
+ end
1915
+
1916
+ class MachineDrawingWithNilStatesTest < Test::Unit::TestCase
1917
+ def setup
1918
+ @klass = Class.new do
1919
+ def self.name; 'Vehicle'; end
1920
+ end
1921
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1922
+ @machine.event :ignite do
1923
+ transition :parked => :idling
1924
+ end
1925
+ @machine.state :parked, :value => nil
1926
+ @graph = @machine.draw
1927
+ end
1928
+
1929
+ def test_should_draw_all_states
1930
+ assert_equal 3, @graph.node_count
1931
+ end
1932
+
1933
+ def test_should_draw_all_events
1934
+ assert_equal 2, @graph.edge_count
1935
+ end
1936
+
1937
+ def test_should_draw_machine
1938
+ assert File.exist?('./Vehicle_state.png')
1939
+ ensure
1940
+ FileUtils.rm('./Vehicle_state.png')
1941
+ end
1942
+ end
1943
+
1944
+ class MachineDrawingWithDynamicStatesTest < Test::Unit::TestCase
1945
+ def setup
1946
+ @klass = Class.new do
1947
+ def self.name; 'Vehicle'; end
1948
+ end
1949
+ @machine = StateMachine::Machine.new(@klass, :initial => :parked)
1950
+ @machine.event :activate do
1951
+ transition :parked => :idling
1952
+ end
1953
+ @machine.state :idling, :value => lambda {Time.now}
1954
+ @graph = @machine.draw
1955
+ end
1956
+
1957
+ def test_should_draw_all_states
1958
+ assert_equal 3, @graph.node_count
1959
+ end
1960
+
1961
+ def test_should_draw_all_events
1962
+ assert_equal 2, @graph.edge_count
1963
+ end
1964
+
1965
+ def test_should_draw_machine
1966
+ assert File.exist?('./Vehicle_state.png')
1967
+ ensure
1968
+ FileUtils.rm('./Vehicle_state.png')
1969
+ end
1970
+ end
1971
+
1972
+ class MachineClassDrawingTest < Test::Unit::TestCase
1973
+ def setup
1974
+ @klass = Class.new do
1975
+ def self.name; 'Vehicle'; end
1976
+ end
1977
+ @machine = StateMachine::Machine.new(@klass)
1978
+ @machine.event :ignite do
1979
+ transition :parked => :idling
1980
+ end
1981
+ end
1982
+
1983
+ def test_should_raise_exception_if_no_class_names_specified
1984
+ exception = assert_raise(ArgumentError) {StateMachine::Machine.draw(nil)}
1985
+ assert_equal 'At least one class must be specified', exception.message
1986
+ end
1987
+
1988
+ def test_should_load_files
1989
+ StateMachine::Machine.draw('Switch', :file => "#{File.dirname(__FILE__)}/../classes/switch.rb")
1990
+ assert defined?(::Switch)
1991
+ ensure
1992
+ FileUtils.rm('./Switch_state.png')
1993
+ end
1994
+
1995
+ def test_should_allow_path_and_format_to_be_customized
1996
+ StateMachine::Machine.draw('Switch', :file => "#{File.dirname(__FILE__)}/../classes/switch.rb", :path => "#{File.dirname(__FILE__)}/", :format => 'jpg')
1997
+ assert File.exist?("#{File.dirname(__FILE__)}/Switch_state.jpg")
1998
+ ensure
1999
+ FileUtils.rm("#{File.dirname(__FILE__)}/Switch_state.jpg")
2000
+ end
2001
+ end
2002
+ rescue LoadError
2003
+ $stderr.puts 'Skipping GraphViz StateMachine::Machine tests. `gem install ruby-graphviz` and try again.'
2004
+ end