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

/test/unit/machine_test.rb

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