blob: a5d102b5f2eaad3c4ab4b154e92969eb9b3e8405 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellanderb24317b2016-02-10 07:54:43 -08002 * Copyright 2012 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellanderb24317b2016-02-10 07:54:43 -08004 * 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.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "pc/dtmfsender.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
kwibergd1fe2812016-04-27 06:47:29 -070013#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000014#include <set>
15#include <string>
16#include <vector>
17
Patrik Höglund563934e2017-09-15 09:04:28 +020018
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "pc/audiotrack.h"
20#include "rtc_base/fakeclock.h"
21#include "rtc_base/gunit.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "rtc_base/timeutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000023
24using webrtc::AudioTrackInterface;
25using webrtc::AudioTrack;
26using webrtc::DtmfProviderInterface;
27using webrtc::DtmfSender;
28using webrtc::DtmfSenderObserverInterface;
29
30static const char kTestAudioLabel[] = "test_audio_track";
deadbeefe7fc7d52016-10-28 13:53:08 -070031// TODO(deadbeef): Even though this test now uses a fake clock, it has a
32// generous 3-second timeout for every test case. The timeout could be tuned
33// to each test based on the tones sent, instead.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000034static const int kMaxWaitMs = 3000;
35
Magnus Jedvertfc950842015-10-12 16:10:43 +020036class FakeDtmfObserver : public DtmfSenderObserverInterface {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000037 public:
38 FakeDtmfObserver() : completed_(false) {}
39
40 // Implements DtmfSenderObserverInterface.
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +000041 void OnToneChange(const std::string& tone) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000042 tones_.push_back(tone);
43 if (tone.empty()) {
44 completed_ = true;
45 }
46 }
47
48 // getters
49 const std::vector<std::string>& tones() const {
50 return tones_;
51 }
52 bool completed() const {
53 return completed_;
54 }
55
56 private:
57 std::vector<std::string> tones_;
58 bool completed_;
59};
60
61class FakeDtmfProvider : public DtmfProviderInterface {
62 public:
63 struct DtmfInfo {
64 DtmfInfo(int code, int duration, int gap)
65 : code(code),
66 duration(duration),
67 gap(gap) {}
68 int code;
69 int duration;
70 int gap;
71 };
72
73 FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
74
75 ~FakeDtmfProvider() {
76 SignalDestroyed();
77 }
78
79 // Implements DtmfProviderInterface.
deadbeef20cb0c12017-02-01 20:27:00 -080080 bool CanInsertDtmf() override { return can_insert_; }
henrike@webrtc.org28e20752013-07-10 00:45:36 +000081
deadbeef20cb0c12017-02-01 20:27:00 -080082 bool InsertDtmf(int code, int duration) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000083 int gap = 0;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000084 // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
henrike@webrtc.org28e20752013-07-10 00:45:36 +000085 // mockable and use a fake timer in the unit tests.
86 if (last_insert_dtmf_call_ > 0) {
Honghai Zhang82d78622016-05-06 11:29:15 -070087 gap = static_cast<int>(rtc::TimeMillis() - last_insert_dtmf_call_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000088 }
Honghai Zhang82d78622016-05-06 11:29:15 -070089 last_insert_dtmf_call_ = rtc::TimeMillis();
henrike@webrtc.org28e20752013-07-10 00:45:36 +000090
henrike@webrtc.org28e20752013-07-10 00:45:36 +000091 dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
92 return true;
93 }
94
nisseef8b61e2016-04-29 06:09:15 -070095 sigslot::signal0<>* GetOnDestroyedSignal() override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000096 return &SignalDestroyed;
97 }
98
99 // getter and setter
100 const std::vector<DtmfInfo>& dtmf_info_queue() const {
101 return dtmf_info_queue_;
102 }
103
104 // helper functions
deadbeef20cb0c12017-02-01 20:27:00 -0800105 void SetCanInsertDtmf(bool can_insert) { can_insert_ = can_insert; }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000106
107 private:
deadbeef20cb0c12017-02-01 20:27:00 -0800108 bool can_insert_ = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000109 std::vector<DtmfInfo> dtmf_info_queue_;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200110 int64_t last_insert_dtmf_call_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000111 sigslot::signal0<> SignalDestroyed;
112};
113
114class DtmfSenderTest : public testing::Test {
115 protected:
116 DtmfSenderTest()
117 : track_(AudioTrack::Create(kTestAudioLabel, NULL)),
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000118 observer_(new rtc::RefCountedObject<FakeDtmfObserver>()),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000119 provider_(new FakeDtmfProvider()) {
deadbeef20cb0c12017-02-01 20:27:00 -0800120 provider_->SetCanInsertDtmf(true);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000121 dtmf_ = DtmfSender::Create(track_, rtc::Thread::Current(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122 provider_.get());
123 dtmf_->RegisterObserver(observer_.get());
124 }
125
126 ~DtmfSenderTest() {
127 if (dtmf_.get()) {
128 dtmf_->UnregisterObserver();
129 }
130 }
131
132 // Constructs a list of DtmfInfo from |tones|, |duration| and
133 // |inter_tone_gap|.
134 void GetDtmfInfoFromString(const std::string& tones, int duration,
135 int inter_tone_gap,
136 std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs) {
137 // Init extra_delay as -inter_tone_gap - duration to ensure the first
138 // DtmfInfo's gap field will be 0.
139 int extra_delay = -1 * (inter_tone_gap + duration);
140
141 std::string::const_iterator it = tones.begin();
142 for (; it != tones.end(); ++it) {
143 char tone = *it;
144 int code = 0;
145 webrtc::GetDtmfCode(tone, &code);
146 if (tone == ',') {
147 extra_delay = 2000; // 2 seconds
148 } else {
149 dtmfs->push_back(FakeDtmfProvider::DtmfInfo(code, duration,
150 duration + inter_tone_gap + extra_delay));
151 extra_delay = 0;
152 }
153 }
154 }
155
156 void VerifyExpectedState(AudioTrackInterface* track,
157 const std::string& tones,
158 int duration, int inter_tone_gap) {
159 EXPECT_EQ(track, dtmf_->track());
160 EXPECT_EQ(tones, dtmf_->tones());
161 EXPECT_EQ(duration, dtmf_->duration());
162 EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
163 }
164
165 // Verify the provider got all the expected calls.
166 void VerifyOnProvider(const std::string& tones, int duration,
167 int inter_tone_gap) {
168 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
169 GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref);
170 VerifyOnProvider(dtmf_queue_ref);
171 }
172
173 void VerifyOnProvider(
174 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
175 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
176 provider_->dtmf_info_queue();
177 ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
178 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
179 dtmf_queue_ref.begin();
180 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
181 dtmf_queue.begin();
182 while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
183 EXPECT_EQ(it_ref->code, it->code);
184 EXPECT_EQ(it_ref->duration, it->duration);
deadbeefe7fc7d52016-10-28 13:53:08 -0700185 // Allow ~10ms error (can be small since we're using a fake clock).
186 EXPECT_GE(it_ref->gap, it->gap - 10);
187 EXPECT_LE(it_ref->gap, it->gap + 10);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000188 ++it_ref;
189 ++it;
190 }
191 }
192
193 // Verify the observer got all the expected callbacks.
194 void VerifyOnObserver(const std::string& tones_ref) {
195 const std::vector<std::string>& tones = observer_->tones();
196 // The observer will get an empty string at the end.
197 EXPECT_EQ(tones_ref.size() + 1, tones.size());
198 EXPECT_TRUE(tones.back().empty());
199 std::string::const_iterator it_ref = tones_ref.begin();
200 std::vector<std::string>::const_iterator it = tones.begin();
201 while (it_ref != tones_ref.end() && it != tones.end()) {
202 EXPECT_EQ(*it_ref, it->at(0));
203 ++it_ref;
204 ++it;
205 }
206 }
207
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000208 rtc::scoped_refptr<AudioTrackInterface> track_;
kwibergd1fe2812016-04-27 06:47:29 -0700209 std::unique_ptr<FakeDtmfObserver> observer_;
210 std::unique_ptr<FakeDtmfProvider> provider_;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000211 rtc::scoped_refptr<DtmfSender> dtmf_;
deadbeefe7fc7d52016-10-28 13:53:08 -0700212 rtc::ScopedFakeClock fake_clock_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000213};
214
215TEST_F(DtmfSenderTest, CanInsertDtmf) {
216 EXPECT_TRUE(dtmf_->CanInsertDtmf());
deadbeef20cb0c12017-02-01 20:27:00 -0800217 provider_->SetCanInsertDtmf(false);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000218 EXPECT_FALSE(dtmf_->CanInsertDtmf());
219}
220
221TEST_F(DtmfSenderTest, InsertDtmf) {
222 std::string tones = "@1%a&*$";
223 int duration = 100;
224 int inter_tone_gap = 50;
225 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
deadbeefe7fc7d52016-10-28 13:53:08 -0700226 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000227
228 // The unrecognized characters should be ignored.
229 std::string known_tones = "1a*";
230 VerifyOnProvider(known_tones, duration, inter_tone_gap);
231 VerifyOnObserver(known_tones);
232}
233
234TEST_F(DtmfSenderTest, InsertDtmfTwice) {
235 std::string tones1 = "12";
236 std::string tones2 = "ab";
237 int duration = 100;
238 int inter_tone_gap = 50;
239 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
240 VerifyExpectedState(track_, tones1, duration, inter_tone_gap);
241 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700242 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
243 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000244 VerifyExpectedState(track_, "2", duration, inter_tone_gap);
245 // Insert with another tone buffer.
246 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
247 VerifyExpectedState(track_, tones2, duration, inter_tone_gap);
248 // Wait until it's completed.
deadbeefe7fc7d52016-10-28 13:53:08 -0700249 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000250
251 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
252 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
253 GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
254 VerifyOnProvider(dtmf_queue_ref);
255 VerifyOnObserver("1ab");
256}
257
258TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
259 std::string tones = "@1%a&*$";
260 int duration = 100;
261 int inter_tone_gap = 50;
262 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
263 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700264 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
265 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000266 // Delete provider.
267 provider_.reset();
268 // The queue should be discontinued so no more tone callbacks.
deadbeefe7fc7d52016-10-28 13:53:08 -0700269 SIMULATED_WAIT(false, 200, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000270 EXPECT_EQ(1U, observer_->tones().size());
271}
272
273TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
274 std::string tones = "@1%a&*$";
275 int duration = 100;
276 int inter_tone_gap = 50;
277 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
278 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700279 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
280 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000281 // Delete the sender.
282 dtmf_ = NULL;
283 // The queue should be discontinued so no more tone callbacks.
deadbeefe7fc7d52016-10-28 13:53:08 -0700284 SIMULATED_WAIT(false, 200, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000285 EXPECT_EQ(1U, observer_->tones().size());
286}
287
288TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
289 std::string tones1 = "12";
290 std::string tones2 = "";
291 int duration = 100;
292 int inter_tone_gap = 50;
293 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
294 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700295 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
296 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000297 // Insert with another tone buffer.
298 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
299 // Wait until it's completed.
deadbeefe7fc7d52016-10-28 13:53:08 -0700300 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000301
302 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
303 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
304 VerifyOnProvider(dtmf_queue_ref);
305 VerifyOnObserver("1");
306}
307
deadbeefe7fc7d52016-10-28 13:53:08 -0700308TEST_F(DtmfSenderTest, InsertDtmfWithCommaAsDelay) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000309 std::string tones = "3,4";
310 int duration = 100;
311 int inter_tone_gap = 50;
312 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
deadbeefe7fc7d52016-10-28 13:53:08 -0700313 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000314
315 VerifyOnProvider(tones, duration, inter_tone_gap);
316 VerifyOnObserver(tones);
317}
318
319TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
320 std::string tones = "3,4";
321 int duration = 100;
322 int inter_tone_gap = 50;
deadbeef20cb0c12017-02-01 20:27:00 -0800323 provider_->SetCanInsertDtmf(false);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000324 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
325}
326
327TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
328 std::string tones = "3,4";
dminor588101c2017-03-28 11:18:32 -0700329 int duration = 40;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000330 int inter_tone_gap = 50;
331
332 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
dminor588101c2017-03-28 11:18:32 -0700333 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 39, inter_tone_gap));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000334 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 49));
335
336 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
337}