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