1 /*
2  *  Copyright 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "pc/dtmf_sender.h"
12 
13 #include <stddef.h>
14 
15 #include <memory>
16 #include <string>
17 #include <vector>
18 
19 #include "rtc_base/fake_clock.h"
20 #include "rtc_base/gunit.h"
21 #include "rtc_base/ref_counted_object.h"
22 #include "rtc_base/time_utils.h"
23 #include "test/gtest.h"
24 
25 using webrtc::DtmfProviderInterface;
26 using webrtc::DtmfSender;
27 using webrtc::DtmfSenderObserverInterface;
28 
29 // TODO(deadbeef): Even though this test now uses a fake clock, it has a
30 // generous 3-second timeout for every test case. The timeout could be tuned
31 // to each test based on the tones sent, instead.
32 static const int kMaxWaitMs = 3000;
33 
34 class FakeDtmfObserver : public DtmfSenderObserverInterface {
35  public:
FakeDtmfObserver()36   FakeDtmfObserver() : completed_(false) {}
37 
38   // Implements DtmfSenderObserverInterface.
OnToneChange(const std::string & tone)39   void OnToneChange(const std::string& tone) override {
40     tones_from_single_argument_callback_.push_back(tone);
41     if (tone.empty()) {
42       completed_ = true;
43     }
44   }
OnToneChange(const std::string & tone,const std::string & tone_buffer)45   void OnToneChange(const std::string& tone,
46                     const std::string& tone_buffer) override {
47     tones_.push_back(tone);
48     tones_remaining_ = tone_buffer;
49     if (tone.empty()) {
50       completed_ = true;
51     }
52   }
53 
54   // getters
tones() const55   const std::vector<std::string>& tones() const { return tones_; }
tones_from_single_argument_callback() const56   const std::vector<std::string>& tones_from_single_argument_callback() const {
57     return tones_from_single_argument_callback_;
58   }
tones_remaining()59   const std::string tones_remaining() { return tones_remaining_; }
completed() const60   bool completed() const { return completed_; }
61 
62  private:
63   std::vector<std::string> tones_;
64   std::vector<std::string> tones_from_single_argument_callback_;
65   std::string tones_remaining_;
66   bool completed_;
67 };
68 
69 class FakeDtmfProvider : public DtmfProviderInterface {
70  public:
71   struct DtmfInfo {
DtmfInfoFakeDtmfProvider::DtmfInfo72     DtmfInfo(int code, int duration, int gap)
73         : code(code), duration(duration), gap(gap) {}
74     int code;
75     int duration;
76     int gap;
77   };
78 
FakeDtmfProvider()79   FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
80 
~FakeDtmfProvider()81   ~FakeDtmfProvider() { SignalDestroyed(); }
82 
83   // Implements DtmfProviderInterface.
CanInsertDtmf()84   bool CanInsertDtmf() override { return can_insert_; }
85 
InsertDtmf(int code,int duration)86   bool InsertDtmf(int code, int duration) override {
87     int gap = 0;
88     // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
89     // mockable and use a fake timer in the unit tests.
90     if (last_insert_dtmf_call_ > 0) {
91       gap = static_cast<int>(rtc::TimeMillis() - last_insert_dtmf_call_);
92     }
93     last_insert_dtmf_call_ = rtc::TimeMillis();
94 
95     dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
96     return true;
97   }
98 
GetOnDestroyedSignal()99   sigslot::signal0<>* GetOnDestroyedSignal() override {
100     return &SignalDestroyed;
101   }
102 
103   // getter and setter
dtmf_info_queue() const104   const std::vector<DtmfInfo>& dtmf_info_queue() const {
105     return dtmf_info_queue_;
106   }
107 
108   // helper functions
SetCanInsertDtmf(bool can_insert)109   void SetCanInsertDtmf(bool can_insert) { can_insert_ = can_insert; }
110 
111  private:
112   bool can_insert_ = false;
113   std::vector<DtmfInfo> dtmf_info_queue_;
114   int64_t last_insert_dtmf_call_;
115   sigslot::signal0<> SignalDestroyed;
116 };
117 
118 class DtmfSenderTest : public ::testing::Test {
119  protected:
DtmfSenderTest()120   DtmfSenderTest()
121       : observer_(new rtc::RefCountedObject<FakeDtmfObserver>()),
122         provider_(new FakeDtmfProvider()) {
123     provider_->SetCanInsertDtmf(true);
124     dtmf_ = DtmfSender::Create(rtc::Thread::Current(), provider_.get());
125     dtmf_->RegisterObserver(observer_.get());
126   }
127 
~DtmfSenderTest()128   ~DtmfSenderTest() {
129     if (dtmf_.get()) {
130       dtmf_->UnregisterObserver();
131     }
132   }
133 
134   // Constructs a list of DtmfInfo from |tones|, |duration| and
135   // |inter_tone_gap|.
GetDtmfInfoFromString(const std::string & tones,int duration,int inter_tone_gap,std::vector<FakeDtmfProvider::DtmfInfo> * dtmfs,int comma_delay=webrtc::DtmfSender::kDtmfDefaultCommaDelayMs)136   void GetDtmfInfoFromString(
137       const std::string& tones,
138       int duration,
139       int inter_tone_gap,
140       std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs,
141       int comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs) {
142     // Init extra_delay as -inter_tone_gap - duration to ensure the first
143     // DtmfInfo's gap field will be 0.
144     int extra_delay = -1 * (inter_tone_gap + duration);
145 
146     std::string::const_iterator it = tones.begin();
147     for (; it != tones.end(); ++it) {
148       char tone = *it;
149       int code = 0;
150       webrtc::GetDtmfCode(tone, &code);
151       if (tone == ',') {
152         extra_delay = comma_delay;
153       } else {
154         dtmfs->push_back(FakeDtmfProvider::DtmfInfo(
155             code, duration, duration + inter_tone_gap + extra_delay));
156         extra_delay = 0;
157       }
158     }
159   }
160 
VerifyExpectedState(const std::string & tones,int duration,int inter_tone_gap)161   void VerifyExpectedState(const std::string& tones,
162                            int duration,
163                            int inter_tone_gap) {
164     EXPECT_EQ(tones, dtmf_->tones());
165     EXPECT_EQ(duration, dtmf_->duration());
166     EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
167   }
168 
169   // Verify the provider got all the expected calls.
VerifyOnProvider(const std::string & tones,int duration,int inter_tone_gap,int comma_delay=webrtc::DtmfSender::kDtmfDefaultCommaDelayMs)170   void VerifyOnProvider(
171       const std::string& tones,
172       int duration,
173       int inter_tone_gap,
174       int comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs) {
175     std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
176     GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref,
177                           comma_delay);
178     VerifyOnProvider(dtmf_queue_ref);
179   }
180 
VerifyOnProvider(const std::vector<FakeDtmfProvider::DtmfInfo> & dtmf_queue_ref)181   void VerifyOnProvider(
182       const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
183     const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
184         provider_->dtmf_info_queue();
185     ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
186     std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
187         dtmf_queue_ref.begin();
188     std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
189         dtmf_queue.begin();
190     while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
191       EXPECT_EQ(it_ref->code, it->code);
192       EXPECT_EQ(it_ref->duration, it->duration);
193       // Allow ~10ms error (can be small since we're using a fake clock).
194       EXPECT_GE(it_ref->gap, it->gap - 10);
195       EXPECT_LE(it_ref->gap, it->gap + 10);
196       ++it_ref;
197       ++it;
198     }
199   }
200 
201   // Verify the observer got all the expected callbacks.
VerifyOnObserver(const std::string & tones_ref)202   void VerifyOnObserver(const std::string& tones_ref) {
203     const std::vector<std::string>& tones = observer_->tones();
204     // The observer will get an empty string at the end.
205     EXPECT_EQ(tones_ref.size() + 1, tones.size());
206     EXPECT_EQ(observer_->tones(),
207               observer_->tones_from_single_argument_callback());
208     EXPECT_TRUE(tones.back().empty());
209     EXPECT_TRUE(observer_->tones_remaining().empty());
210     std::string::const_iterator it_ref = tones_ref.begin();
211     std::vector<std::string>::const_iterator it = tones.begin();
212     while (it_ref != tones_ref.end() && it != tones.end()) {
213       EXPECT_EQ(*it_ref, it->at(0));
214       ++it_ref;
215       ++it;
216     }
217   }
218 
219   std::unique_ptr<FakeDtmfObserver> observer_;
220   std::unique_ptr<FakeDtmfProvider> provider_;
221   rtc::scoped_refptr<DtmfSender> dtmf_;
222   rtc::ScopedFakeClock fake_clock_;
223 };
224 
TEST_F(DtmfSenderTest,CanInsertDtmf)225 TEST_F(DtmfSenderTest, CanInsertDtmf) {
226   EXPECT_TRUE(dtmf_->CanInsertDtmf());
227   provider_->SetCanInsertDtmf(false);
228   EXPECT_FALSE(dtmf_->CanInsertDtmf());
229 }
230 
TEST_F(DtmfSenderTest,InsertDtmf)231 TEST_F(DtmfSenderTest, InsertDtmf) {
232   std::string tones = "@1%a&*$";
233   int duration = 100;
234   int inter_tone_gap = 50;
235   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
236   EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
237 
238   // The unrecognized characters should be ignored.
239   std::string known_tones = "1a*";
240   VerifyOnProvider(known_tones, duration, inter_tone_gap);
241   VerifyOnObserver(known_tones);
242 }
243 
TEST_F(DtmfSenderTest,InsertDtmfTwice)244 TEST_F(DtmfSenderTest, InsertDtmfTwice) {
245   std::string tones1 = "12";
246   std::string tones2 = "ab";
247   int duration = 100;
248   int inter_tone_gap = 50;
249   EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
250   VerifyExpectedState(tones1, duration, inter_tone_gap);
251   // Wait until the first tone got sent.
252   EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
253                              fake_clock_);
254   VerifyExpectedState("2", duration, inter_tone_gap);
255   // Insert with another tone buffer.
256   EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
257   VerifyExpectedState(tones2, duration, inter_tone_gap);
258   // Wait until it's completed.
259   EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
260 
261   std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
262   GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
263   GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
264   VerifyOnProvider(dtmf_queue_ref);
265   VerifyOnObserver("1ab");
266 }
267 
TEST_F(DtmfSenderTest,InsertDtmfWhileProviderIsDeleted)268 TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
269   std::string tones = "@1%a&*$";
270   int duration = 100;
271   int inter_tone_gap = 50;
272   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
273   // Wait until the first tone got sent.
274   EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
275                              fake_clock_);
276   // Delete provider.
277   provider_.reset();
278   // The queue should be discontinued so no more tone callbacks.
279   SIMULATED_WAIT(false, 200, fake_clock_);
280   EXPECT_EQ(1U, observer_->tones().size());
281 }
282 
TEST_F(DtmfSenderTest,InsertDtmfWhileSenderIsDeleted)283 TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
284   std::string tones = "@1%a&*$";
285   int duration = 100;
286   int inter_tone_gap = 50;
287   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
288   // Wait until the first tone got sent.
289   EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
290                              fake_clock_);
291   // Delete the sender.
292   dtmf_ = NULL;
293   // The queue should be discontinued so no more tone callbacks.
294   SIMULATED_WAIT(false, 200, fake_clock_);
295   EXPECT_EQ(1U, observer_->tones().size());
296 }
297 
TEST_F(DtmfSenderTest,InsertEmptyTonesToCancelPreviousTask)298 TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
299   std::string tones1 = "12";
300   std::string tones2 = "";
301   int duration = 100;
302   int inter_tone_gap = 50;
303   EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
304   // Wait until the first tone got sent.
305   EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
306                              fake_clock_);
307   // Insert with another tone buffer.
308   EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
309   // Wait until it's completed.
310   EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
311 
312   std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
313   GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
314   VerifyOnProvider(dtmf_queue_ref);
315   VerifyOnObserver("1");
316 }
317 
TEST_F(DtmfSenderTest,InsertDtmfWithDefaultCommaDelay)318 TEST_F(DtmfSenderTest, InsertDtmfWithDefaultCommaDelay) {
319   std::string tones = "3,4";
320   int duration = 100;
321   int inter_tone_gap = 50;
322   int default_comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs;
323   EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay);
324   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
325   EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
326 
327   VerifyOnProvider(tones, duration, inter_tone_gap);
328   VerifyOnObserver(tones);
329   EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay);
330 }
331 
TEST_F(DtmfSenderTest,InsertDtmfWithNonDefaultCommaDelay)332 TEST_F(DtmfSenderTest, InsertDtmfWithNonDefaultCommaDelay) {
333   std::string tones = "3,4";
334   int duration = 100;
335   int inter_tone_gap = 50;
336   int default_comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs;
337   int comma_delay = 500;
338   EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay);
339   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap, comma_delay));
340   EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
341 
342   VerifyOnProvider(tones, duration, inter_tone_gap, comma_delay);
343   VerifyOnObserver(tones);
344   EXPECT_EQ(dtmf_->comma_delay(), comma_delay);
345 }
346 
TEST_F(DtmfSenderTest,TryInsertDtmfWhenItDoesNotWork)347 TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
348   std::string tones = "3,4";
349   int duration = 100;
350   int inter_tone_gap = 50;
351   provider_->SetCanInsertDtmf(false);
352   EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
353 }
354 
TEST_F(DtmfSenderTest,InsertDtmfWithInvalidDurationOrGap)355 TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
356   std::string tones = "3,4";
357   int duration = 40;
358   int inter_tone_gap = 50;
359 
360   EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
361   EXPECT_FALSE(dtmf_->InsertDtmf(tones, 39, inter_tone_gap));
362   EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 29));
363   EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap, 29));
364 
365   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
366 }
367 
TEST_F(DtmfSenderTest,InsertDtmfSendsAfterWait)368 TEST_F(DtmfSenderTest, InsertDtmfSendsAfterWait) {
369   std::string tones = "ABC";
370   int duration = 100;
371   int inter_tone_gap = 50;
372   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
373   VerifyExpectedState("ABC", duration, inter_tone_gap);
374   // Wait until the first tone got sent.
375   EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
376                              fake_clock_);
377   VerifyExpectedState("BC", duration, inter_tone_gap);
378 }
379