state_machine 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGELOG.rdoc +26 -0
  2. data/README.rdoc +254 -46
  3. data/Rakefile +29 -3
  4. data/examples/AutoShop_state.png +0 -0
  5. data/examples/Car_state.jpg +0 -0
  6. data/examples/Vehicle_state.png +0 -0
  7. data/lib/state_machine.rb +161 -116
  8. data/lib/state_machine/assertions.rb +21 -0
  9. data/lib/state_machine/callback.rb +168 -0
  10. data/lib/state_machine/eval_helpers.rb +67 -0
  11. data/lib/state_machine/event.rb +135 -101
  12. data/lib/state_machine/extensions.rb +83 -0
  13. data/lib/state_machine/guard.rb +115 -0
  14. data/lib/state_machine/integrations/active_record.rb +242 -0
  15. data/lib/state_machine/integrations/data_mapper.rb +198 -0
  16. data/lib/state_machine/integrations/data_mapper/observer.rb +153 -0
  17. data/lib/state_machine/integrations/sequel.rb +169 -0
  18. data/lib/state_machine/machine.rb +746 -352
  19. data/lib/state_machine/transition.rb +104 -212
  20. data/test/active_record.log +34865 -0
  21. data/test/classes/switch.rb +11 -0
  22. data/test/data_mapper.log +14015 -0
  23. data/test/functional/state_machine_test.rb +249 -15
  24. data/test/sequel.log +3835 -0
  25. data/test/test_helper.rb +3 -12
  26. data/test/unit/assertions_test.rb +13 -0
  27. data/test/unit/callback_test.rb +189 -0
  28. data/test/unit/eval_helpers_test.rb +92 -0
  29. data/test/unit/event_test.rb +247 -113
  30. data/test/unit/guard_test.rb +420 -0
  31. data/test/unit/integrations/active_record_test.rb +515 -0
  32. data/test/unit/integrations/data_mapper_test.rb +407 -0
  33. data/test/unit/integrations/sequel_test.rb +244 -0
  34. data/test/unit/invalid_transition_test.rb +1 -1
  35. data/test/unit/machine_test.rb +1056 -98
  36. data/test/unit/state_machine_test.rb +14 -113
  37. data/test/unit/transition_test.rb +269 -495
  38. metadata +44 -30
  39. data/test/app_root/app/models/auto_shop.rb +0 -34
  40. data/test/app_root/app/models/car.rb +0 -19
  41. data/test/app_root/app/models/highway.rb +0 -3
  42. data/test/app_root/app/models/motorcycle.rb +0 -3
  43. data/test/app_root/app/models/switch.rb +0 -23
  44. data/test/app_root/app/models/switch_observer.rb +0 -20
  45. data/test/app_root/app/models/toggle_switch.rb +0 -2
  46. data/test/app_root/app/models/vehicle.rb +0 -78
  47. data/test/app_root/config/environment.rb +0 -7
  48. data/test/app_root/db/migrate/001_create_switches.rb +0 -12
  49. data/test/app_root/db/migrate/002_create_auto_shops.rb +0 -13
  50. data/test/app_root/db/migrate/003_create_highways.rb +0 -11
  51. data/test/app_root/db/migrate/004_create_vehicles.rb +0 -16
  52. data/test/factory.rb +0 -77
@@ -2,6 +2,6 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
 
3
3
  class InvalidTransitionTest < Test::Unit::TestCase
4
4
  def test_should_exist
5
- assert_not_nil PluginAWeek::StateMachine::InvalidTransition
5
+ assert_not_nil StateMachine::InvalidTransition
6
6
  end
7
7
  end
@@ -2,7 +2,13 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
 
3
3
  class MachineByDefaultTest < Test::Unit::TestCase
4
4
  def setup
5
- @machine = PluginAWeek::StateMachine::Machine.new(Switch)
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
6
12
  end
7
13
 
8
14
  def test_should_have_an_attribute
@@ -10,77 +16,299 @@ class MachineByDefaultTest < Test::Unit::TestCase
10
16
  end
11
17
 
12
18
  def test_should_not_have_an_initial_state
13
- assert_nil @machine.initial_state(new_switch)
14
- end
15
-
16
- def test_should_have_an_owner_class
17
- assert_equal Switch, @machine.owner_class
19
+ assert_nil @machine.initial_state(@object)
18
20
  end
19
21
 
20
22
  def test_should_not_have_any_events
21
23
  assert @machine.events.empty?
22
24
  end
23
25
 
26
+ def test_should_not_have_any_before_callbacks
27
+ assert @machine.callbacks[:before].empty?
28
+ end
29
+
30
+ def test_should_not_have_any_after_callbacks
31
+ assert @machine.callbacks[:after].empty?
32
+ end
33
+
34
+ def test_should_not_have_an_action
35
+ assert_nil @machine.action
36
+ end
37
+
24
38
  def test_should_not_have_any_states
25
39
  assert @machine.states.empty?
26
40
  end
41
+
42
+ def test_should_not_be_extended_by_the_active_record_integration
43
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::ActiveRecord)
44
+ end
45
+
46
+ def test_should_not_be_extended_by_the_datamapper_integration
47
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::DataMapper)
48
+ end
49
+
50
+ def test_should_not_be_extended_by_the_sequel_integration
51
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Sequel)
52
+ end
53
+
54
+ def test_should_define_a_reader_attribute_for_the_attribute
55
+ assert @object.respond_to?(:state)
56
+ end
57
+
58
+ def test_should_define_a_writer_attribute_for_the_attribute
59
+ assert @object.respond_to?(:state=)
60
+ end
61
+
62
+ def test_should_define_a_predicate_for_the_attribute
63
+ assert @object.respond_to?(:state?)
64
+ end
65
+
66
+ def test_should_not_define_singular_with_scope
67
+ assert !@klass.respond_to?(:with_state)
68
+ end
69
+
70
+ def test_should_not_define_singular_without_scope
71
+ assert !@klass.respond_to?(:without_state)
72
+ end
73
+
74
+ def test_should_not_define_plural_with_scope
75
+ assert !@klass.respond_to?(:with_states)
76
+ end
77
+
78
+ def test_should_not_define_plural_without_scope
79
+ assert !@klass.respond_to?(:without_states)
80
+ end
81
+
82
+ def test_should_extend_owner_class_with_class_methods
83
+ assert (class << @klass; ancestors; end).include?(StateMachine::ClassMethods)
84
+ end
85
+
86
+ def test_should_include_instance_methods_in_owner_class
87
+ assert @klass.included_modules.include?(StateMachine::InstanceMethods)
88
+ end
89
+
90
+ def test_should_define_state_machines_reader
91
+ expected = {'state' => @machine}
92
+ assert_equal expected, @klass.state_machines
93
+ end
94
+ end
95
+
96
+ class MachineWithCustomAttributeTest < Test::Unit::TestCase
97
+ def setup
98
+ @klass = Class.new
99
+ @machine = StateMachine::Machine.new(@klass, 'status')
100
+ @object = @klass.new
101
+ end
102
+
103
+ def test_should_use_custom_attribute
104
+ assert_equal 'status', @machine.attribute
105
+ end
106
+
107
+ def test_should_define_a_reader_attribute_for_the_attribute
108
+ assert @object.respond_to?(:status)
109
+ end
110
+
111
+ def test_should_define_a_writer_attribute_for_the_attribute
112
+ assert @object.respond_to?(:status=)
113
+ end
114
+
115
+ def test_should_define_a_predicate_for_the_attribute
116
+ assert @object.respond_to?(:status?)
117
+ end
27
118
  end
28
119
 
29
- class MachineWithInitialStateTest < Test::Unit::TestCase
120
+ class MachineWithStaticInitialStateTest < Test::Unit::TestCase
30
121
  def setup
31
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => 'off')
122
+ @klass = Class.new do
123
+ def initialize(attributes = {})
124
+ attributes.each {|attr, value| send("#{attr}=", value)}
125
+ end
126
+ end
127
+
128
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
32
129
  end
33
130
 
34
131
  def test_should_have_an_initial_state
35
- assert_equal 'off', @machine.initial_state(new_switch)
132
+ object = @klass.new
133
+ assert_equal 'off', @machine.initial_state(object)
134
+ end
135
+
136
+ def test_should_set_initial_state_if_existing_is_nil
137
+ object = @klass.new(:state => nil)
138
+ assert_equal 'off', object.state
139
+ end
140
+
141
+ def test_should_set_initial_state_if_existing_is_empty
142
+ object = @klass.new(:state => '')
143
+ assert_equal 'off', object.state
144
+ end
145
+
146
+ def test_should_not_set_initial_state_if_existing_is_not_empty
147
+ object = @klass.new(:state => 'on')
148
+ assert_equal 'on', object.state
149
+ end
150
+
151
+ def test_should_be_included_in_known_states
152
+ assert_equal %w(off), @machine.states
36
153
  end
37
154
  end
38
155
 
39
156
  class MachineWithDynamicInitialStateTest < Test::Unit::TestCase
40
157
  def setup
41
- @initial_state = lambda {|switch| switch.initial_state}
42
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state', :initial => @initial_state)
158
+ @klass = Class.new do
159
+ attr_accessor :initial_state
160
+ end
161
+ @machine = StateMachine::Machine.new(@klass, :initial => lambda {|object| object.initial_state || 'default'})
162
+ @object = @klass.new
43
163
  end
44
164
 
45
165
  def test_should_use_the_record_for_determining_the_initial_state
46
- assert_equal 'off', @machine.initial_state(new_switch(:initial_state => 'off'))
47
- assert_equal 'on', @machine.initial_state(new_switch(:initial_state => 'on'))
166
+ @object.initial_state = 'off'
167
+ assert_equal 'off', @machine.initial_state(@object)
168
+
169
+ @object.initial_state = 'on'
170
+ assert_equal 'on', @machine.initial_state(@object)
171
+ end
172
+
173
+ def test_should_set_initial_state_on_created_object
174
+ assert_equal 'default', @object.state
175
+ end
176
+
177
+ def test_should_not_be_included_in_known_states
178
+ assert_equal [], @machine.states
179
+ end
180
+ end
181
+
182
+ class MachineWithCustomActionTest < Test::Unit::TestCase
183
+ def setup
184
+ @machine = StateMachine::Machine.new(Class.new, :action => :save)
185
+ end
186
+
187
+ def test_should_use_the_custom_action
188
+ assert_equal :save, @machine.action
189
+ end
190
+ end
191
+
192
+ class MachineWithNilActionTest < Test::Unit::TestCase
193
+ def setup
194
+ integration = Module.new do
195
+ def default_action
196
+ :save
197
+ end
198
+ end
199
+ StateMachine::Integrations.const_set('Custom', integration)
200
+ @machine = StateMachine::Machine.new(Class.new, :action => nil, :integration => :custom)
201
+ end
202
+
203
+ def test_should_have_a_nil_action
204
+ assert_nil @machine.action
205
+ end
206
+
207
+ def teardown
208
+ StateMachine::Integrations.send(:remove_const, 'Custom')
209
+ end
210
+ end
211
+
212
+ class MachineWithCustomIntegrationTest < Test::Unit::TestCase
213
+ def setup
214
+ StateMachine::Integrations.const_set('Custom', Module.new)
215
+ @machine = StateMachine::Machine.new(Class.new, :integration => :custom)
216
+ end
217
+
218
+ def test_should_be_extended_by_the_integration
219
+ assert (class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom)
220
+ end
221
+
222
+ def teardown
223
+ StateMachine::Integrations.send(:remove_const, 'Custom')
48
224
  end
49
225
  end
50
226
 
51
227
  class MachineTest < Test::Unit::TestCase
228
+ def test_should_raise_exception_if_invalid_option_specified
229
+ assert_raise(ArgumentError) {StateMachine::Machine.new(Class.new, :invalid => true)}
230
+ end
231
+ end
232
+
233
+ class MachineWithoutIntegrationTest < Test::Unit::TestCase
52
234
  def setup
53
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
235
+ @klass = Class.new
236
+ @machine = StateMachine::Machine.new(@klass)
237
+ @object = @klass.new
54
238
  end
55
239
 
56
- def test_should_define_a_named_scope_for_the_attribute
57
- on = create_switch(:state => 'on')
58
- off = create_switch(:state => 'off')
240
+ def test_transaction_should_yield
241
+ @yielded = false
242
+ @machine.within_transaction(@object) do
243
+ @yielded = true
244
+ end
59
245
 
60
- assert_equal [on], Switch.with_state('on')
246
+ assert @yielded
61
247
  end
62
-
63
- def test_should_define_a_pluralized_named_scope_for_the_attribute
64
- on = create_switch(:state => 'on')
65
- off = create_switch(:state => 'off')
248
+ end
249
+
250
+ class MachineWithIntegrationTest < Test::Unit::TestCase
251
+ def setup
252
+ @integration = Module.new do
253
+ class << self; attr_accessor :initialized, :with_scopes, :without_scopes; end
254
+ @initialized = false
255
+ @with_scopes = []
256
+ @without_scopes = []
257
+
258
+ def after_initialize
259
+ StateMachine::Integrations::Custom.initialized = true
260
+ end
261
+
262
+ def default_action
263
+ :save
264
+ end
265
+
266
+ def define_with_scope(name)
267
+ StateMachine::Integrations::Custom.with_scopes << name
268
+ end
269
+
270
+ def define_without_scope(name)
271
+ StateMachine::Integrations::Custom.without_scopes << name
272
+ end
273
+ end
66
274
 
67
- assert_equal [on, off], Switch.with_states('on', 'off')
275
+ StateMachine::Integrations.const_set('Custom', @integration)
276
+ @machine = StateMachine::Machine.new(Class.new, :integration => :custom)
68
277
  end
69
278
 
70
- def test_should_raise_exception_if_invalid_option_specified
71
- assert_raise(ArgumentError) {PluginAWeek::StateMachine::Machine.new(Switch, 'state', :invalid => true)}
279
+ def test_should_call_after_initialize_hook
280
+ assert @integration.initialized
281
+ end
282
+
283
+ def test_should_use_the_default_action
284
+ assert_equal :save, @machine.action
72
285
  end
73
286
 
74
- def test_should_symbolize_attribute
75
- machine = PluginAWeek::StateMachine::Machine.new(Switch, :state)
76
- assert_equal 'state', machine.attribute
287
+ def test_should_use_the_custom_action_if_specified
288
+ machine = StateMachine::Machine.new(Class.new, :integration => :custom, :action => :save!)
289
+ assert_equal :save!, machine.action
290
+ end
291
+
292
+ def test_should_define_a_singular_and_plural_with_scope
293
+ assert_equal %w(with_state with_states), @integration.with_scopes
294
+ end
295
+
296
+ def test_should_define_a_singular_and_plural_without_scope
297
+ assert_equal %w(without_state without_states), @integration.without_scopes
298
+ end
299
+
300
+ def teardown
301
+ StateMachine::Integrations.send(:remove_const, 'Custom')
77
302
  end
78
303
  end
79
304
 
80
305
  class MachineAfterBeingCopiedTest < Test::Unit::TestCase
81
306
  def setup
82
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
307
+ @machine = StateMachine::Machine.new(Class.new, 'state')
83
308
  @machine.event(:turn_on) {}
309
+ @machine.before_transition(lambda {})
310
+ @machine.after_transition(lambda {})
311
+ @machine.states # Caches the states variable
84
312
 
85
313
  @copied_machine = @machine.dup
86
314
  end
@@ -104,65 +332,238 @@ class MachineAfterBeingCopiedTest < Test::Unit::TestCase
104
332
  def test_should_not_update_machine_for_original_event
105
333
  assert_equal @machine, @machine.events['turn_on'].machine
106
334
  end
335
+
336
+ def test_should_not_have_the_same_callbacks
337
+ assert_not_same @copied_machine.callbacks, @machine.callbacks
338
+ end
339
+
340
+ def test_should_not_have_the_same_before_callbacks
341
+ assert_not_same @copied_machine.callbacks[:before], @machine.callbacks[:before]
342
+ end
343
+
344
+ def test_should_not_have_the_same_after_callbacks
345
+ assert_not_same @copied_machine.callbacks[:after], @machine.callbacks[:after]
346
+ end
107
347
  end
108
348
 
109
349
  class MachineAfterChangingContextTest < Test::Unit::TestCase
110
350
  def setup
111
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
351
+ @original_class = Class.new
352
+ @machine = StateMachine::Machine.new(@original_class, 'state')
353
+
354
+ @new_class = Class.new(@original_class)
355
+ @new_machine = @machine.within_context(@new_class)
356
+
357
+ @object = @new_class.new
112
358
  end
113
359
 
114
360
  def test_should_create_copy_of_machine
115
- new_machine = @machine.within_context(ToggleSwitch)
116
- assert_not_same @machine, new_machine
361
+ assert_not_same @machine, @new_machine
117
362
  end
118
363
 
119
- def test_should_update_owner_clas
120
- new_machine = @machine.within_context(ToggleSwitch)
121
- assert_equal ToggleSwitch, new_machine.owner_class
364
+ def test_should_update_owner_class
365
+ assert_equal @new_class, @new_machine.owner_class
122
366
  end
123
367
 
124
- def test_should_update_initial_state
125
- new_machine = @machine.within_context(ToggleSwitch, :initial => 'off')
126
- assert_equal 'off', new_machine.initial_state(new_switch)
368
+ def test_should_not_change_original_owner_class
369
+ assert_equal @original_class, @machine.owner_class
370
+ end
371
+
372
+ def test_should_allow_changing_the_initial_state
373
+ new_machine = @machine.within_context(@new_class, :initial => 'off')
374
+ assert_equal 'off', new_machine.initial_state(@object)
375
+ end
376
+
377
+ def test_should_not_change_original_initial_state_if_updated
378
+ new_machine = @machine.within_context(@new_class, :initial => 'off')
379
+ assert_nil @machine.initial_state(@object)
127
380
  end
128
381
 
129
382
  def test_should_not_update_initial_state_if_not_provided
130
- new_machine = @machine.within_context(ToggleSwitch)
131
- assert_nil new_machine.initial_state(new_switch)
383
+ assert_nil @new_machine.initial_state(@object)
384
+ end
385
+
386
+ def test_should_allow_changing_the_integration
387
+ StateMachine::Integrations.const_set('Custom', Module.new)
388
+ new_machine = @machine.within_context(@new_class, :integration => :custom)
389
+ assert (class << new_machine; ancestors; end).include?(StateMachine::Integrations::Custom)
390
+ end
391
+
392
+ def test_should_not_change_original_integration_if_updated
393
+ StateMachine::Integrations.const_set('Custom', Module.new)
394
+ new_machine = @machine.within_context(@new_class, :integration => :custom)
395
+ assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom)
396
+ end
397
+
398
+ def test_should_change_the_associated_machine_in_the_new_class
399
+ assert_equal @new_machine, @new_class.state_machines['state']
400
+ end
401
+
402
+ def test_should_not_change_the_associated_machine_in_the_original_class
403
+ assert_equal @machine, @original_class.state_machines['state']
132
404
  end
133
405
 
134
406
  def test_raise_exception_if_invalid_option_specified
135
- assert_raise(ArgumentError) {@machine.within_context(ToggleSwitch, :invalid => true)}
407
+ assert_raise(ArgumentError) {@machine.within_context(@new_class, :invalid => true)}
408
+ end
409
+
410
+ def teardown
411
+ StateMachine::Integrations.send(:remove_const, 'Custom') if StateMachine::Integrations.const_defined?('Custom')
136
412
  end
137
413
  end
138
414
 
139
- class MachineWithConflictingNamedScopesTest < Test::Unit::TestCase
140
- class Switch < ActiveRecord::Base
141
- def self.with_state
142
- :custom
415
+ class MachineWithConflictingAttributeAccessorsTest < Test::Unit::TestCase
416
+ def setup
417
+ @klass = Class.new do
418
+ attr_accessor :status
419
+
420
+ def state
421
+ status
422
+ end
423
+
424
+ def state=(value)
425
+ self.status = value
426
+ end
427
+
428
+ def state?
429
+ true
430
+ end
143
431
  end
144
-
145
- def self.with_states
146
- :custom
432
+ @machine = StateMachine::Machine.new(@klass)
433
+ @object = @klass.new
434
+ end
435
+
436
+ def test_should_not_define_attribute_reader
437
+ @object.status = 'on'
438
+ assert_equal 'on', @object.state
439
+ end
440
+
441
+ def test_should_not_define_attribute_writer
442
+ @object.state = 'on'
443
+ assert_equal 'on', @object.status
444
+ end
445
+
446
+ def test_should_not_define_attribute_predicate
447
+ assert @object.state?
448
+ end
449
+ end
450
+
451
+ class MachineWithConflictingPrivateAttributeAccessorsTest < Test::Unit::TestCase
452
+ def setup
453
+ @klass = Class.new do
454
+ attr_accessor :status
455
+
456
+ private
457
+ def state
458
+ status
459
+ end
460
+
461
+ def state=(value)
462
+ self.status = value
463
+ end
464
+
465
+ def state?
466
+ true
467
+ end
147
468
  end
469
+ @machine = StateMachine::Machine.new(@klass)
470
+ @object = @klass.new
471
+ end
472
+
473
+ def test_should_not_define_attribute_reader
474
+ @object.status = 'on'
475
+ assert_equal 'on', @object.send(:state)
476
+ end
477
+
478
+ def test_should_not_define_attribute_writer
479
+ @object.send(:state=, 'on')
480
+ assert_equal 'on', @object.status
148
481
  end
149
482
 
483
+ def test_should_not_define_attribute_predicate
484
+ assert @object.send(:state?)
485
+ end
486
+ end
487
+
488
+ class MachineWithConflictingStatePredicatesTest < Test::Unit::TestCase
150
489
  def setup
151
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
490
+ @klass = Class.new do
491
+ def on?
492
+ true
493
+ end
494
+
495
+ def off?
496
+ true
497
+ end
498
+ end
499
+ @machine = StateMachine::Machine.new(@klass)
500
+ @machine.before_transition :to => 'on', :from => 'off', :do => lambda {}
501
+ @object = @klass.new
502
+ end
503
+
504
+ def test_should_not_define_state_predicates
505
+ assert @object.on?
506
+ assert @object.off?
507
+ end
508
+ end
509
+
510
+ class MachineWithConflictingScopesTest < Test::Unit::TestCase
511
+ def setup
512
+ @klass = Class.new do
513
+ def self.with_state
514
+ :with_state
515
+ end
516
+
517
+ def self.with_states
518
+ :with_states
519
+ end
520
+
521
+ def self.without_state
522
+ :without_state
523
+ end
524
+
525
+ def self.without_states
526
+ :without_states
527
+ end
528
+ end
529
+
530
+ integration = Module.new do
531
+ def define_with_scope(name)
532
+ raise ArgumentError, 'should not define a with scope'
533
+ end
534
+
535
+ def define_without_scope(name)
536
+ raise ArgumentError, 'should not define a without scope'
537
+ end
538
+ end
539
+ StateMachine::Integrations.const_set('Custom', integration)
540
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
152
541
  end
153
542
 
154
- def test_should_not_define_a_named_scope_for_the_attribute
155
- assert_equal :custom, Switch.with_state
543
+ def test_should_not_define_singular_with_scope
544
+ assert_equal :with_state, @klass.with_state
156
545
  end
157
546
 
158
- def test_should_not_define_a_pluralized_named_scope_for_the_attribute
159
- assert_equal :custom, Switch.with_states
547
+ def test_should_not_define_plural_with_scope
548
+ assert_equal :with_states, @klass.with_states
549
+ end
550
+
551
+ def test_should_not_define_singular_without_scope
552
+ assert_equal :without_state, @klass.without_state
553
+ end
554
+
555
+ def test_should_not_define_plural_without_scope
556
+ assert_equal :without_states, @klass.without_states
557
+ end
558
+
559
+ def teardown
560
+ StateMachine::Integrations.send(:remove_const, 'Custom')
160
561
  end
161
562
  end
162
563
 
163
564
  class MachineWithEventsTest < Test::Unit::TestCase
164
565
  def setup
165
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
566
+ @machine = StateMachine::Machine.new(Class.new)
166
567
  end
167
568
 
168
569
  def test_should_create_event_with_given_name
@@ -185,9 +586,230 @@ class MachineWithEventsTest < Test::Unit::TestCase
185
586
  end
186
587
  end
187
588
 
589
+ class MachineWithConflictingPredefinedInitializeTest < Test::Unit::TestCase
590
+ def setup
591
+ @klass = Class.new do
592
+ attr_reader :initialized
593
+ attr_reader :block_given
594
+
595
+ def initialize
596
+ @initialized = true
597
+ @block_given = block_given?
598
+ end
599
+ end
600
+
601
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
602
+ @object = @klass.new {}
603
+ end
604
+
605
+ def test_should_not_override_existing_method
606
+ assert @object.initialized
607
+ end
608
+
609
+ def test_should_still_initialize_state
610
+ assert_equal 'off', @object.state
611
+ end
612
+
613
+ def test_should_preserve_block
614
+ assert @object.block_given
615
+ end
616
+
617
+ def test_should_not_include_initialize_in_instance_methods
618
+ assert !@klass.instance_methods(false).include?('initialize')
619
+ end
620
+ end
621
+
622
+ class MachineWithConflictingPostdefinedInitializeTest < Test::Unit::TestCase
623
+ def setup
624
+ @klass = Class.new
625
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
626
+ @klass.class_eval do
627
+ attr_reader :initialized
628
+ attr_reader :block_given
629
+
630
+ def initialize
631
+ @initialized = true
632
+ @block_given = block_given?
633
+ end
634
+ end
635
+
636
+ @object = @klass.new {}
637
+ end
638
+
639
+ def test_should_not_override_existing_method
640
+ assert @object.initialized
641
+ end
642
+
643
+ def test_should_still_initialize_state
644
+ assert_equal 'off', @object.state
645
+ end
646
+
647
+ def test_should_preserve_block
648
+ assert @object.block_given
649
+ end
650
+
651
+ def test_should_not_include_initialize_in_instance_methods
652
+ assert !@klass.instance_methods(false).include?('initialize')
653
+ end
654
+ end
655
+
656
+ class MachineWithConflictingSuperclassInitializeTest < Test::Unit::TestCase
657
+ def setup
658
+ @superclass = Class.new do
659
+ attr_reader :initialized
660
+ attr_reader :block_given
661
+
662
+ def initialize
663
+ @initialized = true
664
+ @block_given = block_given?
665
+ end
666
+ end
667
+ @klass = Class.new(@superclass)
668
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
669
+ @object = @klass.new {}
670
+ end
671
+
672
+ def test_should_not_override_existing_method
673
+ assert @object.initialized
674
+ end
675
+
676
+ def test_should_still_initialize_state
677
+ assert_equal 'off', @object.state
678
+ end
679
+
680
+ def test_should_preserve_block
681
+ assert @object.block_given
682
+ end
683
+
684
+ def test_should_not_include_initialize_in_instance_methods
685
+ assert !@klass.instance_methods(false).include?('initialize')
686
+ end
687
+ end
688
+
689
+ class MachineWithConflictingPredefinedAndSuperclassInitializeTest < Test::Unit::TestCase
690
+ def setup
691
+ @superclass = Class.new do
692
+ attr_reader :base_initialized
693
+
694
+ def initialize
695
+ @base_initialized = true
696
+ end
697
+ end
698
+ @klass = Class.new(@superclass) do
699
+ attr_reader :initialized
700
+
701
+ def initialize
702
+ super
703
+ @initialized = true
704
+ end
705
+ end
706
+
707
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
708
+ @object = @klass.new
709
+ end
710
+
711
+ def test_should_not_override_base_method
712
+ assert @object.base_initialized
713
+ end
714
+
715
+ def test_should_not_override_existing_method
716
+ assert @object.initialized
717
+ end
718
+
719
+ def test_should_still_initialize_state
720
+ assert_equal 'off', @object.state
721
+ end
722
+
723
+ def test_should_not_include_initialize_in_instance_methods
724
+ assert !@klass.instance_methods(false).include?('initialize')
725
+ end
726
+ end
727
+
728
+ class MachineWithConflictingPostdefinedAndSuperclassInitializeTest < Test::Unit::TestCase
729
+ def setup
730
+ @superclass = Class.new do
731
+ attr_reader :base_initialized
732
+
733
+ def initialize
734
+ @base_initialized = true
735
+ end
736
+ end
737
+ @klass = Class.new(@superclass)
738
+
739
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
740
+ @klass.class_eval do
741
+ attr_reader :initialized
742
+
743
+ def initialize
744
+ super
745
+ @initialized = true
746
+ end
747
+ end
748
+
749
+ @object = @klass.new
750
+ end
751
+
752
+ def test_should_not_override_base_method
753
+ assert @object.base_initialized
754
+ end
755
+
756
+ def test_should_not_override_existing_method
757
+ assert @object.initialized
758
+ end
759
+
760
+ def test_should_still_initialize_state
761
+ assert_equal 'off', @object.state
762
+ end
763
+
764
+ def test_should_not_include_initialize_in_instance_methods
765
+ assert !@klass.instance_methods(false).include?('initialize')
766
+ end
767
+ end
768
+
769
+ class MachineWithConflictingMethodAddedTest < Test::Unit::TestCase
770
+ def setup
771
+ @klass = Class.new do
772
+ class << self
773
+ attr_reader :called_method_added
774
+
775
+ def method_added(method)
776
+ super
777
+ @called_method_added = true
778
+ end
779
+ end
780
+ end
781
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
782
+ @object = @klass.new
783
+ end
784
+
785
+ def test_should_not_override_existing_method
786
+ assert @klass.called_method_added
787
+ end
788
+
789
+ def test_should_still_initialize_state
790
+ assert_equal 'off', @object.state
791
+ end
792
+ end
793
+
794
+ class MachineWithExistingAttributeValue < Test::Unit::TestCase
795
+ def setup
796
+ @klass = Class.new do
797
+ def initialize
798
+ @state = 'on'
799
+ end
800
+ end
801
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
802
+ @object = @klass.new
803
+ end
804
+
805
+ def test_should_not_set_the_initial_state
806
+ assert_equal 'on', @object.state
807
+ end
808
+ end
809
+
188
810
  class MachineWithExistingEventTest < Test::Unit::TestCase
189
811
  def setup
190
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
812
+ @machine = StateMachine::Machine.new(Class.new)
191
813
  @event = @machine.event(:turn_on) {}
192
814
  @same_event = @machine.event(:turn_on) {}
193
815
  end
@@ -197,9 +819,10 @@ class MachineWithExistingEventTest < Test::Unit::TestCase
197
819
  end
198
820
  end
199
821
 
200
- class MachineWithEventsAndTransitionsTest < Test::Unit::TestCase
822
+ class MachineWithEventsWithTransitionsTest < Test::Unit::TestCase
201
823
  def setup
202
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
824
+ @klass = Class.new
825
+ @machine = StateMachine::Machine.new(@klass)
203
826
  @machine.event(:turn_on) do
204
827
  transition :to => 'on', :from => 'off'
205
828
  transition :to => 'error', :from => 'unknown'
@@ -211,7 +834,7 @@ class MachineWithEventsAndTransitionsTest < Test::Unit::TestCase
211
834
  end
212
835
 
213
836
  def test_should_track_states_defined_in_event_transitions
214
- assert_equal %w(error off on unknown), @machine.states
837
+ assert_equal %w(error off on unknown), @machine.states.sort
215
838
  end
216
839
 
217
840
  def test_should_not_duplicate_states_defined_in_multiple_event_transitions
@@ -219,18 +842,71 @@ class MachineWithEventsAndTransitionsTest < Test::Unit::TestCase
219
842
  transition :to => 'off', :from => 'on'
220
843
  end
221
844
 
222
- assert_equal %w(error off on unknown), @machine.states
845
+ assert_equal %w(error off on unknown), @machine.states.sort
846
+ end
847
+
848
+ def test_should_track_state_from_new_events
849
+ @machine.states
850
+ @machine.event :turn_off do
851
+ transition :to => 'maybe'
852
+ end
853
+
854
+ assert_equal %w(error maybe off on unknown), @machine.states.sort
855
+ end
856
+
857
+ def test_should_define_predicates_for_each_state
858
+ object = @klass.new
859
+
860
+ [:on?, :off?, :error?, :unknown?].each {|predicate| assert object.respond_to?(predicate)}
861
+ end
862
+ end
863
+
864
+ class MachineWithSymbolStatesTest < Test::Unit::TestCase
865
+ def setup
866
+ @klass = Class.new
867
+ @machine = StateMachine::Machine.new(@klass)
868
+ @machine.event(:turn_on) do
869
+ transition :to => :on, :from => :off
870
+ end
871
+ end
872
+
873
+ def test_should_define_predicates_for_each_state
874
+ object = @klass.new
875
+
876
+ [:on?, :off?].each {|predicate| assert object.respond_to?(predicate)}
877
+ end
878
+ end
879
+
880
+ class MachineWithNumericStatesTest < Test::Unit::TestCase
881
+ def setup
882
+ @klass = Class.new
883
+ @machine = StateMachine::Machine.new(@klass)
884
+ @machine.event(:turn_on) do
885
+ transition :to => 1, :from => 2
886
+ end
887
+ end
888
+
889
+ def test_should_not_define_predicates_for_each_state
890
+ object = @klass.new
891
+
892
+ ['1?', '2?'].each {|predicate| assert !object.respond_to?(predicate)}
223
893
  end
224
894
  end
225
895
 
226
896
  class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
227
897
  def setup
228
- @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
898
+ @klass = Class.new do
899
+ attr_accessor :callbacks
900
+ end
901
+
902
+ @machine = StateMachine::Machine.new(@klass)
229
903
  @event = @machine.event :turn_on do
230
904
  transition :to => 'on', :from => 'off'
231
905
  end
232
906
 
233
- @switch = create_switch(:state => 'off')
907
+ @object = @klass.new
908
+ @object.state = 'off'
909
+ @object.callbacks = []
234
910
  end
235
911
 
236
912
  def test_should_raise_exception_if_invalid_option_specified
@@ -242,65 +918,347 @@ class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
242
918
  end
243
919
 
244
920
  def test_should_invoke_callbacks_during_transition
245
- @machine.before_transition lambda {|switch| switch.callbacks << 'before'}
246
- @machine.after_transition lambda {|switch| switch.callbacks << 'after'}
921
+ @machine.before_transition lambda {|object| object.callbacks << 'before'}
922
+ @machine.after_transition lambda {|object| object.callbacks << 'after'}
923
+
924
+ @event.fire(@object)
925
+ assert_equal %w(before after), @object.callbacks
926
+ end
927
+
928
+ def test_should_support_from_requirement
929
+ @machine.before_transition :from => 'off', :do => lambda {|object| object.callbacks << 'off'}
930
+ @machine.before_transition :from => 'on', :do => lambda {|object| object.callbacks << 'on'}
931
+
932
+ @event.fire(@object)
933
+ assert_equal %w(off), @object.callbacks
934
+ end
935
+
936
+ def test_should_support_except_from_requirement
937
+ @machine.before_transition :except_from => 'off', :do => lambda {|object| object.callbacks << 'off'}
938
+ @machine.before_transition :except_from => 'on', :do => lambda {|object| object.callbacks << 'on'}
939
+
940
+ @event.fire(@object)
941
+ assert_equal %w(on), @object.callbacks
942
+ end
943
+
944
+ def test_should_support_to_requirement
945
+ @machine.before_transition :to => 'off', :do => lambda {|object| object.callbacks << 'off'}
946
+ @machine.before_transition :to => 'on', :do => lambda {|object| object.callbacks << 'on'}
947
+
948
+ @event.fire(@object)
949
+ assert_equal %w(on), @object.callbacks
950
+ end
951
+
952
+ def test_should_support_except_to_requirement
953
+ @machine.before_transition :except_to => 'off', :do => lambda {|object| object.callbacks << 'off'}
954
+ @machine.before_transition :except_to => 'on', :do => lambda {|object| object.callbacks << 'on'}
247
955
 
248
- @event.fire(@switch)
249
- assert_equal %w(before after), @switch.callbacks
956
+ @event.fire(@object)
957
+ assert_equal %w(off), @object.callbacks
250
958
  end
251
959
 
252
- def test_should_support_from_query
253
- @machine.before_transition :from => 'off', :do => lambda {|switch| switch.callbacks << 'off'}
254
- @machine.before_transition :from => 'on', :do => lambda {|switch| switch.callbacks << 'on'}
960
+ def test_should_support_on_requirement
961
+ @machine.before_transition :on => 'turn_off', :do => lambda {|object| object.callbacks << 'turn_off'}
962
+ @machine.before_transition :on => 'turn_on', :do => lambda {|object| object.callbacks << 'turn_on'}
255
963
 
256
- @event.fire(@switch)
257
- assert_equal %w(off), @switch.callbacks
964
+ @event.fire(@object)
965
+ assert_equal %w(turn_on), @object.callbacks
258
966
  end
259
967
 
260
- def test_should_support_except_from_query
261
- @machine.before_transition :except_from => 'off', :do => lambda {|switch| switch.callbacks << 'off'}
262
- @machine.before_transition :except_from => 'on', :do => lambda {|switch| switch.callbacks << 'on'}
968
+ def test_should_support_except_on_requirement
969
+ @machine.before_transition :except_on => 'turn_off', :do => lambda {|object| object.callbacks << 'turn_off'}
970
+ @machine.before_transition :except_on => 'turn_on', :do => lambda {|object| object.callbacks << 'turn_on'}
263
971
 
264
- @event.fire(@switch)
265
- assert_equal %w(on), @switch.callbacks
972
+ @event.fire(@object)
973
+ assert_equal %w(turn_off), @object.callbacks
266
974
  end
267
975
 
268
- def test_should_support_to_query
269
- @machine.before_transition :to => 'off', :do => lambda {|switch| switch.callbacks << 'off'}
270
- @machine.before_transition :to => 'on', :do => lambda {|switch| switch.callbacks << 'on'}
976
+ def test_should_track_states_defined_in_transition_callbacks
977
+ @machine.before_transition :from => 'off', :to => 'on', :do => lambda {}
978
+ @machine.after_transition :from => 'unknown', :to => 'error', :do => lambda {}
271
979
 
272
- @event.fire(@switch)
273
- assert_equal %w(on), @switch.callbacks
980
+ assert_equal %w(error off on unknown), @machine.states.sort
274
981
  end
275
982
 
276
- def test_should_support_except_to_query
277
- @machine.before_transition :except_to => 'off', :do => lambda {|switch| switch.callbacks << 'off'}
278
- @machine.before_transition :except_to => 'on', :do => lambda {|switch| switch.callbacks << 'on'}
983
+ def test_should_not_duplicate_states_defined_in_multiple_event_transitions
984
+ @machine.before_transition :from => 'off', :to => 'on', :do => lambda {}
985
+ @machine.after_transition :from => 'unknown', :to => 'error', :do => lambda {}
986
+ @machine.after_transition :from => 'off', :to => 'on', :do => lambda {}
279
987
 
280
- @event.fire(@switch)
281
- assert_equal %w(off), @switch.callbacks
988
+ assert_equal %w(error off on unknown), @machine.states.sort
989
+ end
990
+
991
+ def test_should_define_predicates_for_each_state
992
+ [:on?, :off?].each {|predicate| assert @object.respond_to?(predicate)}
993
+ end
994
+ end
995
+
996
+ class MachineWithOtherStates < Test::Unit::TestCase
997
+ def setup
998
+ @klass = Class.new
999
+ @machine = StateMachine::Machine.new(@klass, :initial => 'on')
1000
+ @machine.other_states('on', 'off')
282
1001
  end
283
1002
 
284
- def test_should_support_on_query
285
- @machine.before_transition :on => 'turn_off', :do => lambda {|switch| switch.callbacks << 'turn_off'}
286
- @machine.before_transition :on => 'turn_on', :do => lambda {|switch| switch.callbacks << 'turn_on'}
1003
+ def test_should_include_other_states_in_known_states
1004
+ assert_equal %w(off on), @machine.states.sort
1005
+ end
1006
+
1007
+ def test_should_define_predicates_for_each_state
1008
+ object = @klass.new
287
1009
 
288
- @event.fire(@switch)
289
- assert_equal %w(turn_on), @switch.callbacks
1010
+ [:on?, :off?].each {|predicate| assert object.respond_to?(predicate)}
1011
+ end
1012
+ end
1013
+
1014
+ class MachineWithOwnerSubclassTest < Test::Unit::TestCase
1015
+ def setup
1016
+ @klass = Class.new
1017
+ @machine = StateMachine::Machine.new(@klass)
1018
+ @subclass = Class.new(@klass)
1019
+ end
1020
+
1021
+ def test_should_have_a_different_collection_of_state_machines
1022
+ assert_not_same @klass.state_machines, @subclass.state_machines
1023
+ end
1024
+
1025
+ def test_should_have_the_same_attribute_associated_state_machines
1026
+ assert_equal @klass.state_machines, @subclass.state_machines
1027
+ end
1028
+ end
1029
+
1030
+ class MachineWithExistingMachinesOnOwnerClassTest < Test::Unit::TestCase
1031
+ def setup
1032
+ @klass = Class.new
1033
+ @machine = StateMachine::Machine.new(@klass, :initial => 'off')
1034
+ @second_machine = StateMachine::Machine.new(@klass, 'status', :initial => 'active')
1035
+ @object = @klass.new
1036
+ end
1037
+
1038
+ def test_should_track_each_state_machine
1039
+ expected = {'state' => @machine, 'status' => @second_machine}
1040
+ assert_equal expected, @klass.state_machines
1041
+ end
1042
+
1043
+ def test_should_initialize_state_for_both_machines
1044
+ assert_equal 'off', @object.state
1045
+ assert_equal 'active', @object.status
1046
+ end
1047
+ end
1048
+
1049
+ class MachineFinderWithoutExistingMachineTest < Test::Unit::TestCase
1050
+ def setup
1051
+ @klass = Class.new
1052
+ @machine = StateMachine::Machine.find_or_create(@klass)
1053
+ end
1054
+
1055
+ def test_should_create_a_new_machine
1056
+ assert_not_nil @machine
1057
+ end
1058
+
1059
+ def test_should_use_default_state
1060
+ assert_equal 'state', @machine.attribute
1061
+ end
1062
+ end
1063
+
1064
+ class MachineFinderWithExistingOnSameClassTest < Test::Unit::TestCase
1065
+ def setup
1066
+ @klass = Class.new
1067
+ @existing_machine = StateMachine::Machine.new(@klass)
1068
+ @machine = StateMachine::Machine.find_or_create(@klass)
290
1069
  end
291
1070
 
292
- def test_should_support_except_on_query
293
- @machine.before_transition :except_on => 'turn_off', :do => lambda {|switch| switch.callbacks << 'turn_off'}
294
- @machine.before_transition :except_on => 'turn_on', :do => lambda {|switch| switch.callbacks << 'turn_on'}
1071
+ def test_should_not_create_a_new_machine
1072
+ assert_same @machine, @existing_machine
1073
+ end
1074
+ end
1075
+
1076
+ class MachineFinderWithExistingMachineOnSuperclassTest < Test::Unit::TestCase
1077
+ def setup
1078
+ integration = Module.new do
1079
+ def self.matches?(klass)
1080
+ false
1081
+ end
1082
+ end
1083
+ StateMachine::Integrations.const_set('Custom', integration)
1084
+
1085
+ @base_class = Class.new
1086
+ @base_machine = StateMachine::Machine.new(@base_class, 'status', :action => :save, :integration => :custom)
1087
+ @base_machine.event(:turn_on) {}
1088
+ @base_machine.before_transition(lambda {})
1089
+ @base_machine.after_transition(lambda {})
295
1090
 
296
- @event.fire(@switch)
297
- assert_equal %w(turn_off), @switch.callbacks
1091
+ @klass = Class.new(@base_class)
1092
+ @machine = StateMachine::Machine.find_or_create(@klass, 'status')
1093
+ end
1094
+
1095
+ def test_should_create_a_new_machine
1096
+ assert_not_nil @machine
1097
+ assert_not_same @machine, @base_machine
1098
+ end
1099
+
1100
+ def test_should_copy_the_base_attribute
1101
+ assert_equal 'status', @machine.attribute
1102
+ end
1103
+
1104
+ def test_should_copy_the_base_configuration
1105
+ assert_equal :save, @machine.action
1106
+ end
1107
+
1108
+ def test_should_copy_events
1109
+ # Can't assert equal arrays since their machines change
1110
+ assert_equal 1, @machine.events.size
1111
+ end
1112
+
1113
+ def test_should_copy_before_callbacks
1114
+ assert_equal @base_machine.callbacks[:before], @machine.callbacks[:before]
1115
+ end
1116
+
1117
+ def test_should_copy_after_transitions
1118
+ assert_equal @base_machine.callbacks[:after], @machine.callbacks[:after]
1119
+ end
1120
+
1121
+ def test_should_use_the_same_integration
1122
+ assert (class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom)
298
1123
  end
299
1124
 
300
1125
  def teardown
301
- Switch.class_eval do
302
- @before_transition_state_callbacks = nil
303
- @after_transition_state_callbacks = nil
1126
+ StateMachine::Integrations.send(:remove_const, 'Custom')
1127
+ end
1128
+ end
1129
+
1130
+ class MachineFinderCustomOptionsTest < Test::Unit::TestCase
1131
+ def setup
1132
+ @klass = Class.new
1133
+ @machine = StateMachine::Machine.find_or_create(@klass, 'status', :initial => 'off')
1134
+ @object = @klass.new
1135
+ end
1136
+
1137
+ def test_should_use_custom_attribute
1138
+ assert_equal 'status', @machine.attribute
1139
+ end
1140
+
1141
+ def test_should_set_custom_initial_state
1142
+ assert_equal 'off', @machine.initial_state(@object)
1143
+ end
1144
+ end
1145
+
1146
+ begin
1147
+ # Load library
1148
+ require 'rubygems'
1149
+ require 'graphviz'
1150
+
1151
+ class MachineDrawingTest < Test::Unit::TestCase
1152
+ def setup
1153
+ @klass = Class.new do
1154
+ def self.name; 'Vehicle'; end
1155
+ end
1156
+ @machine = StateMachine::Machine.new(@klass)
1157
+ @machine.event :ignite do
1158
+ transition :from => 'parked', :to => 'idling'
1159
+ end
1160
+ end
1161
+
1162
+ def test_should_raise_exception_if_invalid_option_specified
1163
+ assert_raise(ArgumentError) {@machine.draw(:invalid => true)}
1164
+ end
1165
+
1166
+ def test_should_save_file_with_class_name_by_default
1167
+ @machine.draw
1168
+ assert File.exist?('./Vehicle_state.png')
1169
+ ensure
1170
+ FileUtils.rm('./Vehicle_state.png')
1171
+ end
1172
+
1173
+ def test_should_allow_base_name_to_be_customized
1174
+ @machine.draw(:name => 'machine')
1175
+ assert File.exist?('./machine.png')
1176
+ ensure
1177
+ FileUtils.rm('./machine.png')
1178
+ end
1179
+
1180
+ def test_should_allow_format_to_be_customized
1181
+ @machine.draw(:format => 'jpg')
1182
+ assert File.exist?('./Vehicle_state.jpg')
1183
+ ensure
1184
+ FileUtils.rm('./Vehicle_state.jpg')
1185
+ end
1186
+
1187
+ def test_should_allow_path_to_be_customized
1188
+ @machine.draw(:path => "#{File.dirname(__FILE__)}/")
1189
+ assert File.exist?("#{File.dirname(__FILE__)}/Vehicle_state.png")
1190
+ ensure
1191
+ FileUtils.rm("#{File.dirname(__FILE__)}/Vehicle_state.png")
1192
+ end
1193
+ end
1194
+
1195
+ class MachineDrawingWithIntegerStatesTest < Test::Unit::TestCase
1196
+ def setup
1197
+ @klass = Class.new do
1198
+ def self.name; 'Vehicle'; end
1199
+ end
1200
+ @machine = StateMachine::Machine.new(@klass, :state_id)
1201
+ @machine.event :ignite do
1202
+ transition :from => 2, :to => 1
1203
+ end
1204
+ @machine.draw
1205
+ end
1206
+
1207
+ def test_should_draw_machine
1208
+ assert File.exist?('./Vehicle_state_id.png')
1209
+ ensure
1210
+ FileUtils.rm('./Vehicle_state_id.png')
1211
+ end
1212
+ end
1213
+
1214
+ class MachineDrawingWithTimeStatesTest < Test::Unit::TestCase
1215
+ def setup
1216
+ @klass = Class.new do
1217
+ def self.name; 'Vehicle'; end
1218
+ end
1219
+ @machine = StateMachine::Machine.new(@klass, :activated_at)
1220
+ @machine.event :activate do
1221
+ transition :from => nil, :to => lambda {Time.now}
1222
+ end
1223
+ @machine.draw
1224
+ end
1225
+
1226
+ def test_should_draw_machine
1227
+ assert File.exist?('./Vehicle_activated_at.png')
1228
+ ensure
1229
+ FileUtils.rm('./Vehicle_activated_at.png')
1230
+ end
1231
+ end
1232
+
1233
+ class MachineClassDrawingTest < Test::Unit::TestCase
1234
+ def setup
1235
+ @klass = Class.new do
1236
+ def self.name; 'Vehicle'; end
1237
+ end
1238
+ @machine = StateMachine::Machine.new(@klass)
1239
+ @machine.event :ignite do
1240
+ transition :from => 'parked', :to => 'idling'
1241
+ end
1242
+ end
1243
+
1244
+ def test_should_raise_exception_if_no_class_names_specified
1245
+ assert_raise(ArgumentError) {StateMachine::Machine.draw(nil)}
1246
+ end
1247
+
1248
+ def test_should_load_files
1249
+ StateMachine::Machine.draw('Switch', :file => "#{File.dirname(__FILE__)}/../classes/switch.rb")
1250
+ assert defined?(::Switch)
1251
+ ensure
1252
+ FileUtils.rm('./Switch_state.png')
1253
+ end
1254
+
1255
+ def test_should_allow_path_and_format_to_be_customized
1256
+ StateMachine::Machine.draw('Switch', :file => "#{File.dirname(__FILE__)}/../classes/switch.rb", :path => "#{File.dirname(__FILE__)}/", :format => 'jpg')
1257
+ assert File.exist?("#{File.dirname(__FILE__)}/Switch_state.jpg")
1258
+ ensure
1259
+ FileUtils.rm("#{File.dirname(__FILE__)}/Switch_state.jpg")
304
1260
  end
305
1261
  end
1262
+ rescue LoadError
1263
+ $stderr.puts 'Skipping GraphViz tests. `gem install ruby-graphviz` and try again.'
306
1264
  end