blob: 400444ee2cd1a8cbd3aaae8ae8c957ebcc767035 [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
ossu7bb87ee2017-01-23 04:56:25 -080011#include "webrtc/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
ossu7bb87ee2017-01-23 04:56:25 -080018#include "webrtc/pc/audiotrack.h"
Edward Lemurc20978e2017-07-06 19:44:34 +020019#include "webrtc/rtc_base/fakeclock.h"
20#include "webrtc/rtc_base/gunit.h"
21#include "webrtc/rtc_base/logging.h"
22#include "webrtc/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 LOG(LS_VERBOSE) << "FakeDtmfObserver::OnToneChange '" << tone << "'.";
43 tones_.push_back(tone);
44 if (tone.empty()) {
45 completed_ = true;
46 }
47 }
48
49 // getters
50 const std::vector<std::string>& tones() const {
51 return tones_;
52 }
53 bool completed() const {
54 return completed_;
55 }
56
57 private:
58 std::vector<std::string> tones_;
59 bool completed_;
60};
61
62class FakeDtmfProvider : public DtmfProviderInterface {
63 public:
64 struct DtmfInfo {
65 DtmfInfo(int code, int duration, int gap)
66 : code(code),
67 duration(duration),
68 gap(gap) {}
69 int code;
70 int duration;
71 int gap;
72 };
73
74 FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
75
76 ~FakeDtmfProvider() {
77 SignalDestroyed();
78 }
79
80 // Implements DtmfProviderInterface.
deadbeef20cb0c12017-02-01 20:27:00 -080081 bool CanInsertDtmf() override { return can_insert_; }
henrike@webrtc.org28e20752013-07-10 00:45:36 +000082
deadbeef20cb0c12017-02-01 20:27:00 -080083 bool InsertDtmf(int code, int duration) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000084 int gap = 0;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000085 // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
henrike@webrtc.org28e20752013-07-10 00:45:36 +000086 // mockable and use a fake timer in the unit tests.
87 if (last_insert_dtmf_call_ > 0) {
Honghai Zhang82d78622016-05-06 11:29:15 -070088 gap = static_cast<int>(rtc::TimeMillis() - last_insert_dtmf_call_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000089 }
Honghai Zhang82d78622016-05-06 11:29:15 -070090 last_insert_dtmf_call_ = rtc::TimeMillis();
henrike@webrtc.org28e20752013-07-10 00:45:36 +000091
92 LOG(LS_VERBOSE) << "FakeDtmfProvider::InsertDtmf code=" << code
93 << " duration=" << duration
94 << " gap=" << gap << ".";
95 dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
96 return true;
97 }
98
nisseef8b61e2016-04-29 06:09:15 -070099 sigslot::signal0<>* GetOnDestroyedSignal() override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000100 return &SignalDestroyed;
101 }
102
103 // getter and setter
104 const std::vector<DtmfInfo>& dtmf_info_queue() const {
105 return dtmf_info_queue_;
106 }
107
108 // helper functions
deadbeef20cb0c12017-02-01 20:27:00 -0800109 void SetCanInsertDtmf(bool can_insert) { can_insert_ = can_insert; }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000110
111 private:
deadbeef20cb0c12017-02-01 20:27:00 -0800112 bool can_insert_ = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113 std::vector<DtmfInfo> dtmf_info_queue_;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200114 int64_t last_insert_dtmf_call_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000115 sigslot::signal0<> SignalDestroyed;
116};
117
118class DtmfSenderTest : public testing::Test {
119 protected:
120 DtmfSenderTest()
121 : track_(AudioTrack::Create(kTestAudioLabel, NULL)),
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000122 observer_(new rtc::RefCountedObject<FakeDtmfObserver>()),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000123 provider_(new FakeDtmfProvider()) {
deadbeef20cb0c12017-02-01 20:27:00 -0800124 provider_->SetCanInsertDtmf(true);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000125 dtmf_ = DtmfSender::Create(track_, rtc::Thread::Current(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000126 provider_.get());
127 dtmf_->RegisterObserver(observer_.get());
128 }
129
130 ~DtmfSenderTest() {
131 if (dtmf_.get()) {
132 dtmf_->UnregisterObserver();
133 }
134 }
135
136 // Constructs a list of DtmfInfo from |tones|, |duration| and
137 // |inter_tone_gap|.
138 void GetDtmfInfoFromString(const std::string& tones, int duration,
139 int inter_tone_gap,
140 std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs) {
141 // Init extra_delay as -inter_tone_gap - duration to ensure the first
142 // DtmfInfo's gap field will be 0.
143 int extra_delay = -1 * (inter_tone_gap + duration);
144
145 std::string::const_iterator it = tones.begin();
146 for (; it != tones.end(); ++it) {
147 char tone = *it;
148 int code = 0;
149 webrtc::GetDtmfCode(tone, &code);
150 if (tone == ',') {
151 extra_delay = 2000; // 2 seconds
152 } else {
153 dtmfs->push_back(FakeDtmfProvider::DtmfInfo(code, duration,
154 duration + inter_tone_gap + extra_delay));
155 extra_delay = 0;
156 }
157 }
158 }
159
160 void VerifyExpectedState(AudioTrackInterface* track,
161 const std::string& tones,
162 int duration, int inter_tone_gap) {
163 EXPECT_EQ(track, dtmf_->track());
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.
170 void VerifyOnProvider(const std::string& tones, int duration,
171 int inter_tone_gap) {
172 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
173 GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref);
174 VerifyOnProvider(dtmf_queue_ref);
175 }
176
177 void VerifyOnProvider(
178 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
179 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
180 provider_->dtmf_info_queue();
181 ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
182 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
183 dtmf_queue_ref.begin();
184 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
185 dtmf_queue.begin();
186 while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
187 EXPECT_EQ(it_ref->code, it->code);
188 EXPECT_EQ(it_ref->duration, it->duration);
deadbeefe7fc7d52016-10-28 13:53:08 -0700189 // Allow ~10ms error (can be small since we're using a fake clock).
190 EXPECT_GE(it_ref->gap, it->gap - 10);
191 EXPECT_LE(it_ref->gap, it->gap + 10);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000192 ++it_ref;
193 ++it;
194 }
195 }
196
197 // Verify the observer got all the expected callbacks.
198 void VerifyOnObserver(const std::string& tones_ref) {
199 const std::vector<std::string>& tones = observer_->tones();
200 // The observer will get an empty string at the end.
201 EXPECT_EQ(tones_ref.size() + 1, tones.size());
202 EXPECT_TRUE(tones.back().empty());
203 std::string::const_iterator it_ref = tones_ref.begin();
204 std::vector<std::string>::const_iterator it = tones.begin();
205 while (it_ref != tones_ref.end() && it != tones.end()) {
206 EXPECT_EQ(*it_ref, it->at(0));
207 ++it_ref;
208 ++it;
209 }
210 }
211
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000212 rtc::scoped_refptr<AudioTrackInterface> track_;
kwibergd1fe2812016-04-27 06:47:29 -0700213 std::unique_ptr<FakeDtmfObserver> observer_;
214 std::unique_ptr<FakeDtmfProvider> provider_;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000215 rtc::scoped_refptr<DtmfSender> dtmf_;
deadbeefe7fc7d52016-10-28 13:53:08 -0700216 rtc::ScopedFakeClock fake_clock_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000217};
218
219TEST_F(DtmfSenderTest, CanInsertDtmf) {
220 EXPECT_TRUE(dtmf_->CanInsertDtmf());
deadbeef20cb0c12017-02-01 20:27:00 -0800221 provider_->SetCanInsertDtmf(false);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000222 EXPECT_FALSE(dtmf_->CanInsertDtmf());
223}
224
225TEST_F(DtmfSenderTest, InsertDtmf) {
226 std::string tones = "@1%a&*$";
227 int duration = 100;
228 int inter_tone_gap = 50;
229 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
deadbeefe7fc7d52016-10-28 13:53:08 -0700230 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000231
232 // The unrecognized characters should be ignored.
233 std::string known_tones = "1a*";
234 VerifyOnProvider(known_tones, duration, inter_tone_gap);
235 VerifyOnObserver(known_tones);
236}
237
238TEST_F(DtmfSenderTest, InsertDtmfTwice) {
239 std::string tones1 = "12";
240 std::string tones2 = "ab";
241 int duration = 100;
242 int inter_tone_gap = 50;
243 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
244 VerifyExpectedState(track_, tones1, duration, inter_tone_gap);
245 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700246 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
247 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000248 VerifyExpectedState(track_, "2", duration, inter_tone_gap);
249 // Insert with another tone buffer.
250 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
251 VerifyExpectedState(track_, tones2, duration, inter_tone_gap);
252 // Wait until it's completed.
deadbeefe7fc7d52016-10-28 13:53:08 -0700253 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254
255 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
256 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
257 GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
258 VerifyOnProvider(dtmf_queue_ref);
259 VerifyOnObserver("1ab");
260}
261
262TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
263 std::string tones = "@1%a&*$";
264 int duration = 100;
265 int inter_tone_gap = 50;
266 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
267 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700268 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
269 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000270 // Delete provider.
271 provider_.reset();
272 // The queue should be discontinued so no more tone callbacks.
deadbeefe7fc7d52016-10-28 13:53:08 -0700273 SIMULATED_WAIT(false, 200, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000274 EXPECT_EQ(1U, observer_->tones().size());
275}
276
277TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
278 std::string tones = "@1%a&*$";
279 int duration = 100;
280 int inter_tone_gap = 50;
281 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
282 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700283 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
284 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000285 // Delete the sender.
286 dtmf_ = NULL;
287 // The queue should be discontinued so no more tone callbacks.
deadbeefe7fc7d52016-10-28 13:53:08 -0700288 SIMULATED_WAIT(false, 200, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289 EXPECT_EQ(1U, observer_->tones().size());
290}
291
292TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
293 std::string tones1 = "12";
294 std::string tones2 = "";
295 int duration = 100;
296 int inter_tone_gap = 50;
297 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
298 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700299 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
300 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000301 // Insert with another tone buffer.
302 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
303 // Wait until it's completed.
deadbeefe7fc7d52016-10-28 13:53:08 -0700304 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000305
306 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
307 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
308 VerifyOnProvider(dtmf_queue_ref);
309 VerifyOnObserver("1");
310}
311
deadbeefe7fc7d52016-10-28 13:53:08 -0700312TEST_F(DtmfSenderTest, InsertDtmfWithCommaAsDelay) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000313 std::string tones = "3,4";
314 int duration = 100;
315 int inter_tone_gap = 50;
316 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
deadbeefe7fc7d52016-10-28 13:53:08 -0700317 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000318
319 VerifyOnProvider(tones, duration, inter_tone_gap);
320 VerifyOnObserver(tones);
321}
322
323TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
324 std::string tones = "3,4";
325 int duration = 100;
326 int inter_tone_gap = 50;
deadbeef20cb0c12017-02-01 20:27:00 -0800327 provider_->SetCanInsertDtmf(false);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000328 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
329}
330
331TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
332 std::string tones = "3,4";
dminor588101c2017-03-28 11:18:32 -0700333 int duration = 40;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000334 int inter_tone_gap = 50;
335
336 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
dminor588101c2017-03-28 11:18:32 -0700337 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 39, inter_tone_gap));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000338 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 49));
339
340 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
341}