mattscilipoti-state_machine 0.8.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/CHANGELOG.rdoc +298 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +474 -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/tasks.rb +30 -0
  51. data/lib/state_machine/transition.rb +394 -0
  52. data/tasks/state_machine.rake +1 -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 +1367 -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 +155 -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