adhearsion 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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