adhearsion 1.1.0 → 1.1.1

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.
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ 1.1.1
2
+ - Command#play now returns false if audio failed to play
3
+ - Added new commands (#play!, #interruptible_play!, #input!) which raise PlaybackError if audio fails to play
4
+
1
5
  1.1.0
2
6
  - Added interactive call control console: ahn start console <path>
3
7
  - Added centralized exception handler through eventing system
@@ -2,7 +2,7 @@ module Adhearsion #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1 unless defined? MAJOR
4
4
  MINOR = 1 unless defined? MINOR
5
- TINY = 0 unless defined? TINY
5
+ TINY = 1 unless defined? TINY
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.') unless defined? STRING
8
8
  end
@@ -42,6 +42,8 @@ module Adhearsion
42
42
  end
43
43
  } unless defined? DYNAMIC_FEATURE_EXTENSIONS
44
44
 
45
+ PLAYBACK_SUCCESS = 'SUCCESS' unless defined? PLAYBACK_SUCCESS
46
+
45
47
  # Utility method to write to pbx.
46
48
  # @param [String] message raw message
47
49
  def write(message)
@@ -197,12 +199,31 @@ module Adhearsion
197
199
  # @example Play two sound files
198
200
  # play "you-sound-cute", "what-are-you-wearing"
199
201
  #
202
+ # @return [Boolean] true is returned if everything was successful. Otherwise, false indicates that
203
+ # some sound file(s) could not be played.
200
204
  def play(*arguments)
205
+ result = true
206
+ unless play_time(arguments)
207
+ arguments.flatten.each do |argument|
208
+ # result starts off as true. But if the following command ever returns false, then result
209
+ # remains false.
210
+ result &= play_numeric(argument) || play_string(argument)
211
+ end
212
+ end
213
+ result
214
+ end
215
+
216
+ # Same as {#play}, but immediately raises an exception if a sound file cannot be played.
217
+ #
218
+ # @return [true]
219
+ # @raise [Adhearsion::VoIP::PlaybackError] If a sound file cannot be played
220
+ def play!(*arguments)
201
221
  unless play_time(arguments)
202
222
  arguments.flatten.each do |argument|
203
- play_numeric(argument) || play_string(argument)
223
+ play_numeric(argument) || play_string!(argument)
204
224
  end
205
225
  end
226
+ true
206
227
  end
207
228
 
208
229
  # Records a sound file with the given name. If no filename is specified a file named by Asterisk
@@ -478,13 +499,33 @@ module Adhearsion
478
499
  # Note that when the digit limit is not specified the :accept_key becomes "#".
479
500
  # Otherwise there would be no way to end the collection of digits. You can
480
501
  # obviously override this by passing in a new key with :accept_key.
502
+ #
503
+ # @return [String] The keypad input received. An empty string is returned in the
504
+ # absense of input. If the :accept_key argument was pressed, it
505
+ # will not appear in the output.
481
506
  def input(*args)
482
507
  options = args.last.kind_of?(Hash) ? args.pop : {}
483
508
  number_of_digits = args.shift
484
509
 
485
- sound_files = Array options.delete(:play)
486
- timeout = options.delete(:timeout)
487
- terminating_key = options.delete(:accept_key)
510
+ begin
511
+ input! number_of_digits, options
512
+ rescue PlaybackError => e
513
+ ahn_log.agi.warn { e }
514
+ retry # If sound playback fails, play the remaining sound files and wait for digits
515
+ end
516
+ end
517
+
518
+ # Same as {#input}, but immediately raises an exception if sound playback fails
519
+ #
520
+ # @return (see #input)
521
+ # @raise [Adhearsion::VoIP::PlaybackError] If a sound file cannot be played
522
+ def input!(*args)
523
+ options = args.last.kind_of?(Hash) ? args.pop : {}
524
+ number_of_digits = args.shift
525
+
526
+ options[:play] = [*options[:play]].compact
527
+ timeout = options[:timeout]
528
+ terminating_key = options[:accept_key]
488
529
  terminating_key = if terminating_key
489
530
  terminating_key.to_s
490
531
  elsif number_of_digits.nil? && !terminating_key.equal?(false)
@@ -498,7 +539,17 @@ module Adhearsion
498
539
  end
499
540
 
500
541
  buffer = ''
501
- key = sound_files.any? ? interruptible_play(*sound_files) || '' : wait_for_digit(timeout || -1)
542
+ if options[:play].any?
543
+ # Consume the sound files one at a time. In the event of playback failure, this
544
+ # tells us which files remain unplayed.
545
+ while file = options[:play].shift
546
+ key = interruptible_play! file
547
+ break if key
548
+ end
549
+ key ||= ''
550
+ else
551
+ key = wait_for_digit timeout || -1
552
+ end
502
553
  loop do
503
554
  return buffer if key.nil?
504
555
  if terminating_key
@@ -921,6 +972,23 @@ module Adhearsion
921
972
  nil
922
973
  end
923
974
 
975
+ #
976
+ # Same as {#interruptible_play}, but immediately raises an exception if a sound file cannot be played.
977
+ #
978
+ # @return (see #interruptible_play)
979
+ # @raise [Adhearsion::VoIP::PlaybackError] If a sound file cannot be played
980
+ def interruptible_play!(*files)
981
+ startpos = 0
982
+ files.flatten.each do |file|
983
+ result = stream_file_result_from response("STREAM FILE", file, "1234567890*#")
984
+ if result[:endpos].to_i <= startpos
985
+ raise Adhearsion::VoIP::PlaybackError, "The sound file could not opened to stream. The parsed response was #{result.inspect}"
986
+ end
987
+ return result[:digit] unless result[:digit] == 0.chr
988
+ end
989
+ nil
990
+ end
991
+
924
992
  ##
925
993
  # Executes the SayPhonetic command. This command will read the text passed in
926
994
  # out load using the NATO phonetic alphabet.
@@ -1062,6 +1130,15 @@ module Adhearsion
1062
1130
  digit.to_i.chr if digit && digit.to_s != "-1"
1063
1131
  end
1064
1132
 
1133
+ def stream_file_result_from(response_string)
1134
+ raise ArgumentError, "Can't coerce nil into AGI response! This could be a bug!" unless response_string
1135
+ params = {}
1136
+ digit, endpos = response_string.match(/^#{response_prefix}(-?\d+) endpos=(\d+)/).values_at 1, 2
1137
+ params[:digit] = digit.to_i.chr if digit && digit.to_s != "-1"
1138
+ params[:endpos] = endpos.to_i if endpos
1139
+ params
1140
+ end
1141
+
1065
1142
  def extract_input_from(result)
1066
1143
  return false if error?(result)
1067
1144
  # return false if input_timed_out?(result)
@@ -1091,6 +1168,19 @@ module Adhearsion
1091
1168
 
1092
1169
  def play_string(argument)
1093
1170
  execute(:playback, argument)
1171
+ get_variable('PLAYBACKSTATUS') == PLAYBACK_SUCCESS
1172
+ end
1173
+
1174
+ # Like play_string(), but this will raise Exceptions if there's a problem.
1175
+ #
1176
+ # @return [true]
1177
+ # @raise [Adhearsion::VoIP::PlaybackError] If a sound file cannot be played
1178
+ # @see http://www.voip-info.org/wiki/view/Asterisk+cmd+Playback More information on the Asterisk Playback command
1179
+ def play_string!(argument)
1180
+ response = execute :playback, argument
1181
+ playback = get_variable 'PLAYBACKSTATUS'
1182
+ return true if playback == PLAYBACK_SUCCESS
1183
+ raise PlaybackError, "Playback failed with PLAYBACKSTATUS: #{playback.inspect}. The raw response was #{response.inspect}."
1094
1184
  end
1095
1185
 
1096
1186
  def play_sound_files_for_menu(menu_instance, sound_files)
@@ -5,5 +5,9 @@ module Adhearsion
5
5
  Adhearsion::VoIP.const_get(platform_name.to_s.classify).const_get("Commands")
6
6
  end
7
7
  end
8
+
9
+ class PlaybackError < StandardError
10
+ # Represents failure to play audio, such as when the sound file cannot be found
11
+ end
8
12
  end
9
13
  end
@@ -114,6 +114,24 @@ module DialplanCommandTestHelpers
114
114
  pbx_should_respond_with_success digit.kind_of?(String) ? digit[0] : digit
115
115
  end
116
116
 
117
+ def pbx_should_respond_with_playback_success
118
+ pbx_should_respond_with pbx_raw_response
119
+ mock_call.should_receive(:get_variable).once.with('PLAYBACKSTATUS').and_return 'SUCCESS'
120
+ end
121
+
122
+ def pbx_should_respond_with_playback_failure
123
+ pbx_should_respond_with pbx_raw_response
124
+ mock_call.should_receive(:get_variable).once.with('PLAYBACKSTATUS').and_return 'FAILED'
125
+ end
126
+
127
+ def pbx_should_respond_with_stream_file_success(success_code = nil, endpos = '20000')
128
+ pbx_should_respond_with pbx_raw_stream_file_response(success_code, endpos)
129
+ end
130
+
131
+ def pbx_should_respond_with_stream_file_failure_on_open(endpos = nil)
132
+ pbx_should_respond_with pbx_raw_stream_file_response(nil, endpos)
133
+ end
134
+
117
135
  def pbx_should_respond_with_a_wait_for_digit_timeout
118
136
  pbx_should_respond_with_successful_background_response 0
119
137
  end
@@ -122,10 +140,22 @@ module DialplanCommandTestHelpers
122
140
  "200 result=#{success_code || default_success_code}"
123
141
  end
124
142
 
143
+ def pbx_raw_response(code = nil)
144
+ "200 result=#{code || default_code}\n"
145
+ end
146
+
147
+ def pbx_raw_stream_file_response(code = nil, endpos = nil)
148
+ "200 result=#{code || default_code} endpos=#{endpos || default_code}\n"
149
+ end
150
+
125
151
  def default_success_code
126
152
  '1'
127
153
  end
128
154
 
155
+ def default_code
156
+ '0'
157
+ end
158
+
129
159
  def pbx_failure_response(failure_code = nil)
130
160
  "200 result=#{failure_code || default_failure_code}"
131
161
  end
@@ -153,6 +183,12 @@ module DialplanCommandTestHelpers
153
183
  output_stream_matches(/sayunixtime "#{number}"/)
154
184
  end
155
185
 
186
+ def pbx_was_asked_to_stream(*audio_files)
187
+ audio_files.flatten.each do |audio_file|
188
+ output_stream_matches /^STREAM FILE "#{audio_file}" "1234567890\*#"\n$/
189
+ end
190
+ end
191
+
156
192
  def pbx_was_asked_to_execute(application, *options)
157
193
  output_stream_matches(/exec saydigits "#{options.join('|')}"/i)
158
194
  end
@@ -219,25 +255,121 @@ describe 'interruptible_play command' do
219
255
  it 'should return a string for the digit that was pressed' do
220
256
  digits = %w{0 1 # * 9}.map{|c| c.ord}
221
257
  file = "file_doesnt_matter"
222
- digits.each { |digit| pbx_should_respond_with_success digit }
223
- digits.map { |digit| mock_call.send(:interruptible_play, file) }.should == digits.map(&:chr)
258
+ digits.each { |digit| pbx_should_respond_with_stream_file_success digit }
259
+ digits.map { |digit| mock_call.interruptible_play file }.should == digits.map(&:chr)
260
+ pbx_was_asked_to_stream file
224
261
  end
225
262
 
226
263
  it "should return nil if no digit was pressed" do
227
- pbx_should_respond_with_success 0
228
- mock_call.send(:interruptible_play, 'foobar').should be nil
264
+ pbx_should_respond_with_stream_file_success 0
265
+ file = 'foobar'
266
+ mock_call.interruptible_play(file).should be nil
267
+ pbx_was_asked_to_stream file
268
+ end
269
+
270
+ it 'should return nil if no digit was pressed, even if the sound file is not found' do
271
+ pbx_should_respond_with_stream_file_failure_on_open
272
+ file = 'foobar'
273
+ mock_call.interruptible_play(file).should be nil
274
+ pbx_was_asked_to_stream file
275
+ end
276
+
277
+ it "should play a series of files, stopping the series when a digit is played" do
278
+ stubbed_keypad_input = [0, 0, ?3.ord]
279
+ stubbed_keypad_input.each do |digit|
280
+ pbx_should_respond_with_stream_file_success digit
281
+ end
282
+
283
+ play_files = (100..105).map &:to_s
284
+ played_files = (100..102).map &:to_s
285
+ mock_call.interruptible_play(*play_files).should == '3'
286
+ pbx_was_asked_to_stream played_files
287
+ end
288
+
289
+ it 'should play a series of files, stopping the series when a digit is played, even if the sound files cannot be found' do
290
+ pbx_should_respond_with_stream_file_success 0
291
+ pbx_should_respond_with_stream_file_success 0
292
+ pbx_should_respond_with_stream_file_failure_on_open
293
+ pbx_should_respond_with_stream_file_success ?9.ord
294
+
295
+ play_files = ('sound1'..'sound6').map &:to_s
296
+ played_files = ('sound1'..'sound4').map &:to_s
297
+ mock_call.interruptible_play(*play_files).should == '9'
298
+ pbx_was_asked_to_stream played_files
299
+ end
300
+
301
+ end
302
+
303
+ describe 'interruptible_play! command' do
304
+ include DialplanCommandTestHelpers
305
+
306
+ it 'should return a string for the digit that was pressed' do
307
+ digits = %w{0 1 # * 9}.map{|c| c.ord}
308
+ file = "file_doesnt_matter"
309
+ digits.each { |digit| pbx_should_respond_with_stream_file_success digit }
310
+ digits.map { |digit| mock_call.interruptible_play! file }.should == digits.map(&:chr)
311
+ pbx_was_asked_to_stream file
312
+ end
313
+
314
+ it "should return nil if no digit was pressed" do
315
+ pbx_should_respond_with_stream_file_success 0
316
+ file = 'foobar'
317
+ mock_call.interruptible_play!(file).should be nil
318
+ pbx_was_asked_to_stream file
319
+ end
320
+
321
+ it 'should raise an error when the sound file is not found' do
322
+ pbx_should_respond_with_stream_file_failure_on_open
323
+ file = 'foobar'
324
+ the_following_code {
325
+ mock_call.interruptible_play! file
326
+ }.should raise_error Adhearsion::VoIP::PlaybackError
327
+ pbx_was_asked_to_stream file
229
328
  end
230
329
 
231
330
  it "should play a series of files, stopping the series when a digit is played" do
232
331
  stubbed_keypad_input = [0, 0, ?3.ord]
233
332
  stubbed_keypad_input.each do |digit|
234
- pbx_should_respond_with_success digit
333
+ pbx_should_respond_with_stream_file_success digit
235
334
  end
236
335
 
237
- files = (100..105).map(&:to_s)
238
- mock_call.send(:interruptible_play, *files).should == '3'
336
+ play_files = (100..105).map &:to_s
337
+ played_files = (100..102).map &:to_s
338
+ mock_call.interruptible_play!(*play_files).should == '3'
339
+ pbx_was_asked_to_stream played_files
239
340
  end
240
341
 
342
+ it 'should play a series of files, raising an error if a sound file cannot be found' do
343
+ pbx_should_respond_with_stream_file_success 0
344
+ pbx_should_respond_with_stream_file_failure_on_open
345
+
346
+ play_files = ('sound1'..'sound6').map &:to_s
347
+ played_files = ('sound1'..'sound2').map &:to_s
348
+ the_following_code {
349
+ mock_call.interruptible_play! *play_files
350
+ }.should raise_error Adhearsion::VoIP::PlaybackError
351
+ pbx_was_asked_to_stream played_files
352
+ end
353
+
354
+ it 'should raise an error if an audio file cannot be found' do
355
+ pbx_should_respond_with_stream_file_failure_on_open
356
+ audio_file = 'nixon-tapes'
357
+ the_following_code {
358
+ mock_call.interruptible_play! audio_file
359
+ }.should raise_error Adhearsion::VoIP::PlaybackError
360
+ pbx_was_asked_to_stream audio_file
361
+ end
362
+
363
+ it 'should raise an error when audio files cannot be found' do
364
+ pbx_should_respond_with_stream_file_success
365
+ pbx_should_respond_with_stream_file_failure_on_open # 'paperz' is the only audio that is missing
366
+ audio_files = ['rock', 'paperz', 'scissors']
367
+
368
+ the_following_code {
369
+ mock_call.interruptible_play! audio_files
370
+ }.should raise_error Adhearsion::VoIP::PlaybackError
371
+ pbx_was_asked_to_stream ['rock', 'paperz'] # stop short before playing with scissors!
372
+ end
241
373
  end
242
374
 
243
375
  describe 'wait_for_digit command' do
@@ -307,60 +439,77 @@ describe 'play command' do
307
439
  include DialplanCommandTestHelpers
308
440
 
309
441
  it 'passing a single string to play results in the playback application being executed with that file name on the PBX' do
310
- pbx_should_respond_with_success
442
+ pbx_should_respond_with_playback_success
311
443
  audio_file = "cents-per-minute"
312
- mock_call.play audio_file
444
+ mock_call.play(audio_file).should be true
313
445
  pbx_was_asked_to_play audio_file
314
446
  end
315
447
 
316
448
  it 'multiple strings can be passed to play, causing multiple playback commands to be issued' do
317
449
  2.times do
318
- pbx_should_respond_with_success
450
+ pbx_should_respond_with_playback_success
319
451
  end
320
452
  audio_files = ["cents-per-minute", 'o-hai']
321
- mock_call.play audio_files
453
+ mock_call.play(audio_files).should be true
454
+ pbx_was_asked_to_play audio_files
455
+ end
456
+
457
+ it 'should return false if an audio file cannot be found' do
458
+ pbx_should_respond_with_playback_failure
459
+ audio_file = 'nixon-tapes'
460
+ mock_call.play(audio_file).should be false
461
+ pbx_was_asked_to_play audio_file
462
+ end
463
+
464
+ it 'should return false when audio files cannot be found' do
465
+ pbx_should_respond_with_playback_success
466
+ pbx_should_respond_with_playback_failure # 'paperz' is the only audio that is missing
467
+ pbx_should_respond_with_playback_success
468
+ audio_files = ['rock', 'paperz', 'scissors']
469
+
470
+ mock_call.play(audio_files).should be false
322
471
  pbx_was_asked_to_play audio_files
323
472
  end
324
473
 
325
474
  it 'If a number is passed to play(), the saynumber application is executed with the number as an argument' do
326
475
  pbx_should_respond_with_success
327
- mock_call.play 123
476
+ mock_call.play(123).should be true
328
477
  pbx_was_asked_to_play_number(123)
329
478
  end
330
479
 
331
480
  it 'if a string representation of a number is passed to play(), the saynumber application is executed with the number as an argument' do
332
481
  pbx_should_respond_with_success
333
- mock_call.play '123'
482
+ mock_call.play('123').should be true
334
483
  pbx_was_asked_to_play_number(123)
335
484
  end
336
485
 
337
486
  it 'If a Time is passed to play(), the SayUnixTime application will be executed with the time since the UNIX epoch in seconds as an argument' do
338
487
  time = Time.parse("12/5/2000")
339
488
  pbx_should_respond_with_success
340
- mock_call.play time
489
+ mock_call.play(time).should be true
341
490
  pbx_was_asked_to_play_time(time.to_i)
342
491
  end
343
492
 
344
493
  it 'If a Date is passed to play(), the SayUnixTime application will be executed with the date passed in' do
345
494
  date = Date.parse('2011-01-23')
346
- mock_call.should_receive(:execute).once.with(:sayunixtime, date.to_time.to_i, "",'BdY').and_return('200')
347
- mock_call.play date
495
+ mock_call.should_receive(:execute).once.with(:sayunixtime, date.to_time.to_i, "",'BdY').and_return pbx_raw_response
496
+ mock_call.play(date).should be true
348
497
  end
349
498
 
350
499
  it 'If a Date or Time is passed to play_time(), the SayUnixTime application will be executed with the date and format passed in' do
351
500
  date, format = Date.parse('2011-01-23'), 'ABdY'
352
- mock_call.should_receive(:execute).once.with(:sayunixtime, date.to_time.to_i, "",format).and_return('200')
353
- mock_call.play_time date, :format => format
501
+ mock_call.should_receive(:execute).once.with(:sayunixtime, date.to_time.to_i, "",format).and_return "200 result=0\n"
502
+ mock_call.play_time(date, :format => format).should == pbx_raw_response
354
503
 
355
504
  time, format = Time.at(875121313), 'BdY \'digits/at\' IMp'
356
- mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, "",format).and_return('200')
357
- mock_call.play_time time, :format => format
505
+ mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, "",format).and_return pbx_raw_response
506
+ mock_call.play_time(time, :format => format).should == pbx_raw_response
358
507
  end
359
508
 
360
509
  it 'If a Time object is passed to play_time, the SayUnixTime application will be executed with the default parameters' do
361
510
  time = Time.at(875121313)
362
- mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, "",'').and_return('200')
363
- mock_call.play_time time
511
+ mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, "",'').and_return pbx_raw_response
512
+ mock_call.play_time(time).should == pbx_raw_response
364
513
  end
365
514
 
366
515
  it 'If an object other than Time, DateTime, or Date is passed to play_time false will be returned' do
@@ -370,16 +519,16 @@ describe 'play command' do
370
519
 
371
520
  it 'If an array containing a Date/DateTime/Time object and a hash is passed to play(), the SayUnixTime application will be executed with the object passed in with the specified format and timezone' do
372
521
  date, format = Date.parse('2011-01-23'), 'ABdY'
373
- mock_call.should_receive(:execute).once.with(:sayunixtime, date.to_time.to_i, "",format).and_return('200')
374
- mock_call.play [date, {:format => format}]
522
+ mock_call.should_receive(:execute).once.with(:sayunixtime, date.to_time.to_i, "",format).and_return pbx_raw_response
523
+ mock_call.play([date, {:format => format}]).should be true
375
524
 
376
525
  time, timezone = Time.at(1295843084), 'US/Eastern'
377
- mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, timezone,'').and_return('200')
378
- mock_call.play [time, {:timezone => timezone}]
526
+ mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, timezone,'').and_return pbx_raw_response
527
+ mock_call.play([time, {:timezone => timezone}]).should be true
379
528
 
380
529
  time, timezone, format = Time.at(1295843084), 'US/Eastern', 'ABdY \'digits/at\' IMp'
381
- mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, timezone,format).and_return('200')
382
- mock_call.play [time, {:timezone => timezone, :format => format}]
530
+ mock_call.should_receive(:execute).once.with(:sayunixtime, time.to_i, timezone,format).and_return pbx_raw_response
531
+ mock_call.play([time, {:timezone => timezone, :format => format}]).should be true
383
532
  end
384
533
 
385
534
  it 'If a string matching dollars and (optionally) cents is passed to play(), a series of command will be executed to read the dollar amount', :ignore => true do
@@ -388,6 +537,39 @@ describe 'play command' do
388
537
  end
389
538
  end
390
539
 
540
+ describe 'play! command' do
541
+ include DialplanCommandTestHelpers
542
+
543
+ it 'should accept multiple strings to play, causing multiple playback commands to be issued' do
544
+ 2.times do
545
+ pbx_should_respond_with_playback_success
546
+ end
547
+ audio_files = ["cents-per-minute", 'o-hai']
548
+ mock_call.play!(audio_files).should be true
549
+ pbx_was_asked_to_play audio_files
550
+ end
551
+
552
+ it 'should raise an error if an audio file cannot be found' do
553
+ pbx_should_respond_with_playback_failure
554
+ audio_file = 'nixon-tapes'
555
+ the_following_code {
556
+ mock_call.play! audio_file
557
+ }.should raise_error Adhearsion::VoIP::PlaybackError
558
+ pbx_was_asked_to_play audio_file
559
+ end
560
+
561
+ it 'should raise an error when audio files cannot be found' do
562
+ pbx_should_respond_with_playback_success
563
+ pbx_should_respond_with_playback_failure # 'paperz' is the only audio that is missing
564
+ audio_files = ['rock', 'paperz', 'scissors']
565
+
566
+ the_following_code {
567
+ mock_call.play! audio_files
568
+ }.should raise_error Adhearsion::VoIP::PlaybackError
569
+ pbx_was_asked_to_play ['rock', 'paperz'] # stop short before playing with scissors!
570
+ end
571
+ end
572
+
391
573
  describe 'input command' do
392
574
 
393
575
  include DialplanCommandTestHelpers
@@ -402,14 +584,16 @@ describe 'input command' do
402
584
  end
403
585
 
404
586
  it 'input() calls wait_for_digit the specified number of times (when no sound files are given)' do
405
- # mock_call.should_receive(:interruptible_play).never
587
+ mock_call.should_receive(:interruptible_play!).never
406
588
  mock_call.should_receive(:wait_for_digit).times(4).and_return('1', '2', '3', '4')
407
589
  mock_call.input(4).should == '1234'
408
590
  end
409
591
 
410
- it 'should execute wait_for_digit if no digit is pressed during interruptible_play' do
592
+ it 'should execute wait_for_digit if no digit is pressed during interruptible_play!' do
411
593
  sound_files = %w[one two three]
412
- mock_call.should_receive(:interruptible_play).once.with(*sound_files).and_return nil
594
+ mock_call.should_receive(:interruptible_play!).once.with('one').and_return nil
595
+ mock_call.should_receive(:interruptible_play!).once.with('two').and_return nil
596
+ mock_call.should_receive(:interruptible_play!).once.with('three').and_return nil
413
597
  mock_call.should_receive(:wait_for_digit).once.and_throw :digit_request
414
598
  should_throw(:digit_request) { mock_call.input(10, :play => sound_files) }
415
599
  end
@@ -435,14 +619,16 @@ describe 'input command' do
435
619
  end
436
620
 
437
621
  it 'passes wait_for_digit the :timeout option when one is given' do
438
- mock_call.should_receive(:interruptible_play).never
622
+ mock_call.should_receive(:interruptible_play!).never
439
623
  mock_call.should_receive(:wait_for_digit).twice.and_return '1', '2'
440
624
  mock_call.input(2, :timeout => 1.minute).should == '12'
441
625
  end
442
626
 
443
- it 'executes interruptible_play() with all of the files given to :play' do
627
+ it 'executes interruptible_play!() with all of the files given to :play' do
444
628
  sound_files = %w[foo bar qaz]
445
- mock_call.should_receive(:interruptible_play).once.with(*sound_files).and_return '#'
629
+ mock_call.should_receive(:interruptible_play!).once.with('foo').and_return nil
630
+ mock_call.should_receive(:interruptible_play!).once.with('bar').and_return nil
631
+ mock_call.should_receive(:interruptible_play!).once.with('qaz').and_return '#'
446
632
  mock_call.should_receive(:wait_for_digit).once.and_return '*'
447
633
  mock_call.input(2, :play => sound_files).should == '#*'
448
634
  end
@@ -453,14 +639,121 @@ describe 'input command' do
453
639
  end
454
640
 
455
641
  it 'should execute wait_for_digit first if no sound files are given' do
456
- mock_call.should_receive(:interruptible_play).never
642
+ mock_call.should_receive(:interruptible_play!).never
457
643
  mock_call.should_receive(:wait_for_digit).once.and_throw :digit_request
458
644
  should_throw(:digit_request) { mock_call.input(1) }
459
645
  end
460
646
 
461
647
  it "Input timing out when digits are pressed returns only the collected digits" do
462
- mock_call.should_receive(:wait_for_digit).twice.and_return '5', nil
463
- mock_call.input(9, :timeout => 1.day).should == '5'
648
+ timeout = 1.day
649
+ mock_call.should_receive(:wait_for_digit).twice.with(timeout).and_return '5', nil
650
+ mock_call.input(9, :timeout => timeout).should == '5'
651
+ end
652
+
653
+ it 'should execute wait_for_digit, even if some sound files are not found' do
654
+ pbx_should_respond_with_stream_file_failure_on_open
655
+ file = 'foobar'
656
+ timeout = 1.hour
657
+ mock_call.should_receive(:wait_for_digit).twice.with(timeout).and_return '8', '9'
658
+ mock_call.input(2, :timeout => timeout, :play => file).should == '89'
659
+ pbx_was_asked_to_stream file
660
+ end
661
+
662
+ it 'should return an empty string if no keys are pressed, even if the sound file is not found' do
663
+ pbx_should_respond_with_stream_file_failure_on_open
664
+ file = 'foobar'
665
+ timeout = 1.second
666
+ mock_call.should_receive(:wait_for_digit).once.with(timeout).and_return nil
667
+ mock_call.input(5, :timeout => timeout, :play => file).should == ''
668
+ pbx_was_asked_to_stream file
669
+ end
670
+
671
+ it 'should play a series of files, collecting digits even if some of the sound files cannot be found' do
672
+ pbx_should_respond_with_stream_file_success 0
673
+ pbx_should_respond_with_stream_file_success 0
674
+ pbx_should_respond_with_stream_file_failure_on_open
675
+ pbx_should_respond_with_stream_file_success ?1.ord
676
+
677
+ play_files = ('sound1'..'sound6').map &:to_s
678
+ played_files = ('sound1'..'sound4').map &:to_s
679
+ timeout = 1.minute
680
+ mock_call.should_receive(:wait_for_digit).twice.with(timeout).and_return '2', '3'
681
+ mock_call.input(3, :timeout => timeout, :play => play_files).should == '123'
682
+ pbx_was_asked_to_stream played_files
683
+ end
684
+
685
+ it 'should play a series of 4 files, collecting digits even if some of the sound files cannot be found' do
686
+ pbx_should_respond_with_stream_file_success 0
687
+ pbx_should_respond_with_stream_file_success 0
688
+ pbx_should_respond_with_stream_file_failure_on_open
689
+ pbx_should_respond_with_stream_file_success 0
690
+ pbx_should_respond_with_stream_file_success ?1.ord
691
+
692
+ play_files = ('sound1'..'sound8').map &:to_s
693
+ played_files = ('sound1'..'sound5').map &:to_s
694
+ timeout = 1.second
695
+ mock_call.should_receive(:wait_for_digit).times(3).with(timeout).and_return '2', '3', '4'
696
+ mock_call.input(4, :timeout => timeout, :play => play_files).should == '1234'
697
+ pbx_was_asked_to_stream played_files
698
+ end
699
+
700
+ end
701
+
702
+ describe 'input! command' do
703
+
704
+ include DialplanCommandTestHelpers
705
+
706
+ it 'should raise an error when the number of digits expected is -1 (this is deprecated behavior)' do
707
+ the_following_code {
708
+ mock_call.input! -1
709
+ }.should raise_error ArgumentError
710
+ end
711
+
712
+ it 'should execute wait_for_digit if no digit is pressed during interruptible_play!' do
713
+ sound_files = %w[one two three]
714
+ mock_call.should_receive(:interruptible_play!).once.with('one').and_return nil
715
+ mock_call.should_receive(:interruptible_play!).once.with('two').and_return nil
716
+ mock_call.should_receive(:interruptible_play!).once.with('three').and_return nil
717
+ mock_call.should_receive(:wait_for_digit).once.and_throw :digit_request
718
+ should_throw(:digit_request) { mock_call.input! 10, :play => sound_files }
719
+ end
720
+
721
+ it 'executes interruptible_play!() with all of the files given to :play' do
722
+ sound_files = %w[foo bar qaz]
723
+ mock_call.should_receive(:interruptible_play!).once.with('foo').and_return nil
724
+ mock_call.should_receive(:interruptible_play!).once.with('bar').and_return nil
725
+ mock_call.should_receive(:interruptible_play!).once.with('qaz').and_return '#'
726
+ mock_call.should_receive(:wait_for_digit).once.and_return '*'
727
+ mock_call.input!(2, :play => sound_files).should == '#*'
728
+ end
729
+
730
+ it 'should execute wait_for_digit first if no sound files are given' do
731
+ mock_call.should_receive(:interruptible_play!).never
732
+ mock_call.should_receive(:wait_for_digit).once.and_throw :digit_request
733
+ should_throw(:digit_request) { mock_call.input! 1 }
734
+ end
735
+
736
+ it 'should raise an error when the sound file is not found' do
737
+ pbx_should_respond_with_stream_file_failure_on_open
738
+ file = 'foobar'
739
+ mock_call.should_receive(:wait_for_digit).never
740
+ the_following_code {
741
+ mock_call.input! 1, :play => file
742
+ }.should raise_error Adhearsion::VoIP::PlaybackError
743
+ pbx_was_asked_to_stream file
744
+ end
745
+
746
+ it 'should play a series of files, raising an error if a sound file cannot be found' do
747
+ pbx_should_respond_with_stream_file_success 0
748
+ pbx_should_respond_with_stream_file_failure_on_open
749
+ mock_call.should_receive(:wait_for_digit).never
750
+
751
+ play_files = ('sound1'..'sound6').map &:to_s
752
+ played_files = ('sound1'..'sound2').map &:to_s
753
+ the_following_code {
754
+ mock_call.input! 10, :play => play_files, :timeout => 5.seconds
755
+ }.should raise_error Adhearsion::VoIP::PlaybackError
756
+ pbx_was_asked_to_stream played_files
464
757
  end
465
758
 
466
759
  end
metadata CHANGED
@@ -1,10 +1,15 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: adhearsion
3
- version: !ruby/object:Gem::Version
4
- version: 1.1.0
3
+ version: !ruby/object:Gem::Version
4
+ hash: 17
5
5
  prerelease:
6
+ segments:
7
+ - 1
8
+ - 1
9
+ - 1
10
+ version: 1.1.1
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Jay Phillips
9
14
  - Jason Goecke
10
15
  - Ben Klang
@@ -12,150 +17,201 @@ authors:
12
17
  autorequire:
13
18
  bindir: bin
14
19
  cert_chain: []
15
- date: 2011-05-29 00:00:00.000000000 -04:00
20
+
21
+ date: 2011-06-14 00:00:00 +01:00
16
22
  default_executable:
17
- dependencies:
18
- - !ruby/object:Gem::Dependency
23
+ dependencies:
24
+ - !ruby/object:Gem::Dependency
19
25
  name: bundler
20
- requirement: &2153140220 !ruby/object:Gem::Requirement
26
+ prerelease: false
27
+ requirement: &id001 !ruby/object:Gem::Requirement
21
28
  none: false
22
- requirements:
23
- - - ! '>='
24
- - !ruby/object:Gem::Version
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ hash: 3
33
+ segments:
34
+ - 1
35
+ - 0
36
+ - 10
25
37
  version: 1.0.10
26
38
  type: :runtime
27
- prerelease: false
28
- version_requirements: *2153140220
29
- - !ruby/object:Gem::Dependency
39
+ version_requirements: *id001
40
+ - !ruby/object:Gem::Dependency
30
41
  name: log4r
31
- requirement: &2153139740 !ruby/object:Gem::Requirement
42
+ prerelease: false
43
+ requirement: &id002 !ruby/object:Gem::Requirement
32
44
  none: false
33
- requirements:
34
- - - ! '>='
35
- - !ruby/object:Gem::Version
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ hash: 29
49
+ segments:
50
+ - 1
51
+ - 0
52
+ - 5
36
53
  version: 1.0.5
37
54
  type: :runtime
38
- prerelease: false
39
- version_requirements: *2153139740
40
- - !ruby/object:Gem::Dependency
55
+ version_requirements: *id002
56
+ - !ruby/object:Gem::Dependency
41
57
  name: activesupport
42
- requirement: &2153139260 !ruby/object:Gem::Requirement
58
+ prerelease: false
59
+ requirement: &id003 !ruby/object:Gem::Requirement
43
60
  none: false
44
- requirements:
45
- - - ! '>='
46
- - !ruby/object:Gem::Version
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 11
65
+ segments:
66
+ - 2
67
+ - 1
68
+ - 0
47
69
  version: 2.1.0
48
70
  type: :runtime
49
- prerelease: false
50
- version_requirements: *2153139260
51
- - !ruby/object:Gem::Dependency
71
+ version_requirements: *id003
72
+ - !ruby/object:Gem::Dependency
52
73
  name: i18n
53
- requirement: &2153138880 !ruby/object:Gem::Requirement
74
+ prerelease: false
75
+ requirement: &id004 !ruby/object:Gem::Requirement
54
76
  none: false
55
- requirements:
56
- - - ! '>='
57
- - !ruby/object:Gem::Version
58
- version: '0'
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ hash: 3
81
+ segments:
82
+ - 0
83
+ version: "0"
59
84
  type: :runtime
60
- prerelease: false
61
- version_requirements: *2153138880
62
- - !ruby/object:Gem::Dependency
85
+ version_requirements: *id004
86
+ - !ruby/object:Gem::Dependency
63
87
  name: rubigen
64
- requirement: &2153138320 !ruby/object:Gem::Requirement
88
+ prerelease: false
89
+ requirement: &id005 !ruby/object:Gem::Requirement
65
90
  none: false
66
- requirements:
67
- - - ! '>='
68
- - !ruby/object:Gem::Version
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 15
95
+ segments:
96
+ - 1
97
+ - 5
98
+ - 6
69
99
  version: 1.5.6
70
100
  type: :runtime
71
- prerelease: false
72
- version_requirements: *2153138320
73
- - !ruby/object:Gem::Dependency
101
+ version_requirements: *id005
102
+ - !ruby/object:Gem::Dependency
74
103
  name: rake
75
- requirement: &2153137900 !ruby/object:Gem::Requirement
104
+ prerelease: false
105
+ requirement: &id006 !ruby/object:Gem::Requirement
76
106
  none: false
77
- requirements:
78
- - - ! '>='
79
- - !ruby/object:Gem::Version
80
- version: '0'
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ hash: 3
111
+ segments:
112
+ - 0
113
+ version: "0"
81
114
  type: :runtime
82
- prerelease: false
83
- version_requirements: *2153137900
84
- - !ruby/object:Gem::Dependency
115
+ version_requirements: *id006
116
+ - !ruby/object:Gem::Dependency
85
117
  name: pry
86
- requirement: &2153137440 !ruby/object:Gem::Requirement
118
+ prerelease: false
119
+ requirement: &id007 !ruby/object:Gem::Requirement
87
120
  none: false
88
- requirements:
89
- - - ! '>='
90
- - !ruby/object:Gem::Version
91
- version: '0'
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ hash: 3
125
+ segments:
126
+ - 0
127
+ version: "0"
92
128
  type: :runtime
93
- prerelease: false
94
- version_requirements: *2153137440
95
- - !ruby/object:Gem::Dependency
129
+ version_requirements: *id007
130
+ - !ruby/object:Gem::Dependency
96
131
  name: rubigen
97
- requirement: &2153136920 !ruby/object:Gem::Requirement
132
+ prerelease: false
133
+ requirement: &id008 !ruby/object:Gem::Requirement
98
134
  none: false
99
- requirements:
100
- - - ! '>='
101
- - !ruby/object:Gem::Version
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ hash: 15
139
+ segments:
140
+ - 1
141
+ - 5
142
+ - 6
102
143
  version: 1.5.6
103
144
  type: :development
104
- prerelease: false
105
- version_requirements: *2153136920
106
- - !ruby/object:Gem::Dependency
145
+ version_requirements: *id008
146
+ - !ruby/object:Gem::Dependency
107
147
  name: rspec
108
- requirement: &2153136400 !ruby/object:Gem::Requirement
148
+ prerelease: false
149
+ requirement: &id009 !ruby/object:Gem::Requirement
109
150
  none: false
110
- requirements:
111
- - - ! '>='
112
- - !ruby/object:Gem::Version
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ hash: 31
155
+ segments:
156
+ - 2
157
+ - 4
158
+ - 0
113
159
  version: 2.4.0
114
160
  type: :development
115
- prerelease: false
116
- version_requirements: *2153136400
117
- - !ruby/object:Gem::Dependency
161
+ version_requirements: *id009
162
+ - !ruby/object:Gem::Dependency
118
163
  name: flexmock
119
- requirement: &2153136020 !ruby/object:Gem::Requirement
164
+ prerelease: false
165
+ requirement: &id010 !ruby/object:Gem::Requirement
120
166
  none: false
121
- requirements:
122
- - - ! '>='
123
- - !ruby/object:Gem::Version
124
- version: '0'
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ hash: 3
171
+ segments:
172
+ - 0
173
+ version: "0"
125
174
  type: :development
126
- prerelease: false
127
- version_requirements: *2153136020
128
- - !ruby/object:Gem::Dependency
175
+ version_requirements: *id010
176
+ - !ruby/object:Gem::Dependency
129
177
  name: activerecord
130
- requirement: &2153135560 !ruby/object:Gem::Requirement
178
+ prerelease: false
179
+ requirement: &id011 !ruby/object:Gem::Requirement
131
180
  none: false
132
- requirements:
133
- - - ! '>='
134
- - !ruby/object:Gem::Version
135
- version: '0'
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ hash: 3
185
+ segments:
186
+ - 0
187
+ version: "0"
136
188
  type: :development
137
- prerelease: false
138
- version_requirements: *2153135560
139
- - !ruby/object:Gem::Dependency
189
+ version_requirements: *id011
190
+ - !ruby/object:Gem::Dependency
140
191
  name: rake
141
- requirement: &2153135140 !ruby/object:Gem::Requirement
192
+ prerelease: false
193
+ requirement: &id012 !ruby/object:Gem::Requirement
142
194
  none: false
143
- requirements:
144
- - - ! '>='
145
- - !ruby/object:Gem::Version
146
- version: '0'
195
+ requirements:
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ hash: 3
199
+ segments:
200
+ - 0
201
+ version: "0"
147
202
  type: :development
148
- prerelease: false
149
- version_requirements: *2153135140
203
+ version_requirements: *id012
150
204
  description: Adhearsion is an open-source telephony development framework
151
205
  email: dev&Adhearsion.com
152
- executables:
206
+ executables:
153
207
  - ahn
154
208
  - ahnctl
155
209
  - jahn
156
210
  extensions: []
211
+
157
212
  extra_rdoc_files: []
158
- files:
213
+
214
+ files:
159
215
  - .gitignore
160
216
  - CHANGELOG
161
217
  - EVENTS
@@ -331,29 +387,38 @@ files:
331
387
  has_rdoc: true
332
388
  homepage: http://adhearsion.com
333
389
  licenses: []
390
+
334
391
  post_install_message:
335
392
  rdoc_options: []
336
- require_paths:
393
+
394
+ require_paths:
337
395
  - lib
338
- required_ruby_version: !ruby/object:Gem::Requirement
396
+ required_ruby_version: !ruby/object:Gem::Requirement
339
397
  none: false
340
- requirements:
341
- - - ! '>='
342
- - !ruby/object:Gem::Version
343
- version: '0'
344
- required_rubygems_version: !ruby/object:Gem::Requirement
398
+ requirements:
399
+ - - ">="
400
+ - !ruby/object:Gem::Version
401
+ hash: 3
402
+ segments:
403
+ - 0
404
+ version: "0"
405
+ required_rubygems_version: !ruby/object:Gem::Requirement
345
406
  none: false
346
- requirements:
347
- - - ! '>='
348
- - !ruby/object:Gem::Version
349
- version: '0'
407
+ requirements:
408
+ - - ">="
409
+ - !ruby/object:Gem::Version
410
+ hash: 3
411
+ segments:
412
+ - 0
413
+ version: "0"
350
414
  requirements: []
415
+
351
416
  rubyforge_project: adhearsion
352
- rubygems_version: 1.6.2
417
+ rubygems_version: 1.4.2
353
418
  signing_key:
354
419
  specification_version: 2
355
420
  summary: Adhearsion, open-source telephony development framework
356
- test_files:
421
+ test_files:
357
422
  - spec/ahn_command_spec.rb
358
423
  - spec/component_manager_spec.rb
359
424
  - spec/constants_spec.rb