PageRenderTime 50ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/plugins/state_machine/test/unit/machine_test.rb

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