Permalink
Browse files

Merge branch 'feature/prompt_interrupt' into develop

  • Loading branch information...
2 parents bb79ffc + fb433eb commit 880ad78894831a63b4fd42e1db4309cc0d8bea01 @benlangfeld benlangfeld committed Nov 15, 2012
@@ -26,9 +26,10 @@ def inspect
# @option :opts [Float, optional] :sensitivity Indicates how sensitive the interpreter should be to loud versus quiet input. Higher values represent greater sensitivity.
# @option :opts [Integer, optional] :initial_timeout Indicates the amount of time preceding input which may expire before a timeout is triggered.
# @option :opts [Integer, optional] :inter_digit_timeout Indicates (in the case of DTMF input) the amount of time between input digits which may expire before a timeout is triggered.
- #
+ # @option :opts [Integer, optional] :timeout Times out the grammar (and terminates output) if no response after this value in seconds
#
def listen(opts = {})
+ opts = opts.dup
raise ArgumentError, "You must provide a grammar, a grammar URL or a set of options" unless opts[:grammar] || opts[:grammar_url] || opts[:options].respond_to?(:each)
grammar_opts = if opts[:grammar_url]
{ url: opts[:grammar_url] }
@@ -48,11 +49,30 @@ def listen(opts = {})
end
input_options = opts.merge(grammar: grammar_opts, min_confidence: AdhearsionASR::Plugin.config[:min_confidence])
prompts = Array(opts.delete :prompt)
- [:prompt, :options, :grammar_url].each { |o| input_options.delete o }
+ timeout = opts.has_key?(:timeout) ? opts.delete(:timeout) : AdhearsionASR::Plugin.config[:timeout]
+ [:prompt, :options, :grammar_url, :timeout].each { |o| input_options.delete o }
input_component = Punchblock::Component::Input.new input_options
- execute_component_and_await_completion input_component do
- player.output Adhearsion::CallController::Output::Formatter.ssml_for_collection(prompts) if prompts.any?
+
+ if prompts.any?
+ output = player.output Adhearsion::CallController::Output::Formatter.ssml_for_collection(prompts) do |output_component|
+ input_component.register_event_handler Punchblock::Event::Complete do |event|
+ unless output_component.complete?
+ output_component.stop!
+ end
+ end
+ write_and_await_response input_component
+ end
+ output.complete_event
+ else
+ execute_component_and_await_completion input_component
+ end
+
+ if timeout
+ call.after(timeout) do
+ logger.debug "Timeout triggered, halting input component"
+ input_component.stop! unless input_component.complete?
+ end
end
reason = input_component.complete_event.reason
@@ -2,6 +2,7 @@ module AdhearsionASR
class Plugin < Adhearsion::Plugin
config :adhearsion_asr do
min_confidence 0.5, desc: 'The default minimum confidence level used for all recognizer invocations.', transform: Proc.new { |v| v.to_f }
+ timeout 5, desc: 'The default timeout (in seconds) used for all recognizer invocations.', transform: Proc.new { |v| v.to_i }
end
end
end
@@ -67,10 +67,11 @@ def expect_component_execution(component, fail = false)
end
end
- def expect_component_complete_event(reason = nil)
- reason ||= Punchblock::Component::Input::Complete::Match.new :nlsml => nlsml
- complete_event = Punchblock::Event::Complete.new :reason => reason
- Punchblock::Component::Input.any_instance.should_receive(:complete_event).at_least(:once).and_return(complete_event)
+ let(:input_complete_reason) { Punchblock::Component::Input::Complete::Match.new nlsml: nlsml }
+ let(:input_complete_event) { Punchblock::Event::Complete.new reason: input_complete_reason }
+
+ def expect_component_complete_event
+ Punchblock::Component::Input.any_instance.should_receive(:complete_event).at_least(:once).and_return(input_complete_event)
end
it "sends the correct input component" do
@@ -124,21 +125,11 @@ def expect_component_complete_event(reason = nil)
result.nlsml.should be == nlsml
end
- context "with a nil timeout" do
- it "does not set a timeout on the component" do
- pending
- expect_component_complete_event
- expect_component_execution input_component
- subject.wait_for_digit timeout
- end
- end
-
context "when a nomatch occurrs" do
- before do
- expect_component_complete_event Punchblock::Component::Input::Complete::NoMatch.new
- end
+ let(:input_complete_reason) { Punchblock::Component::Input::Complete::NoMatch.new }
it "should return a response of nil and a status of nomatch" do
+ expect_component_complete_event
expect_component_execution input_component
result = subject.listen options: %w{yes no}
result.response.should be nil
@@ -159,19 +150,54 @@ def expect_component_complete_event(reason = nil)
Punchblock::Component::Output.new ssml: ssml
end
- before do
- reason ||= Punchblock::Component::Output::Complete::Success.new
- complete_event = Punchblock::Event::Complete.new :reason => reason
- Punchblock::Component::Output.any_instance.should_receive(:complete_event).at_least(:once).and_return(complete_event)
+ let(:output_complete_event) do
+ reason = Punchblock::Component::Output::Complete::Success.new
+ Punchblock::Event::Complete.new reason: reason
+ end
+
+ def expect_output_completion
+ Punchblock::Component::Output.any_instance.should_receive(:complete_event).at_least(:once).and_return(output_complete_event)
end
it "plays the correct output" do
expect_component_complete_event
+ expect_output_completion
expect_message_waiting_for_response input_component
expect_message_waiting_for_response output_component
subject.listen prompt: prompt, options: %w{yes no}
end
+ it "should terminate the output when the input completes" do
+ latch = CountDownLatch.new 1
+
+ expect_message_waiting_for_response input_component
+ input_component.request!
+ input_component.execute!
+ Punchblock::Component::Input.should_receive(:new).and_return(input_component)
+ expect_message_waiting_for_response output_component
+ output_component.request!
+ output_component.execute!
+ Punchblock::Component::Output.should_receive(:new).and_return(output_component)
+
+ thread = Thread.new do
+ result = subject.listen prompt: prompt, options: %w{yes no}
+ latch.countdown!
+ result
+ end
+ latch.wait(1).should be_false
+
+ output_component.should_receive(:stop!).once do
+ output_component.add_event output_complete_event
+ end
+
+ input_component.add_event input_complete_event
+ input_component.trigger_event_handler input_complete_event
+
+ latch.wait(1).should be_true
+
+ thread.join.should_not be_nil
+ end
+
context "with a collection of prompts" do
let(:prompts) { ["/srv/foo.mp3", "Press 3 or 5 to make something happen."] }
@@ -184,9 +210,13 @@ def expect_component_complete_event(reason = nil)
it "plays all prompts concatenated" do
expect_component_complete_event
+ expect_output_completion
expect_message_waiting_for_response input_component
expect_message_waiting_for_response output_component
- subject.listen prompt: prompts, options: %w{yes no}
+ original_options = {prompt: prompts, options: %w{yes no}}
+ options = original_options.dup
+ subject.listen options
+ options.should == original_options
end
end
end
View
@@ -8,4 +8,6 @@
config.mock_with :rspec
config.filter_run :focus => true
config.run_all_when_everything_filtered = true
+
+ config.backtrace_clean_patterns = [/rspec/]
end

0 comments on commit 880ad78

Please sign in to comment.