<commit_before># encoding: utf-8

require 'spec_helper'

module Punchblock
  module Translator
    class Asterisk
      module Component
        describe Input do
          include HasMockCallbackConnection

          let(:ami_client)      { double('AMI') }
          let(:translator)      { Punchblock::Translator::Asterisk.new ami_client, connection }
          let(:call)            { Punchblock::Translator::Asterisk::Call.new 'foo', translator, ami_client, connection }
          let(:original_command_options) { {} }

          let :original_command do
            Punchblock::Component::Input.new original_command_options
          end

          let :grammar do
            RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'pin' do
              rule id: 'digit' do
                one_of do
                  0.upto(9) { |d| item { d.to_s } }
                end
              end

              rule id: 'pin', scope: 'public' do
                item repeat: '2' do
                  ruleref uri: '#digit'
                end
              end
            end
          end

          subject { Input.new original_command, call }

          describe '#execute' do
            before { original_command.request! }

            it "calls send_progress on the call" do
              call.should_receive(:send_progress)
              subject.execute
            end

            before { call.stub :send_progress }

            let(:original_command_opts) { {} }

            let :original_command_options do
              { :mode => :dtmf, :grammar => { :value => grammar } }.merge(original_command_opts)
            end

            def ami_event_for_dtmf(digit, position)
              RubyAMI::Event.new 'DTMF',
                'Digit' => digit.to_s,
                'Start' => position == :start ? 'Yes' : 'No',
                'End'   => position == :end ? 'Yes' : 'No'
            end

            def send_ami_events_for_dtmf(digit)
              call.process_ami_event ami_event_for_dtmf(digit, :start)
              call.process_ami_event ami_event_for_dtmf(digit, :end)
            end

            let(:reason) { original_command.complete_event(5).reason }

            describe "receiving DTMF events" do
              before do
                subject.execute
                expected_event
              end

              context "when a match is found" do
                before do
                  send_ami_events_for_dtmf 1
                  send_ami_events_for_dtmf 2
                end

                let :expected_nlsml do
                  RubySpeech::NLSML.draw do
                    interpretation confidence: 1 do
                      instance "dtmf-1 dtmf-2"
                      input "12", mode: :dtmf
                    end
                  end
                end

                let :expected_event do
                  Punchblock::Component::Input::Complete::Match.new nlsml: expected_nlsml
                end

                it "should send a success complete event with the relevant data" do
                  reason.should be == expected_event
                end

                it "should not process further dtmf events" do
                  subject.should_receive(:process_dtmf).never
                  send_ami_events_for_dtmf 3
                end

                it "should not leave the recognizer running" do
                  Celluloid::Actor.all.map { |a| a.class }.should_not include(Punchblock::Translator::DTMFRecognizer)
                end
              end

              context "when the match is invalid" do
                before do
                  send_ami_events_for_dtmf 1
                  send_ami_events_for_dtmf '#'
                end

                let :expected_event do
                  Punchblock::Component::Input::Complete::NoMatch.new
                end

                it "should send a nomatch complete event" do
                  reason.should be == expected_event
                end
              end

              context "dtmf event received after recognizer has terminated" do
                before do
                  send_ami_events_for_dtmf 1
                  send_ami_events_for_dtmf '#'
                  subject.execute
                end

                let :expected_event do
                  Punchblock::Component::Input::Complete::NoMatch.new
                end

                it "should not crash the translator if the recognizer is dead" do
                  Celluloid::Actor.all.map { |a| a.class }.should include(Punchblock::Translator::DTMFRecognizer)
                  recognizer = Celluloid::Actor.all.find { |a| a.class == Punchblock::Translator::DTMFRecognizer }
                  recognizer.terminate if recognizer
                  Celluloid::Actor.all.map { |a| a.class }.should_not include(Punchblock::Translator::DTMFRecognizer)
                  subject.process_dtmf 1
                  Celluloid::Actor.all.map { |a| a.class }.should include(translator.class)
                end
              end
            end

            describe 'grammar' do
              context 'unset' do
                let(:original_command_opts) { { :grammar => nil } }<commit_msg>added an explanation comment<commit_after># encoding: utf-8

require 'spec_helper'

module Punchblock
  module Translator
    class Asterisk
      module Component
        describe Input do
          include HasMockCallbackConnection

          let(:ami_client)      { double('AMI') }
          let(:translator)      { Punchblock::Translator::Asterisk.new ami_client, connection }
          let(:call)            { Punchblock::Translator::Asterisk::Call.new 'foo', translator, ami_client, connection }
          let(:original_command_options) { {} }

          let :original_command do
            Punchblock::Component::Input.new original_command_options
          end

          let :grammar do
            RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'pin' do
              rule id: 'digit' do
                one_of do
                  0.upto(9) { |d| item { d.to_s } }
                end
              end

              rule id: 'pin', scope: 'public' do
                item repeat: '2' do
                  ruleref uri: '#digit'
                end
              end
            end
          end

          subject { Input.new original_command, call }

          describe '#execute' do
            before { original_command.request! }

            it "calls send_progress on the call" do
              call.should_receive(:send_progress)
              subject.execute
            end

            before { call.stub :send_progress }

            let(:original_command_opts) { {} }

            let :original_command_options do
              { :mode => :dtmf, :grammar => { :value => grammar } }.merge(original_command_opts)
            end

            def ami_event_for_dtmf(digit, position)
              RubyAMI::Event.new 'DTMF',
                'Digit' => digit.to_s,
                'Start' => position == :start ? 'Yes' : 'No',
                'End'   => position == :end ? 'Yes' : 'No'
            end

            def send_ami_events_for_dtmf(digit)
              call.process_ami_event ami_event_for_dtmf(digit, :start)
              call.process_ami_event ami_event_for_dtmf(digit, :end)
            end

            let(:reason) { original_command.complete_event(5).reason }

            describe "receiving DTMF events" do
              before do
                subject.execute
                expected_event
              end

              context "when a match is found" do
                before do
                  send_ami_events_for_dtmf 1
                  send_ami_events_for_dtmf 2
                end

                let :expected_nlsml do
                  RubySpeech::NLSML.draw do
                    interpretation confidence: 1 do
                      instance "dtmf-1 dtmf-2"
                      input "12", mode: :dtmf
                    end
                  end
                end

                let :expected_event do
                  Punchblock::Component::Input::Complete::Match.new nlsml: expected_nlsml
                end

                it "should send a success complete event with the relevant data" do
                  reason.should be == expected_event
                end

                it "should not process further dtmf events" do
                  subject.should_receive(:process_dtmf).never
                  send_ami_events_for_dtmf 3
                end

                it "should not leave the recognizer running" do
                  Celluloid::Actor.all.map { |a| a.class }.should_not include(Punchblock::Translator::DTMFRecognizer)
                end
              end

              context "when the match is invalid" do
                before do
                  send_ami_events_for_dtmf 1
                  send_ami_events_for_dtmf '#'
                end

                let :expected_event do
                  Punchblock::Component::Input::Complete::NoMatch.new
                end

                it "should send a nomatch complete event" do
                  reason.should be == expected_event
                end
              end

              context "dtmf event received after recognizer has terminated" do
                before do
                  send_ami_events_for_dtmf 1
                  send_ami_events_for_dtmf '#'
                  subject.execute
                end

                let :expected_event do
                  Punchblock::Component::Input::Complete::NoMatch.new
                end

                it "should not crash the translator if the recognizer is dead" do
                  Celluloid::Actor.all.map { |a| a.class }.should include(Punchblock::Translator::DTMFRecognizer)
                  recognizer = Celluloid::Actor.all.find { |a| a.class == Punchblock::Translator::DTMFRecognizer }
                  recognizer.terminate if recognizer
                  Celluloid::Actor.all.map { |a| a.class }.should_not include(Punchblock::Translator::DTMFRecognizer)
                  subject.process_dtmf 1 # trigger failure
                  Celluloid::Actor.all.map { |a| a.class }.should include(translator.class)
                end
              end
            end

            describe 'grammar' do
              context 'unset' do
                let(:original_command_opts) { { :grammar => nil } }
{'fraction_non_alphanumeric': 0.07403505411359065, 'fraction_numerical': 0.0030648405325160426, 'mean_word_length': 1.387834438600503, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '934971', 'n_tokens_mistral': 2829, 'n_tokens_neox': 2604, 'n_words': 667}