PageRenderTime 75ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/gemcache/ruby/1.9.1/gems/state_machine-1.1.2/test/unit/machine_test.rb

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