/test/functional/ft_5_on_error.rb
Ruby | 739 lines | 521 code | 168 blank | 50 comment | 7 complexity | 3998a6cfd20fd9a9ce4bb572bf93b030 MD5 | raw file
- #
- # testing ruote
- #
- # Tue Jun 2 18:48:02 JST 2009
- #
- require File.expand_path('../base', __FILE__)
- require 'ruote/participant'
- class FtOnErrorTest < Test::Unit::TestCase
- include FunctionalBase
- class TroubleMaker
- include Ruote::LocalParticipant
- def consume(workitem)
- hits = (workitem.fields['hits'] || 0) + 1
- workitem.fields['hits'] = hits
- workitem.trace << "#{hits.to_s}\n"
- raise 'Houston, we have a problem !' if hits == 1
- workitem.trace << 'done.'
- reply(workitem)
- end
- def cancel(fei, flavour)
- # nothing to do
- end
- end
- def test_on_error
- pdef = Ruote.process_definition do
- sequence :on_error => 'catcher' do
- nada
- end
- end
- @dashboard.register_participant :catcher do
- tracer << "caught\n"
- end
- assert_trace('caught', pdef)
- assert_equal 1, logger.log.select { |e| e['action'] == 'fail' }.size
- end
- def test_on_error_unknown_participant_name
- pdef = Ruote.process_definition :name => 'test' do
- participant :mark_started
- sequence :on_error => :mark_failed do
- participant :bogus
- end
- participant :mark_finished
- end
- @dashboard.context.stash[:marks] = []
- @dashboard.register_participant 'mark\_.+' do |workitem|
- stash[:marks] << workitem.participant_name
- end
- wfid = @dashboard.launch(pdef)
- wait_for(wfid)
- assert_equal(
- %w[ mark_started mark_failed mark_finished ],
- @dashboard.context.stash[:marks])
- end
- def test_on_error_unknown_participant_name_2
- pdef = Ruote.process_definition :name => 'test' do
- participant :mark_started
- participant :bogus, :on_error => :mark_failed
- participant :mark_finished
- end
- @dashboard.context.stash[:marks] = []
- @dashboard.register_participant 'mark\_.+' do |workitem|
- stash[:marks] << workitem.participant_name
- end
- wfid = @dashboard.launch(pdef)
- wait_for(wfid)
- assert_equal(
- %w[ mark_started mark_failed mark_finished ],
- @dashboard.context.stash[:marks])
- end
- def test_on_error_neutralization
- pdef = Ruote.process_definition do
- sequence :on_error => 'catcher' do
- sequence :on_error => '' do
- nada
- end
- end
- end
- @dashboard.register_participant :catcher do
- tracer << "caught\n"
- end
- wfid = @dashboard.launch(pdef)
- wait_for(wfid)
- ps = @dashboard.process(wfid)
- assert_equal(1, ps.errors.size)
- end
- # redo and retry are aliases, the faulty segment is retried
- #
- def test_on_error_redo
- pdef = Ruote.process_definition do
- sequence :on_error => :redo do
- troublemaker
- end
- end
- @dashboard.register_participant :troublemaker, TroubleMaker
- assert_trace(%w[ 1 2 done. ], pdef)
- end
- # redo and retry are aliases, the faulty segment is retried
- #
- def test_on_error_retry
- pdef = Ruote.process_definition do
- sequence :on_error => :retry do
- troublemaker
- end
- end
- @dashboard.register_participant :troublemaker, TroubleMaker
- assert_trace(%w[ 1 2 done. ], pdef)
- end
- def test_on_error_raise
- pdef = Ruote.define do
- sequence :on_error => :raise do
- error 'nada'
- end
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal 'error_intercepted', r['action']
- assert_equal 'nada', r['error']['message']
- end
- def test_on_error_undo
- pdef = Ruote.process_definition do
- sequence do
- echo 'a'
- sequence :on_error => :undo do
- echo 'b'
- nemo
- echo 'c'
- end
- echo 'd'
- end
- end
- wfid = assert_trace(%w[ a b d ], pdef)
- assert_nil @dashboard.process(wfid)
- end
- def test_on_error_undo_single_expression
- @dashboard.register_participant :nemo do |wi|
- wi.fields['fail_count'] = 1
- raise 'nemo'
- end
- pdef = Ruote.process_definition do
- sequence do
- echo 'in'
- nemo :on_error => 'undo'
- echo '${f:error}|${f:fail_count}'
- end
- end
- wfid = assert_trace(%w[ in |1 ], pdef)
- assert_nil @dashboard.process(wfid)
- end
- def test_on_error_pass
- pdef = Ruote.process_definition do
- sequence do
- echo 'a'
- sequence :on_error => :pass do
- echo 'b'
- nemo
- echo 'c'
- end
- echo 'd'
- end
- end
- wfid = assert_trace(%w[ a b d ], pdef)
- assert_nil @dashboard.process(wfid)
- end
- def test_missing_handler_triggers_regular_error
- pdef = Ruote.process_definition :on_error => 'failpath' do
- nemo
- end
- wfid = @dashboard.launch(pdef)
- wait_for(wfid)
- ps = @dashboard.process(wfid)
- assert_equal 1, ps.errors.size
- assert_equal 1, logger.log.select { |e| e['action'] == 'error_intercepted' }.size
- end
- def test_on_error_at_process_level
- pdef = Ruote.process_definition :on_error => 'failpath' do
- nemo
- define :failpath do
- echo 'failed.'
- end
- end
- assert_trace('failed.', pdef)
- end
- def test_with_concurrence
- pdef = Ruote.process_definition do
- sequence do
- concurrence :on_error => 'emil' do
- alpha
- error 'nada0'
- error 'nada1'
- end
- echo 'done.'
- end
- end
- @dashboard.context.stash[:a_count] = 0
- @dashboard.context.stash[:e_count] = 0
- @dashboard.register_participant(:alpha) { |wi| stash[:a_count] += 1 }
- @dashboard.register_participant(:emil) { |wi| stash[:e_count] += 1 }
- assert_trace 'done.', pdef
- assert_equal 1, @dashboard.context.stash[:a_count]
- assert_equal 1, @dashboard.context.stash[:e_count]
- end
- def test_participant_on_error
- pdef = Ruote.process_definition do
- troublemaker :on_error => 'handle_error'
- define 'handle_error' do
- troublespotter
- end
- end
- @dashboard.register_participant :troublemaker do |wi|
- wi.fields['seen'] = true
- raise 'Beijing, we have a problem !'
- end
- @dashboard.register_participant :troublespotter do |wi|
- stash[:workitem] = wi
- tracer << 'err...'
- end
- wfid = @dashboard.launch(pdef)
- wait_for(wfid)
- #er = @dashboard.process(wfid).errors.first
- #puts er.message
- #puts er.trace
- wi = @dashboard.context.stash[:workitem]
- assert_equal 'err...', @tracer.to_s
- assert_equal 'RuntimeError', wi.error['class']
- assert_equal 'Beijing, we have a problem !', wi.error['message']
- assert_equal Array, wi.error['trace'].class
- assert_equal true, wi.fields['seen']
- assert_equal(
- %w[ at class details deviations fei message trace tree ],
- wi.error.keys.sort)
- end
- class Murphy
- include Ruote::LocalParticipant
- def cancel(fei, flavour)
- # nothing to do
- end
- def consume(workitem)
- raise "something got wrong"
- end
- end
- def test_subprocess_on_error
- pdef = Ruote.process_definition do
- sequence :on_error => 'error_path' do
- murphy
- end
- define 'error_path' do
- catcher
- end
- end
- @dashboard.register do
- murphy FtOnErrorTest::Murphy
- catchall
- end
- @dashboard.launch(pdef)
- @dashboard.wait_for(:catcher)
- end
- class RescuerOne
- include Ruote::LocalParticipant
- def consume(workitem)
- @context.tracer << 'one'
- reply_to_engine(workitem)
- end
- def accept?(workitem)
- false
- end
- end
- class RescuerTwo
- include Ruote::LocalParticipant
- def consume(workitem)
- @context.tracer << 'two'
- reply_to_engine(workitem)
- end
- #def accept?(workitem)
- # true
- #end
- end
- def test_participants_and_accept
- pdef = Ruote.process_definition do
- sequence :on_error => 'rescuer' do
- nada
- end
- end
- @dashboard.register do
- rescuer RescuerOne
- rescuer RescuerTwo
- end
- assert_trace('two', pdef)
- assert_equal 1, logger.log.select { |e| e['action'] == 'fail' }.size
- end
- # Only to show what's behind :on_error
- #
- def test_on_error_multi
- pdef = Ruote.define do
- sequence :on_error => [
- [ /unknown participant/, 'alpha' ],
- [ nil, 'bravo' ]
- ] do
- nada
- end
- end
- @dashboard.register_participant /alpha|bravo/ do |workitem|
- tracer << workitem.participant_name
- end
- wfid = @dashboard.launch(pdef)
- @dashboard.wait_for(wfid)
- assert_equal 'alpha', @tracer.to_s
- end
- # Let's be open
- #
- def test_on_error_multi_nice
- pdef = Ruote.process_definition do
- sequence :on_error => [
- { /unknown participant/ => 'alpha' },
- { // => 'bravo' }
- ] do
- nada
- end
- end
- @dashboard.register_participant /alpha|bravo/ do |workitem|
- tracer << workitem.participant_name
- end
- wfid = @dashboard.launch(pdef)
- @dashboard.wait_for(wfid)
- assert_equal 'alpha', @tracer.to_s
- end
- def test_on_error_multi_pass
- @dashboard.register_participant /alpha|bravo/ do |workitem|
- tracer << workitem.participant_name
- end
- pdef = Ruote.define do
- sequence :on_error => [
- { /unknown participant/ => :pass },
- { // => 'bravo' }
- ] do
- nada
- end
- echo 'done.'
- end
- wfid = @dashboard.launch(pdef)
- @dashboard.wait_for(wfid)
- assert_equal 'done.', @tracer.to_s
- end
- def test_on_error_rewind
- pdef = Ruote.define do
- cursor :on_error => 'rewind' do
- echo 'in'
- inc 'v:counter'
- error 'fail', :if => '${v:counter} == 1'
- echo 'over.'
- end
- end
- wfid = @dashboard.launch(pdef)
- @dashboard.wait_for(wfid)
- assert_equal %w[ in in over. ], @tracer.to_a
- end
- def test_on_error_jump_to
- pdef = Ruote.define do
- cursor :on_error => 'jump to shark' do
- alpha
- error 'fail'
- bravo
- shark
- delta
- end
- end
- @dashboard.register '.+' do |workitem|
- tracer << workitem.participant_name + "\n"
- end
- wfid = @dashboard.launch(pdef)
- @dashboard.wait_for(wfid)
- assert_equal %w[ alpha shark delta ], @tracer.to_a
- end
- def test_on_error_var
- pdef = Ruote.define do
- define 'sub0' do
- set 'v:/a' => '$f:__error__'
- end
- sequence :on_error => 'sub0' do
- error 'nada'
- end
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal(
- 'terminated',
- r['action'])
- assert_equal(
- %w[ at class details deviations fei message trace tree ],
- r['variables']['a'].keys.sort)
- assert_equal(
- [ 'error', { 'nada' => nil }, [] ],
- r['variables']['a']['tree'])
- end
- def test_on_error_kill_process
- pdef = Ruote.define do
- sequence do
- sequence :on_error => 'cancel_process' do
- nemo0
- end
- nemo1
- end
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal 'terminated', r['action']
- end
- #
- # the "second take" feature
- def test_second_take
- @dashboard.register_participant :troublemaker, TroubleMaker
- pdef = Ruote.define do
- define 'sub0' do
- set '__on_error__' => 'redo'
- end
- sequence :on_error => 'sub0' do
- troublemaker
- end
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal 'terminated', r['action']
- assert_equal 3, r['workitem']['fields']['_trace'].size
- end
- def test_blank_second_take
- pdef = Ruote.define do
- define 'sub0' do
- set '__on_error__' => ''
- end
- sequence :on_error => 'sub0' do
- nada
- end
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal 'terminated', r['action']
- end
- def test_second_take_raise
- pdef = Ruote.define do
- define 'sub0' do
- set '__on_error__' => 'raise'
- end
- sequence :on_error => 'sub0' do
- error 'nada'
- end
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal 'error_intercepted', r['action']
- assert_equal 'nada', r['error']['message']
- end
- # "stashing that for now"
- #
- # def test_second_take_skip
- #
- # pdef = Ruote.define do
- # define 'sub0' do
- # set '__on_error__' => 'cancel'
- # end
- # define 'sub1' do
- # set 'sub1' => true
- # end
- # sequence :on_error => 'sub0', :on_cancel => 'sub1' do
- # error 'nada'
- # end
- # end
- #
- # wfid = @dashboard.launch(pdef)
- # r = @dashboard.wait_for(wfid)
- #
- # #assert_equal 'error_intercepted', r['action']
- # #assert_equal 'nada', r['error']['message']
- # end
- # behaves like 'undo' when there is no on_cancel present
- #
- def test_on_error_cancel
- pdef = Ruote.define do
- sequence :on_error => 'cancel' do
- echo 'n'
- error 'nada'
- end
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal 'terminated', r['action']
- assert_equal %w[ n ], @tracer.to_a
- end
- def test_on_error_and_on_cancel
- pdef = Ruote.define do
- define 'rollback' do
- echo 'rollback'
- end
- sequence :on_cancel => 'rollback', :on_error => 'cancel_process' do
- echo 'in'
- error 'nada'
- end
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal 'terminated', r['action']
- assert_equal %w[ in rollback ], @tracer.to_a
- assert_equal(
- 1,
- @dashboard.logger.log.select { |m| m['action'] == 'cancel_process' }.size)
- end
- def test_on_error_immediate
- # the rollback isn't performed,
- # the !kill_process short-circuits cancelling the errored segment...
- pdef = Ruote.define do
- define 'rollback' do
- echo 'rollback'
- end
- sequence :on_error => '!kill_process' do
- sequence :on_cancel => 'rollback' do
- echo 'in'
- error 'nada'
- end
- end
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal 'terminated', r['action']
- assert_equal %w[ in ], @tracer.to_a
- assert_nil @dashboard.ps(wfid)
- end
- def test_on_error_immediate_with_tree
- pdef = Ruote.define do
- define 'rollback' do
- echo 'rollback'
- end
- sequence :on_error => [ '!kill_process', {}, [] ] do
- sequence :on_cancel => 'rollback' do
- echo 'in'
- error 'nada'
- end
- end
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal 'terminated', r['action']
- assert_equal %w[ in ], @tracer.to_a
- assert_nil @dashboard.ps(wfid)
- end
- def test_on_error_store_to_field
- pdef =
- Ruote.define do
- sequence :on_error => 'store: x' do
- error 'nada'
- end
- echo 'out'
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal %w[ out ], @tracer.to_a
- assert_equal 'Ruote::ForcedError', r['workitem']['fields']['x']['class']
- end
- def test_on_error_store_to_variable
- pdef =
- Ruote.define do
- #sequence :on_error => 'store:v:y' do
- sequence :on_error => 'bury:v:y' do
- error 'nada'
- end
- echo 'out'
- end
- wfid = @dashboard.launch(pdef)
- r = @dashboard.wait_for(wfid)
- assert_equal 'terminated', r['action']
- assert_equal %w[ out ], @tracer.to_a
- assert_equal 'Ruote::ForcedError', r['variables']['y']['class']
- end
- end