PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/test/unit/event_test.rb

http://github.com/pluginaweek/state_machine
Ruby | 1196 lines | 940 code | 255 blank | 1 comment | 1 complexity | 76d31f38410d0365e4688df1c92c4beb MD5 | raw file
  1. require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
  2. class EventByDefaultTest < Test::Unit::TestCase
  3. def setup
  4. @klass = Class.new
  5. @machine = StateMachine::Machine.new(@klass)
  6. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  7. @object = @klass.new
  8. end
  9. def test_should_have_a_machine
  10. assert_equal @machine, @event.machine
  11. end
  12. def test_should_have_a_name
  13. assert_equal :ignite, @event.name
  14. end
  15. def test_should_have_a_qualified_name
  16. assert_equal :ignite, @event.qualified_name
  17. end
  18. def test_should_have_a_human_name
  19. assert_equal 'ignite', @event.human_name
  20. end
  21. def test_should_not_have_any_branches
  22. assert @event.branches.empty?
  23. end
  24. def test_should_have_no_known_states
  25. assert @event.known_states.empty?
  26. end
  27. def test_should_not_be_able_to_fire
  28. assert !@event.can_fire?(@object)
  29. end
  30. def test_should_not_have_a_transition
  31. assert_nil @event.transition_for(@object)
  32. end
  33. def test_should_define_a_predicate
  34. assert @object.respond_to?(:can_ignite?)
  35. end
  36. def test_should_define_a_transition_accessor
  37. assert @object.respond_to?(:ignite_transition)
  38. end
  39. def test_should_define_an_action
  40. assert @object.respond_to?(:ignite)
  41. end
  42. def test_should_define_a_bang_action
  43. assert @object.respond_to?(:ignite!)
  44. end
  45. end
  46. class EventTest < Test::Unit::TestCase
  47. def setup
  48. @machine = StateMachine::Machine.new(Class.new)
  49. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  50. @event.transition :parked => :idling
  51. end
  52. def test_should_allow_changing_machine
  53. new_machine = StateMachine::Machine.new(Class.new)
  54. @event.machine = new_machine
  55. assert_equal new_machine, @event.machine
  56. end
  57. def test_should_allow_changing_human_name
  58. @event.human_name = 'Stop'
  59. assert_equal 'Stop', @event.human_name
  60. end
  61. def test_should_provide_matcher_helpers_during_initialization
  62. matchers = []
  63. @event.instance_eval do
  64. matchers = [all, any, same]
  65. end
  66. assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
  67. end
  68. def test_should_use_pretty_inspect
  69. assert_match "#<StateMachine::Event name=:ignite transitions=[:parked => :idling]>", @event.inspect
  70. end
  71. end
  72. class EventWithHumanNameTest < Test::Unit::TestCase
  73. def setup
  74. @klass = Class.new
  75. @machine = StateMachine::Machine.new(@klass)
  76. @machine.events << @event = StateMachine::Event.new(@machine, :ignite, :human_name => 'start')
  77. end
  78. def test_should_use_custom_human_name
  79. assert_equal 'start', @event.human_name
  80. end
  81. end
  82. class EventWithDynamicHumanNameTest < Test::Unit::TestCase
  83. def setup
  84. @klass = Class.new
  85. @machine = StateMachine::Machine.new(@klass)
  86. @machine.events << @event = StateMachine::Event.new(@machine, :ignite, :human_name => lambda {|event, object| ['start', object]})
  87. end
  88. def test_should_use_custom_human_name
  89. human_name, klass = @event.human_name
  90. assert_equal 'start', human_name
  91. assert_equal @klass, klass
  92. end
  93. def test_should_allow_custom_class_to_be_passed_through
  94. human_name, klass = @event.human_name(1)
  95. assert_equal 'start', human_name
  96. assert_equal 1, klass
  97. end
  98. def test_should_not_cache_value
  99. assert_not_same @event.human_name, @event.human_name
  100. end
  101. end
  102. class EventWithConflictingHelpersBeforeDefinitionTest < Test::Unit::TestCase
  103. def setup
  104. require 'stringio'
  105. @original_stderr, $stderr = $stderr, StringIO.new
  106. @superclass = Class.new do
  107. def can_ignite?
  108. 0
  109. end
  110. def ignite_transition
  111. 0
  112. end
  113. def ignite
  114. 0
  115. end
  116. def ignite!
  117. 0
  118. end
  119. end
  120. @klass = Class.new(@superclass)
  121. @machine = StateMachine::Machine.new(@klass)
  122. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  123. @object = @klass.new
  124. end
  125. def test_should_not_redefine_predicate
  126. assert_equal 0, @object.can_ignite?
  127. end
  128. def test_should_not_redefine_transition_accessor
  129. assert_equal 0, @object.ignite_transition
  130. end
  131. def test_should_not_redefine_action
  132. assert_equal 0, @object.ignite
  133. end
  134. def test_should_not_redefine_bang_action
  135. assert_equal 0, @object.ignite!
  136. end
  137. def test_should_output_warning
  138. expected = %w(can_ignite? ignite_transition ignite ignite!).map do |method|
  139. "Instance method \"#{method}\" is already defined in #{@superclass.to_s}, use generic helper instead or set StateMachine::Machine.ignore_method_conflicts = true.\n"
  140. end.join
  141. assert_equal expected, $stderr.string
  142. end
  143. def teardown
  144. $stderr = @original_stderr
  145. end
  146. end
  147. class EventWithConflictingHelpersAfterDefinitionTest < Test::Unit::TestCase
  148. def setup
  149. require 'stringio'
  150. @original_stderr, $stderr = $stderr, StringIO.new
  151. @klass = Class.new do
  152. def can_ignite?
  153. 0
  154. end
  155. def ignite_transition
  156. 0
  157. end
  158. def ignite
  159. 0
  160. end
  161. def ignite!
  162. 0
  163. end
  164. end
  165. @machine = StateMachine::Machine.new(@klass)
  166. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  167. @object = @klass.new
  168. end
  169. def test_should_not_redefine_predicate
  170. assert_equal 0, @object.can_ignite?
  171. end
  172. def test_should_not_redefine_transition_accessor
  173. assert_equal 0, @object.ignite_transition
  174. end
  175. def test_should_not_redefine_action
  176. assert_equal 0, @object.ignite
  177. end
  178. def test_should_not_redefine_bang_action
  179. assert_equal 0, @object.ignite!
  180. end
  181. def test_should_allow_super_chaining
  182. @klass.class_eval do
  183. def can_ignite?
  184. super
  185. end
  186. def ignite_transition
  187. super
  188. end
  189. def ignite
  190. super
  191. end
  192. def ignite!
  193. super
  194. end
  195. end
  196. assert_equal false, @object.can_ignite?
  197. assert_equal nil, @object.ignite_transition
  198. assert_equal false, @object.ignite
  199. assert_raise(StateMachine::InvalidTransition) { @object.ignite! }
  200. end
  201. def test_should_not_output_warning
  202. assert_equal '', $stderr.string
  203. end
  204. def teardown
  205. $stderr = @original_stderr
  206. end
  207. end
  208. class EventWithConflictingMachineTest < Test::Unit::TestCase
  209. def setup
  210. require 'stringio'
  211. @original_stderr, $stderr = $stderr, StringIO.new
  212. @klass = Class.new
  213. @state_machine = StateMachine::Machine.new(@klass, :state)
  214. @state_machine.state :parked, :idling
  215. @state_machine.events << @state_event = StateMachine::Event.new(@state_machine, :ignite)
  216. end
  217. def test_should_not_overwrite_first_event
  218. @status_machine = StateMachine::Machine.new(@klass, :status)
  219. @status_machine.state :first_gear, :second_gear
  220. @status_machine.events << @status_event = StateMachine::Event.new(@status_machine, :ignite)
  221. @object = @klass.new
  222. @object.state = 'parked'
  223. @object.status = 'first_gear'
  224. @state_event.transition(:parked => :idling)
  225. @status_event.transition(:parked => :first_gear)
  226. @object.ignite
  227. assert_equal 'idling', @object.state
  228. assert_equal 'first_gear', @object.status
  229. end
  230. def test_should_output_warning
  231. @status_machine = StateMachine::Machine.new(@klass, :status)
  232. @status_machine.events << @status_event = StateMachine::Event.new(@status_machine, :ignite)
  233. assert_equal "Event :ignite for :status is already defined in :state\n", $stderr.string
  234. end
  235. def test_should_not_output_warning_if_using_different_namespace
  236. @status_machine = StateMachine::Machine.new(@klass, :status, :namespace => 'alarm')
  237. @status_machine.events << @status_event = StateMachine::Event.new(@status_machine, :ignite)
  238. assert_equal '', $stderr.string
  239. end
  240. def teardown
  241. $stderr = @original_stderr
  242. end
  243. end
  244. class EventWithNamespaceTest < Test::Unit::TestCase
  245. def setup
  246. @klass = Class.new
  247. @machine = StateMachine::Machine.new(@klass, :namespace => 'alarm')
  248. @machine.events << @event = StateMachine::Event.new(@machine, :enable)
  249. @object = @klass.new
  250. end
  251. def test_should_have_a_name
  252. assert_equal :enable, @event.name
  253. end
  254. def test_should_have_a_qualified_name
  255. assert_equal :enable_alarm, @event.qualified_name
  256. end
  257. def test_should_namespace_predicate
  258. assert @object.respond_to?(:can_enable_alarm?)
  259. end
  260. def test_should_namespace_transition_accessor
  261. assert @object.respond_to?(:enable_alarm_transition)
  262. end
  263. def test_should_namespace_action
  264. assert @object.respond_to?(:enable_alarm)
  265. end
  266. def test_should_namespace_bang_action
  267. assert @object.respond_to?(:enable_alarm!)
  268. end
  269. end
  270. class EventContextTest < Test::Unit::TestCase
  271. def setup
  272. @klass = Class.new
  273. @machine = StateMachine::Machine.new(@klass)
  274. @machine.events << @event = StateMachine::Event.new(@machine, :ignite, :human_name => 'start')
  275. end
  276. def test_should_evaluate_within_the_event
  277. scope = nil
  278. @event.context { scope = self }
  279. assert_equal @event, scope
  280. end
  281. end
  282. class EventTransitionsTest < Test::Unit::TestCase
  283. def setup
  284. @machine = StateMachine::Machine.new(Class.new)
  285. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  286. end
  287. def test_should_not_raise_exception_if_implicit_option_specified
  288. assert_nothing_raised {@event.transition(:invalid => :valid)}
  289. end
  290. def test_should_not_allow_on_option
  291. exception = assert_raise(ArgumentError) {@event.transition(:on => :ignite)}
  292. assert_equal 'Invalid key(s): on', exception.message
  293. end
  294. def test_should_automatically_set_on_option
  295. branch = @event.transition(:to => :idling)
  296. assert_instance_of StateMachine::WhitelistMatcher, branch.event_requirement
  297. assert_equal [:ignite], branch.event_requirement.values
  298. end
  299. def test_should_not_allow_except_on_option
  300. exception = assert_raise(ArgumentError) {@event.transition(:except_on => :ignite)}
  301. assert_equal 'Invalid key(s): except_on', exception.message
  302. end
  303. def test_should_allow_transitioning_without_a_to_state
  304. assert_nothing_raised {@event.transition(:from => :parked)}
  305. end
  306. def test_should_allow_transitioning_without_a_from_state
  307. assert_nothing_raised {@event.transition(:to => :idling)}
  308. end
  309. def test_should_allow_except_from_option
  310. assert_nothing_raised {@event.transition(:except_from => :idling)}
  311. end
  312. def test_should_allow_except_to_option
  313. assert_nothing_raised {@event.transition(:except_to => :idling)}
  314. end
  315. def test_should_allow_transitioning_from_a_single_state
  316. assert @event.transition(:parked => :idling)
  317. end
  318. def test_should_allow_transitioning_from_multiple_states
  319. assert @event.transition([:parked, :idling] => :idling)
  320. end
  321. def test_should_allow_transitions_to_multiple_states
  322. assert @event.transition(:parked => [:parked, :idling])
  323. end
  324. def test_should_have_transitions
  325. branch = @event.transition(:to => :idling)
  326. assert_equal [branch], @event.branches
  327. end
  328. end
  329. class EventAfterBeingCopiedTest < Test::Unit::TestCase
  330. def setup
  331. @machine = StateMachine::Machine.new(Class.new)
  332. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  333. @copied_event = @event.dup
  334. end
  335. def test_should_not_have_the_same_collection_of_branches
  336. assert_not_same @event.branches, @copied_event.branches
  337. end
  338. def test_should_not_have_the_same_collection_of_known_states
  339. assert_not_same @event.known_states, @copied_event.known_states
  340. end
  341. end
  342. class EventWithoutTransitionsTest < Test::Unit::TestCase
  343. def setup
  344. @klass = Class.new
  345. @machine = StateMachine::Machine.new(@klass)
  346. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  347. @object = @klass.new
  348. end
  349. def test_should_not_be_able_to_fire
  350. assert !@event.can_fire?(@object)
  351. end
  352. def test_should_not_have_a_transition
  353. assert_nil @event.transition_for(@object)
  354. end
  355. def test_should_not_fire
  356. assert !@event.fire(@object)
  357. end
  358. def test_should_not_change_the_current_state
  359. @event.fire(@object)
  360. assert_nil @object.state
  361. end
  362. end
  363. class EventWithTransitionsTest < Test::Unit::TestCase
  364. def setup
  365. @klass = Class.new
  366. @machine = StateMachine::Machine.new(@klass)
  367. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  368. @event.transition(:parked => :idling)
  369. @event.transition(:first_gear => :idling)
  370. end
  371. def test_should_include_all_transition_states_in_known_states
  372. assert_equal [:parked, :idling, :first_gear], @event.known_states
  373. end
  374. def test_should_include_new_transition_states_after_calling_known_states
  375. @event.known_states
  376. @event.transition(:stalled => :idling)
  377. assert_equal [:parked, :idling, :first_gear, :stalled], @event.known_states
  378. end
  379. def test_should_clear_known_states_on_reset
  380. @event.reset
  381. assert_equal [], @event.known_states
  382. end
  383. def test_should_use_pretty_inspect
  384. assert_match "#<StateMachine::Event name=:ignite transitions=[:parked => :idling, :first_gear => :idling]>", @event.inspect
  385. end
  386. end
  387. class EventWithoutMatchingTransitionsTest < Test::Unit::TestCase
  388. def setup
  389. @klass = Class.new
  390. @machine = StateMachine::Machine.new(@klass)
  391. @machine.state :parked, :idling
  392. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  393. @event.transition(:parked => :idling)
  394. @object = @klass.new
  395. @object.state = 'idling'
  396. end
  397. def test_should_not_be_able_to_fire
  398. assert !@event.can_fire?(@object)
  399. end
  400. def test_should_be_able_to_fire_with_custom_from_state
  401. assert @event.can_fire?(@object, :from => :parked)
  402. end
  403. def test_should_not_have_a_transition
  404. assert_nil @event.transition_for(@object)
  405. end
  406. def test_should_have_a_transition_with_custom_from_state
  407. assert_not_nil @event.transition_for(@object, :from => :parked)
  408. end
  409. def test_should_not_fire
  410. assert !@event.fire(@object)
  411. end
  412. def test_should_not_change_the_current_state
  413. @event.fire(@object)
  414. assert_equal 'idling', @object.state
  415. end
  416. end
  417. class EventWithMatchingDisabledTransitionsTest < Test::Unit::TestCase
  418. def setup
  419. StateMachine::Integrations.const_set('Custom', Module.new do
  420. include StateMachine::Integrations::Base
  421. def invalidate(object, attribute, message, values = [])
  422. (object.errors ||= []) << generate_message(message, values)
  423. end
  424. def reset(object)
  425. object.errors = []
  426. end
  427. end)
  428. @klass = Class.new do
  429. attr_accessor :errors
  430. end
  431. @machine = StateMachine::Machine.new(@klass, :integration => :custom)
  432. @machine.state :parked, :idling
  433. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  434. @event.transition(:parked => :idling, :if => lambda {false})
  435. @object = @klass.new
  436. @object.state = 'parked'
  437. end
  438. def test_should_not_be_able_to_fire
  439. assert !@event.can_fire?(@object)
  440. end
  441. def test_should_be_able_to_fire_with_disabled_guards
  442. assert @event.can_fire?(@object, :guard => false)
  443. end
  444. def test_should_not_have_a_transition
  445. assert_nil @event.transition_for(@object)
  446. end
  447. def test_should_have_a_transition_with_disabled_guards
  448. assert_not_nil @event.transition_for(@object, :guard => false)
  449. end
  450. def test_should_not_fire
  451. assert !@event.fire(@object)
  452. end
  453. def test_should_not_change_the_current_state
  454. @event.fire(@object)
  455. assert_equal 'parked', @object.state
  456. end
  457. def test_should_invalidate_the_state
  458. @event.fire(@object)
  459. assert_equal ['cannot transition via "ignite"'], @object.errors
  460. end
  461. def test_should_invalidate_with_human_event_name
  462. @event.human_name = 'start'
  463. @event.fire(@object)
  464. assert_equal ['cannot transition via "start"'], @object.errors
  465. end
  466. def test_should_invalid_with_human_state_name_if_specified
  467. klass = Class.new do
  468. attr_accessor :errors
  469. end
  470. machine = StateMachine::Machine.new(klass, :integration => :custom, :messages => {:invalid_transition => 'cannot transition via "%s" from "%s"'})
  471. parked, idling = machine.state :parked, :idling
  472. parked.human_name = 'stopped'
  473. machine.events << event = StateMachine::Event.new(machine, :ignite)
  474. event.transition(:parked => :idling, :if => lambda {false})
  475. object = @klass.new
  476. object.state = 'parked'
  477. event.fire(object)
  478. assert_equal ['cannot transition via "ignite" from "stopped"'], object.errors
  479. end
  480. def test_should_reset_existing_error
  481. @object.errors = ['invalid']
  482. @event.fire(@object)
  483. assert_equal ['cannot transition via "ignite"'], @object.errors
  484. end
  485. def test_should_run_failure_callbacks
  486. callback_args = nil
  487. @machine.after_failure {|*args| callback_args = args}
  488. @event.fire(@object)
  489. object, transition = callback_args
  490. assert_equal @object, object
  491. assert_not_nil transition
  492. assert_equal @object, transition.object
  493. assert_equal @machine, transition.machine
  494. assert_equal :ignite, transition.event
  495. assert_equal :parked, transition.from_name
  496. assert_equal :parked, transition.to_name
  497. end
  498. def teardown
  499. StateMachine::Integrations.send(:remove_const, 'Custom')
  500. end
  501. end
  502. class EventWithMatchingEnabledTransitionsTest < Test::Unit::TestCase
  503. def setup
  504. StateMachine::Integrations.const_set('Custom', Module.new do
  505. include StateMachine::Integrations::Base
  506. def invalidate(object, attribute, message, values = [])
  507. (object.errors ||= []) << generate_message(message, values)
  508. end
  509. def reset(object)
  510. object.errors = []
  511. end
  512. end)
  513. @klass = Class.new do
  514. attr_accessor :errors
  515. end
  516. @machine = StateMachine::Machine.new(@klass, :integration => :custom)
  517. @machine.state :parked, :idling
  518. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  519. @event.transition(:parked => :idling)
  520. @object = @klass.new
  521. @object.state = 'parked'
  522. end
  523. def test_should_be_able_to_fire
  524. assert @event.can_fire?(@object)
  525. end
  526. def test_should_have_a_transition
  527. transition = @event.transition_for(@object)
  528. assert_not_nil transition
  529. assert_equal 'parked', transition.from
  530. assert_equal 'idling', transition.to
  531. assert_equal :ignite, transition.event
  532. end
  533. def test_should_fire
  534. assert @event.fire(@object)
  535. end
  536. def test_should_change_the_current_state
  537. @event.fire(@object)
  538. assert_equal 'idling', @object.state
  539. end
  540. def test_should_reset_existing_error
  541. @object.errors = ['invalid']
  542. @event.fire(@object)
  543. assert_equal [], @object.errors
  544. end
  545. def test_should_not_invalidate_the_state
  546. @event.fire(@object)
  547. assert_equal [], @object.errors
  548. end
  549. def test_should_not_be_able_to_fire_on_reset
  550. @event.reset
  551. assert !@event.can_fire?(@object)
  552. end
  553. def teardown
  554. StateMachine::Integrations.send(:remove_const, 'Custom')
  555. end
  556. end
  557. class EventWithTransitionWithoutToStateTest < Test::Unit::TestCase
  558. def setup
  559. @klass = Class.new
  560. @machine = StateMachine::Machine.new(@klass)
  561. @machine.state :parked
  562. @machine.events << @event = StateMachine::Event.new(@machine, :park)
  563. @event.transition(:from => :parked)
  564. @object = @klass.new
  565. @object.state = 'parked'
  566. end
  567. def test_should_be_able_to_fire
  568. assert @event.can_fire?(@object)
  569. end
  570. def test_should_have_a_transition
  571. transition = @event.transition_for(@object)
  572. assert_not_nil transition
  573. assert_equal 'parked', transition.from
  574. assert_equal 'parked', transition.to
  575. assert_equal :park, transition.event
  576. end
  577. def test_should_fire
  578. assert @event.fire(@object)
  579. end
  580. def test_should_not_change_the_current_state
  581. @event.fire(@object)
  582. assert_equal 'parked', @object.state
  583. end
  584. end
  585. class EventWithTransitionWithNilToStateTest < Test::Unit::TestCase
  586. def setup
  587. @klass = Class.new
  588. @machine = StateMachine::Machine.new(@klass)
  589. @machine.state nil, :idling
  590. @machine.events << @event = StateMachine::Event.new(@machine, :park)
  591. @event.transition(:idling => nil)
  592. @object = @klass.new
  593. @object.state = 'idling'
  594. end
  595. def test_should_be_able_to_fire
  596. assert @event.can_fire?(@object)
  597. end
  598. def test_should_have_a_transition
  599. transition = @event.transition_for(@object)
  600. assert_not_nil transition
  601. assert_equal 'idling', transition.from
  602. assert_equal nil, transition.to
  603. assert_equal :park, transition.event
  604. end
  605. def test_should_fire
  606. assert @event.fire(@object)
  607. end
  608. def test_should_not_change_the_current_state
  609. @event.fire(@object)
  610. assert_equal nil, @object.state
  611. end
  612. end
  613. class EventWithTransitionWithLoopbackStateTest < Test::Unit::TestCase
  614. def setup
  615. @klass = Class.new
  616. @machine = StateMachine::Machine.new(@klass)
  617. @machine.state :parked
  618. @machine.events << @event = StateMachine::Event.new(@machine, :park)
  619. @event.transition(:from => :parked, :to => StateMachine::LoopbackMatcher.instance)
  620. @object = @klass.new
  621. @object.state = 'parked'
  622. end
  623. def test_should_be_able_to_fire
  624. assert @event.can_fire?(@object)
  625. end
  626. def test_should_have_a_transition
  627. transition = @event.transition_for(@object)
  628. assert_not_nil transition
  629. assert_equal 'parked', transition.from
  630. assert_equal 'parked', transition.to
  631. assert_equal :park, transition.event
  632. end
  633. def test_should_fire
  634. assert @event.fire(@object)
  635. end
  636. def test_should_not_change_the_current_state
  637. @event.fire(@object)
  638. assert_equal 'parked', @object.state
  639. end
  640. end
  641. class EventWithTransitionWithBlacklistedToStateTest < Test::Unit::TestCase
  642. def setup
  643. @klass = Class.new
  644. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  645. @machine.state :parked, :idling, :first_gear, :second_gear
  646. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  647. @event.transition(:from => :parked, :to => StateMachine::BlacklistMatcher.new([:parked, :idling]))
  648. @object = @klass.new
  649. @object.state = 'parked'
  650. end
  651. def test_should_be_able_to_fire
  652. assert @event.can_fire?(@object)
  653. end
  654. def test_should_have_a_transition
  655. transition = @event.transition_for(@object)
  656. assert_not_nil transition
  657. assert_equal 'parked', transition.from
  658. assert_equal 'first_gear', transition.to
  659. assert_equal :ignite, transition.event
  660. end
  661. def test_should_allow_loopback_first_when_possible
  662. @event.transition(:from => :second_gear, :to => StateMachine::BlacklistMatcher.new([:parked, :idling]))
  663. @object.state = 'second_gear'
  664. transition = @event.transition_for(@object)
  665. assert_not_nil transition
  666. assert_equal 'second_gear', transition.from
  667. assert_equal 'second_gear', transition.to
  668. assert_equal :ignite, transition.event
  669. end
  670. def test_should_allow_specific_transition_selection_using_to
  671. transition = @event.transition_for(@object, :from => :parked, :to => :second_gear)
  672. assert_not_nil transition
  673. assert_equal 'parked', transition.from
  674. assert_equal 'second_gear', transition.to
  675. assert_equal :ignite, transition.event
  676. end
  677. def test_should_not_allow_transition_selection_if_not_matching
  678. transition = @event.transition_for(@object, :from => :parked, :to => :parked)
  679. assert_nil transition
  680. end
  681. def test_should_fire
  682. assert @event.fire(@object)
  683. end
  684. def test_should_change_the_current_state
  685. @event.fire(@object)
  686. assert_equal 'first_gear', @object.state
  687. end
  688. end
  689. class EventWithTransitionWithWhitelistedToStateTest < Test::Unit::TestCase
  690. def setup
  691. @klass = Class.new
  692. @machine = StateMachine::Machine.new(@klass, :initial => :parked)
  693. @machine.state :parked, :idling, :first_gear, :second_gear
  694. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  695. @event.transition(:from => :parked, :to => StateMachine::WhitelistMatcher.new([:first_gear, :second_gear]))
  696. @object = @klass.new
  697. @object.state = 'parked'
  698. end
  699. def test_should_be_able_to_fire
  700. assert @event.can_fire?(@object)
  701. end
  702. def test_should_have_a_transition
  703. transition = @event.transition_for(@object)
  704. assert_not_nil transition
  705. assert_equal 'parked', transition.from
  706. assert_equal 'first_gear', transition.to
  707. assert_equal :ignite, transition.event
  708. end
  709. def test_should_allow_specific_transition_selection_using_to
  710. transition = @event.transition_for(@object, :from => :parked, :to => :second_gear)
  711. assert_not_nil transition
  712. assert_equal 'parked', transition.from
  713. assert_equal 'second_gear', transition.to
  714. assert_equal :ignite, transition.event
  715. end
  716. def test_should_not_allow_transition_selection_if_not_matching
  717. transition = @event.transition_for(@object, :from => :parked, :to => :parked)
  718. assert_nil transition
  719. end
  720. def test_should_fire
  721. assert @event.fire(@object)
  722. end
  723. def test_should_change_the_current_state
  724. @event.fire(@object)
  725. assert_equal 'first_gear', @object.state
  726. end
  727. end
  728. class EventWithMultipleTransitionsTest < Test::Unit::TestCase
  729. def setup
  730. @klass = Class.new
  731. @machine = StateMachine::Machine.new(@klass)
  732. @machine.state :parked, :idling
  733. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  734. @event.transition(:idling => :idling)
  735. @event.transition(:parked => :idling)
  736. @event.transition(:parked => :parked)
  737. @object = @klass.new
  738. @object.state = 'parked'
  739. end
  740. def test_should_be_able_to_fire
  741. assert @event.can_fire?(@object)
  742. end
  743. def test_should_have_a_transition
  744. transition = @event.transition_for(@object)
  745. assert_not_nil transition
  746. assert_equal 'parked', transition.from
  747. assert_equal 'idling', transition.to
  748. assert_equal :ignite, transition.event
  749. end
  750. def test_should_allow_specific_transition_selection_using_from
  751. transition = @event.transition_for(@object, :from => :idling)
  752. assert_not_nil transition
  753. assert_equal 'idling', transition.from
  754. assert_equal 'idling', transition.to
  755. assert_equal :ignite, transition.event
  756. end
  757. def test_should_allow_specific_transition_selection_using_to
  758. transition = @event.transition_for(@object, :from => :parked, :to => :parked)
  759. assert_not_nil transition
  760. assert_equal 'parked', transition.from
  761. assert_equal 'parked', transition.to
  762. assert_equal :ignite, transition.event
  763. end
  764. def test_should_not_allow_specific_transition_selection_using_on
  765. exception = assert_raise(ArgumentError) { @event.transition_for(@object, :on => :park) }
  766. assert_equal 'Invalid key(s): on', exception.message
  767. end
  768. def test_should_fire
  769. assert @event.fire(@object)
  770. end
  771. def test_should_change_the_current_state
  772. @event.fire(@object)
  773. assert_equal 'idling', @object.state
  774. end
  775. end
  776. class EventWithMachineActionTest < Test::Unit::TestCase
  777. def setup
  778. @klass = Class.new do
  779. attr_reader :saved
  780. def save
  781. @saved = true
  782. end
  783. end
  784. @machine = StateMachine::Machine.new(@klass, :action => :save)
  785. @machine.state :parked, :idling
  786. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  787. @event.transition(:parked => :idling)
  788. @object = @klass.new
  789. @object.state = 'parked'
  790. end
  791. def test_should_run_action_on_fire
  792. @event.fire(@object)
  793. assert @object.saved
  794. end
  795. def test_should_not_run_action_if_configured_to_skip
  796. @event.fire(@object, false)
  797. assert !@object.saved
  798. end
  799. end
  800. class EventWithInvalidCurrentStateTest < Test::Unit::TestCase
  801. def setup
  802. @klass = Class.new
  803. @machine = StateMachine::Machine.new(@klass)
  804. @machine.state :parked, :idling
  805. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  806. @event.transition(:parked => :idling)
  807. @object = @klass.new
  808. @object.state = 'invalid'
  809. end
  810. def test_should_raise_exception_when_checking_availability
  811. exception = assert_raise(ArgumentError) { @event.can_fire?(@object) }
  812. assert_equal '"invalid" is not a known state value', exception.message
  813. end
  814. def test_should_raise_exception_when_finding_transition
  815. exception = assert_raise(ArgumentError) { @event.transition_for(@object) }
  816. assert_equal '"invalid" is not a known state value', exception.message
  817. end
  818. def test_should_raise_exception_when_firing
  819. exception = assert_raise(ArgumentError) { @event.fire(@object) }
  820. assert_equal '"invalid" is not a known state value', exception.message
  821. end
  822. end
  823. class EventOnFailureTest < Test::Unit::TestCase
  824. def setup
  825. StateMachine::Integrations.const_set('Custom', Module.new do
  826. include StateMachine::Integrations::Base
  827. def invalidate(object, attribute, message, values = [])
  828. (object.errors ||= []) << generate_message(message, values)
  829. end
  830. def reset(object)
  831. object.errors = []
  832. end
  833. end)
  834. @klass = Class.new do
  835. attr_accessor :errors
  836. end
  837. @machine = StateMachine::Machine.new(@klass, :integration => :custom)
  838. @machine.state :parked
  839. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  840. @object = @klass.new
  841. @object.state = 'parked'
  842. end
  843. def test_should_invalidate_the_state
  844. @event.fire(@object)
  845. assert_equal ['cannot transition via "ignite"'], @object.errors
  846. end
  847. def test_should_run_failure_callbacks
  848. callback_args = nil
  849. @machine.after_failure {|*args| callback_args = args}
  850. @event.fire(@object)
  851. object, transition = callback_args
  852. assert_equal @object, object
  853. assert_not_nil transition
  854. assert_equal @object, transition.object
  855. assert_equal @machine, transition.machine
  856. assert_equal :ignite, transition.event
  857. assert_equal :parked, transition.from_name
  858. assert_equal :parked, transition.to_name
  859. end
  860. def teardown
  861. StateMachine::Integrations.send(:remove_const, 'Custom')
  862. end
  863. end
  864. class EventWithMarshallingTest < Test::Unit::TestCase
  865. def setup
  866. @klass = Class.new do
  867. def save
  868. true
  869. end
  870. end
  871. self.class.const_set('Example', @klass)
  872. @machine = StateMachine::Machine.new(@klass, :action => :save)
  873. @machine.state :parked, :idling
  874. @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
  875. @event.transition(:parked => :idling)
  876. @object = @klass.new
  877. @object.state = 'parked'
  878. end
  879. def test_should_marshal_during_before_callbacks
  880. @machine.before_transition {|object, transition| Marshal.dump(object)}
  881. assert_nothing_raised { @event.fire(@object) }
  882. end
  883. def test_should_marshal_during_action
  884. @klass.class_eval do
  885. remove_method :save
  886. def save
  887. Marshal.dump(self)
  888. end
  889. end
  890. assert_nothing_raised { @event.fire(@object) }
  891. end
  892. def test_should_marshal_during_after_callbacks
  893. @machine.after_transition {|object, transition| Marshal.dump(object)}
  894. assert_nothing_raised { @event.fire(@object) }
  895. end
  896. def teardown
  897. self.class.send(:remove_const, 'Example')
  898. end
  899. end
  900. begin
  901. # Load library
  902. require 'graphviz'
  903. class EventDrawingTest < Test::Unit::TestCase
  904. def setup
  905. states = [:parked, :idling, :first_gear]
  906. @machine = StateMachine::Machine.new(Class.new, :initial => :parked)
  907. @machine.other_states(*states)
  908. @graph = StateMachine::Graph.new('test')
  909. states.each {|state| @graph.add_nodes(state.to_s)}
  910. @machine.events << @event = StateMachine::Event.new(@machine , :park)
  911. @event.transition :parked => :idling
  912. @event.transition :first_gear => :parked
  913. @event.transition :except_from => :parked, :to => :parked
  914. @event.draw(@graph)
  915. end
  916. def test_should_generate_edges_for_each_transition
  917. assert_equal 4, @graph.edge_count
  918. end
  919. def test_should_use_event_name_for_edge_label
  920. assert_equal 'park', @graph.get_edge_at_index(0)['label'].to_s.gsub('"', '')
  921. end
  922. end
  923. class EventDrawingWithHumanNameTest < Test::Unit::TestCase
  924. def setup
  925. states = [:parked, :idling]
  926. @machine = StateMachine::Machine.new(Class.new, :initial => :parked)
  927. @machine.other_states(*states)
  928. graph = StateMachine::Graph.new('test')
  929. states.each {|state| graph.add_nodes(state.to_s)}
  930. @machine.events << @event = StateMachine::Event.new(@machine , :park, :human_name => 'Park')
  931. @event.transition :parked => :idling
  932. @event.draw(graph, :human_name => true)
  933. @edge = graph.get_edge_at_index(0)
  934. end
  935. def test_should_use_event_human_name_for_edge_label
  936. assert_equal 'Park', @edge['label'].to_s.gsub('"', '')
  937. end
  938. end
  939. rescue LoadError
  940. $stderr.puts 'Skipping GraphViz StateMachine::Event tests. `gem install ruby-graphviz` >= v0.9.17 and try again.'
  941. end unless ENV['TRAVIS']