adhearsion 2.6.4 → 3.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -9
  3. data/CHANGELOG.md +22 -7
  4. data/Gemfile +2 -0
  5. data/README.markdown +4 -5
  6. data/Rakefile +1 -2
  7. data/adhearsion.gemspec +19 -8
  8. data/features/cli_create.feature +19 -3
  9. data/features/step_definitions/cli_steps.rb +0 -11
  10. data/features/support/env.rb +3 -4
  11. data/lib/adhearsion.rb +48 -27
  12. data/lib/adhearsion/call.rb +34 -50
  13. data/lib/adhearsion/call_controller.rb +6 -12
  14. data/lib/adhearsion/call_controller/dial.rb +15 -53
  15. data/lib/adhearsion/call_controller/input.rb +39 -162
  16. data/lib/adhearsion/call_controller/input/ask_grammar_builder.rb +44 -0
  17. data/lib/adhearsion/call_controller/input/menu_builder.rb +136 -0
  18. data/lib/adhearsion/call_controller/input/prompt_builder.rb +78 -0
  19. data/lib/adhearsion/call_controller/input/result.rb +46 -0
  20. data/lib/adhearsion/call_controller/output.rb +48 -67
  21. data/lib/adhearsion/call_controller/output/abstract_player.rb +3 -3
  22. data/lib/adhearsion/call_controller/output/async_player.rb +3 -3
  23. data/lib/adhearsion/call_controller/output/player.rb +1 -1
  24. data/lib/adhearsion/call_controller/record.rb +23 -8
  25. data/lib/adhearsion/calls.rb +1 -1
  26. data/lib/adhearsion/cli_commands/ahn_command.rb +2 -65
  27. data/lib/adhearsion/cli_commands/thor_errors.rb +0 -6
  28. data/lib/adhearsion/configuration.rb +91 -39
  29. data/lib/adhearsion/core_ext/blather/stanza.rb +41 -0
  30. data/lib/adhearsion/core_ext/blather/stanza/presence.rb +13 -0
  31. data/lib/adhearsion/error.rb +5 -0
  32. data/lib/adhearsion/event.rb +21 -0
  33. data/lib/adhearsion/event/active_speaker.rb +11 -0
  34. data/lib/adhearsion/event/answered.rb +11 -0
  35. data/lib/adhearsion/event/asterisk.rb +10 -0
  36. data/lib/adhearsion/event/asterisk/ami.rb +34 -0
  37. data/lib/adhearsion/event/complete.rb +75 -0
  38. data/lib/adhearsion/event/dtmf.rb +11 -0
  39. data/lib/adhearsion/event/end.rb +22 -0
  40. data/lib/adhearsion/event/input_timers_started.rb +9 -0
  41. data/lib/adhearsion/event/joined.rb +17 -0
  42. data/lib/adhearsion/event/offer.rb +14 -0
  43. data/lib/adhearsion/event/ringing.rb +11 -0
  44. data/lib/adhearsion/event/started_speaking.rb +13 -0
  45. data/lib/adhearsion/event/stopped_speaking.rb +13 -0
  46. data/lib/adhearsion/event/unjoined.rb +17 -0
  47. data/lib/adhearsion/events.rb +47 -66
  48. data/lib/adhearsion/foundation.rb +0 -1
  49. data/lib/adhearsion/foundation/object.rb +0 -5
  50. data/lib/adhearsion/generators/app/app_generator.rb +4 -1
  51. data/lib/adhearsion/generators/app/templates/Gemfile.erb +2 -10
  52. data/lib/adhearsion/generators/app/templates/adhearsion.erb +9 -9
  53. data/lib/adhearsion/generators/app/templates/config.ru +7 -0
  54. data/lib/adhearsion/generators/app/templates/en.yml +4 -0
  55. data/lib/adhearsion/generators/app/templates/events.erb +2 -2
  56. data/lib/adhearsion/generators/app/templates/hello_world.wav +0 -0
  57. data/lib/adhearsion/generators/app/templates/simon_game.rb +2 -1
  58. data/lib/adhearsion/generators/app/templates/simon_game_spec.rb +2 -2
  59. data/lib/adhearsion/has_headers.rb +34 -0
  60. data/lib/adhearsion/http_server.rb +37 -0
  61. data/lib/adhearsion/initializer.rb +19 -153
  62. data/lib/adhearsion/logging.rb +6 -25
  63. data/lib/adhearsion/outbound_call.rb +5 -5
  64. data/lib/adhearsion/plugin.rb +1 -0
  65. data/lib/adhearsion/protocol_error.rb +26 -0
  66. data/lib/adhearsion/rayo.rb +30 -0
  67. data/lib/adhearsion/rayo/client.rb +62 -0
  68. data/lib/adhearsion/rayo/client/component_registry.rb +33 -0
  69. data/lib/adhearsion/rayo/command.rb +21 -0
  70. data/lib/adhearsion/rayo/command/accept.rb +16 -0
  71. data/lib/adhearsion/rayo/command/answer.rb +16 -0
  72. data/lib/adhearsion/rayo/command/dial.rb +57 -0
  73. data/lib/adhearsion/rayo/command/hangup.rb +16 -0
  74. data/lib/adhearsion/rayo/command/join.rb +43 -0
  75. data/lib/adhearsion/rayo/command/mute.rb +13 -0
  76. data/lib/adhearsion/rayo/command/redirect.rb +23 -0
  77. data/lib/adhearsion/rayo/command/reject.rb +40 -0
  78. data/lib/adhearsion/rayo/command/unjoin.rb +24 -0
  79. data/lib/adhearsion/rayo/command/unmute.rb +13 -0
  80. data/lib/adhearsion/rayo/command_node.rb +47 -0
  81. data/lib/adhearsion/rayo/component.rb +21 -0
  82. data/lib/adhearsion/rayo/component/asterisk.rb +13 -0
  83. data/lib/adhearsion/rayo/component/asterisk/agi.rb +14 -0
  84. data/lib/adhearsion/rayo/component/asterisk/agi/command.rb +46 -0
  85. data/lib/adhearsion/rayo/component/asterisk/ami.rb +14 -0
  86. data/lib/adhearsion/rayo/component/asterisk/ami/action.rb +61 -0
  87. data/lib/adhearsion/rayo/component/component_node.rb +90 -0
  88. data/lib/adhearsion/rayo/component/input.rb +186 -0
  89. data/lib/adhearsion/rayo/component/output.rb +471 -0
  90. data/lib/adhearsion/rayo/component/prompt.rb +53 -0
  91. data/lib/adhearsion/rayo/component/receive_fax.rb +26 -0
  92. data/lib/adhearsion/rayo/component/record.rb +165 -0
  93. data/lib/adhearsion/rayo/component/send_fax.rb +64 -0
  94. data/lib/adhearsion/rayo/component/stop.rb +11 -0
  95. data/lib/adhearsion/rayo/connection.rb +12 -0
  96. data/lib/adhearsion/rayo/connection/asterisk.rb +74 -0
  97. data/lib/adhearsion/rayo/connection/connected.rb +22 -0
  98. data/lib/adhearsion/rayo/connection/generic_connection.rb +22 -0
  99. data/lib/adhearsion/rayo/connection/xmpp.rb +198 -0
  100. data/lib/adhearsion/rayo/disconnected_error.rb +22 -0
  101. data/lib/adhearsion/{punchblock_plugin → rayo}/initializer.rb +19 -19
  102. data/lib/adhearsion/rayo/rayo_node.rb +127 -0
  103. data/lib/adhearsion/rayo/ref.rb +57 -0
  104. data/lib/adhearsion/statistics.rb +1 -1
  105. data/lib/adhearsion/tasks.rb +1 -2
  106. data/lib/adhearsion/tasks/configuration.rb +1 -1
  107. data/lib/adhearsion/tasks/environment.rb +0 -2
  108. data/lib/adhearsion/tasks/i18n.rb +49 -0
  109. data/lib/adhearsion/translator.rb +11 -0
  110. data/lib/adhearsion/translator/asterisk.rb +234 -0
  111. data/lib/adhearsion/translator/asterisk/agi_app.rb +17 -0
  112. data/lib/adhearsion/translator/asterisk/agi_command.rb +45 -0
  113. data/lib/adhearsion/translator/asterisk/ami_error_converter.rb +20 -0
  114. data/lib/adhearsion/translator/asterisk/call.rb +416 -0
  115. data/lib/adhearsion/translator/asterisk/channel.rb +43 -0
  116. data/lib/adhearsion/translator/asterisk/component.rb +88 -0
  117. data/lib/adhearsion/translator/asterisk/component/asterisk.rb +15 -0
  118. data/lib/adhearsion/translator/asterisk/component/asterisk/agi_command.rb +42 -0
  119. data/lib/adhearsion/translator/asterisk/component/asterisk/ami_action.rb +68 -0
  120. data/lib/adhearsion/translator/asterisk/component/composed_prompt.rb +76 -0
  121. data/lib/adhearsion/translator/asterisk/component/dtmf_recognizer.rb +137 -0
  122. data/lib/adhearsion/translator/asterisk/component/input.rb +34 -0
  123. data/lib/adhearsion/translator/asterisk/component/input_component.rb +90 -0
  124. data/lib/adhearsion/translator/asterisk/component/mrcp_native_prompt.rb +71 -0
  125. data/lib/adhearsion/translator/asterisk/component/mrcp_prompt.rb +55 -0
  126. data/lib/adhearsion/translator/asterisk/component/mrcp_recog_prompt.rb +165 -0
  127. data/lib/adhearsion/translator/asterisk/component/output.rb +233 -0
  128. data/lib/adhearsion/translator/asterisk/component/record.rb +101 -0
  129. data/lib/adhearsion/translator/asterisk/component/stop_by_redirect.rb +30 -0
  130. data/lib/adhearsion/translator/asterisk/unimrcp_app.rb +28 -0
  131. data/lib/adhearsion/uri_list.rb +21 -0
  132. data/lib/adhearsion/version.rb +1 -1
  133. data/spec/adhearsion/call_controller/dial_spec.rb +79 -1420
  134. data/spec/adhearsion/call_controller/input_spec.rb +1141 -237
  135. data/spec/adhearsion/call_controller/output/async_player_spec.rb +10 -10
  136. data/spec/adhearsion/call_controller/output/player_spec.rb +8 -8
  137. data/spec/adhearsion/call_controller/output_spec.rb +162 -215
  138. data/spec/adhearsion/call_controller/record_spec.rb +15 -16
  139. data/spec/adhearsion/call_controller_spec.rb +23 -40
  140. data/spec/adhearsion/call_spec.rb +123 -129
  141. data/spec/adhearsion/calls_spec.rb +3 -3
  142. data/spec/adhearsion/configuration_spec.rb +94 -108
  143. data/spec/adhearsion/event/answered_spec.rb +50 -0
  144. data/spec/adhearsion/event/asterisk/Find Results +402 -0
  145. data/spec/adhearsion/event/asterisk/ami_spec.rb +81 -0
  146. data/spec/adhearsion/event/complete_spec.rb +176 -0
  147. data/spec/adhearsion/event/dtmf_spec.rb +35 -0
  148. data/spec/adhearsion/event/end_spec.rb +85 -0
  149. data/spec/adhearsion/event/input_timers_started_spec.rb +19 -0
  150. data/spec/adhearsion/event/joined_spec.rb +53 -0
  151. data/spec/adhearsion/event/offer_spec.rb +106 -0
  152. data/spec/adhearsion/event/ringing_spec.rb +50 -0
  153. data/spec/adhearsion/event/started_speaking_spec.rb +37 -0
  154. data/spec/adhearsion/event/stopped_speaking_spec.rb +37 -0
  155. data/spec/adhearsion/event/unjoined_spec.rb +48 -0
  156. data/spec/adhearsion/event/untitled +0 -0
  157. data/spec/adhearsion/events_spec.rb +19 -45
  158. data/spec/adhearsion/initializer_spec.rb +12 -184
  159. data/spec/adhearsion/logging_spec.rb +5 -20
  160. data/spec/adhearsion/outbound_call_spec.rb +13 -13
  161. data/spec/adhearsion/plugin_spec.rb +3 -4
  162. data/spec/adhearsion/protocol_error_spec.rb +91 -0
  163. data/spec/adhearsion/rayo/client/component_registry_spec.rb +26 -0
  164. data/spec/adhearsion/rayo/client_spec.rb +134 -0
  165. data/spec/adhearsion/rayo/command/accept_spec.rb +63 -0
  166. data/spec/adhearsion/rayo/command/answer_spec.rb +73 -0
  167. data/spec/adhearsion/rayo/command/dial_spec.rb +156 -0
  168. data/spec/adhearsion/rayo/command/hangup_spec.rb +63 -0
  169. data/spec/adhearsion/rayo/command/join_spec.rb +158 -0
  170. data/spec/adhearsion/rayo/command/mute_spec.rb +32 -0
  171. data/spec/adhearsion/rayo/command/redirect_spec.rb +89 -0
  172. data/spec/adhearsion/rayo/command/reject_spec.rb +117 -0
  173. data/spec/adhearsion/rayo/command/unjoin_spec.rb +82 -0
  174. data/spec/adhearsion/rayo/command/unmute_spec.rb +32 -0
  175. data/spec/adhearsion/rayo/command_node_spec.rb +101 -0
  176. data/spec/adhearsion/rayo/component/asterisk/agi/command_spec.rb +111 -0
  177. data/spec/adhearsion/rayo/component/asterisk/ami/action_spec.rb +173 -0
  178. data/spec/adhearsion/rayo/component/component_node_spec.rb +110 -0
  179. data/spec/adhearsion/rayo/component/input_spec.rb +715 -0
  180. data/spec/adhearsion/rayo/component/output_spec.rb +1030 -0
  181. data/spec/adhearsion/rayo/component/prompt_spec.rb +171 -0
  182. data/spec/adhearsion/rayo/component/receive_fax_spec.rb +136 -0
  183. data/spec/adhearsion/rayo/component/record_spec.rb +497 -0
  184. data/spec/adhearsion/rayo/component/send_fax_spec.rb +144 -0
  185. data/spec/adhearsion/rayo/connection/asterisk_spec.rb +118 -0
  186. data/spec/adhearsion/rayo/connection/xmpp_spec.rb +449 -0
  187. data/spec/adhearsion/rayo/initializer_spec.rb +353 -0
  188. data/spec/adhearsion/rayo/ref_spec.rb +168 -0
  189. data/spec/adhearsion/rayo_spec.rb +7 -0
  190. data/spec/adhearsion/router/route_spec.rb +1 -1
  191. data/spec/adhearsion/statistics_spec.rb +2 -5
  192. data/spec/adhearsion/translator/asterisk/call_spec.rb +2047 -0
  193. data/spec/adhearsion/translator/asterisk/component/asterisk/agi_command_spec.rb +256 -0
  194. data/spec/adhearsion/translator/asterisk/component/asterisk/ami_action_spec.rb +151 -0
  195. data/spec/adhearsion/translator/asterisk/component/composed_prompt_spec.rb +257 -0
  196. data/spec/adhearsion/translator/asterisk/component/input_spec.rb +571 -0
  197. data/spec/adhearsion/translator/asterisk/component/mrcp_native_prompt_spec.rb +774 -0
  198. data/spec/adhearsion/translator/asterisk/component/mrcp_prompt_spec.rb +1244 -0
  199. data/spec/adhearsion/translator/asterisk/component/output_spec.rb +1850 -0
  200. data/spec/adhearsion/translator/asterisk/component/record_spec.rb +426 -0
  201. data/spec/adhearsion/translator/asterisk/component/stop_by_redirect_spec.rb +62 -0
  202. data/spec/adhearsion/translator/asterisk/component_spec.rb +83 -0
  203. data/spec/adhearsion/translator/asterisk_spec.rb +685 -0
  204. data/spec/adhearsion/uri_list_spec.rb +88 -0
  205. data/spec/adhearsion_spec.rb +89 -14
  206. data/spec/fixtures/locale/en.yml +13 -0
  207. data/spec/fixtures/locale/it.yml +13 -0
  208. data/spec/spec_helper.rb +18 -5
  209. data/spec/support/call_controller_test_helpers.rb +3 -2
  210. data/spec/support/initializer_stubs.rb +3 -1
  211. data/spec/support/punchblock_examples.rb +65 -0
  212. data/spec/support/punchblock_mocks.rb +12 -0
  213. metadata +412 -70
  214. data/features/cli_daemon.feature +0 -20
  215. data/features/cli_restart.feature +0 -52
  216. data/features/cli_stop.feature +0 -50
  217. data/lib/adhearsion/call_controller/menu_dsl.rb +0 -21
  218. data/lib/adhearsion/call_controller/menu_dsl/array_match_calculator.rb +0 -26
  219. data/lib/adhearsion/call_controller/menu_dsl/calculated_match.rb +0 -43
  220. data/lib/adhearsion/call_controller/menu_dsl/calculated_match_collection.rb +0 -45
  221. data/lib/adhearsion/call_controller/menu_dsl/fixnum_match_calculator.rb +0 -11
  222. data/lib/adhearsion/call_controller/menu_dsl/match_calculator.rb +0 -40
  223. data/lib/adhearsion/call_controller/menu_dsl/menu.rb +0 -207
  224. data/lib/adhearsion/call_controller/menu_dsl/menu_builder.rb +0 -92
  225. data/lib/adhearsion/call_controller/menu_dsl/range_match_calculator.rb +0 -60
  226. data/lib/adhearsion/call_controller/menu_dsl/string_match_calculator.rb +0 -25
  227. data/lib/adhearsion/call_controller/utility.rb +0 -77
  228. data/lib/adhearsion/foundation/custom_daemonizer.rb +0 -52
  229. data/lib/adhearsion/punchblock_plugin.rb +0 -63
  230. data/scripts/cloc-1.64.pl +0 -10483
  231. data/spec/adhearsion/call_controller/menu_dsl/array_match_calculator_spec.rb +0 -76
  232. data/spec/adhearsion/call_controller/menu_dsl/calculated_match_collection_spec.rb +0 -60
  233. data/spec/adhearsion/call_controller/menu_dsl/calculated_match_spec.rb +0 -61
  234. data/spec/adhearsion/call_controller/menu_dsl/fixnum_match_calculator_spec.rb +0 -39
  235. data/spec/adhearsion/call_controller/menu_dsl/match_calculator_spec.rb +0 -17
  236. data/spec/adhearsion/call_controller/menu_dsl/menu_builder_spec.rb +0 -165
  237. data/spec/adhearsion/call_controller/menu_dsl/menu_spec.rb +0 -420
  238. data/spec/adhearsion/call_controller/menu_dsl/range_match_calculator_spec.rb +0 -32
  239. data/spec/adhearsion/call_controller/menu_dsl/string_match_calculator_spec.rb +0 -40
  240. data/spec/adhearsion/call_controller/utility_spec.rb +0 -90
  241. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +0 -356
  242. data/spec/adhearsion/punchblock_plugin_spec.rb +0 -59
@@ -0,0 +1,571 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Adhearsion
6
+ module Translator
7
+ class Asterisk
8
+ module Component
9
+ describe Input do
10
+ include HasMockCallbackConnection
11
+
12
+ let(:ami_client) { double('AMI') }
13
+ let(:translator) { Translator::Asterisk.new ami_client, connection }
14
+ let(:call) { Translator::Asterisk::Call.new 'foo', translator, ami_client, connection }
15
+ let(:original_command_options) { {} }
16
+
17
+ let :original_command do
18
+ Adhearsion::Rayo::Component::Input.new original_command_options
19
+ end
20
+
21
+ let :grammar do
22
+ RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'pin' do
23
+ rule id: 'digit' do
24
+ one_of do
25
+ 0.upto(9) { |d| item { d.to_s } }
26
+ end
27
+ end
28
+
29
+ rule id: 'pin', scope: 'public' do
30
+ item repeat: '2' do
31
+ ruleref uri: '#digit'
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ subject { Input.new original_command, call }
38
+
39
+ describe '#execute' do
40
+ before { original_command.request! }
41
+
42
+ it "calls send_progress on the call" do
43
+ expect(call).to receive(:send_progress)
44
+ subject.execute
45
+ end
46
+
47
+ before { allow(call).to receive :send_progress }
48
+
49
+ let(:original_command_opts) { {} }
50
+
51
+ let :original_command_options do
52
+ { :mode => :dtmf, :grammar => { :value => grammar } }.merge(original_command_opts)
53
+ end
54
+
55
+ let (:ast13mode) { false }
56
+
57
+ def ami_event_for_dtmf(digit, position)
58
+ if ast13mode
59
+ RubyAMI::Event.new 'DTMF' + (position == :start ? 'Begin' : '') + (position == :end ? 'End' : ''),
60
+ 'Digit' => digit.to_s
61
+ else
62
+ RubyAMI::Event.new 'DTMF',
63
+ 'Digit' => digit.to_s,
64
+ 'Start' => position == :start ? 'Yes' : 'No',
65
+ 'End' => position == :end ? 'Yes' : 'No'
66
+ end
67
+ end
68
+
69
+ def send_ami_events_for_dtmf(digit)
70
+ call.process_ami_event ami_event_for_dtmf(digit, :start)
71
+ call.process_ami_event ami_event_for_dtmf(digit, :end)
72
+ end
73
+
74
+ let(:reason) { original_command.complete_event(5).reason }
75
+
76
+ describe "receiving DTMF events" do
77
+ before do
78
+ subject.execute
79
+ expected_event
80
+ end
81
+
82
+ context "when a match is found" do
83
+ before do
84
+ send_ami_events_for_dtmf 1
85
+ send_ami_events_for_dtmf 2
86
+ end
87
+
88
+ let :expected_nlsml do
89
+ RubySpeech::NLSML.draw do
90
+ interpretation confidence: 1 do
91
+ instance "dtmf-1 dtmf-2"
92
+ input "12", mode: :dtmf
93
+ end
94
+ end
95
+ end
96
+
97
+ let :expected_event do
98
+ Adhearsion::Rayo::Component::Input::Complete::Match.new nlsml: expected_nlsml
99
+ end
100
+
101
+ it "should send a success complete event with the relevant data" do
102
+ expect(reason).to eq(expected_event)
103
+ end
104
+
105
+ it "should not process further dtmf events" do
106
+ expect(subject).to receive(:process_dtmf).never
107
+ send_ami_events_for_dtmf 3
108
+ end
109
+
110
+ it "should not leave the recognizer running" do
111
+ expect(Celluloid::Actor.all.any? { |a| a.class == Translator::Asterisk::Component::DTMFRecognizer rescue false }).to eq(false)
112
+ end
113
+
114
+ context 'with an Asterisk 13 DTMFEnd event' do
115
+ let(:ast13mode) { true }
116
+ it "should send a success complete event with the relevant data" do
117
+ expect(reason).to eq(expected_event)
118
+ end
119
+ end
120
+ end
121
+
122
+ context "when the match is invalid" do
123
+ before do
124
+ send_ami_events_for_dtmf 1
125
+ send_ami_events_for_dtmf '#'
126
+ end
127
+
128
+ let :expected_event do
129
+ Adhearsion::Rayo::Component::Input::Complete::NoMatch.new
130
+ end
131
+
132
+ it "should send a nomatch complete event" do
133
+ expect(reason).to eq(expected_event)
134
+ end
135
+
136
+ context 'with an Asterisk 13 DTMFEnd event' do
137
+ let(:ast13mode) { true }
138
+ it "should send a nomatch complete event" do
139
+ expect(reason).to eq(expected_event)
140
+ end
141
+ end
142
+ end
143
+
144
+ context "dtmf event received after recognizer has terminated" do
145
+ before do
146
+ send_ami_events_for_dtmf 1
147
+ send_ami_events_for_dtmf '#'
148
+ subject.execute
149
+ end
150
+
151
+ let :expected_event do
152
+ Adhearsion::Rayo::Component::Input::Complete::NoMatch.new
153
+ end
154
+
155
+ it "should not crash the translator if the recognizer is dead" do
156
+ expect(Celluloid::Actor.all.map { |a| a.class }).to include(Translator::Asterisk::Component::DTMFRecognizer)
157
+ recognizer = Celluloid::Actor.all.find { |a| a.class == Translator::Asterisk::Component::DTMFRecognizer }
158
+ recognizer.terminate if recognizer
159
+ expect(Celluloid::Actor.all.map { |a| a.class }).not_to include(Translator::Asterisk::Component::DTMFRecognizer)
160
+ subject.process_dtmf 1 # trigger failure
161
+ expect(Celluloid::Actor.all.map { |a| a.class }).to include(translator.class)
162
+ end
163
+ end
164
+ end
165
+
166
+ describe 'grammar' do
167
+ context 'unset' do
168
+ let(:original_command_opts) { { :grammar => nil } }
169
+ it "should return an error and not execute any actions" do
170
+ subject.execute
171
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A grammar document is required.'
172
+ expect(original_command.response(0.1)).to eq(error)
173
+ end
174
+ end
175
+
176
+ context 'with a builtin grammar' do
177
+ let(:original_command_opts) { { grammar: { url: 'builtin:dtmf/boolean' } } }
178
+
179
+ before do
180
+ subject.execute
181
+ expected_event
182
+ send_ami_events_for_dtmf 1
183
+ end
184
+
185
+ let :expected_nlsml do
186
+ RubySpeech::NLSML.draw do
187
+ interpretation confidence: 1 do
188
+ instance "true"
189
+ input "1", mode: :dtmf
190
+ end
191
+ end
192
+ end
193
+
194
+ let :expected_event do
195
+ Adhearsion::Rayo::Component::Input::Complete::Match.new nlsml: expected_nlsml
196
+ end
197
+
198
+ it "should use RubySpeech builtin grammar" do
199
+ expect(reason).to eq(expected_event)
200
+ end
201
+ end
202
+
203
+ context 'with a parameterized builtin grammar' do
204
+ let(:original_command_opts) { { grammar: { url: 'builtin:dtmf/boolean?n=3;y=4' } } }
205
+
206
+ before do
207
+ subject.execute
208
+ expected_event
209
+ send_ami_events_for_dtmf 4
210
+ end
211
+
212
+ let :expected_nlsml do
213
+ RubySpeech::NLSML.draw do
214
+ interpretation confidence: 1 do
215
+ instance "true"
216
+ input "4", mode: :dtmf
217
+ end
218
+ end
219
+ end
220
+
221
+ let :expected_event do
222
+ Adhearsion::Rayo::Component::Input::Complete::Match.new nlsml: expected_nlsml
223
+ end
224
+
225
+ it "should use RubySpeech builtin grammar" do
226
+ expect(reason).to eq(expected_event)
227
+ end
228
+ end
229
+
230
+ context 'with multiple grammars' do
231
+ let(:original_command_opts) { { :grammars => [{:value => grammar}, {:value => grammar}] } }
232
+ it "should return an error and not execute any actions" do
233
+ subject.execute
234
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'Only a single grammar is supported.'
235
+ expect(original_command.response(0.1)).to eq(error)
236
+ end
237
+ end
238
+ end
239
+
240
+ describe 'mode' do
241
+ context 'unset' do
242
+ let(:original_command_opts) { { :mode => nil } }
243
+ it "should return an error and not execute any actions" do
244
+ subject.execute
245
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A mode value other than DTMF is unsupported.'
246
+ expect(original_command.response(0.1)).to eq(error)
247
+ end
248
+ end
249
+
250
+ context 'any' do
251
+ let(:original_command_opts) { { :mode => :any } }
252
+ it "should return an error and not execute any actions" do
253
+ subject.execute
254
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A mode value other than DTMF is unsupported.'
255
+ expect(original_command.response(0.1)).to eq(error)
256
+ end
257
+ end
258
+
259
+ context 'voice' do
260
+ let(:original_command_opts) { { :mode => :voice } }
261
+ it "should return an error and not execute any actions" do
262
+ subject.execute
263
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'A mode value other than DTMF is unsupported.'
264
+ expect(original_command.response(0.1)).to eq(error)
265
+ end
266
+ end
267
+ end
268
+
269
+ describe 'terminator' do
270
+ context 'set' do
271
+ let(:original_command_opts) { { terminator: '#' } }
272
+
273
+ before do
274
+ subject.execute
275
+ expected_event
276
+ end
277
+
278
+ let :grammar do
279
+ RubySpeech::GRXML.draw mode: 'dtmf', root: 'digits' do
280
+ rule id: 'digits' do
281
+ item repeat: '2-5' do
282
+ one_of do
283
+ 0.upto(9) { |d| item { d.to_s } }
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
289
+
290
+ let :expected_nlsml do
291
+ RubySpeech::NLSML.draw do
292
+ interpretation confidence: 1 do
293
+ instance "dtmf-1 dtmf-2"
294
+ input "12", mode: :dtmf
295
+ end
296
+ end
297
+ end
298
+
299
+ let :expected_event do
300
+ Adhearsion::Rayo::Component::Input::Complete::Match.new nlsml: expected_nlsml
301
+ end
302
+
303
+ context "when encountered with a match" do
304
+ before do
305
+ send_ami_events_for_dtmf 1
306
+ send_ami_events_for_dtmf 2
307
+ send_ami_events_for_dtmf '#'
308
+ end
309
+
310
+ it "should send a match complete event with the relevant data" do
311
+ expect(reason).to eq(expected_event)
312
+ end
313
+
314
+ it "should not process further dtmf events" do
315
+ expect(subject).to receive(:process_dtmf).never
316
+ send_ami_events_for_dtmf 3
317
+ end
318
+ end
319
+
320
+ context "when encountered with a NoMatch" do
321
+ before do
322
+ send_ami_events_for_dtmf '#'
323
+ end
324
+
325
+ let :expected_event do
326
+ Adhearsion::Rayo::Component::Input::Complete::NoMatch.new
327
+ end
328
+
329
+ it "should send a nomatch complete event with the relevant data" do
330
+ expect(reason).to eq(expected_event)
331
+ end
332
+ end
333
+
334
+ context "when encountered with a PotentialMatch" do
335
+ before do
336
+ send_ami_events_for_dtmf 1
337
+ send_ami_events_for_dtmf '#'
338
+ end
339
+
340
+ let :expected_event do
341
+ Adhearsion::Rayo::Component::Input::Complete::NoMatch.new
342
+ end
343
+
344
+ it "should send a nomatch complete event with the relevant data" do
345
+ expect(reason).to eq(expected_event)
346
+ end
347
+ end
348
+ end
349
+ end
350
+
351
+ describe 'recognizer' do
352
+ skip
353
+ end
354
+
355
+ describe 'initial-timeout' do
356
+ context 'a positive number' do
357
+ let(:original_command_opts) { { :initial_timeout => 1000 } }
358
+
359
+ it "should not cause a NoInput if first input is received in time" do
360
+ subject.execute
361
+ send_ami_events_for_dtmf 1
362
+ sleep 1.5
363
+ send_ami_events_for_dtmf 2
364
+ expect(reason).to be_a Adhearsion::Rayo::Component::Input::Complete::Match
365
+ end
366
+
367
+ it "should cause a NoInput complete event to be sent after the timeout" do
368
+ subject.execute
369
+ sleep 1.5
370
+ send_ami_events_for_dtmf 1
371
+ send_ami_events_for_dtmf 2
372
+ expect(reason).to be_a Adhearsion::Rayo::Component::Input::Complete::NoInput
373
+ end
374
+ end
375
+
376
+ context '-1' do
377
+ let(:original_command_opts) { { :initial_timeout => -1 } }
378
+
379
+ it "should not start a timer" do
380
+ expect(subject).to receive(:begin_initial_timer).never
381
+ subject.execute
382
+ end
383
+ end
384
+
385
+ context 'unset' do
386
+ let(:original_command_opts) { { :initial_timeout => nil } }
387
+
388
+ it "should not start a timer" do
389
+ expect(subject).to receive(:begin_initial_timer).never
390
+ subject.execute
391
+ end
392
+ end
393
+
394
+ context 'a negative number other than -1' do
395
+ let(:original_command_opts) { { :initial_timeout => -1000 } }
396
+
397
+ it "should return an error and not execute any actions" do
398
+ subject.execute
399
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'An initial timeout value that is negative (and not -1) is invalid.'
400
+ expect(original_command.response(0.1)).to eq(error)
401
+ end
402
+ end
403
+ end
404
+
405
+ describe 'inter-digit-timeout' do
406
+ context 'a positive number' do
407
+ let(:original_command_opts) { { :inter_digit_timeout => 1000 } }
408
+
409
+ it "should not prevent a Match if input is received in time" do
410
+ subject.execute
411
+ sleep 1.5
412
+ send_ami_events_for_dtmf 1
413
+ sleep 0.5
414
+ send_ami_events_for_dtmf 2
415
+ expect(reason).to be_a Adhearsion::Rayo::Component::Input::Complete::Match
416
+ end
417
+
418
+ it "should cause a NoMatch complete event to be sent after the timeout" do
419
+ subject.execute
420
+ sleep 1.5
421
+ send_ami_events_for_dtmf 1
422
+ sleep 1.5
423
+ send_ami_events_for_dtmf 2
424
+ expect(reason).to be_a Adhearsion::Rayo::Component::Input::Complete::NoMatch
425
+ end
426
+
427
+ context "with a trailing range repeat" do
428
+ let :grammar do
429
+ RubySpeech::GRXML.draw mode: 'dtmf', root: 'digits' do
430
+ rule id: 'digits', scope: 'public' do
431
+ item repeat: '2-5' do
432
+ '1'
433
+ end
434
+ end
435
+ end
436
+ end
437
+
438
+ context "when the buffer potentially matches the grammar" do
439
+ it "should cause a NoMatch complete event to be sent after the timeout" do
440
+ subject.execute
441
+ sleep 1.5
442
+ send_ami_events_for_dtmf 1
443
+ sleep 1.5
444
+ expect(reason).to be_a Adhearsion::Rayo::Component::Input::Complete::NoMatch
445
+ end
446
+ end
447
+
448
+ context "when the buffer matches the grammar" do
449
+ let :expected_nlsml do
450
+ RubySpeech::NLSML.draw do
451
+ interpretation confidence: 1 do
452
+ instance "dtmf-1 dtmf-1"
453
+ input '11', mode: :dtmf
454
+ end
455
+ end
456
+ end
457
+
458
+ it "should fire a match on timeout" do
459
+ subject.execute
460
+ sleep 1.5
461
+ send_ami_events_for_dtmf 1
462
+ sleep 0.5
463
+ send_ami_events_for_dtmf 1
464
+ sleep 1.5
465
+ expect(reason).to be_a Adhearsion::Rayo::Component::Input::Complete::Match
466
+ expect(reason.nlsml).to eq(expected_nlsml)
467
+ end
468
+
469
+ context "on the first keypress" do
470
+ let :grammar do
471
+ RubySpeech::GRXML.draw mode: 'dtmf', root: 'digits' do
472
+ rule id: 'digits', scope: 'public' do
473
+ item repeat: '1-5' do
474
+ '1'
475
+ end
476
+ end
477
+ end
478
+ end
479
+
480
+ it "should fire a match on timeout" do
481
+ subject.execute
482
+ sleep 1.5
483
+ send_ami_events_for_dtmf 1
484
+ sleep 0.5
485
+ send_ami_events_for_dtmf 1
486
+ sleep 1.5
487
+ expect(reason).to be_a Adhearsion::Rayo::Component::Input::Complete::Match
488
+ expect(reason.nlsml).to eq(expected_nlsml)
489
+ end
490
+ end
491
+ end
492
+ end
493
+ end
494
+
495
+ context '-1' do
496
+ let(:original_command_opts) { { :inter_digit_timeout => -1 } }
497
+
498
+ it "should not start a timer" do
499
+ expect(subject).to receive(:begin_inter_digit_timer).never
500
+ subject.execute
501
+ end
502
+ end
503
+
504
+ context 'unset' do
505
+ let(:original_command_opts) { { :inter_digit_timeout => nil } }
506
+
507
+ it "should not start a timer" do
508
+ expect(subject).to receive(:begin_inter_digit_timer).never
509
+ subject.execute
510
+ end
511
+ end
512
+
513
+ context 'a negative number other than -1' do
514
+ let(:original_command_opts) { { :inter_digit_timeout => -1000 } }
515
+
516
+ it "should return an error and not execute any actions" do
517
+ subject.execute
518
+ error = Adhearsion::ProtocolError.new.setup 'option error', 'An inter-digit timeout value that is negative (and not -1) is invalid.'
519
+ expect(original_command.response(0.1)).to eq(error)
520
+ end
521
+ end
522
+ end
523
+
524
+ describe 'sensitivity' do
525
+ skip
526
+ end
527
+
528
+ describe 'min-confidence' do
529
+ skip
530
+ end
531
+
532
+ describe 'max-silence' do
533
+ skip
534
+ end
535
+ end
536
+
537
+ describe "#execute_command" do
538
+ context "with a command it does not understand" do
539
+ let(:command) { Adhearsion::Rayo::Component::Output::Pause.new }
540
+
541
+ before { command.request! }
542
+
543
+ it "returns a Adhearsion::ProtocolError response" do
544
+ subject.execute_command command
545
+ expect(command.response(0.1)).to be_a Adhearsion::ProtocolError
546
+ end
547
+ end
548
+
549
+ context "with a Stop command" do
550
+ let(:command) { Adhearsion::Rayo::Component::Stop.new }
551
+ let(:reason) { original_command.complete_event(5).reason }
552
+
553
+ before do
554
+ command.request!
555
+ original_command.request!
556
+ original_command.execute!
557
+ end
558
+
559
+ it "sets the command response to true" do
560
+ subject.execute_command command
561
+ expect(command.response(0.1)).to eq(true)
562
+ expect(reason).to be_a Adhearsion::Event::Complete::Stop
563
+ end
564
+ end
565
+ end
566
+
567
+ end
568
+ end
569
+ end
570
+ end
571
+ end