blob: 54f3a7b96d6f6a3ec4355bd86b0eb77eb4ddd136 [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
Henrik Kjellander15583c12016-02-10 10:53:12 +010011#include "webrtc/api/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
Henrik Kjellander15583c12016-02-10 10:53:12 +010018#include "webrtc/api/audiotrack.h"
deadbeefe7fc7d52016-10-28 13:53:08 -070019#include "webrtc/base/fakeclock.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000020#include "webrtc/base/gunit.h"
21#include "webrtc/base/logging.h"
22#include "webrtc/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.
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +000081 bool CanInsertDtmf(const std::string& track_label) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000082 return (can_insert_dtmf_tracks_.count(track_label) != 0);
83 }
84
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +000085 bool InsertDtmf(const std::string& track_label,
86 int code,
87 int duration) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000088 int gap = 0;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000089 // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
henrike@webrtc.org28e20752013-07-10 00:45:36 +000090 // mockable and use a fake timer in the unit tests.
91 if (last_insert_dtmf_call_ > 0) {
Honghai Zhang82d78622016-05-06 11:29:15 -070092 gap = static_cast<int>(rtc::TimeMillis() - last_insert_dtmf_call_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000093 }
Honghai Zhang82d78622016-05-06 11:29:15 -070094 last_insert_dtmf_call_ = rtc::TimeMillis();
henrike@webrtc.org28e20752013-07-10 00:45:36 +000095
96 LOG(LS_VERBOSE) << "FakeDtmfProvider::InsertDtmf code=" << code
97 << " duration=" << duration
98 << " gap=" << gap << ".";
99 dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
100 return true;
101 }
102
nisseef8b61e2016-04-29 06:09:15 -0700103 sigslot::signal0<>* GetOnDestroyedSignal() override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000104 return &SignalDestroyed;
105 }
106
107 // getter and setter
108 const std::vector<DtmfInfo>& dtmf_info_queue() const {
109 return dtmf_info_queue_;
110 }
111
112 // helper functions
113 void AddCanInsertDtmfTrack(const std::string& label) {
114 can_insert_dtmf_tracks_.insert(label);
115 }
116 void RemoveCanInsertDtmfTrack(const std::string& label) {
117 can_insert_dtmf_tracks_.erase(label);
118 }
119
120 private:
121 std::set<std::string> can_insert_dtmf_tracks_;
122 std::vector<DtmfInfo> dtmf_info_queue_;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200123 int64_t last_insert_dtmf_call_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000124 sigslot::signal0<> SignalDestroyed;
125};
126
127class DtmfSenderTest : public testing::Test {
128 protected:
129 DtmfSenderTest()
130 : track_(AudioTrack::Create(kTestAudioLabel, NULL)),
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000131 observer_(new rtc::RefCountedObject<FakeDtmfObserver>()),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000132 provider_(new FakeDtmfProvider()) {
133 provider_->AddCanInsertDtmfTrack(kTestAudioLabel);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000134 dtmf_ = DtmfSender::Create(track_, rtc::Thread::Current(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000135 provider_.get());
136 dtmf_->RegisterObserver(observer_.get());
137 }
138
139 ~DtmfSenderTest() {
140 if (dtmf_.get()) {
141 dtmf_->UnregisterObserver();
142 }
143 }
144
145 // Constructs a list of DtmfInfo from |tones|, |duration| and
146 // |inter_tone_gap|.
147 void GetDtmfInfoFromString(const std::string& tones, int duration,
148 int inter_tone_gap,
149 std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs) {
150 // Init extra_delay as -inter_tone_gap - duration to ensure the first
151 // DtmfInfo's gap field will be 0.
152 int extra_delay = -1 * (inter_tone_gap + duration);
153
154 std::string::const_iterator it = tones.begin();
155 for (; it != tones.end(); ++it) {
156 char tone = *it;
157 int code = 0;
158 webrtc::GetDtmfCode(tone, &code);
159 if (tone == ',') {
160 extra_delay = 2000; // 2 seconds
161 } else {
162 dtmfs->push_back(FakeDtmfProvider::DtmfInfo(code, duration,
163 duration + inter_tone_gap + extra_delay));
164 extra_delay = 0;
165 }
166 }
167 }
168
169 void VerifyExpectedState(AudioTrackInterface* track,
170 const std::string& tones,
171 int duration, int inter_tone_gap) {
172 EXPECT_EQ(track, dtmf_->track());
173 EXPECT_EQ(tones, dtmf_->tones());
174 EXPECT_EQ(duration, dtmf_->duration());
175 EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
176 }
177
178 // Verify the provider got all the expected calls.
179 void VerifyOnProvider(const std::string& tones, int duration,
180 int inter_tone_gap) {
181 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
182 GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref);
183 VerifyOnProvider(dtmf_queue_ref);
184 }
185
186 void VerifyOnProvider(
187 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
188 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
189 provider_->dtmf_info_queue();
190 ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
191 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
192 dtmf_queue_ref.begin();
193 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
194 dtmf_queue.begin();
195 while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
196 EXPECT_EQ(it_ref->code, it->code);
197 EXPECT_EQ(it_ref->duration, it->duration);
deadbeefe7fc7d52016-10-28 13:53:08 -0700198 // Allow ~10ms error (can be small since we're using a fake clock).
199 EXPECT_GE(it_ref->gap, it->gap - 10);
200 EXPECT_LE(it_ref->gap, it->gap + 10);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000201 ++it_ref;
202 ++it;
203 }
204 }
205
206 // Verify the observer got all the expected callbacks.
207 void VerifyOnObserver(const std::string& tones_ref) {
208 const std::vector<std::string>& tones = observer_->tones();
209 // The observer will get an empty string at the end.
210 EXPECT_EQ(tones_ref.size() + 1, tones.size());
211 EXPECT_TRUE(tones.back().empty());
212 std::string::const_iterator it_ref = tones_ref.begin();
213 std::vector<std::string>::const_iterator it = tones.begin();
214 while (it_ref != tones_ref.end() && it != tones.end()) {
215 EXPECT_EQ(*it_ref, it->at(0));
216 ++it_ref;
217 ++it;
218 }
219 }
220
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000221 rtc::scoped_refptr<AudioTrackInterface> track_;
kwibergd1fe2812016-04-27 06:47:29 -0700222 std::unique_ptr<FakeDtmfObserver> observer_;
223 std::unique_ptr<FakeDtmfProvider> provider_;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000224 rtc::scoped_refptr<DtmfSender> dtmf_;
deadbeefe7fc7d52016-10-28 13:53:08 -0700225 rtc::ScopedFakeClock fake_clock_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000226};
227
228TEST_F(DtmfSenderTest, CanInsertDtmf) {
229 EXPECT_TRUE(dtmf_->CanInsertDtmf());
230 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
231 EXPECT_FALSE(dtmf_->CanInsertDtmf());
232}
233
234TEST_F(DtmfSenderTest, InsertDtmf) {
235 std::string tones = "@1%a&*$";
236 int duration = 100;
237 int inter_tone_gap = 50;
238 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
deadbeefe7fc7d52016-10-28 13:53:08 -0700239 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000240
241 // The unrecognized characters should be ignored.
242 std::string known_tones = "1a*";
243 VerifyOnProvider(known_tones, duration, inter_tone_gap);
244 VerifyOnObserver(known_tones);
245}
246
247TEST_F(DtmfSenderTest, InsertDtmfTwice) {
248 std::string tones1 = "12";
249 std::string tones2 = "ab";
250 int duration = 100;
251 int inter_tone_gap = 50;
252 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
253 VerifyExpectedState(track_, tones1, duration, inter_tone_gap);
254 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700255 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
256 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000257 VerifyExpectedState(track_, "2", duration, inter_tone_gap);
258 // Insert with another tone buffer.
259 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
260 VerifyExpectedState(track_, tones2, duration, inter_tone_gap);
261 // Wait until it's completed.
deadbeefe7fc7d52016-10-28 13:53:08 -0700262 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000263
264 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
265 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
266 GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
267 VerifyOnProvider(dtmf_queue_ref);
268 VerifyOnObserver("1ab");
269}
270
271TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
272 std::string tones = "@1%a&*$";
273 int duration = 100;
274 int inter_tone_gap = 50;
275 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
276 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700277 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
278 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000279 // Delete provider.
280 provider_.reset();
281 // The queue should be discontinued so no more tone callbacks.
deadbeefe7fc7d52016-10-28 13:53:08 -0700282 SIMULATED_WAIT(false, 200, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000283 EXPECT_EQ(1U, observer_->tones().size());
284}
285
286TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
287 std::string tones = "@1%a&*$";
288 int duration = 100;
289 int inter_tone_gap = 50;
290 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
291 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700292 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
293 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000294 // Delete the sender.
295 dtmf_ = NULL;
296 // The queue should be discontinued so no more tone callbacks.
deadbeefe7fc7d52016-10-28 13:53:08 -0700297 SIMULATED_WAIT(false, 200, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000298 EXPECT_EQ(1U, observer_->tones().size());
299}
300
301TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
302 std::string tones1 = "12";
303 std::string tones2 = "";
304 int duration = 100;
305 int inter_tone_gap = 50;
306 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
307 // Wait until the first tone got sent.
deadbeefe7fc7d52016-10-28 13:53:08 -0700308 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
309 fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000310 // Insert with another tone buffer.
311 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
312 // Wait until it's completed.
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 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
316 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
317 VerifyOnProvider(dtmf_queue_ref);
318 VerifyOnObserver("1");
319}
320
deadbeefe7fc7d52016-10-28 13:53:08 -0700321TEST_F(DtmfSenderTest, InsertDtmfWithCommaAsDelay) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000322 std::string tones = "3,4";
323 int duration = 100;
324 int inter_tone_gap = 50;
325 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
deadbeefe7fc7d52016-10-28 13:53:08 -0700326 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000327
328 VerifyOnProvider(tones, duration, inter_tone_gap);
329 VerifyOnObserver(tones);
330}
331
332TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
333 std::string tones = "3,4";
334 int duration = 100;
335 int inter_tone_gap = 50;
336 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
337 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
338}
339
340TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
341 std::string tones = "3,4";
342 int duration = 100;
343 int inter_tone_gap = 50;
344
345 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
346 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 69, inter_tone_gap));
347 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 49));
348
349 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
350}