blob: c9017639119049a6421e72fb187dbd046508943c [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "pc/audiotrack.h"
19#include "rtc_base/fakeclock.h"
20#include "rtc_base/gunit.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "rtc_base/timeutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000022
23using webrtc::AudioTrackInterface;
24using webrtc::AudioTrack;
25using webrtc::DtmfProviderInterface;
26using webrtc::DtmfSender;
27using webrtc::DtmfSenderObserverInterface;
28
29static const char kTestAudioLabel[] = "test_audio_track";
deadbeefe7fc7d52016-10-28 13:53:08 -070030// TODO(deadbeef): Even though this test now uses a fake clock, it has a
31// generous 3-second timeout for every test case. The timeout could be tuned
32// to each test based on the tones sent, instead.
henrike@webrtc.org28e20752013-07-10 00:45:36 +000033static const int kMaxWaitMs = 3000;
34
Magnus Jedvertfc950842015-10-12 16:10:43 +020035class FakeDtmfObserver : public DtmfSenderObserverInterface {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000036 public:
37 FakeDtmfObserver() : completed_(false) {}
38
39 // Implements DtmfSenderObserverInterface.
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +000040 void OnToneChange(const std::string& tone) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000041 tones_.push_back(tone);
42 if (tone.empty()) {
43 completed_ = true;
44 }
45 }
46
47 // getters
Yves Gerey665174f2018-06-19 15:03:05 +020048 const std::vector<std::string>& tones() const { return tones_; }
49 bool completed() const { return completed_; }
henrike@webrtc.org28e20752013-07-10 00:45:36 +000050
51 private:
52 std::vector<std::string> tones_;
53 bool completed_;
54};
55
56class FakeDtmfProvider : public DtmfProviderInterface {
57 public:
58 struct DtmfInfo {
59 DtmfInfo(int code, int duration, int gap)
Yves Gerey665174f2018-06-19 15:03:05 +020060 : code(code), duration(duration), gap(gap) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +000061 int code;
62 int duration;
63 int gap;
64 };
65
66 FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
67
Yves Gerey665174f2018-06-19 15:03:05 +020068 ~FakeDtmfProvider() { SignalDestroyed(); }
henrike@webrtc.org28e20752013-07-10 00:45:36 +000069
70 // Implements DtmfProviderInterface.
deadbeef20cb0c12017-02-01 20:27:00 -080071 bool CanInsertDtmf() override { return can_insert_; }
henrike@webrtc.org28e20752013-07-10 00:45:36 +000072
deadbeef20cb0c12017-02-01 20:27:00 -080073 bool InsertDtmf(int code, int duration) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000074 int gap = 0;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000075 // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
henrike@webrtc.org28e20752013-07-10 00:45:36 +000076 // mockable and use a fake timer in the unit tests.
77 if (last_insert_dtmf_call_ > 0) {
Honghai Zhang82d78622016-05-06 11:29:15 -070078 gap = static_cast<int>(rtc::TimeMillis() - last_insert_dtmf_call_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000079 }
Honghai Zhang82d78622016-05-06 11:29:15 -070080 last_insert_dtmf_call_ = rtc::TimeMillis();
henrike@webrtc.org28e20752013-07-10 00:45:36 +000081
henrike@webrtc.org28e20752013-07-10 00:45:36 +000082 dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
83 return true;
84 }
85
nisseef8b61e2016-04-29 06:09:15 -070086 sigslot::signal0<>* GetOnDestroyedSignal() override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000087 return &SignalDestroyed;
88 }
89
90 // getter and setter
91 const std::vector<DtmfInfo>& dtmf_info_queue() const {
92 return dtmf_info_queue_;
93 }
94
95 // helper functions
deadbeef20cb0c12017-02-01 20:27:00 -080096 void SetCanInsertDtmf(bool can_insert) { can_insert_ = can_insert; }
henrike@webrtc.org28e20752013-07-10 00:45:36 +000097
98 private:
deadbeef20cb0c12017-02-01 20:27:00 -080099 bool can_insert_ = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000100 std::vector<DtmfInfo> dtmf_info_queue_;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200101 int64_t last_insert_dtmf_call_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000102 sigslot::signal0<> SignalDestroyed;
103};
104
105class DtmfSenderTest : public testing::Test {
106 protected:
107 DtmfSenderTest()
108 : track_(AudioTrack::Create(kTestAudioLabel, NULL)),
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000109 observer_(new rtc::RefCountedObject<FakeDtmfObserver>()),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000110 provider_(new FakeDtmfProvider()) {
deadbeef20cb0c12017-02-01 20:27:00 -0800111 provider_->SetCanInsertDtmf(true);
Yves Gerey665174f2018-06-19 15:03:05 +0200112 dtmf_ = DtmfSender::Create(track_, rtc::Thread::Current(), provider_.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113 dtmf_->RegisterObserver(observer_.get());
114 }
115
116 ~DtmfSenderTest() {
117 if (dtmf_.get()) {
118 dtmf_->UnregisterObserver();
119 }
120 }
121
122 // Constructs a list of DtmfInfo from |tones|, |duration| and
123 // |inter_tone_gap|.
Yves Gerey665174f2018-06-19 15:03:05 +0200124 void GetDtmfInfoFromString(const std::string& tones,
125 int duration,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000126 int inter_tone_gap,
127 std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs) {
128 // Init extra_delay as -inter_tone_gap - duration to ensure the first
129 // DtmfInfo's gap field will be 0.
130 int extra_delay = -1 * (inter_tone_gap + duration);
131
132 std::string::const_iterator it = tones.begin();
133 for (; it != tones.end(); ++it) {
134 char tone = *it;
135 int code = 0;
136 webrtc::GetDtmfCode(tone, &code);
137 if (tone == ',') {
138 extra_delay = 2000; // 2 seconds
139 } else {
Yves Gerey665174f2018-06-19 15:03:05 +0200140 dtmfs->push_back(FakeDtmfProvider::DtmfInfo(
141 code, duration, duration + inter_tone_gap + extra_delay));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000142 extra_delay = 0;
143 }
144 }
145 }
146
147 void VerifyExpectedState(AudioTrackInterface* track,
Yves Gerey665174f2018-06-19 15:03:05 +0200148 const std::string& tones,
149 int duration,
150 int inter_tone_gap) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000151 EXPECT_EQ(track, dtmf_->track());
152 EXPECT_EQ(tones, dtmf_->tones());
153 EXPECT_EQ(duration, dtmf_->duration());
154 EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
155 }
156
157 // Verify the provider got all the expected calls.
Yves Gerey665174f2018-06-19 15:03:05 +0200158 void VerifyOnProvider(const std::string& tones,
159 int duration,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000160 int inter_tone_gap) {
161 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
162 GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref);
163 VerifyOnProvider(dtmf_queue_ref);
164 }
165
166 void VerifyOnProvider(
167 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
168 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
169 provider_->dtmf_info_queue();
170 ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
171 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
172 dtmf_queue_ref.begin();
173 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
174 dtmf_queue.begin();
175 while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
176 EXPECT_EQ(it_ref->code, it->code);
177 EXPECT_EQ(it_ref->duration, it->duration);
deadbeefe7fc7d52016-10-28 13:53:08 -0700178 // Allow ~10ms error (can be small since we're using a fake clock).
179 EXPECT_GE(it_ref->gap, it->gap - 10);
180 EXPECT_LE(it_ref->gap, it->gap + 10);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000181 ++it_ref;
182 ++it;
183 }
184 }
185
186 // Verify the observer got all the expected callbacks.
187 void VerifyOnObserver(const std::string& tones_ref) {
188 const std::vector<std::string>& tones = observer_->tones();
189 // The observer will get an empty string at the end.
190 EXPECT_EQ(tones_ref.size() + 1, tones.size());
191 EXPECT_TRUE(tones.back().empty());
192 std::string::const_iterator it_ref = tones_ref.begin();
193 std::vector<std::string>::const_iterator it = tones.begin();
194 while (it_ref != tones_ref.end() && it != tones.end()) {
195 EXPECT_EQ(*it_ref, it->at(0));
196 ++it_ref;
197 ++it;
198 }
199 }
200
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000201 rtc::scoped_refptr<AudioTrackInterface> track_;
kwibergd1fe2812016-04-27 06:47:29 -0700202 std::unique_ptr<FakeDtmfObserver> observer_;
203 std::unique_ptr<FakeDtmfProvider> provider_;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000204 rtc::scoped_refptr<DtmfSender> dtmf_;
deadbeefe7fc7d52016-10-28 13:53:08 -0700205 rtc::ScopedFakeClock fake_clock_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000206};
207
208TEST_F(DtmfSenderTest, CanInsertDtmf) {
209 EXPECT_TRUE(dtmf_->CanInsertDtmf());
deadbeef20cb0c12017-02-01 20:27:00 -0800210 provider_->SetCanInsertDtmf(false);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000211 EXPECT_FALSE(dtmf_->CanInsertDtmf());
212}
213
214TEST_F(DtmfSenderTest, InsertDtmf) {
215 std::string tones = "@1%a&*$";
216 int duration = 100;
217 int inter_tone_gap = 50;
218 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
deadbeefe7fc7d52016-10-28 13:53:08 -0700219 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220
221 // The unrecognized characters should be ignored.
222 std::string known_tones = "1a*";
223 VerifyOnProvider(known_tones, duration, inter_tone_gap);
224 VerifyOnObserver(known_tones);
225}
226
227TEST_F(DtmfSenderTest, InsertDtmfTwice) {
228 std::string tones1 = "12";
229 std::string tones2 = "ab";
230 int duration = 100;
231 int inter_tone_gap = 50;
232 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
233 VerifyExpectedState(track_, tones1, duration, inter_tone_gap);
234 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700235 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
236 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000237 VerifyExpectedState(track_, "2", duration, inter_tone_gap);
238 // Insert with another tone buffer.
239 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
240 VerifyExpectedState(track_, tones2, duration, inter_tone_gap);
241 // Wait until it's completed.
deadbeefe7fc7d52016-10-28 13:53:08 -0700242 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000243
244 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
245 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
246 GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
247 VerifyOnProvider(dtmf_queue_ref);
248 VerifyOnObserver("1ab");
249}
250
251TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
252 std::string tones = "@1%a&*$";
253 int duration = 100;
254 int inter_tone_gap = 50;
255 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
256 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700257 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
258 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000259 // Delete provider.
260 provider_.reset();
261 // The queue should be discontinued so no more tone callbacks.
deadbeefe7fc7d52016-10-28 13:53:08 -0700262 SIMULATED_WAIT(false, 200, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000263 EXPECT_EQ(1U, observer_->tones().size());
264}
265
266TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
267 std::string tones = "@1%a&*$";
268 int duration = 100;
269 int inter_tone_gap = 50;
270 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
271 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700272 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
273 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000274 // Delete the sender.
275 dtmf_ = NULL;
276 // The queue should be discontinued so no more tone callbacks.
deadbeefe7fc7d52016-10-28 13:53:08 -0700277 SIMULATED_WAIT(false, 200, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000278 EXPECT_EQ(1U, observer_->tones().size());
279}
280
281TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
282 std::string tones1 = "12";
283 std::string tones2 = "";
284 int duration = 100;
285 int inter_tone_gap = 50;
286 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
287 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700288 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
289 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000290 // Insert with another tone buffer.
291 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
292 // Wait until it's completed.
deadbeefe7fc7d52016-10-28 13:53:08 -0700293 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000294
295 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
296 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
297 VerifyOnProvider(dtmf_queue_ref);
298 VerifyOnObserver("1");
299}
300
deadbeefe7fc7d52016-10-28 13:53:08 -0700301TEST_F(DtmfSenderTest, InsertDtmfWithCommaAsDelay) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000302 std::string tones = "3,4";
303 int duration = 100;
304 int inter_tone_gap = 50;
305 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
deadbeefe7fc7d52016-10-28 13:53:08 -0700306 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000307
308 VerifyOnProvider(tones, duration, inter_tone_gap);
309 VerifyOnObserver(tones);
310}
311
312TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
313 std::string tones = "3,4";
314 int duration = 100;
315 int inter_tone_gap = 50;
deadbeef20cb0c12017-02-01 20:27:00 -0800316 provider_->SetCanInsertDtmf(false);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000317 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
318}
319
320TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
321 std::string tones = "3,4";
dminor588101c2017-03-28 11:18:32 -0700322 int duration = 40;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000323 int inter_tone_gap = 50;
324
325 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
dminor588101c2017-03-28 11:18:32 -0700326 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 39, inter_tone_gap));
Harald Alvestrand52e58522018-02-20 08:15:36 +0100327 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 29));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000328
329 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
330}
Harald Alvestrand1e0c8042018-03-06 10:51:27 +0100331
332TEST_F(DtmfSenderTest, InsertDtmfSendsAfterWait) {
333 std::string tones = "ABC";
334 int duration = 100;
335 int inter_tone_gap = 50;
336 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
337 VerifyExpectedState(track_, "ABC", duration, inter_tone_gap);
338 // Wait until the first tone got sent.
339 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
340 fake_clock_);
341 VerifyExpectedState(track_, "BC", duration, inter_tone_gap);
342}