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