1/*
2 * libjingle
3 * Copyright 2012, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/app/webrtc/dtmfsender.h"
29
30#include <set>
31#include <string>
32#include <vector>
33
34#include "talk/app/webrtc/audiotrack.h"
35#include "webrtc/base/gunit.h"
36#include "webrtc/base/logging.h"
37#include "webrtc/base/timeutils.h"
38
39using webrtc::AudioTrackInterface;
40using webrtc::AudioTrack;
41using webrtc::DtmfProviderInterface;
42using webrtc::DtmfSender;
43using webrtc::DtmfSenderObserverInterface;
44
45static const char kTestAudioLabel[] = "test_audio_track";
46static const int kMaxWaitMs = 3000;
47
48class FakeDtmfObserver : public DtmfSenderObserverInterface {
49 public:
50  FakeDtmfObserver() : completed_(false) {}
51
52  // Implements DtmfSenderObserverInterface.
53  virtual void OnToneChange(const std::string& tone) OVERRIDE {
54    LOG(LS_VERBOSE) << "FakeDtmfObserver::OnToneChange '" << tone << "'.";
55    tones_.push_back(tone);
56    if (tone.empty()) {
57      completed_ = true;
58    }
59  }
60
61  // getters
62  const std::vector<std::string>& tones() const {
63    return tones_;
64  }
65  bool completed() const {
66    return completed_;
67  }
68
69 private:
70  std::vector<std::string> tones_;
71  bool completed_;
72};
73
74class FakeDtmfProvider : public DtmfProviderInterface {
75 public:
76  struct DtmfInfo {
77    DtmfInfo(int code, int duration, int gap)
78      : code(code),
79        duration(duration),
80        gap(gap) {}
81    int code;
82    int duration;
83    int gap;
84  };
85
86  FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
87
88  ~FakeDtmfProvider() {
89    SignalDestroyed();
90  }
91
92  // Implements DtmfProviderInterface.
93  virtual bool CanInsertDtmf(const std::string&  track_label) OVERRIDE {
94    return (can_insert_dtmf_tracks_.count(track_label) != 0);
95  }
96
97  virtual bool InsertDtmf(const std::string& track_label,
98                          int code, int duration) OVERRIDE {
99    int gap = 0;
100    // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
101    // mockable and use a fake timer in the unit tests.
102    if (last_insert_dtmf_call_ > 0) {
103      gap = static_cast<int>(rtc::Time() - last_insert_dtmf_call_);
104    }
105    last_insert_dtmf_call_ = rtc::Time();
106
107    LOG(LS_VERBOSE) << "FakeDtmfProvider::InsertDtmf code=" << code
108                    << " duration=" << duration
109                    << " gap=" << gap << ".";
110    dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
111    return true;
112  }
113
114  virtual sigslot::signal0<>* GetOnDestroyedSignal() {
115    return &SignalDestroyed;
116  }
117
118  // getter and setter
119  const std::vector<DtmfInfo>& dtmf_info_queue() const {
120    return dtmf_info_queue_;
121  }
122
123  // helper functions
124  void AddCanInsertDtmfTrack(const std::string& label) {
125    can_insert_dtmf_tracks_.insert(label);
126  }
127  void RemoveCanInsertDtmfTrack(const std::string& label) {
128    can_insert_dtmf_tracks_.erase(label);
129  }
130
131 private:
132  std::set<std::string> can_insert_dtmf_tracks_;
133  std::vector<DtmfInfo> dtmf_info_queue_;
134  int64 last_insert_dtmf_call_;
135  sigslot::signal0<> SignalDestroyed;
136};
137
138class DtmfSenderTest : public testing::Test {
139 protected:
140  DtmfSenderTest()
141      : track_(AudioTrack::Create(kTestAudioLabel, NULL)),
142        observer_(new rtc::RefCountedObject<FakeDtmfObserver>()),
143        provider_(new FakeDtmfProvider()) {
144    provider_->AddCanInsertDtmfTrack(kTestAudioLabel);
145    dtmf_ = DtmfSender::Create(track_, rtc::Thread::Current(),
146                               provider_.get());
147    dtmf_->RegisterObserver(observer_.get());
148  }
149
150  ~DtmfSenderTest() {
151    if (dtmf_.get()) {
152      dtmf_->UnregisterObserver();
153    }
154  }
155
156  // Constructs a list of DtmfInfo from |tones|, |duration| and
157  // |inter_tone_gap|.
158  void GetDtmfInfoFromString(const std::string& tones, int duration,
159                             int inter_tone_gap,
160                             std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs) {
161    // Init extra_delay as -inter_tone_gap - duration to ensure the first
162    // DtmfInfo's gap field will be 0.
163    int extra_delay = -1 * (inter_tone_gap + duration);
164
165    std::string::const_iterator it = tones.begin();
166    for (; it != tones.end(); ++it) {
167      char tone = *it;
168      int code = 0;
169      webrtc::GetDtmfCode(tone, &code);
170      if (tone == ',') {
171        extra_delay = 2000;  // 2 seconds
172      } else {
173        dtmfs->push_back(FakeDtmfProvider::DtmfInfo(code, duration,
174                         duration + inter_tone_gap + extra_delay));
175        extra_delay = 0;
176      }
177    }
178  }
179
180  void VerifyExpectedState(AudioTrackInterface* track,
181                          const std::string& tones,
182                          int duration, int inter_tone_gap) {
183    EXPECT_EQ(track, dtmf_->track());
184    EXPECT_EQ(tones, dtmf_->tones());
185    EXPECT_EQ(duration, dtmf_->duration());
186    EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
187  }
188
189  // Verify the provider got all the expected calls.
190  void VerifyOnProvider(const std::string& tones, int duration,
191                        int inter_tone_gap) {
192    std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
193    GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref);
194    VerifyOnProvider(dtmf_queue_ref);
195  }
196
197  void VerifyOnProvider(
198      const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
199    const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
200        provider_->dtmf_info_queue();
201    ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
202    std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
203        dtmf_queue_ref.begin();
204    std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
205        dtmf_queue.begin();
206    while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
207      EXPECT_EQ(it_ref->code, it->code);
208      EXPECT_EQ(it_ref->duration, it->duration);
209      // Allow ~100ms error.
210      EXPECT_GE(it_ref->gap, it->gap - 100);
211      EXPECT_LE(it_ref->gap, it->gap + 100);
212      ++it_ref;
213      ++it;
214    }
215  }
216
217  // Verify the observer got all the expected callbacks.
218  void VerifyOnObserver(const std::string& tones_ref) {
219    const std::vector<std::string>& tones = observer_->tones();
220    // The observer will get an empty string at the end.
221    EXPECT_EQ(tones_ref.size() + 1, tones.size());
222    EXPECT_TRUE(tones.back().empty());
223    std::string::const_iterator it_ref = tones_ref.begin();
224    std::vector<std::string>::const_iterator it = tones.begin();
225    while (it_ref != tones_ref.end() && it != tones.end()) {
226      EXPECT_EQ(*it_ref, it->at(0));
227      ++it_ref;
228      ++it;
229    }
230  }
231
232  rtc::scoped_refptr<AudioTrackInterface> track_;
233  rtc::scoped_ptr<FakeDtmfObserver> observer_;
234  rtc::scoped_ptr<FakeDtmfProvider> provider_;
235  rtc::scoped_refptr<DtmfSender> dtmf_;
236};
237
238TEST_F(DtmfSenderTest, CanInsertDtmf) {
239  EXPECT_TRUE(dtmf_->CanInsertDtmf());
240  provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
241  EXPECT_FALSE(dtmf_->CanInsertDtmf());
242}
243
244TEST_F(DtmfSenderTest, InsertDtmf) {
245  std::string tones = "@1%a&*$";
246  int duration = 100;
247  int inter_tone_gap = 50;
248  EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
249  EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
250
251  // The unrecognized characters should be ignored.
252  std::string known_tones = "1a*";
253  VerifyOnProvider(known_tones, duration, inter_tone_gap);
254  VerifyOnObserver(known_tones);
255}
256
257TEST_F(DtmfSenderTest, InsertDtmfTwice) {
258  std::string tones1 = "12";
259  std::string tones2 = "ab";
260  int duration = 100;
261  int inter_tone_gap = 50;
262  EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
263  VerifyExpectedState(track_, tones1, duration, inter_tone_gap);
264  // Wait until the first tone got sent.
265  EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
266  VerifyExpectedState(track_, "2", duration, inter_tone_gap);
267  // Insert with another tone buffer.
268  EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
269  VerifyExpectedState(track_, tones2, duration, inter_tone_gap);
270  // Wait until it's completed.
271  EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
272
273  std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
274  GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
275  GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
276  VerifyOnProvider(dtmf_queue_ref);
277  VerifyOnObserver("1ab");
278}
279
280TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
281  std::string tones = "@1%a&*$";
282  int duration = 100;
283  int inter_tone_gap = 50;
284  EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
285  // Wait until the first tone got sent.
286  EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
287  // Delete provider.
288  provider_.reset();
289  // The queue should be discontinued so no more tone callbacks.
290  WAIT(false, 200);
291  EXPECT_EQ(1U, observer_->tones().size());
292}
293
294TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
295  std::string tones = "@1%a&*$";
296  int duration = 100;
297  int inter_tone_gap = 50;
298  EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
299  // Wait until the first tone got sent.
300  EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
301  // Delete the sender.
302  dtmf_ = NULL;
303  // The queue should be discontinued so no more tone callbacks.
304  WAIT(false, 200);
305  EXPECT_EQ(1U, observer_->tones().size());
306}
307
308TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
309  std::string tones1 = "12";
310  std::string tones2 = "";
311  int duration = 100;
312  int inter_tone_gap = 50;
313  EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
314  // Wait until the first tone got sent.
315  EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
316  // Insert with another tone buffer.
317  EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
318  // Wait until it's completed.
319  EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
320
321  std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
322  GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
323  VerifyOnProvider(dtmf_queue_ref);
324  VerifyOnObserver("1");
325}
326
327TEST_F(DtmfSenderTest, InsertDtmfWithCommaAsDelay) {
328  std::string tones = "3,4";
329  int duration = 100;
330  int inter_tone_gap = 50;
331  EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
332  EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
333
334  VerifyOnProvider(tones, duration, inter_tone_gap);
335  VerifyOnObserver(tones);
336}
337
338TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
339  std::string tones = "3,4";
340  int duration = 100;
341  int inter_tone_gap = 50;
342  provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
343  EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
344}
345
346TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
347  std::string tones = "3,4";
348  int duration = 100;
349  int inter_tone_gap = 50;
350
351  EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
352  EXPECT_FALSE(dtmf_->InsertDtmf(tones, 69, inter_tone_gap));
353  EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 49));
354
355  EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
356}
357