PageRenderTime 57ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/test/unit/machine_test.rb

http://github.com/pluginaweek/state_machine
Ruby | 3407 lines | 2703 code | 702 blank | 2 comment | 10 complexity | ed0267e8a6db2b963dc205033b3167e8 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_any_failure_callbacks
  36. assert @machine.callbacks[:failure].empty?
  37. end
  38. def test_should_not_have_an_action
  39. assert_nil @machine.action
  40. end
  41. def test_should_use_tranactions
  42. assert_equal true, @machine.use_transactions
  43. end
  44. def test_should_not_have_a_namespace
  45. assert_nil @machine.namespace
  46. end
  47. def test_should_have_a_nil_state
  48. assert_equal [nil], @machine.states.keys
  49. end
  50. def test_should_set_initial_on_nil_state
  51. assert @machine.state(nil).initial
  52. end
  53. def test_should_generate_default_messages
  54. assert_equal 'is invalid', @machine.generate_message(:invalid)
  55. assert_equal 'cannot transition when parked', @machine.generate_message(:invalid_event, [[:state, :parked]])
  56. assert_equal 'cannot transition via "park"', @machine.generate_message(:invalid_transition, [[:event, :park]])
  57. end
  58. def test_should_not_be_extended_by_the_base_integration
  59. assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Base)
  60. end
  61. def test_should_not_be_extended_by_the_active_model_integration
  62. assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::ActiveModel)
  63. end
  64. def test_should_not_be_extended_by_the_active_record_integration
  65. assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::ActiveRecord)
  66. end
  67. def test_should_not_be_extended_by_the_datamapper_integration
  68. assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::DataMapper)
  69. end
  70. def test_should_not_be_extended_by_the_mongo_mapper_integration
  71. assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::MongoMapper)
  72. end
  73. def test_should_not_be_extended_by_the_sequel_integration
  74. assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Sequel)
  75. end
  76. def test_should_define_a_reader_attribute_for_the_attribute
  77. assert @object.respond_to?(:state)
  78. end
  79. def test_should_define_a_writer_attribute_for_the_attribute
  80. assert @object.respond_to?(:state=)
  81. end
  82. def test_should_define_a_predicate_for_the_attribute
  83. assert @object.respond_to?(:state?)
  84. end
  85. def test_should_define_a_name_reader_for_the_attribute
  86. assert @object.respond_to?(:state_name)
  87. end
  88. def test_should_define_an_event_reader_for_the_attribute
  89. assert @object.respond_to?(:state_events)
  90. end
  91. def test_should_define_a_transition_reader_for_the_attribute
  92. assert @object.respond_to?(:state_transitions)
  93. end
  94. def test_should_define_a_path_reader_for_the_attribute
  95. assert @object.respond_to?(:state_paths)
  96. end
  97. def test_should_define_an_event_runner_for_the_attribute
  98. assert @object.respond_to?(:fire_state_event)
  99. end
  100. def test_should_not_define_an_event_attribute_reader
  101. assert !@object.respond_to?(:state_event)
  102. end
  103. def test_should_not_define_an_event_attribute_writer
  104. assert !@object.respond_to?(:state_event=)
  105. end
  106. def test_should_not_define_an_event_transition_attribute_reader
  107. assert !@object.respond_to?(:state_event_transition)
  108. end
  109. def test_should_not_define_an_event_transition_attribute_writer
  110. assert !@object.respond_to?(:state_event_transition=)
  111. end
  112. def test_should_define_a_human_attribute_name_reader_for_the_attribute
  113. assert @klass.respond_to?(:human_state_name)
  114. end
  115. def test_should_define_a_human_event_name_reader_for_the_attribute
  116. assert @klass.respond_to?(:human_state_event_name)
  117. end
  118. def test_should_not_define_singular_with_scope
  119. assert !@klass.respond_to?(:with_state)
  120. end
  121. def test_should_not_define_singular_without_scope
  122. assert !@klass.respond_to?(:without_state)
  123. end
  124. def test_should_not_define_plural_with_scope
  125. assert !@klass.respond_to?(:with_states)
  126. end
  127. def test_should_not_define_plural_without_scope
  128. assert !@klass.respond_to?(:without_states)
  129. end
  130. def test_should_extend_owner_class_with_class_methods
  131. assert((class << @klass; ancestors; end).include?(StateMachine::ClassMethods))
  132. end
  133. def test_should_include_instance_methods_in_owner_class
  134. assert @klass.included_modules.include?(StateMachine::InstanceMethods)
  135. end
  136. def test_should_define_state_machines_reader
  137. expected = {:state => @machine}
  138. assert_equal expected, @klass.state_machines
  139. end
  140. end
  141. class MachineWithCustomNameTest < Test::Unit::TestCase
  142. def setup
  143. @klass = Class.new
  144. @machine = StateMachine::Machine.new(@klass, :status)
  145. @object = @klass.new
  146. end
  147. def test_should_use_custom_name
  148. assert_equal :status, @machine.name
  149. end
  150. def test_should_use_custom_name_for_attribute
  151. assert_equal :status, @machine.attribute
  152. end
  153. def test_should_prefix_custom_attributes_with_custom_name
  154. assert_equal :status_event, @machine.attribute(:event)
  155. end
  156. def test_should_define_a_reader_attribute_for_the_attribute
  157. assert @object.respond_to?(:status)
  158. end
  159. def test_should_define_a_writer_attribute_for_the_attribute
  160. assert @object.respond_to?(:status=)
  161. end
  162. def test_should_define_a_predicate_for_the_attribute
  163. assert @object.respond_to?(:status?)
  164. end
  165. def test_should_define_a_name_reader_for_the_attribute
  166. assert @object.respond_to?(:status_name)
  167. end
  168. def test_should_define_an_event_reader_for_the_attribute
  169. assert @object.respond_to?(:status_events)
  170. end
  171. def test_should_define_a_transition_reader_for_the_attribute
  172. assert @object.respond_to?(:status_transitions)
  173. end
  174. def test_should_define_an_event_runner_for_the_attribute
  175. assert @object.respond_to?(:fire_status_event)
  176. end
  177. def test_should_define_a_human_attribute_name_reader_for_the_attribute
  178. assert @klass.respond_to?(:human_status_name)
  179. end
  180. def test_should_define_a_human_event_name_reader_for_the_attribute
  181. assert @klass.respond_to?(:human_status_event_name)
  182. end
  183. end
  184. class MachineWithoutInitializationTest < Test::Unit::TestCase
  185. def setup
  186. @klass = Class.new do
  187. def initialize(attributes = {})
  188. attributes.each {|attr, value| send("#{attr}=", value)}
  189. super()
  190. end
  191. end
  192. @machine = StateMachine::Machine.new(@klass, :initial => :parked, :initialize => false)
  193. end
  194. def test_should_not_have_an_initial_state
  195. object = @klass.new
  196. assert_nil object.state
  197. end
  198. def test_should_still_allow_manual_initialization
  199. @klass.send(:include, Module.new do
  200. def initialize(attributes = {})
  201. super()
  202. initialize_state_machines
  203. end
  204. end)
  205. object = @klass.new
  206. assert_equal 'parked', object.state
  207. end
  208. end
  209. class MachineWithStaticInitialStateTest < Test::Unit::TestCase
  210. def setup
  211. @klass = Class.new
  212. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  213. end
  214. def test_should_not_have_dynamic_initial_state
  215. assert !@machine.dynamic_initial_state?
  216. end
  217. def test_should_have_an_initial_state
  218. object = @klass.new
  219. assert_equal 'parked', @machine.initial_state(object).value
  220. end
  221. def test_should_write_to_attribute_when_initializing_state
  222. object = @klass.allocate
  223. @machine.initialize_state(object)
  224. assert_equal 'parked', object.state
  225. end
  226. def test_should_set_initial_on_state_object
  227. assert @machine.state(:parked).initial
  228. end
  229. def test_should_set_initial_state_on_created_object
  230. assert_equal 'parked', @klass.new.state
  231. end
  232. def test_not_set_initial_state_even_if_not_empty
  233. @klass.class_eval do
  234. def initialize(attributes = {})
  235. self.state = 'idling'
  236. super()
  237. end
  238. end
  239. object = @klass.new
  240. assert_equal 'idling', object.state
  241. end
  242. def test_should_set_initial_state_prior_to_initialization
  243. base = Class.new do
  244. attr_accessor :state_on_init
  245. def initialize
  246. self.state_on_init = state
  247. end
  248. end
  249. klass = Class.new(base)
  250. StateMachine::Machine.new(klass, :initial => :parked)
  251. assert_equal 'parked', klass.new.state_on_init
  252. end
  253. def test_should_be_included_in_known_states
  254. assert_equal [:parked], @machine.states.keys
  255. end
  256. end
  257. class MachineWithDynamicInitialStateTest < Test::Unit::TestCase
  258. def setup
  259. @klass = Class.new do
  260. attr_accessor :initial_state
  261. end
  262. @machine = StateMachine::Machine.new(@klass, :initial => lambda {|object| object.initial_state || :default})
  263. @machine.state :parked, :idling, :default
  264. @object = @klass.new
  265. end
  266. def test_should_have_dynamic_initial_state
  267. assert @machine.dynamic_initial_state?
  268. end
  269. def test_should_use_the_record_for_determining_the_initial_state
  270. @object.initial_state = :parked
  271. assert_equal :parked, @machine.initial_state(@object).name
  272. @object.initial_state = :idling
  273. assert_equal :idling, @machine.initial_state(@object).name
  274. end
  275. def test_should_write_to_attribute_when_initializing_state
  276. object = @klass.allocate
  277. object.initial_state = :parked
  278. @machine.initialize_state(object)
  279. assert_equal 'parked', object.state
  280. end
  281. def test_should_set_initial_state_on_created_object
  282. assert_equal 'default', @object.state
  283. end
  284. def test_should_not_set_initial_state_even_if_not_empty
  285. @klass.class_eval do
  286. def initialize(attributes = {})
  287. self.state = 'parked'
  288. super()
  289. end
  290. end
  291. object = @klass.new
  292. assert_equal 'parked', object.state
  293. end
  294. def test_should_set_initial_state_after_initialization
  295. base = Class.new do
  296. attr_accessor :state_on_init
  297. def initialize
  298. self.state_on_init = state
  299. end
  300. end
  301. klass = Class.new(base)
  302. machine = StateMachine::Machine.new(klass, :initial => lambda {|object| :parked})
  303. machine.state :parked
  304. assert_nil klass.new.state_on_init
  305. end
  306. def test_should_not_be_included_in_known_states
  307. assert_equal [:parked, :idling, :default], @machine.states.map {|state| state.name}
  308. end
  309. end
  310. class MachineStateInitializationTest < Test::Unit::TestCase
  311. def setup
  312. @klass = Class.new
  313. @machine = StateMachine::Machine.new(@klass, :state, :initial => :parked, :initialize => false)
  314. @object = @klass.new
  315. @object.state = nil
  316. end
  317. def test_should_set_states_if_nil
  318. @machine.initialize_state(@object)
  319. assert_equal 'parked', @object.state
  320. end
  321. def test_should_set_states_if_empty
  322. @object.state = ''
  323. @machine.initialize_state(@object)
  324. assert_equal 'parked', @object.state
  325. end
  326. def test_should_not_set_states_if_not_empty
  327. @object.state = 'idling'
  328. @machine.initialize_state(@object)
  329. assert_equal 'idling', @object.state
  330. end
  331. def test_should_set_states_if_not_empty_and_forced
  332. @object.state = 'idling'
  333. @machine.initialize_state(@object, :force => true)
  334. assert_equal 'parked', @object.state
  335. end
  336. def test_should_not_set_state_if_nil_and_nil_is_valid_state
  337. @machine.state :initial, :value => nil
  338. @machine.initialize_state(@object)
  339. assert_nil @object.state
  340. end
  341. def test_should_write_to_hash_if_specified
  342. @machine.initialize_state(@object, :to => hash = {})
  343. assert_equal({'state' => 'parked'}, hash)
  344. end
  345. def test_should_not_write_to_object_if_writing_to_hash
  346. @machine.initialize_state(@object, :to => {})
  347. assert_nil @object.state
  348. end
  349. end
  350. class MachineWithCustomActionTest < Test::Unit::TestCase
  351. def setup
  352. @machine = StateMachine::Machine.new(Class.new, :action => :save)
  353. end
  354. def test_should_use_the_custom_action
  355. assert_equal :save, @machine.action
  356. end
  357. end
  358. class MachineWithNilActionTest < Test::Unit::TestCase
  359. def setup
  360. integration = Module.new do
  361. include StateMachine::Integrations::Base
  362. @defaults = {:action => :save}
  363. end
  364. StateMachine::Integrations.const_set('Custom', integration)
  365. @machine = StateMachine::Machine.new(Class.new, :action => nil, :integration => :custom)
  366. end
  367. def test_should_have_a_nil_action
  368. assert_nil @machine.action
  369. end
  370. def teardown
  371. StateMachine::Integrations.send(:remove_const, 'Custom')
  372. end
  373. end
  374. class MachineWithoutIntegrationTest < Test::Unit::TestCase
  375. def setup
  376. @klass = Class.new
  377. @machine = StateMachine::Machine.new(@klass)
  378. @object = @klass.new
  379. end
  380. def test_transaction_should_yield
  381. @yielded = false
  382. @machine.within_transaction(@object) do
  383. @yielded = true
  384. end
  385. assert @yielded
  386. end
  387. def test_invalidation_should_do_nothing
  388. assert_nil @machine.invalidate(@object, :state, :invalid_transition, [[:event, 'park']])
  389. end
  390. def test_reset_should_do_nothing
  391. assert_nil @machine.reset(@object)
  392. end
  393. def test_errors_for_should_be_empty
  394. assert_equal '', @machine.errors_for(@object)
  395. end
  396. end
  397. class MachineWithCustomIntegrationTest < Test::Unit::TestCase
  398. def setup
  399. integration = Module.new do
  400. include StateMachine::Integrations::Base
  401. def self.matching_ancestors
  402. ['MachineWithCustomIntegrationTest::Vehicle']
  403. end
  404. end
  405. StateMachine::Integrations.const_set('Custom', integration)
  406. superclass = Class.new
  407. self.class.const_set('Vehicle', superclass)
  408. @klass = Class.new(superclass)
  409. end
  410. def test_should_be_extended_by_the_integration_if_explicit
  411. machine = StateMachine::Machine.new(@klass, :integration => :custom)
  412. assert((class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
  413. end
  414. def test_should_not_be_extended_by_the_integration_if_implicit_but_not_available
  415. StateMachine::Integrations::Custom.class_eval do
  416. class << self; remove_method :matching_ancestors; end
  417. def self.matching_ancestors
  418. []
  419. end
  420. end
  421. machine = StateMachine::Machine.new(@klass)
  422. assert(!(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
  423. end
  424. def test_should_not_be_extended_by_the_integration_if_implicit_but_not_matched
  425. StateMachine::Integrations::Custom.class_eval do
  426. class << self; remove_method :matching_ancestors; end
  427. def self.matching_ancestors
  428. []
  429. end
  430. end
  431. machine = StateMachine::Machine.new(@klass)
  432. assert(!(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
  433. end
  434. def test_should_be_extended_by_the_integration_if_implicit_and_available_and_matches
  435. machine = StateMachine::Machine.new(@klass)
  436. assert((class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
  437. end
  438. def test_should_not_be_extended_by_the_integration_if_nil
  439. machine = StateMachine::Machine.new(@klass, :integration => nil)
  440. assert(!(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
  441. end
  442. def test_should_not_be_extended_by_the_integration_if_false
  443. machine = StateMachine::Machine.new(@klass, :integration => false)
  444. assert(!(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom))
  445. end
  446. def teardown
  447. self.class.send(:remove_const, 'Vehicle')
  448. StateMachine::Integrations.send(:remove_const, 'Custom')
  449. end
  450. end
  451. class MachineWithIntegrationTest < Test::Unit::TestCase
  452. def setup
  453. StateMachine::Integrations.const_set('Custom', Module.new do
  454. include StateMachine::Integrations::Base
  455. @defaults = {:action => :save, :use_transactions => false}
  456. attr_reader :initialized, :with_scopes, :without_scopes, :ran_transaction
  457. def after_initialize
  458. @initialized = true
  459. end
  460. def create_with_scope(name)
  461. (@with_scopes ||= []) << name
  462. lambda {}
  463. end
  464. def create_without_scope(name)
  465. (@without_scopes ||= []) << name
  466. lambda {}
  467. end
  468. def transaction(object)
  469. @ran_transaction = true
  470. yield
  471. end
  472. end)
  473. @machine = StateMachine::Machine.new(Class.new, :integration => :custom)
  474. end
  475. def test_should_call_after_initialize_hook
  476. assert @machine.initialized
  477. end
  478. def test_should_use_the_default_action
  479. assert_equal :save, @machine.action
  480. end
  481. def test_should_use_the_custom_action_if_specified
  482. machine = StateMachine::Machine.new(Class.new, :integration => :custom, :action => :save!)
  483. assert_equal :save!, machine.action
  484. end
  485. def test_should_use_the_default_use_transactions
  486. assert_equal false, @machine.use_transactions
  487. end
  488. def test_should_use_the_custom_use_transactions_if_specified
  489. machine = StateMachine::Machine.new(Class.new, :integration => :custom, :use_transactions => true)
  490. assert_equal true, machine.use_transactions
  491. end
  492. def test_should_define_a_singular_and_plural_with_scope
  493. assert_equal %w(with_state with_states), @machine.with_scopes
  494. end
  495. def test_should_define_a_singular_and_plural_without_scope
  496. assert_equal %w(without_state without_states), @machine.without_scopes
  497. end
  498. def teardown
  499. StateMachine::Integrations.send(:remove_const, 'Custom')
  500. end
  501. end
  502. class MachineWithActionUndefinedTest < Test::Unit::TestCase
  503. def setup
  504. @klass = Class.new
  505. @machine = StateMachine::Machine.new(@klass, :action => :save)
  506. @object = @klass.new
  507. end
  508. def test_should_define_an_event_attribute_reader
  509. assert @object.respond_to?(:state_event)
  510. end
  511. def test_should_define_an_event_attribute_writer
  512. assert @object.respond_to?(:state_event=)
  513. end
  514. def test_should_define_an_event_transition_attribute_reader
  515. assert @object.respond_to?(:state_event_transition, true)
  516. end
  517. def test_should_define_an_event_transition_attribute_writer
  518. assert @object.respond_to?(:state_event_transition=, true)
  519. end
  520. def test_should_not_define_action
  521. assert !@object.respond_to?(:save)
  522. end
  523. def test_should_not_mark_action_hook_as_defined
  524. assert !@machine.action_hook?
  525. end
  526. end
  527. class MachineWithActionDefinedInClassTest < Test::Unit::TestCase
  528. def setup
  529. @klass = Class.new do
  530. def save
  531. end
  532. end
  533. @machine = StateMachine::Machine.new(@klass, :action => :save)
  534. @object = @klass.new
  535. end
  536. def test_should_define_an_event_attribute_reader
  537. assert @object.respond_to?(:state_event)
  538. end
  539. def test_should_define_an_event_attribute_writer
  540. assert @object.respond_to?(:state_event=)
  541. end
  542. def test_should_define_an_event_transition_attribute_reader
  543. assert @object.respond_to?(:state_event_transition, true)
  544. end
  545. def test_should_define_an_event_transition_attribute_writer
  546. assert @object.respond_to?(:state_event_transition=, true)
  547. end
  548. def test_should_not_define_action
  549. assert !@klass.ancestors.any? {|ancestor| ancestor != @klass && ancestor.method_defined?(:save)}
  550. end
  551. def test_should_not_mark_action_hook_as_defined
  552. assert !@machine.action_hook?
  553. end
  554. end
  555. class MachineWithActionDefinedInIncludedModuleTest < Test::Unit::TestCase
  556. def setup
  557. @mod = mod = Module.new do
  558. def save
  559. end
  560. end
  561. @klass = Class.new do
  562. include mod
  563. end
  564. @machine = StateMachine::Machine.new(@klass, :action => :save)
  565. @object = @klass.new
  566. end
  567. def test_should_define_an_event_attribute_reader
  568. assert @object.respond_to?(:state_event)
  569. end
  570. def test_should_define_an_event_attribute_writer
  571. assert @object.respond_to?(:state_event=)
  572. end
  573. def test_should_define_an_event_transition_attribute_reader
  574. assert @object.respond_to?(:state_event_transition, true)
  575. end
  576. def test_should_define_an_event_transition_attribute_writer
  577. assert @object.respond_to?(:state_event_transition=, true)
  578. end
  579. def test_should_define_action
  580. assert @klass.ancestors.any? {|ancestor| ![@klass, @mod].include?(ancestor) && ancestor.method_defined?(:save)}
  581. end
  582. def test_should_keep_action_public
  583. assert @klass.public_method_defined?(:save)
  584. end
  585. def test_should_mark_action_hook_as_defined
  586. assert @machine.action_hook?
  587. end
  588. end
  589. class MachineWithActionDefinedInSuperclassTest < Test::Unit::TestCase
  590. def setup
  591. @superclass = Class.new do
  592. def save
  593. end
  594. end
  595. @klass = Class.new(@superclass)
  596. @machine = StateMachine::Machine.new(@klass, :action => :save)
  597. @object = @klass.new
  598. end
  599. def test_should_define_an_event_attribute_reader
  600. assert @object.respond_to?(:state_event)
  601. end
  602. def test_should_define_an_event_attribute_writer
  603. assert @object.respond_to?(:state_event=)
  604. end
  605. def test_should_define_an_event_transition_attribute_reader
  606. assert @object.respond_to?(:state_event_transition, true)
  607. end
  608. def test_should_define_an_event_transition_attribute_writer
  609. assert @object.respond_to?(:state_event_transition=, true)
  610. end
  611. def test_should_define_action
  612. assert @klass.ancestors.any? {|ancestor| ![@klass, @superclass].include?(ancestor) && ancestor.method_defined?(:save)}
  613. end
  614. def test_should_keep_action_public
  615. assert @klass.public_method_defined?(:save)
  616. end
  617. def test_should_mark_action_hook_as_defined
  618. assert @machine.action_hook?
  619. end
  620. end
  621. class MachineWithPrivateActionTest < Test::Unit::TestCase
  622. def setup
  623. @superclass = Class.new do
  624. private
  625. def save
  626. end
  627. end
  628. @klass = Class.new(@superclass)
  629. @machine = StateMachine::Machine.new(@klass, :action => :save)
  630. @object = @klass.new
  631. end
  632. def test_should_define_an_event_attribute_reader
  633. assert @object.respond_to?(:state_event)
  634. end
  635. def test_should_define_an_event_attribute_writer
  636. assert @object.respond_to?(:state_event=)
  637. end
  638. def test_should_define_an_event_transition_attribute_reader
  639. assert @object.respond_to?(:state_event_transition, true)
  640. end
  641. def test_should_define_an_event_transition_attribute_writer
  642. assert @object.respond_to?(:state_event_transition=, true)
  643. end
  644. def test_should_define_action
  645. assert @klass.ancestors.any? {|ancestor| ![@klass, @superclass].include?(ancestor) && ancestor.private_method_defined?(:save)}
  646. end
  647. def test_should_keep_action_private
  648. assert @klass.private_method_defined?(:save)
  649. end
  650. def test_should_mark_action_hook_as_defined
  651. assert @machine.action_hook?
  652. end
  653. end
  654. class MachineWithActionAlreadyOverriddenTest < Test::Unit::TestCase
  655. def setup
  656. @superclass = Class.new do
  657. def save
  658. end
  659. end
  660. @klass = Class.new(@superclass)
  661. StateMachine::Machine.new(@klass, :action => :save)
  662. @machine = StateMachine::Machine.new(@klass, :status, :action => :save)
  663. @object = @klass.new
  664. end
  665. def test_should_not_redefine_action
  666. assert_equal 1, @klass.ancestors.select {|ancestor| ![@klass, @superclass].include?(ancestor) && ancestor.method_defined?(:save)}.length
  667. end
  668. def test_should_mark_action_hook_as_defined
  669. assert @machine.action_hook?
  670. end
  671. end
  672. class MachineWithCustomPluralTest < Test::Unit::TestCase
  673. def setup
  674. @integration = Module.new do
  675. include StateMachine::Integrations::Base
  676. class << self; attr_accessor :with_scopes, :without_scopes; end
  677. @with_scopes = []
  678. @without_scopes = []
  679. def create_with_scope(name)
  680. StateMachine::Integrations::Custom.with_scopes << name
  681. lambda {}
  682. end
  683. def create_without_scope(name)
  684. StateMachine::Integrations::Custom.without_scopes << name
  685. lambda {}
  686. end
  687. end
  688. StateMachine::Integrations.const_set('Custom', @integration)
  689. end
  690. def test_should_define_a_singular_and_plural_with_scope
  691. StateMachine::Machine.new(Class.new, :integration => :custom, :plural => 'staties')
  692. assert_equal %w(with_state with_staties), @integration.with_scopes
  693. end
  694. def test_should_define_a_singular_and_plural_without_scope
  695. StateMachine::Machine.new(Class.new, :integration => :custom, :plural => 'staties')
  696. assert_equal %w(without_state without_staties), @integration.without_scopes
  697. end
  698. def test_should_define_single_with_scope_if_singular_same_as_plural
  699. StateMachine::Machine.new(Class.new, :integration => :custom, :plural => 'state')
  700. assert_equal %w(with_state), @integration.with_scopes
  701. end
  702. def test_should_define_single_without_scope_if_singular_same_as_plural
  703. StateMachine::Machine.new(Class.new, :integration => :custom, :plural => 'state')
  704. assert_equal %w(without_state), @integration.without_scopes
  705. end
  706. def teardown
  707. StateMachine::Integrations.send(:remove_const, 'Custom')
  708. end
  709. end
  710. class MachineWithCustomInvalidationTest < Test::Unit::TestCase
  711. def setup
  712. @integration = Module.new do
  713. include StateMachine::Integrations::Base
  714. def invalidate(object, attribute, message, values = [])
  715. object.error = generate_message(message, values)
  716. end
  717. end
  718. StateMachine::Integrations.const_set('Custom', @integration)
  719. @klass = Class.new do
  720. attr_accessor :error
  721. end
  722. @machine = StateMachine::Machine.new(@klass, :integration => :custom, :messages => {:invalid_transition => 'cannot %s'})
  723. @machine.state :parked
  724. @object = @klass.new
  725. @object.state = 'parked'
  726. end
  727. def test_generate_custom_message
  728. assert_equal 'cannot park', @machine.generate_message(:invalid_transition, [[:event, :park]])
  729. end
  730. def test_use_custom_message
  731. @machine.invalidate(@object, :state, :invalid_transition, [[:event, 'park']])
  732. assert_equal 'cannot park', @object.error
  733. end
  734. def teardown
  735. StateMachine::Integrations.send(:remove_const, 'Custom')
  736. end
  737. end
  738. class MachineTest < Test::Unit::TestCase
  739. def test_should_raise_exception_if_invalid_option_specified
  740. assert_raise(ArgumentError) {StateMachine::Machine.new(Class.new, :invalid => true)}
  741. end
  742. def test_should_not_raise_exception_if_custom_messages_specified
  743. assert_nothing_raised {StateMachine::Machine.new(Class.new, :messages => {:invalid_transition => 'custom'})}
  744. end
  745. def test_should_evaluate_a_block_during_initialization
  746. called = true
  747. StateMachine::Machine.new(Class.new) do
  748. called = respond_to?(:event)
  749. end
  750. assert called
  751. end
  752. def test_should_provide_matcher_helpers_during_initialization
  753. matchers = []
  754. StateMachine::Machine.new(Class.new) do
  755. matchers = [all, any, same]
  756. end
  757. assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
  758. end
  759. end
  760. class MachineAfterBeingCopiedTest < Test::Unit::TestCase
  761. def setup
  762. @machine = StateMachine::Machine.new(Class.new, :state, :initial => :parked)
  763. @machine.event(:ignite) {}
  764. @machine.before_transition(lambda {})
  765. @machine.after_transition(lambda {})
  766. @machine.around_transition(lambda {})
  767. @machine.after_failure(lambda {})
  768. @copied_machine = @machine.clone
  769. end
  770. def test_should_not_have_the_same_collection_of_states
  771. assert_not_same @copied_machine.states, @machine.states
  772. end
  773. def test_should_copy_each_state
  774. assert_not_same @copied_machine.states[:parked], @machine.states[:parked]
  775. end
  776. def test_should_update_machine_for_each_state
  777. assert_equal @copied_machine, @copied_machine.states[:parked].machine
  778. end
  779. def test_should_not_update_machine_for_original_state
  780. assert_equal @machine, @machine.states[:parked].machine
  781. end
  782. def test_should_not_have_the_same_collection_of_events
  783. assert_not_same @copied_machine.events, @machine.events
  784. end
  785. def test_should_copy_each_event
  786. assert_not_same @copied_machine.events[:ignite], @machine.events[:ignite]
  787. end
  788. def test_should_update_machine_for_each_event
  789. assert_equal @copied_machine, @copied_machine.events[:ignite].machine
  790. end
  791. def test_should_not_update_machine_for_original_event
  792. assert_equal @machine, @machine.events[:ignite].machine
  793. end
  794. def test_should_not_have_the_same_callbacks
  795. assert_not_same @copied_machine.callbacks, @machine.callbacks
  796. end
  797. def test_should_not_have_the_same_before_callbacks
  798. assert_not_same @copied_machine.callbacks[:before], @machine.callbacks[:before]
  799. end
  800. def test_should_not_have_the_same_after_callbacks
  801. assert_not_same @copied_machine.callbacks[:after], @machine.callbacks[:after]
  802. end
  803. def test_should_not_have_the_same_failure_callbacks
  804. assert_not_same @copied_machine.callbacks[:failure], @machine.callbacks[:failure]
  805. end
  806. end
  807. class MachineAfterChangingOwnerClassTest < Test::Unit::TestCase
  808. def setup
  809. @original_class = Class.new
  810. @machine = StateMachine::Machine.new(@original_class)
  811. @new_class = Class.new(@original_class)
  812. @new_machine = @machine.clone
  813. @new_machine.owner_class = @new_class
  814. @object = @new_class.new
  815. end
  816. def test_should_update_owner_class
  817. assert_equal @new_class, @new_machine.owner_class
  818. end
  819. def test_should_not_change_original_owner_class
  820. assert_equal @original_class, @machine.owner_class
  821. end
  822. def test_should_change_the_associated_machine_in_the_new_class
  823. assert_equal @new_machine, @new_class.state_machines[:state]
  824. end
  825. def test_should_not_change_the_associated_machine_in_the_original_class
  826. assert_equal @machine, @original_class.state_machines[:state]
  827. end
  828. end
  829. class MachineAfterChangingInitialState < Test::Unit::TestCase
  830. def setup
  831. @klass = Class.new
  832. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  833. @machine.initial_state = :idling
  834. @object = @klass.new
  835. end
  836. def test_should_change_the_initial_state
  837. assert_equal :idling, @machine.initial_state(@object).name
  838. end
  839. def test_should_include_in_known_states
  840. assert_equal [:parked, :idling], @machine.states.map {|state| state.name}
  841. end
  842. def test_should_reset_original_initial_state
  843. assert !@machine.state(:parked).initial
  844. end
  845. def test_should_set_new_state_to_initial
  846. assert @machine.state(:idling).initial
  847. end
  848. end
  849. class MachineWithHelpersTest < Test::Unit::TestCase
  850. def setup
  851. @klass = Class.new
  852. @machine = StateMachine::Machine.new(@klass)
  853. @object = @klass.new
  854. end
  855. def test_should_throw_exception_with_invalid_scope
  856. assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) { @machine.define_helper(:invalid, :park) {} }
  857. end
  858. end
  859. class MachineWithInstanceHelpersTest < Test::Unit::TestCase
  860. def setup
  861. @klass = Class.new
  862. @machine = StateMachine::Machine.new(@klass)
  863. @object = @klass.new
  864. end
  865. def test_should_not_redefine_existing_public_methods
  866. @klass.class_eval do
  867. def park
  868. true
  869. end
  870. end
  871. @machine.define_helper(:instance, :park) {}
  872. assert_equal true, @object.park
  873. end
  874. def test_should_not_redefine_existing_protected_methods
  875. @klass.class_eval do
  876. protected
  877. def park
  878. true
  879. end
  880. end
  881. @machine.define_helper(:instance, :park) {}
  882. assert_equal true, @object.send(:park)
  883. end
  884. def test_should_not_redefine_existing_private_methods
  885. @klass.class_eval do
  886. private
  887. def park
  888. true
  889. end
  890. end
  891. @machine.define_helper(:instance, :park) {}
  892. assert_equal true, @object.send(:park)
  893. end
  894. def test_should_warn_if_defined_in_superclass
  895. require 'stringio'
  896. @original_stderr, $stderr = $stderr, StringIO.new
  897. superclass = Class.new do
  898. def park
  899. end
  900. end
  901. klass = Class.new(superclass)
  902. machine = StateMachine::Machine.new(klass)
  903. machine.define_helper(:instance, :park) {}
  904. assert_equal "Instance method \"park\" is already defined in #{superclass.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
  905. ensure
  906. $stderr = @original_stderr
  907. end
  908. def test_should_warn_if_defined_in_multiple_superclasses
  909. require 'stringio'
  910. @original_stderr, $stderr = $stderr, StringIO.new
  911. superclass1 = Class.new do
  912. def park
  913. end
  914. end
  915. superclass2 = Class.new(superclass1) do
  916. def park
  917. end
  918. end
  919. klass = Class.new(superclass2)
  920. machine = StateMachine::Machine.new(klass)
  921. machine.define_helper(:instance, :park) {}
  922. assert_equal "Instance method \"park\" is already defined in #{superclass1.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
  923. ensure
  924. $stderr = @original_stderr
  925. end
  926. def test_should_warn_if_defined_in_module_prior_to_helper_module
  927. require 'stringio'
  928. @original_stderr, $stderr = $stderr, StringIO.new
  929. mod = Module.new do
  930. def park
  931. end
  932. end
  933. klass = Class.new do
  934. include mod
  935. end
  936. machine = StateMachine::Machine.new(klass)
  937. machine.define_helper(:instance, :park) {}
  938. assert_equal "Instance method \"park\" is already defined in #{mod.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
  939. ensure
  940. $stderr = @original_stderr
  941. end
  942. def test_should_not_warn_if_defined_in_module_after_helper_module
  943. require 'stringio'
  944. @original_stderr, $stderr = $stderr, StringIO.new
  945. klass = Class.new
  946. machine = StateMachine::Machine.new(klass)
  947. mod = Module.new do
  948. def park
  949. end
  950. end
  951. klass.class_eval do
  952. include mod
  953. end
  954. machine.define_helper(:instance, :park) {}
  955. assert_equal '', $stderr.string
  956. ensure
  957. $stderr = @original_stderr
  958. end
  959. def test_should_define_if_ignoring_method_conflicts_and_defined_in_superclass
  960. require 'stringio'
  961. @original_stderr, $stderr = $stderr, StringIO.new
  962. StateMachine::Machine.ignore_method_conflicts = true
  963. superclass = Class.new do
  964. def park
  965. end
  966. end
  967. klass = Class.new(superclass)
  968. machine = StateMachine::Machine.new(klass)
  969. machine.define_helper(:instance, :park) {true}
  970. assert_equal '', $stderr.string
  971. assert_equal true, klass.new.park
  972. ensure
  973. StateMachine::Machine.ignore_method_conflicts = false
  974. $stderr = @original_stderr
  975. end
  976. def test_should_define_nonexistent_methods
  977. @machine.define_helper(:instance, :park) {false}
  978. assert_equal false, @object.park
  979. end
  980. def test_should_warn_if_defined_multiple_times
  981. require 'stringio'
  982. @original_stderr, $stderr = $stderr, StringIO.new
  983. @machine.define_helper(:instance, :park) {}
  984. @machine.define_helper(:instance, :park) {}
  985. assert_equal "Instance method \"park\" is already defined in #{@klass} :state instance helpers, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
  986. ensure
  987. $stderr = @original_stderr
  988. end
  989. def test_should_pass_context_as_arguments
  990. helper_args = nil
  991. @machine.define_helper(:instance, :park) {|*args| helper_args = args}
  992. @object.park
  993. assert_equal 2, helper_args.length
  994. assert_equal [@machine, @object], helper_args
  995. end
  996. def test_should_pass_method_arguments_through
  997. helper_args = nil
  998. @machine.define_helper(:instance, :park) {|*args| helper_args = args}
  999. @object.park(1, 2, 3)
  1000. assert_equal 5, helper_args.length
  1001. assert_equal [@machine, @object, 1, 2, 3], helper_args
  1002. end
  1003. def test_should_allow_string_evaluation
  1004. @machine.define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
  1005. def park
  1006. false
  1007. end
  1008. end_eval
  1009. assert_equal false, @object.park
  1010. end
  1011. end
  1012. class MachineWithClassHelpersTest < Test::Unit::TestCase
  1013. def setup
  1014. @klass = Class.new
  1015. @machine = StateMachine::Machine.new(@klass)
  1016. end
  1017. def test_should_not_redefine_existing_public_methods
  1018. class << @klass
  1019. def states
  1020. []
  1021. end
  1022. end
  1023. @machine.define_helper(:class, :states) {}
  1024. assert_equal [], @klass.states
  1025. end
  1026. def test_should_not_redefine_existing_protected_methods
  1027. class << @klass
  1028. protected
  1029. def states
  1030. []
  1031. end
  1032. end
  1033. @machine.define_helper(:class, :states) {}
  1034. assert_equal [], @klass.send(:states)
  1035. end
  1036. def test_should_not_redefine_existing_private_methods
  1037. class << @klass
  1038. private
  1039. def states
  1040. []
  1041. end
  1042. end
  1043. @machine.define_helper(:class, :states) {}
  1044. assert_equal [], @klass.send(:states)
  1045. end
  1046. def test_should_warn_if_defined_in_superclass
  1047. require 'stringio'
  1048. @original_stderr, $stderr = $stderr, StringIO.new
  1049. superclass = Class.new do
  1050. def self.park
  1051. end
  1052. end
  1053. klass = Class.new(superclass)
  1054. machine = StateMachine::Machine.new(klass)
  1055. machine.define_helper(:class, :park) {}
  1056. assert_equal "Class method \"park\" is already defined in #{superclass.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
  1057. ensure
  1058. $stderr = @original_stderr
  1059. end
  1060. def test_should_warn_if_defined_in_multiple_superclasses
  1061. require 'stringio'
  1062. @original_stderr, $stderr = $stderr, StringIO.new
  1063. superclass1 = Class.new do
  1064. def self.park
  1065. end
  1066. end
  1067. superclass2 = Class.new(superclass1) do
  1068. def self.park
  1069. end
  1070. end
  1071. klass = Class.new(superclass2)
  1072. machine = StateMachine::Machine.new(klass)
  1073. machine.define_helper(:class, :park) {}
  1074. assert_equal "Class method \"park\" is already defined in #{superclass1.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
  1075. ensure
  1076. $stderr = @original_stderr
  1077. end
  1078. def test_should_warn_if_defined_in_module_prior_to_helper_module
  1079. require 'stringio'
  1080. @original_stderr, $stderr = $stderr, StringIO.new
  1081. mod = Module.new do
  1082. def park
  1083. end
  1084. end
  1085. klass = Class.new do
  1086. extend mod
  1087. end
  1088. machine = StateMachine::Machine.new(klass)
  1089. machine.define_helper(:class, :park) {}
  1090. assert_equal "Class method \"park\" is already defined in #{mod.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
  1091. ensure
  1092. $stderr = @original_stderr
  1093. end
  1094. def test_should_not_warn_if_defined_in_module_after_helper_module
  1095. require 'stringio'
  1096. @original_stderr, $stderr = $stderr, StringIO.new
  1097. klass = Class.new
  1098. machine = StateMachine::Machine.new(klass)
  1099. mod = Module.new do
  1100. def park
  1101. end
  1102. end
  1103. klass.class_eval do
  1104. extend mod
  1105. end
  1106. machine.define_helper(:class, :park) {}
  1107. assert_equal '', $stderr.string
  1108. ensure
  1109. $stderr = @original_stderr
  1110. end
  1111. def test_should_define_if_ignoring_method_conflicts_and_defined_in_superclass
  1112. require 'stringio'
  1113. @original_stderr, $stderr = $stderr, StringIO.new
  1114. StateMachine::Machine.ignore_method_conflicts = true
  1115. superclass = Class.new do
  1116. def self.park
  1117. end
  1118. end
  1119. klass = Class.new(superclass)
  1120. machine = StateMachine::Machine.new(klass)
  1121. machine.define_helper(:class, :park) {true}
  1122. assert_equal '', $stderr.string
  1123. assert_equal true, klass.park
  1124. ensure
  1125. StateMachine::Machine.ignore_method_conflicts = false
  1126. $stderr = @original_stderr
  1127. end
  1128. def test_should_define_nonexistent_methods
  1129. @machine.define_helper(:class, :states) {[]}
  1130. assert_equal [], @klass.states
  1131. end
  1132. def test_should_warn_if_defined_multiple_times
  1133. require 'stringio'
  1134. @original_stderr, $stderr = $stderr, StringIO.new
  1135. @machine.define_helper(:class, :states) {}
  1136. @machine.define_helper(:class, :states) {}
  1137. assert_equal "Class method \"states\" is already defined in #{@klass} :state class helpers, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n", $stderr.string
  1138. ensure
  1139. $stderr = @original_stderr
  1140. end
  1141. def test_should_pass_context_as_arguments
  1142. helper_args = nil
  1143. @machine.define_helper(:class, :states) {|*args| helper_args = args}
  1144. @klass.states
  1145. assert_equal 2, helper_args.length
  1146. assert_equal [@machine, @klass], helper_args
  1147. end
  1148. def test_should_pass_method_arguments_through
  1149. helper_args = nil
  1150. @machine.define_helper(:class, :states) {|*args| helper_args = args}
  1151. @klass.states(1, 2, 3)
  1152. assert_equal 5, helper_args.length
  1153. assert_equal [@machine, @klass, 1, 2, 3], helper_args
  1154. end
  1155. def test_should_allow_string_evaluation
  1156. @machine.define_helper :class, <<-end_eval, __FILE__, __LINE__ + 1
  1157. def states
  1158. []
  1159. end
  1160. end_eval
  1161. assert_equal [], @klass.states
  1162. end
  1163. end
  1164. class MachineWithConflictingHelpersBeforeDefinitionTest < Test::Unit::TestCase
  1165. def setup
  1166. require 'stringio'
  1167. @original_stderr, $stderr = $stderr, StringIO.new
  1168. @superclass = Class.new do
  1169. def self.with_state
  1170. :with_state
  1171. end
  1172. def self.with_states
  1173. :with_states
  1174. end
  1175. def self.without_state
  1176. :without_state
  1177. end
  1178. def self.without_states
  1179. :without_states
  1180. end
  1181. def self.human_state_name
  1182. :human_state_name
  1183. end
  1184. def self.human_state_event_name
  1185. :human_state_event_name
  1186. end
  1187. attr_accessor :status
  1188. def state
  1189. 'parked'
  1190. end
  1191. def state=(value)
  1192. self.status = value
  1193. end
  1194. def state?
  1195. true
  1196. end
  1197. def state_name
  1198. :parked
  1199. end
  1200. def human_state_name
  1201. 'parked'
  1202. end
  1203. def state_events
  1204. [:ignite]
  1205. end
  1206. def state_transitions
  1207. [{:parked => :idling}]
  1208. end
  1209. def state_paths
  1210. [[{:parked => :idling}]]
  1211. end
  1212. def fire_state_event
  1213. true
  1214. end
  1215. end
  1216. @klass = Class.new(@superclass)
  1217. StateMachine::Integrations.const_set('Custom', Module.new do
  1218. include StateMachine::Integrations::Base
  1219. def create_with_scope(name)
  1220. lambda {|klass, values| []}
  1221. end
  1222. def create_without_scope(name)
  1223. lambda {|klass, values| []}
  1224. end
  1225. end)
  1226. @machine = StateMachine::Machine.new(@klass, :integration => :custom)
  1227. @machine.state :parked, :idling
  1228. @machine.event :ignite
  1229. @object = @klass.new
  1230. end
  1231. def test_should_not_redefine_singular_with_scope
  1232. assert_equal :with_state, @klass.with_state
  1233. end
  1234. def test_should_not_redefine_plural_with_scope
  1235. assert_equal :with_states, @klass.with_states
  1236. end
  1237. def test_should_not_redefine_singular_without_scope
  1238. assert_equal :without_state, @klass.without_state
  1239. end
  1240. def test_should_not_redefine_plural_without_scope
  1241. assert_equal :without_states, @klass.without_states
  1242. end
  1243. def test_should_not_redefine_human_attribute_name_reader
  1244. assert_equal :human_state_name, @klass.human_state_name
  1245. end
  1246. def test_should_not_redefine_human_event_name_reader
  1247. assert_equal :human_state_event_name, @klass.human_state_event_name
  1248. end
  1249. def test_should_not_redefine_attribute_reader
  1250. assert_equal 'parked', @object.state
  1251. end
  1252. def test_should_not_redefine_attribute_writer
  1253. @object.state = 'parked'
  1254. assert_equal 'parked', @object.status
  1255. end
  1256. def test_should_not_define_attribute_predicate
  1257. assert @object.state?
  1258. end
  1259. def test_should_not_redefine_attribute_name_reader
  1260. assert_equal :parked, @object.state_name
  1261. end
  1262. def test_should_not_redefine_attribute_human_name_reader
  1263. assert_equal 'parked', @object.human_state_name
  1264. end
  1265. def test_should_not_redefine_attribute_events_reader
  1266. assert_equal [:ignite], @object.state_events
  1267. end
  1268. def test_should_not_redefine_attribute_transitions_reader
  1269. assert_equal [{:parked => :idling}], @object.state_transitions
  1270. end
  1271. def test_should_not_redefine_attribute_paths_reader
  1272. assert_equal [[{:parked => :idling}]], @object.state_paths
  1273. end
  1274. def test_should_not_redefine_event_runner
  1275. assert_equal true, @object.fire_state_event
  1276. end
  1277. def test_should_output_warning
  1278. expected = [
  1279. 'Instance method "state_events"',
  1280. 'Instance method "state_transitions"',
  1281. 'Instance method "fire_state_event"',
  1282. 'Instance method "state_paths"',
  1283. 'Class method "human_state_name"',
  1284. 'Class method "human_state_event_name"',
  1285. 'Instance method "state_name"',
  1286. 'Instance method "human_state_name"',
  1287. 'Class method "with_state"',
  1288. 'Class method "with_states"',
  1289. 'Class method "without_state"',
  1290. 'Class method "without_states"'
  1291. ].map {|method| "#{method} is already defined in #{@superclass.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n"}.join
  1292. assert_equal expected, $stderr.string
  1293. end
  1294. def teardown
  1295. $stderr = @original_stderr
  1296. StateMachine::Integrations.send(:remove_const, 'Custom')
  1297. end
  1298. end
  1299. class MachineWithConflictingHelpersAfterDefinitionTest < Test::Unit::TestCase
  1300. def setup
  1301. require 'stringio'
  1302. @original_stderr, $stderr = $stderr, StringIO.new
  1303. @klass = Class.new do
  1304. def self.with_state
  1305. :with_state
  1306. end
  1307. def self.with_states
  1308. :with_states
  1309. end
  1310. def self.without_state
  1311. :without_state
  1312. end
  1313. def self.without_states
  1314. :without_states
  1315. end
  1316. def self.human_state_name
  1317. :human_state_name
  1318. end
  1319. def self.human_state_event_name
  1320. :human_state_event_name
  1321. end
  1322. attr_accessor :status
  1323. def state
  1324. 'parked'
  1325. end
  1326. def state=(value)
  1327. self.status = value
  1328. end
  1329. def state?
  1330. true
  1331. end
  1332. def state_name
  1333. :parked
  1334. end
  1335. def human_state_name
  1336. 'parked'
  1337. end
  1338. def state_events
  1339. [:ignite]
  1340. end
  1341. def state_transitions
  1342. [{:parked => :idling}]
  1343. end
  1344. def state_paths
  1345. [[{:parked => :idling}]]
  1346. end
  1347. def fire_state_event
  1348. true
  1349. end
  1350. end
  1351. StateMachine::Integrations.const_set('Custom', Module.new do
  1352. include StateMachine::Integrations::Base
  1353. def create_with_scope(name)
  1354. lambda {|klass, values| []}
  1355. end
  1356. def create_without_scope(name)
  1357. lambda {|klass, values| []}
  1358. end
  1359. end)
  1360. @machine = StateMachine::Machine.new(@klass, :integration => :custom)
  1361. @machine.state :parked, :idling
  1362. @machine.event :ignite
  1363. @object = @klass.new
  1364. end
  1365. def test_should_not_redefine_singular_with_scope
  1366. assert_equal :with_state, @klass.with_state
  1367. end
  1368. def test_should_not_redefine_plural_with_scope
  1369. assert_equal :with_states, @klass.with_states
  1370. end
  1371. def test_should_not_redefine_singular_without_scope
  1372. assert_equal :without_state, @klass.without_state
  1373. end
  1374. def test_should_not_redefine_plural_without_scope
  1375. assert_equal :without_states, @klass.without_states
  1376. end
  1377. def test_should_not_redefine_human_attribute_name_reader
  1378. assert_equal :human_state_name, @klass.human_state_name
  1379. end
  1380. def test_should_not_redefine_human_event_name_reader
  1381. assert_equal :human_state_event_name, @klass.human_state_event_name
  1382. end
  1383. def test_should_not_redefine_attribute_reader
  1384. assert_equal 'parked', @object.state
  1385. end
  1386. def test_should_not_redefine_attribute_writer
  1387. @object.state = 'parked'
  1388. assert_equal 'parked', @object.status
  1389. end
  1390. def test_should_not_define_attribute_predicate
  1391. assert @object.state?
  1392. end
  1393. def test_should_not_redefine_attribute_name_reader
  1394. assert_equal :parked, @object.state_name
  1395. end
  1396. def test_should_not_redefine_attribute_human_name_reader
  1397. assert_equal 'parked', @object.human_state_name
  1398. end
  1399. def test_should_not_redefine_attribute_events_reader
  1400. assert_equal [:ignite], @object.state_events
  1401. end
  1402. def test_should_not_redefine_attribute_transitions_reader
  1403. assert_equal [{:parked => :idling}], @object.state_transitions
  1404. end
  1405. def test_should_not_redefine_attribute_paths_reader
  1406. assert_equal [[{:parked => :idling}]], @object.state_paths
  1407. end
  1408. def test_should_not_redefine_event_runner
  1409. assert_equal true, @object.fire_state_event
  1410. end
  1411. def test_should_allow_super_chaining
  1412. @klass.class_eval do
  1413. def self.with_state(*states)
  1414. super
  1415. end
  1416. def self.with_states(*states)
  1417. super
  1418. end
  1419. def self.without_state(*states)
  1420. super
  1421. end
  1422. def self.without_states(*states)
  1423. super
  1424. end
  1425. def self.human_state_name(state)
  1426. super
  1427. end
  1428. def self.human_state_event_name(event)
  1429. super
  1430. end
  1431. attr_accessor :status
  1432. def state
  1433. super
  1434. end
  1435. def state=(value)
  1436. super
  1437. end
  1438. def state?(state)
  1439. super
  1440. end
  1441. def state_name
  1442. super
  1443. end
  1444. def human_state_name
  1445. super
  1446. end
  1447. def state_events
  1448. super
  1449. end
  1450. def state_transitions
  1451. super
  1452. end
  1453. def state_paths
  1454. super
  1455. end
  1456. def fire_state_event(event)
  1457. super
  1458. end
  1459. end
  1460. assert_equal [], @klass.with_state
  1461. assert_equal [], @klass.with_states
  1462. assert_equal [], @klass.without_state
  1463. assert_equal [], @klass.without_states
  1464. assert_equal 'parked', @klass.human_state_name(:parked)
  1465. assert_equal 'ignite', @klass.human_state_event_name(:ignite)
  1466. assert_equal nil, @object.state
  1467. @object.state = 'idling'
  1468. assert_equal 'idling', @object.state
  1469. assert_equal nil, @object.status
  1470. assert_equal false, @object.state?(:parked)
  1471. assert_equal :idling, @object.state_name
  1472. assert_equal 'idling', @object.human_state_name
  1473. assert_equal [], @object.state_events
  1474. assert_equal [], @object.state_transitions
  1475. assert_equal [], @object.state_paths
  1476. assert_equal false, @object.fire_state_event(:ignite)
  1477. end
  1478. def test_should_not_output_warning
  1479. assert_equal '', $stderr.string
  1480. end
  1481. def teardown
  1482. $stderr = @original_stderr
  1483. StateMachine::Integrations.send(:remove_const, 'Custom')
  1484. end
  1485. end
  1486. class MachineWithSuperclassConflictingHelpersAfterDefinitionTest < Test::Unit::TestCase
  1487. def setup
  1488. require 'stringio'
  1489. @original_stderr, $stderr = $stderr, StringIO.new
  1490. @superclass = Class.new
  1491. @klass = Class.new(@superclass)
  1492. @machine = StateMachine::Machine.new(@klass)
  1493. @machine.state :parked, :idling
  1494. @machine.event :ignite
  1495. @superclass.class_eval do
  1496. def state?
  1497. true
  1498. end
  1499. end
  1500. @object = @klass.new
  1501. end
  1502. def test_should_call_superclass_attribute_predicate_without_arguments
  1503. assert @object.state?
  1504. end
  1505. def test_should_define_attribute_predicate_with_arguments
  1506. assert !@object.state?(:parked)
  1507. end
  1508. def teardown
  1509. $stderr = @original_stderr
  1510. end
  1511. end
  1512. class MachineWithoutInitializeTest < Test::Unit::TestCase
  1513. def setup
  1514. @klass = Class.new
  1515. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  1516. @object = @klass.new
  1517. end
  1518. def test_should_initialize_state
  1519. assert_equal 'parked', @object.state
  1520. end
  1521. end
  1522. class MachineWithInitializeWithoutSuperTest < Test::Unit::TestCase
  1523. def setup
  1524. @klass = Class.new do
  1525. def initialize
  1526. end
  1527. end
  1528. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  1529. @object = @klass.new
  1530. end
  1531. def test_should_not_initialize_state
  1532. assert_nil @object.state
  1533. end
  1534. end
  1535. class MachineWithInitializeAndSuperTest < Test::Unit::TestCase
  1536. def setup
  1537. @klass = Class.new do
  1538. def initialize
  1539. super()
  1540. end
  1541. end
  1542. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  1543. @object = @klass.new
  1544. end
  1545. def test_should_initialize_state
  1546. assert_equal 'parked', @object.state
  1547. end
  1548. end
  1549. class MachineWithInitializeArgumentsAndBlockTest < Test::Unit::TestCase
  1550. def setup
  1551. @superclass = Class.new do
  1552. attr_reader :args
  1553. attr_reader :block_given
  1554. def initialize(*args)
  1555. @args = args
  1556. @block_given = block_given?
  1557. end
  1558. end
  1559. @klass = Class.new(@superclass)
  1560. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  1561. @object = @klass.new(1, 2, 3) {}
  1562. end
  1563. def test_should_initialize_state
  1564. assert_equal 'parked', @object.state
  1565. end
  1566. def test_should_preserve_arguments
  1567. assert_equal [1, 2, 3], @object.args
  1568. end
  1569. def test_should_preserve_block
  1570. assert @object.block_given
  1571. end
  1572. end
  1573. class MachineWithCustomInitializeTest < Test::Unit::TestCase
  1574. def setup
  1575. @klass = Class.new do
  1576. def initialize(state = nil, options = {})
  1577. @state = state
  1578. initialize_state_machines(options)
  1579. end
  1580. end
  1581. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  1582. @object = @klass.new
  1583. end
  1584. def test_should_initialize_state
  1585. assert_equal 'parked', @object.state
  1586. end
  1587. def test_should_allow_custom_options
  1588. @machine.state :idling
  1589. @object = @klass.new('idling', :static => :force)
  1590. assert_equal 'parked', @object.state
  1591. end
  1592. end
  1593. class MachinePersistenceTest < Test::Unit::TestCase
  1594. def setup
  1595. @klass = Class.new do
  1596. attr_accessor :state_event
  1597. end
  1598. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  1599. @object = @klass.new
  1600. end
  1601. def test_should_allow_reading_state
  1602. assert_equal 'parked', @machine.read(@object, :state)
  1603. end
  1604. def test_should_allow_reading_custom_attributes
  1605. assert_nil @machine.read(@object, :event)
  1606. @object.state_event = 'ignite'
  1607. assert_equal 'ignite', @machine.read(@object, :event)
  1608. end
  1609. def test_should_allow_reading_custom_instance_variables
  1610. @klass.class_eval do
  1611. attr_writer :state_value
  1612. end
  1613. @object.state_value = 1
  1614. assert_raise(NoMethodError) { @machine.read(@object, :value) }
  1615. assert_equal 1, @machine.read(@object, :value, true)
  1616. end
  1617. def test_should_allow_writing_state
  1618. @machine.write(@object, :state, 'idling')
  1619. assert_equal 'idling', @object.state
  1620. end
  1621. def test_should_allow_writing_custom_attributes
  1622. @machine.write(@object, :event, 'ignite')
  1623. assert_equal 'ignite', @object.state_event
  1624. end
  1625. def test_should_allow_writing_custom_instance_variables
  1626. @klass.class_eval do
  1627. attr_reader :state_value
  1628. end
  1629. assert_raise(NoMethodError) { @machine.write(@object, :value, 1) }
  1630. assert_equal 1, @machine.write(@object, :value, 1, true)
  1631. assert_equal 1, @object.state_value
  1632. end
  1633. end
  1634. class MachineWithStatesTest < Test::Unit::TestCase
  1635. def setup
  1636. @klass = Class.new
  1637. @machine = StateMachine::Machine.new(@klass)
  1638. @parked, @idling = @machine.state :parked, :idling
  1639. @object = @klass.new
  1640. end
  1641. def test_should_have_states
  1642. assert_equal [nil, :parked, :idling], @machine.states.map {|state| state.name}
  1643. end
  1644. def test_should_allow_state_lookup_by_name
  1645. assert_equal @parked, @machine.states[:parked]
  1646. end
  1647. def test_should_allow_state_lookup_by_value
  1648. assert_equal @parked, @machine.states['parked', :value]
  1649. end
  1650. def test_should_allow_human_state_name_lookup
  1651. assert_equal 'parked', @klass.human_state_name(:parked)
  1652. end
  1653. def test_should_raise_exception_on_invalid_human_state_name_lookup
  1654. exception = assert_raise(IndexError) {@klass.human_state_name(:invalid)}
  1655. assert_equal ':invalid is an invalid name', exception.message
  1656. end
  1657. def test_should_use_stringified_name_for_value
  1658. assert_equal 'parked', @parked.value
  1659. end
  1660. def test_should_not_use_custom_matcher
  1661. assert_nil @parked.matcher
  1662. end
  1663. def test_should_raise_exception_if_invalid_option_specified
  1664. exception = assert_raise(ArgumentError) {@machine.state(:first_gear, :invalid => true)}
  1665. assert_equal 'Invalid key(s): invalid', exception.message
  1666. end
  1667. def test_should_raise_exception_if_conflicting_type_used_for_name
  1668. exception = assert_raise(ArgumentError) { @machine.state 'first_gear' }
  1669. assert_equal '"first_gear" state defined as String, :parked defined as Symbol; all states must be consistent', exception.message
  1670. end
  1671. def test_should_not_raise_exception_if_conflicting_type_is_nil_for_name
  1672. assert_nothing_raised { @machine.state nil }
  1673. end
  1674. end
  1675. class MachineWithStatesWithCustomValuesTest < Test::Unit::TestCase
  1676. def setup
  1677. @klass = Class.new
  1678. @machine = StateMachine::Machine.new(@klass)
  1679. @state = @machine.state :parked, :value => 1
  1680. @object = @klass.new
  1681. @object.state = 1
  1682. end
  1683. def test_should_use_custom_value
  1684. assert_equal 1, @state.value
  1685. end
  1686. def test_should_allow_lookup_by_custom_value
  1687. assert_equal @state, @machine.states[1, :value]
  1688. end
  1689. end
  1690. class MachineWithStatesWithCustomHumanNamesTest < Test::Unit::TestCase
  1691. def setup
  1692. @klass = Class.new
  1693. @machine = StateMachine::Machine.new(@klass)
  1694. @state = @machine.state :parked, :human_name => 'stopped'
  1695. end
  1696. def test_should_use_custom_human_name
  1697. assert_equal 'stopped', @state.human_name
  1698. end
  1699. def test_should_allow_human_state_name_lookup
  1700. assert_equal 'stopped', @klass.human_state_name(:parked)
  1701. end
  1702. end
  1703. class MachineWithStatesWithRuntimeDependenciesTest < Test::Unit::TestCase
  1704. def setup
  1705. @klass = Class.new
  1706. @machine = StateMachine::Machine.new(@klass)
  1707. @machine.state :parked
  1708. end
  1709. def test_should_not_evaluate_value_during_definition
  1710. assert_nothing_raised { @machine.state :parked, :value => lambda {raise ArgumentError} }
  1711. end
  1712. def test_should_not_evaluate_if_not_initial_state
  1713. @machine.state :parked, :value => lambda {raise ArgumentError}
  1714. assert_nothing_raised { @klass.new }
  1715. end
  1716. end
  1717. class MachineWithStateWithMatchersTest < Test::Unit::TestCase
  1718. def setup
  1719. @klass = Class.new
  1720. @machine = StateMachine::Machine.new(@klass)
  1721. @state = @machine.state :parked, :if => lambda {|value| !value.nil?}
  1722. @object = @klass.new
  1723. @object.state = 1
  1724. end
  1725. def test_should_use_custom_matcher
  1726. assert_not_nil @state.matcher
  1727. assert @state.matches?(1)
  1728. assert !@state.matches?(nil)
  1729. end
  1730. end
  1731. class MachineWithCachedStateTest < Test::Unit::TestCase
  1732. def setup
  1733. @klass = Class.new
  1734. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  1735. @state = @machine.state :parked, :value => lambda {Object.new}, :cache => true
  1736. @object = @klass.new
  1737. end
  1738. def test_should_use_evaluated_value
  1739. assert_instance_of Object, @object.state
  1740. end
  1741. def test_use_same_value_across_multiple_objects
  1742. assert_equal @object.state, @klass.new.state
  1743. end
  1744. end
  1745. class MachineWithStatesWithBehaviorsTest < Test::Unit::TestCase
  1746. def setup
  1747. @klass = Class.new
  1748. @machine = StateMachine::Machine.new(@klass)
  1749. @parked, @idling = @machine.state :parked, :idling do
  1750. def speed
  1751. 0
  1752. end
  1753. end
  1754. end
  1755. def test_should_define_behaviors_for_each_state
  1756. assert_not_nil @parked.context_methods[:speed]
  1757. assert_not_nil @idling.context_methods[:speed]
  1758. end
  1759. def test_should_define_different_behaviors_for_each_state
  1760. assert_not_equal @parked.context_methods[:speed], @idling.context_methods[:speed]
  1761. end
  1762. end
  1763. class MachineWithExistingStateTest < Test::Unit::TestCase
  1764. def setup
  1765. @klass = Class.new
  1766. @machine = StateMachine::Machine.new(@klass)
  1767. @state = @machine.state :parked
  1768. @same_state = @machine.state :parked, :value => 1
  1769. end
  1770. def test_should_not_create_a_new_state
  1771. assert_same @state, @same_state
  1772. end
  1773. def test_should_update_attributes
  1774. assert_equal 1, @state.value
  1775. end
  1776. def test_should_no_longer_be_able_to_look_up_state_by_original_value
  1777. assert_nil @machine.states['parked', :value]
  1778. end
  1779. def test_should_be_able_to_look_up_state_by_new_value
  1780. assert_equal @state, @machine.states[1, :value]
  1781. end
  1782. end
  1783. class MachineWithStateMatchersTest < Test::Unit::TestCase
  1784. def setup
  1785. @klass = Class.new
  1786. @machine = StateMachine::Machine.new(@klass)
  1787. end
  1788. def test_should_empty_array_for_all_matcher
  1789. assert_equal [], @machine.state(StateMachine::AllMatcher.instance)
  1790. end
  1791. def test_should_return_referenced_states_for_blacklist_matcher
  1792. assert_instance_of StateMachine::State, @machine.state(StateMachine::BlacklistMatcher.new([:parked]))
  1793. end
  1794. def test_should_not_allow_configurations
  1795. exception = assert_raise(ArgumentError) { @machine.state(StateMachine::BlacklistMatcher.new([:parked]), :human_name => 'Parked') }
  1796. assert_equal 'Cannot configure states when using matchers (using {:human_name=>"Parked"})', exception.message
  1797. end
  1798. def test_should_track_referenced_states
  1799. @machine.state(StateMachine::BlacklistMatcher.new([:parked]))
  1800. assert_equal [nil, :parked], @machine.states.map {|state| state.name}
  1801. end
  1802. def test_should_eval_context_for_matching_states
  1803. contexts_run = []
  1804. @machine.event(StateMachine::BlacklistMatcher.new([:parked])) { contexts_run << self.name }
  1805. @machine.event :parked
  1806. assert_equal [], contexts_run
  1807. @machine.event :idling
  1808. assert_equal [:idling], contexts_run
  1809. @machine.event :first_gear, :second_gear
  1810. assert_equal [:idling, :first_gear, :second_gear], contexts_run
  1811. end
  1812. end
  1813. class MachineWithOtherStates < Test::Unit::TestCase
  1814. def setup
  1815. @klass = Class.new
  1816. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  1817. @parked, @idling = @machine.other_states(:parked, :idling)
  1818. end
  1819. def test_should_include_other_states_in_known_states
  1820. assert_equal [@parked, @idling], @machine.states.to_a
  1821. end
  1822. def test_should_use_default_value
  1823. assert_equal 'idling', @idling.value
  1824. end
  1825. def test_should_not_create_matcher
  1826. assert_nil @idling.matcher
  1827. end
  1828. end
  1829. class MachineWithEventsTest < Test::Unit::TestCase
  1830. def setup
  1831. @klass = Class.new
  1832. @machine = StateMachine::Machine.new(@klass)
  1833. end
  1834. def test_should_return_the_created_event
  1835. assert_instance_of StateMachine::Event, @machine.event(:ignite)
  1836. end
  1837. def test_should_create_event_with_given_name
  1838. event = @machine.event(:ignite) {}
  1839. assert_equal :ignite, event.name
  1840. end
  1841. def test_should_evaluate_block_within_event_context
  1842. responded = false
  1843. @machine.event :ignite do
  1844. responded = respond_to?(:transition)
  1845. end
  1846. assert responded
  1847. end
  1848. def test_should_be_aliased_as_on
  1849. event = @machine.on(:ignite) {}
  1850. assert_equal :ignite, event.name
  1851. end
  1852. def test_should_have_events
  1853. event = @machine.event(:ignite)
  1854. assert_equal [event], @machine.events.to_a
  1855. end
  1856. def test_should_allow_human_state_name_lookup
  1857. @machine.event(:ignite)
  1858. assert_equal 'ignite', @klass.human_state_event_name(:ignite)
  1859. end
  1860. def test_should_raise_exception_on_invalid_human_state_event_name_lookup
  1861. exception = assert_raise(IndexError) {@klass.human_state_event_name(:invalid)}
  1862. assert_equal ':invalid is an invalid name', exception.message
  1863. end
  1864. def test_should_raise_exception_if_conflicting_type_used_for_name
  1865. @machine.event :park
  1866. exception = assert_raise(ArgumentError) { @machine.event 'ignite' }
  1867. assert_equal '"ignite" event defined as String, :park defined as Symbol; all events must be consistent', exception.message
  1868. end
  1869. end
  1870. class MachineWithExistingEventTest < Test::Unit::TestCase
  1871. def setup
  1872. @machine = StateMachine::Machine.new(Class.new)
  1873. @event = @machine.event(:ignite)
  1874. @same_event = @machine.event(:ignite)
  1875. end
  1876. def test_should_not_create_new_event
  1877. assert_same @event, @same_event
  1878. end
  1879. def test_should_allow_accessing_event_without_block
  1880. assert_equal @event, @machine.event(:ignite)
  1881. end
  1882. end
  1883. class MachineWithEventsWithCustomHumanNamesTest < Test::Unit::TestCase
  1884. def setup
  1885. @klass = Class.new
  1886. @machine = StateMachine::Machine.new(@klass)
  1887. @event = @machine.event(:ignite, :human_name => 'start')
  1888. end
  1889. def test_should_use_custom_human_name
  1890. assert_equal 'start', @event.human_name
  1891. end
  1892. def test_should_allow_human_state_name_lookup
  1893. assert_equal 'start', @klass.human_state_event_name(:ignite)
  1894. end
  1895. end
  1896. class MachineWithEventMatchersTest < Test::Unit::TestCase
  1897. def setup
  1898. @klass = Class.new
  1899. @machine = StateMachine::Machine.new(@klass)
  1900. end
  1901. def test_should_empty_array_for_all_matcher
  1902. assert_equal [], @machine.event(StateMachine::AllMatcher.instance)
  1903. end
  1904. def test_should_return_referenced_events_for_blacklist_matcher
  1905. assert_instance_of StateMachine::Event, @machine.event(StateMachine::BlacklistMatcher.new([:park]))
  1906. end
  1907. def test_should_not_allow_configurations
  1908. exception = assert_raise(ArgumentError) { @machine.event(StateMachine::BlacklistMatcher.new([:park]), :human_name => 'Park') }
  1909. assert_equal 'Cannot configure events when using matchers (using {:human_name=>"Park"})', exception.message
  1910. end
  1911. def test_should_track_referenced_events
  1912. @machine.event(StateMachine::BlacklistMatcher.new([:park]))
  1913. assert_equal [:park], @machine.events.map {|event| event.name}
  1914. end
  1915. def test_should_eval_context_for_matching_events
  1916. contexts_run = []
  1917. @machine.event(StateMachine::BlacklistMatcher.new([:park])) { contexts_run << self.name }
  1918. @machine.event :park
  1919. assert_equal [], contexts_run
  1920. @machine.event :ignite
  1921. assert_equal [:ignite], contexts_run
  1922. @machine.event :shift_up, :shift_down
  1923. assert_equal [:ignite, :shift_up, :shift_down], contexts_run
  1924. end
  1925. end
  1926. class MachineWithEventsWithTransitionsTest < Test::Unit::TestCase
  1927. def setup
  1928. @klass = Class.new
  1929. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  1930. @event = @machine.event(:ignite) do
  1931. transition :parked => :idling
  1932. transition :stalled => :idling
  1933. end
  1934. end
  1935. def test_should_have_events
  1936. assert_equal [@event], @machine.events.to_a
  1937. end
  1938. def test_should_track_states_defined_in_event_transitions
  1939. assert_equal [:parked, :idling, :stalled], @machine.states.map {|state| state.name}
  1940. end
  1941. def test_should_not_duplicate_states_defined_in_multiple_event_transitions
  1942. @machine.event :park do
  1943. transition :idling => :parked
  1944. end
  1945. assert_equal [:parked, :idling, :stalled], @machine.states.map {|state| state.name}
  1946. end
  1947. def test_should_track_state_from_new_events
  1948. @machine.event :shift_up do
  1949. transition :idling => :first_gear
  1950. end
  1951. assert_equal [:parked, :idling, :stalled, :first_gear], @machine.states.map {|state| state.name}
  1952. end
  1953. end
  1954. class MachineWithMultipleEventsTest < Test::Unit::TestCase
  1955. def setup
  1956. @klass = Class.new
  1957. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  1958. @park, @shift_down = @machine.event(:park, :shift_down) do
  1959. transition :first_gear => :parked
  1960. end
  1961. end
  1962. def test_should_have_events
  1963. assert_equal [@park, @shift_down], @machine.events.to_a
  1964. end
  1965. def test_should_define_transitions_for_each_event
  1966. [@park, @shift_down].each {|event| assert_equal 1, event.branches.size}
  1967. end
  1968. def test_should_transition_the_same_for_each_event
  1969. object = @klass.new
  1970. object.state = 'first_gear'
  1971. object.park
  1972. assert_equal 'parked', object.state
  1973. object = @klass.new
  1974. object.state = 'first_gear'
  1975. object.shift_down
  1976. assert_equal 'parked', object.state
  1977. end
  1978. end
  1979. class MachineWithTransitionsTest < Test::Unit::TestCase
  1980. def setup
  1981. @klass = Class.new
  1982. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  1983. end
  1984. def test_should_require_on_event
  1985. exception = assert_raise(ArgumentError) { @machine.transition(:parked => :idling) }
  1986. assert_equal 'Must specify :on event', exception.message
  1987. end
  1988. def test_should_not_allow_except_on_option
  1989. exception = assert_raise(ArgumentError) {@machine.transition(:except_on => :ignite, :on => :ignite)}
  1990. assert_equal 'Invalid key(s): except_on', exception.message
  1991. end
  1992. def test_should_allow_transitioning_without_a_to_state
  1993. assert_nothing_raised {@machine.transition(:from => :parked, :on => :ignite)}
  1994. end
  1995. def test_should_allow_transitioning_without_a_from_state
  1996. assert_nothing_raised {@machine.transition(:to => :idling, :on => :ignite)}
  1997. end
  1998. def test_should_allow_except_from_option
  1999. assert_nothing_raised {@machine.transition(:except_from => :idling, :on => :ignite)}
  2000. end
  2001. def test_should_allow_except_to_option
  2002. assert_nothing_raised {@machine.transition(:except_to => :parked, :on => :ignite)}
  2003. end
  2004. def test_should_allow_implicit_options
  2005. branch = @machine.transition(:first_gear => :second_gear, :on => :shift_up)
  2006. assert_instance_of StateMachine::Branch, branch
  2007. state_requirements = branch.state_requirements
  2008. assert_equal 1, state_requirements.length
  2009. assert_instance_of StateMachine::WhitelistMatcher, state_requirements[0][:from]
  2010. assert_equal [:first_gear], state_requirements[0][:from].values
  2011. assert_instance_of StateMachine::WhitelistMatcher, state_requirements[0][:to]
  2012. assert_equal [:second_gear], state_requirements[0][:to].values
  2013. assert_instance_of StateMachine::WhitelistMatcher, branch.event_requirement
  2014. assert_equal [:shift_up], branch.event_requirement.values
  2015. end
  2016. def test_should_allow_multiple_implicit_options
  2017. branch = @machine.transition(:first_gear => :second_gear, :second_gear => :third_gear, :on => :shift_up)
  2018. state_requirements = branch.state_requirements
  2019. assert_equal 2, state_requirements.length
  2020. end
  2021. def test_should_allow_verbose_options
  2022. branch = @machine.transition(:from => :parked, :to => :idling, :on => :ignite)
  2023. assert_instance_of StateMachine::Branch, branch
  2024. end
  2025. def test_should_include_all_transition_states_in_machine_states
  2026. @machine.transition(:parked => :idling, :on => :ignite)
  2027. assert_equal [:parked, :idling], @machine.states.map {|state| state.name}
  2028. end
  2029. def test_should_include_all_transition_events_in_machine_events
  2030. @machine.transition(:parked => :idling, :on => :ignite)
  2031. assert_equal [:ignite], @machine.events.map {|event| event.name}
  2032. end
  2033. def test_should_allow_multiple_events
  2034. branches = @machine.transition(:parked => :ignite, :on => [:ignite, :shift_up])
  2035. assert_equal 2, branches.length
  2036. assert_equal [:ignite, :shift_up], @machine.events.map {|event| event.name}
  2037. end
  2038. def test_should_not_modify_options
  2039. options = {:parked => :idling, :on => :ignite}
  2040. @machine.transition(options)
  2041. assert_equal options, {:parked => :idling, :on => :ignite}
  2042. end
  2043. end
  2044. class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
  2045. def setup
  2046. @klass = Class.new do
  2047. attr_accessor :callbacks
  2048. end
  2049. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  2050. @event = @machine.event :ignite do
  2051. transition :parked => :idling
  2052. end
  2053. @object = @klass.new
  2054. @object.callbacks = []
  2055. end
  2056. def test_should_not_raise_exception_if_implicit_option_specified
  2057. assert_nothing_raised {@machine.before_transition :invalid => :valid, :do => lambda {}}
  2058. end
  2059. def test_should_raise_exception_if_method_not_specified
  2060. exception = assert_raise(ArgumentError) {@machine.before_transition :to => :idling}
  2061. assert_equal 'Method(s) for callback must be specified', exception.message
  2062. end
  2063. def test_should_invoke_callbacks_during_transition
  2064. @machine.before_transition lambda {|object| object.callbacks << 'before'}
  2065. @machine.after_transition lambda {|object| object.callbacks << 'after'}
  2066. @machine.around_transition lambda {|object, transition, block| object.callbacks << 'before_around'; block.call; object.callbacks << 'after_around'}
  2067. @event.fire(@object)
  2068. assert_equal %w(before before_around after_around after), @object.callbacks
  2069. end
  2070. def test_should_allow_multiple_callbacks
  2071. @machine.before_transition lambda {|object| object.callbacks << 'before1'}, lambda {|object| object.callbacks << 'before2'}
  2072. @machine.after_transition lambda {|object| object.callbacks << 'after1'}, lambda {|object| object.callbacks << 'after2'}
  2073. @machine.around_transition(
  2074. lambda {|object, transition, block| object.callbacks << 'before_around1'; block.call; object.callbacks << 'after_around1'},
  2075. lambda {|object, transition, block| object.callbacks << 'before_around2'; block.call; object.callbacks << 'after_around2'}
  2076. )
  2077. @event.fire(@object)
  2078. assert_equal %w(before1 before2 before_around1 before_around2 after_around2 after_around1 after1 after2), @object.callbacks
  2079. end
  2080. def test_should_allow_multiple_callbacks_with_requirements
  2081. @machine.before_transition lambda {|object| object.callbacks << 'before_parked1'}, lambda {|object| object.callbacks << 'before_parked2'}, :from => :parked
  2082. @machine.before_transition lambda {|object| object.callbacks << 'before_idling1'}, lambda {|object| object.callbacks << 'before_idling2'}, :from => :idling
  2083. @machine.after_transition lambda {|object| object.callbacks << 'after_parked1'}, lambda {|object| object.callbacks << 'after_parked2'}, :from => :parked
  2084. @machine.after_transition lambda {|object| object.callbacks << 'after_idling1'}, lambda {|object| object.callbacks << 'after_idling2'}, :from => :idling
  2085. @machine.around_transition(
  2086. lambda {|object, transition, block| object.callbacks << 'before_around_parked1'; block.call; object.callbacks << 'after_around_parked1'},
  2087. lambda {|object, transition, block| object.callbacks << 'before_around_parked2'; block.call; object.callbacks << 'after_around_parked2'},
  2088. :from => :parked
  2089. )
  2090. @machine.around_transition(
  2091. lambda {|object, transition, block| object.callbacks << 'before_around_idling1'; block.call; object.callbacks << 'after_around_idling1'},
  2092. lambda {|object, transition, block| object.callbacks << 'before_around_idling2'; block.call; object.callbacks << 'after_around_idling2'},
  2093. :from => :idling
  2094. )
  2095. @event.fire(@object)
  2096. 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
  2097. end
  2098. def test_should_support_from_requirement
  2099. @machine.before_transition :from => :parked, :do => lambda {|object| object.callbacks << :parked}
  2100. @machine.before_transition :from => :idling, :do => lambda {|object| object.callbacks << :idling}
  2101. @event.fire(@object)
  2102. assert_equal [:parked], @object.callbacks
  2103. end
  2104. def test_should_support_except_from_requirement
  2105. @machine.before_transition :except_from => :parked, :do => lambda {|object| object.callbacks << :parked}
  2106. @machine.before_transition :except_from => :idling, :do => lambda {|object| object.callbacks << :idling}
  2107. @event.fire(@object)
  2108. assert_equal [:idling], @object.callbacks
  2109. end
  2110. def test_should_support_to_requirement
  2111. @machine.before_transition :to => :parked, :do => lambda {|object| object.callbacks << :parked}
  2112. @machine.before_transition :to => :idling, :do => lambda {|object| object.callbacks << :idling}
  2113. @event.fire(@object)
  2114. assert_equal [:idling], @object.callbacks
  2115. end
  2116. def test_should_support_except_to_requirement
  2117. @machine.before_transition :except_to => :parked, :do => lambda {|object| object.callbacks << :parked}
  2118. @machine.before_transition :except_to => :idling, :do => lambda {|object| object.callbacks << :idling}
  2119. @event.fire(@object)
  2120. assert_equal [:parked], @object.callbacks
  2121. end
  2122. def test_should_support_on_requirement
  2123. @machine.before_transition :on => :park, :do => lambda {|object| object.callbacks << :park}
  2124. @machine.before_transition :on => :ignite, :do => lambda {|object| object.callbacks << :ignite}
  2125. @event.fire(@object)
  2126. assert_equal [:ignite], @object.callbacks
  2127. end
  2128. def test_should_support_except_on_requirement
  2129. @machine.before_transition :except_on => :park, :do => lambda {|object| object.callbacks << :park}
  2130. @machine.before_transition :except_on => :ignite, :do => lambda {|object| object.callbacks << :ignite}
  2131. @event.fire(@object)
  2132. assert_equal [:park], @object.callbacks
  2133. end
  2134. def test_should_support_implicit_requirement
  2135. @machine.before_transition :parked => :idling, :do => lambda {|object| object.callbacks << :parked}
  2136. @machine.before_transition :idling => :parked, :do => lambda {|object| object.callbacks << :idling}
  2137. @event.fire(@object)
  2138. assert_equal [:parked], @object.callbacks
  2139. end
  2140. def test_should_track_states_defined_in_transition_callbacks
  2141. @machine.before_transition :parked => :idling, :do => lambda {}
  2142. @machine.after_transition :first_gear => :second_gear, :do => lambda {}
  2143. @machine.around_transition :third_gear => :fourth_gear, :do => lambda {}
  2144. assert_equal [:parked, :idling, :first_gear, :second_gear, :third_gear, :fourth_gear], @machine.states.map {|state| state.name}
  2145. end
  2146. def test_should_not_duplicate_states_defined_in_multiple_event_transitions
  2147. @machine.before_transition :parked => :idling, :do => lambda {}
  2148. @machine.after_transition :first_gear => :second_gear, :do => lambda {}
  2149. @machine.after_transition :parked => :idling, :do => lambda {}
  2150. @machine.around_transition :parked => :idling, :do => lambda {}
  2151. assert_equal [:parked, :idling, :first_gear, :second_gear], @machine.states.map {|state| state.name}
  2152. end
  2153. def test_should_define_predicates_for_each_state
  2154. [:parked?, :idling?].each {|predicate| assert @object.respond_to?(predicate)}
  2155. end
  2156. end
  2157. class MachineWithFailureCallbacksTest < Test::Unit::TestCase
  2158. def setup
  2159. @klass = Class.new do
  2160. attr_accessor :callbacks
  2161. end
  2162. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  2163. @event = @machine.event :ignite
  2164. @object = @klass.new
  2165. @object.callbacks = []
  2166. end
  2167. def test_should_raise_exception_if_implicit_option_specified
  2168. exception = assert_raise(ArgumentError) {@machine.after_failure :invalid => :valid, :do => lambda {}}
  2169. assert_equal 'Invalid key(s): invalid', exception.message
  2170. end
  2171. def test_should_raise_exception_if_method_not_specified
  2172. exception = assert_raise(ArgumentError) {@machine.after_failure :on => :ignite}
  2173. assert_equal 'Method(s) for callback must be specified', exception.message
  2174. end
  2175. def test_should_invoke_callbacks_during_failed_transition
  2176. @machine.after_failure lambda {|object| object.callbacks << 'failure'}
  2177. @event.fire(@object)
  2178. assert_equal %w(failure), @object.callbacks
  2179. end
  2180. def test_should_allow_multiple_callbacks
  2181. @machine.after_failure lambda {|object| object.callbacks << 'failure1'}, lambda {|object| object.callbacks << 'failure2'}
  2182. @event.fire(@object)
  2183. assert_equal %w(failure1 failure2), @object.callbacks
  2184. end
  2185. def test_should_allow_multiple_callbacks_with_requirements
  2186. @machine.after_failure lambda {|object| object.callbacks << 'failure_ignite1'}, lambda {|object| object.callbacks << 'failure_ignite2'}, :on => :ignite
  2187. @machine.after_failure lambda {|object| object.callbacks << 'failure_park1'}, lambda {|object| object.callbacks << 'failure_park2'}, :on => :park
  2188. @event.fire(@object)
  2189. assert_equal %w(failure_ignite1 failure_ignite2), @object.callbacks
  2190. end
  2191. end
  2192. class MachineWithPathsTest < Test::Unit::TestCase
  2193. def setup
  2194. @klass = Class.new
  2195. @machine = StateMachine::Machine.new(@klass)
  2196. @machine.event :ignite do
  2197. transition :parked => :idling
  2198. end
  2199. @machine.event :shift_up do
  2200. transition :first_gear => :second_gear
  2201. end
  2202. @object = @klass.new
  2203. @object.state = 'parked'
  2204. end
  2205. def test_should_have_paths
  2206. assert_equal [[StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)]], @machine.paths_for(@object)
  2207. end
  2208. def test_should_allow_requirement_configuration
  2209. assert_equal [[StateMachine::Transition.new(@object, @machine, :shift_up, :first_gear, :second_gear)]], @machine.paths_for(@object, :from => :first_gear)
  2210. end
  2211. end
  2212. class MachineWithOwnerSubclassTest < Test::Unit::TestCase
  2213. def setup
  2214. @klass = Class.new
  2215. @machine = StateMachine::Machine.new(@klass)
  2216. @subclass = Class.new(@klass)
  2217. end
  2218. def test_should_have_a_different_collection_of_state_machines
  2219. assert_not_same @klass.state_machines, @subclass.state_machines
  2220. end
  2221. def test_should_have_the_same_attribute_associated_state_machines
  2222. assert_equal @klass.state_machines, @subclass.state_machines
  2223. end
  2224. end
  2225. class MachineWithExistingMachinesOnOwnerClassTest < Test::Unit::TestCase
  2226. def setup
  2227. @klass = Class.new
  2228. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  2229. @second_machine = StateMachine::Machine.new(@klass, :status, :initial => :idling)
  2230. @object = @klass.new
  2231. end
  2232. def test_should_track_each_state_machine
  2233. expected = {:state => @machine, :status => @second_machine}
  2234. assert_equal expected, @klass.state_machines
  2235. end
  2236. def test_should_initialize_state_for_both_machines
  2237. assert_equal 'parked', @object.state
  2238. assert_equal 'idling', @object.status
  2239. end
  2240. end
  2241. class MachineWithExistingMachinesWithSameAttributesOnOwnerClassTest < Test::Unit::TestCase
  2242. def setup
  2243. @klass = Class.new
  2244. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  2245. @second_machine = StateMachine::Machine.new(@klass, :public_state, :initial => :idling, :attribute => :state)
  2246. @object = @klass.new
  2247. end
  2248. def test_should_track_each_state_machine
  2249. expected = {:state => @machine, :public_state => @second_machine}
  2250. assert_equal expected, @klass.state_machines
  2251. end
  2252. def test_should_write_to_state_only_once
  2253. @klass.class_eval do
  2254. attr_reader :write_count
  2255. def state=(value)
  2256. @write_count ||= 0
  2257. @write_count += 1
  2258. end
  2259. end
  2260. object = @klass.new
  2261. assert_equal 1, object.write_count
  2262. end
  2263. def test_should_initialize_based_on_first_machine
  2264. assert_equal 'parked', @object.state
  2265. end
  2266. def test_should_not_allow_second_machine_to_initialize_state
  2267. @object.state = nil
  2268. @second_machine.initialize_state(@object)
  2269. assert_nil @object.state
  2270. end
  2271. def test_should_allow_transitions_on_both_machines
  2272. @machine.event :ignite do
  2273. transition :parked => :idling
  2274. end
  2275. @second_machine.event :park do
  2276. transition :idling => :parked
  2277. end
  2278. @object.ignite
  2279. assert_equal 'idling', @object.state
  2280. @object.park
  2281. assert_equal 'parked', @object.state
  2282. end
  2283. def test_should_copy_new_states_to_sibling_machines
  2284. @first_gear = @machine.state :first_gear
  2285. assert_equal @first_gear, @second_machine.state(:first_gear)
  2286. @second_gear = @second_machine.state :second_gear
  2287. assert_equal @second_gear, @machine.state(:second_gear)
  2288. end
  2289. def test_should_copy_all_existing_states_to_new_machines
  2290. third_machine = StateMachine::Machine.new(@klass, :protected_state, :attribute => :state)
  2291. assert_equal @machine.state(:parked), third_machine.state(:parked)
  2292. assert_equal @machine.state(:idling), third_machine.state(:idling)
  2293. end
  2294. end
  2295. class MachineWithExistingMachinesWithSameAttributesOnOwnerSubclassTest < Test::Unit::TestCase
  2296. def setup
  2297. @klass = Class.new
  2298. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  2299. @second_machine = StateMachine::Machine.new(@klass, :public_state, :initial => :idling, :attribute => :state)
  2300. @subclass = Class.new(@klass)
  2301. @object = @subclass.new
  2302. end
  2303. def test_should_not_copy_sibling_machines_to_subclass_after_initialization
  2304. @subclass.state_machine(:state) {}
  2305. assert_equal @klass.state_machine(:public_state), @subclass.state_machine(:public_state)
  2306. end
  2307. def test_should_copy_sibling_machines_to_subclass_after_new_state
  2308. subclass_machine = @subclass.state_machine(:state) {}
  2309. subclass_machine.state :first_gear
  2310. assert_not_equal @klass.state_machine(:public_state), @subclass.state_machine(:public_state)
  2311. end
  2312. def test_should_copy_new_states_to_sibling_machines
  2313. subclass_machine = @subclass.state_machine(:state) {}
  2314. @first_gear = subclass_machine.state :first_gear
  2315. second_subclass_machine = @subclass.state_machine(:public_state)
  2316. assert_equal @first_gear, second_subclass_machine.state(:first_gear)
  2317. end
  2318. end
  2319. class MachineWithNamespaceTest < Test::Unit::TestCase
  2320. def setup
  2321. @klass = Class.new
  2322. @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm', :initial => :active) do
  2323. event :enable do
  2324. transition :off => :active
  2325. end
  2326. event :disable do
  2327. transition :active => :off
  2328. end
  2329. end
  2330. @object = @klass.new
  2331. end
  2332. def test_should_namespace_state_predicates
  2333. [:alarm_active?, :alarm_off?].each do |name|
  2334. assert @object.respond_to?(name)
  2335. end
  2336. end
  2337. def test_should_namespace_event_checks
  2338. [:can_enable_alarm?, :can_disable_alarm?].each do |name|
  2339. assert @object.respond_to?(name)
  2340. end
  2341. end
  2342. def test_should_namespace_event_transition_readers
  2343. [:enable_alarm_transition, :disable_alarm_transition].each do |name|
  2344. assert @object.respond_to?(name)
  2345. end
  2346. end
  2347. def test_should_namespace_events
  2348. [:enable_alarm, :disable_alarm].each do |name|
  2349. assert @object.respond_to?(name)
  2350. end
  2351. end
  2352. def test_should_namespace_bang_events
  2353. [:enable_alarm!, :disable_alarm!].each do |name|
  2354. assert @object.respond_to?(name)
  2355. end
  2356. end
  2357. end
  2358. class MachineWithCustomAttributeTest < Test::Unit::TestCase
  2359. def setup
  2360. StateMachine::Integrations.const_set('Custom', Module.new do
  2361. include StateMachine::Integrations::Base
  2362. @defaults = {:action => :save, :use_transactions => false}
  2363. def create_with_scope(name)
  2364. lambda {}
  2365. end
  2366. def create_without_scope(name)
  2367. lambda {}
  2368. end
  2369. end)
  2370. @klass = Class.new
  2371. @machine = StateMachine::Machine.new(@klass, :state, :attribute => :state_id, :initial => :active, :integration => :custom) do
  2372. event :ignite do
  2373. transition :parked => :idling
  2374. end
  2375. end
  2376. @object = @klass.new
  2377. end
  2378. def test_should_define_a_reader_attribute_for_the_attribute
  2379. assert @object.respond_to?(:state_id)
  2380. end
  2381. def test_should_define_a_writer_attribute_for_the_attribute
  2382. assert @object.respond_to?(:state_id=)
  2383. end
  2384. def test_should_define_a_predicate_for_the_attribute
  2385. assert @object.respond_to?(:state?)
  2386. end
  2387. def test_should_define_a_name_reader_for_the_attribute
  2388. assert @object.respond_to?(:state_name)
  2389. end
  2390. def test_should_define_a_human_name_reader_for_the_attribute
  2391. assert @object.respond_to?(:state_name)
  2392. end
  2393. def test_should_define_an_event_reader_for_the_attribute
  2394. assert @object.respond_to?(:state_events)
  2395. end
  2396. def test_should_define_a_transition_reader_for_the_attribute
  2397. assert @object.respond_to?(:state_transitions)
  2398. end
  2399. def test_should_define_a_path_reader_for_the_attribute
  2400. assert @object.respond_to?(:state_paths)
  2401. end
  2402. def test_should_define_an_event_runner_for_the_attribute
  2403. assert @object.respond_to?(:fire_state_event)
  2404. end
  2405. def test_should_define_a_human_attribute_name_reader
  2406. assert @klass.respond_to?(:human_state_name)
  2407. end
  2408. def test_should_define_a_human_event_name_reader
  2409. assert @klass.respond_to?(:human_state_event_name)
  2410. end
  2411. def test_should_define_singular_with_scope
  2412. assert @klass.respond_to?(:with_state)
  2413. end
  2414. def test_should_define_singular_without_scope
  2415. assert @klass.respond_to?(:without_state)
  2416. end
  2417. def test_should_define_plural_with_scope
  2418. assert @klass.respond_to?(:with_states)
  2419. end
  2420. def test_should_define_plural_without_scope
  2421. assert @klass.respond_to?(:without_states)
  2422. end
  2423. def test_should_define_state_machines_reader
  2424. expected = {:state => @machine}
  2425. assert_equal expected, @klass.state_machines
  2426. end
  2427. def teardown
  2428. StateMachine::Integrations.send(:remove_const, 'Custom')
  2429. end
  2430. end
  2431. class MachineFinderWithoutExistingMachineTest < Test::Unit::TestCase
  2432. def setup
  2433. @klass = Class.new
  2434. @machine = StateMachine::Machine.find_or_create(@klass)
  2435. end
  2436. def test_should_accept_a_block
  2437. called = false
  2438. StateMachine::Machine.find_or_create(Class.new) do
  2439. called = respond_to?(:event)
  2440. end
  2441. assert called
  2442. end
  2443. def test_should_create_a_new_machine
  2444. assert_not_nil @machine
  2445. end
  2446. def test_should_use_default_state
  2447. assert_equal :state, @machine.attribute
  2448. end
  2449. end
  2450. class MachineFinderWithExistingOnSameClassTest < Test::Unit::TestCase
  2451. def setup
  2452. @klass = Class.new
  2453. @existing_machine = StateMachine::Machine.new(@klass)
  2454. @machine = StateMachine::Machine.find_or_create(@klass)
  2455. end
  2456. def test_should_accept_a_block
  2457. called = false
  2458. StateMachine::Machine.find_or_create(@klass) do
  2459. called = respond_to?(:event)
  2460. end
  2461. assert called
  2462. end
  2463. def test_should_not_create_a_new_machine
  2464. assert_same @machine, @existing_machine
  2465. end
  2466. end
  2467. class MachineFinderWithExistingMachineOnSuperclassTest < Test::Unit::TestCase
  2468. def setup
  2469. integration = Module.new do
  2470. include StateMachine::Integrations::Base
  2471. def self.matches?(klass)
  2472. false
  2473. end
  2474. end
  2475. StateMachine::Integrations.const_set('Custom', integration)
  2476. @base_class = Class.new
  2477. @base_machine = StateMachine::Machine.new(@base_class, :status, :action => :save, :integration => :custom)
  2478. @base_machine.event(:ignite) {}
  2479. @base_machine.before_transition(lambda {})
  2480. @base_machine.after_transition(lambda {})
  2481. @base_machine.around_transition(lambda {})
  2482. @klass = Class.new(@base_class)
  2483. @machine = StateMachine::Machine.find_or_create(@klass, :status) {}
  2484. end
  2485. def test_should_accept_a_block
  2486. called = false
  2487. StateMachine::Machine.find_or_create(Class.new(@base_class)) do
  2488. called = respond_to?(:event)
  2489. end
  2490. assert called
  2491. end
  2492. def test_should_not_create_a_new_machine_if_no_block_or_options
  2493. machine = StateMachine::Machine.find_or_create(Class.new(@base_class), :status)
  2494. assert_same machine, @base_machine
  2495. end
  2496. def test_should_create_a_new_machine_if_given_options
  2497. machine = StateMachine::Machine.find_or_create(@klass, :status, :initial => :parked)
  2498. assert_not_nil machine
  2499. assert_not_same machine, @base_machine
  2500. end
  2501. def test_should_create_a_new_machine_if_given_block
  2502. assert_not_nil @machine
  2503. assert_not_same @machine, @base_machine
  2504. end
  2505. def test_should_copy_the_base_attribute
  2506. assert_equal :status, @machine.attribute
  2507. end
  2508. def test_should_copy_the_base_configuration
  2509. assert_equal :save, @machine.action
  2510. end
  2511. def test_should_copy_events
  2512. # Can't assert equal arrays since their machines change
  2513. assert_equal 1, @machine.events.length
  2514. end
  2515. def test_should_copy_before_callbacks
  2516. assert_equal @base_machine.callbacks[:before], @machine.callbacks[:before]
  2517. end
  2518. def test_should_copy_after_transitions
  2519. assert_equal @base_machine.callbacks[:after], @machine.callbacks[:after]
  2520. end
  2521. def test_should_use_the_same_integration
  2522. assert((class << @machine; ancestors; end).include?(StateMachine::Integrations::Custom))
  2523. end
  2524. def teardown
  2525. StateMachine::Integrations.send(:remove_const, 'Custom')
  2526. end
  2527. end
  2528. class MachineFinderCustomOptionsTest < Test::Unit::TestCase
  2529. def setup
  2530. @klass = Class.new
  2531. @machine = StateMachine::Machine.find_or_create(@klass, :status, :initial => :parked)
  2532. @object = @klass.new
  2533. end
  2534. def test_should_use_custom_attribute
  2535. assert_equal :status, @machine.attribute
  2536. end
  2537. def test_should_set_custom_initial_state
  2538. assert_equal :parked, @machine.initial_state(@object).name
  2539. end
  2540. end
  2541. begin
  2542. # Load library
  2543. require 'graphviz'
  2544. class MachineDrawingTest < Test::Unit::TestCase
  2545. def setup
  2546. @klass = Class.new do
  2547. def self.name; @name ||= "Vehicle_#{rand(1000000)}"; end
  2548. end
  2549. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  2550. @machine.event :ignite do
  2551. transition :parked => :idling
  2552. end
  2553. end
  2554. def test_should_raise_exception_if_invalid_option_specified
  2555. assert_raise(ArgumentError) {@machine.draw(:invalid => true)}
  2556. end
  2557. def test_should_save_file_with_class_name_by_default
  2558. @machine.draw
  2559. assert File.exists?("./#{@klass.name}_state.png")
  2560. end
  2561. def test_should_allow_base_name_to_be_customized
  2562. name = "machine_#{rand(1000000)}"
  2563. @machine.draw(:name => name)
  2564. @path = "./#{name}.png"
  2565. assert File.exists?(@path)
  2566. end
  2567. def test_should_allow_format_to_be_customized
  2568. @machine.draw(:format => 'jpg')
  2569. @path = "./#{@klass.name}_state.jpg"
  2570. assert File.exists?(@path)
  2571. end
  2572. def test_should_allow_path_to_be_customized
  2573. @machine.draw(:path => "#{File.dirname(__FILE__)}/")
  2574. @path = "#{File.dirname(__FILE__)}/#{@klass.name}_state.png"
  2575. assert File.exists?(@path)
  2576. end
  2577. def test_should_allow_orientation_to_be_landscape
  2578. graph = @machine.draw(:orientation => 'landscape')
  2579. assert_equal 'LR', graph['rankdir'].to_s.gsub('"', '')
  2580. end
  2581. def test_should_allow_orientation_to_be_portrait
  2582. graph = @machine.draw(:orientation => 'portrait')
  2583. assert_equal 'TB', graph['rankdir'].to_s.gsub('"', '')
  2584. end
  2585. if Constants::RGV_VERSION != '0.9.0'
  2586. def test_should_allow_human_names_to_be_displayed
  2587. @machine.event :ignite, :human_name => 'Ignite'
  2588. @machine.state :parked, :human_name => 'Parked'
  2589. @machine.state :idling, :human_name => 'Idling'
  2590. graph = @machine.draw(:human_names => true)
  2591. parked_node = graph.get_node('parked')
  2592. assert_equal 'Parked', parked_node['label'].to_s.gsub('"', '')
  2593. idling_node = graph.get_node('idling')
  2594. assert_equal 'Idling', idling_node['label'].to_s.gsub('"', '')
  2595. end
  2596. end
  2597. def teardown
  2598. FileUtils.rm Dir[@path || "./#{@klass.name}_state.png"]
  2599. end
  2600. end
  2601. class MachineDrawingWithIntegerStatesTest < Test::Unit::TestCase
  2602. def setup
  2603. @klass = Class.new do
  2604. def self.name; @name ||= "Vehicle_#{rand(1000000)}"; end
  2605. end
  2606. @machine = StateMachine::Machine.new(@klass, :state_id, :initial => :parked)
  2607. @machine.event :ignite do
  2608. transition :parked => :idling
  2609. end
  2610. @machine.state :parked, :value => 1
  2611. @machine.state :idling, :value => 2
  2612. @graph = @machine.draw
  2613. end
  2614. def test_should_draw_all_states
  2615. assert_equal 3, @graph.node_count
  2616. end
  2617. def test_should_draw_all_events
  2618. assert_equal 2, @graph.edge_count
  2619. end
  2620. def test_should_draw_machine
  2621. assert File.exist?("./#{@klass.name}_state_id.png")
  2622. end
  2623. def teardown
  2624. FileUtils.rm Dir["./#{@klass.name}_state_id.png"]
  2625. end
  2626. end
  2627. class MachineDrawingWithNilStatesTest < Test::Unit::TestCase
  2628. def setup
  2629. @klass = Class.new do
  2630. def self.name; @name ||= "Vehicle_#{rand(1000000)}"; end
  2631. end
  2632. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  2633. @machine.event :ignite do
  2634. transition :parked => :idling
  2635. end
  2636. @machine.state :parked, :value => nil
  2637. @graph = @machine.draw
  2638. end
  2639. def test_should_draw_all_states
  2640. assert_equal 3, @graph.node_count
  2641. end
  2642. def test_should_draw_all_events
  2643. assert_equal 2, @graph.edge_count
  2644. end
  2645. def test_should_draw_machine
  2646. assert File.exist?("./#{@klass.name}_state.png")
  2647. end
  2648. def teardown
  2649. FileUtils.rm Dir["./#{@klass.name}_state.png"]
  2650. end
  2651. end
  2652. class MachineDrawingWithDynamicStatesTest < Test::Unit::TestCase
  2653. def setup
  2654. @klass = Class.new do
  2655. def self.name; @name ||= "Vehicle_#{rand(1000000)}"; end
  2656. end
  2657. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  2658. @machine.event :activate do
  2659. transition :parked => :idling
  2660. end
  2661. @machine.state :idling, :value => lambda {Time.now}
  2662. @graph = @machine.draw
  2663. end
  2664. def test_should_draw_all_states
  2665. assert_equal 3, @graph.node_count
  2666. end
  2667. def test_should_draw_all_events
  2668. assert_equal 2, @graph.edge_count
  2669. end
  2670. def test_should_draw_machine
  2671. assert File.exist?("./#{@klass.name}_state.png")
  2672. end
  2673. def teardown
  2674. FileUtils.rm Dir["./#{@klass.name}_state.png"]
  2675. end
  2676. end
  2677. class MachineClassDrawingTest < Test::Unit::TestCase
  2678. def setup
  2679. @klass = Class.new do
  2680. def self.name; @name ||= "Vehicle_#{rand(1000000)}"; end
  2681. end
  2682. @machine = StateMachine::Machine.new(@klass)
  2683. @machine.event :ignite do
  2684. transition :parked => :idling
  2685. end
  2686. end
  2687. def test_should_raise_exception_if_no_class_names_specified
  2688. exception = assert_raise(ArgumentError) {StateMachine::Machine.draw(nil)}
  2689. assert_equal 'At least one class must be specified', exception.message
  2690. end
  2691. def test_should_load_files
  2692. StateMachine::Machine.draw('Switch', :file => File.expand_path("#{File.dirname(__FILE__)}/../files/switch.rb"))
  2693. assert defined?(::Switch)
  2694. end
  2695. def test_should_allow_path_and_format_to_be_customized
  2696. StateMachine::Machine.draw('Switch', :file => File.expand_path("#{File.dirname(__FILE__)}/../files/switch.rb"), :path => "#{File.dirname(__FILE__)}/", :format => 'jpg')
  2697. assert File.exist?("#{File.dirname(__FILE__)}/#{Switch.name}_state.jpg")
  2698. end
  2699. def teardown
  2700. FileUtils.rm Dir["{.,#{File.dirname(__FILE__)}}/#{Switch.name}_state.{jpg,png}"]
  2701. end
  2702. end
  2703. rescue LoadError
  2704. $stderr.puts 'Skipping GraphViz StateMachine::Machine tests. `gem install ruby-graphviz` >= v0.9.17 and try again.'
  2705. end unless ENV['TRAVIS']