blob: a4835054ade54ef538c372e112ffd050e3147e53 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2012, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/app/webrtc/dtmfsender.h"
29
30#include <set>
31#include <string>
32#include <vector>
33
34#include "talk/app/webrtc/audiotrack.h"
35#include "talk/base/gunit.h"
36#include "talk/base/logging.h"
37#include "talk/base/timeutils.h"
38
39using webrtc::AudioTrackInterface;
40using webrtc::AudioTrack;
41using webrtc::DtmfProviderInterface;
42using webrtc::DtmfSender;
43using webrtc::DtmfSenderObserverInterface;
44
45static const char kTestAudioLabel[] = "test_audio_track";
46static const int kMaxWaitMs = 3000;
47
48class FakeDtmfObserver : public DtmfSenderObserverInterface {
49 public:
50 FakeDtmfObserver() : completed_(false) {}
51
52 // Implements DtmfSenderObserverInterface.
53 virtual void OnToneChange(const std::string& tone) OVERRIDE {
54 LOG(LS_VERBOSE) << "FakeDtmfObserver::OnToneChange '" << tone << "'.";
55 tones_.push_back(tone);
56 if (tone.empty()) {
57 completed_ = true;
58 }
59 }
60
61 // getters
62 const std::vector<std::string>& tones() const {
63 return tones_;
64 }
65 bool completed() const {
66 return completed_;
67 }
68
69 private:
70 std::vector<std::string> tones_;
71 bool completed_;
72};
73
74class FakeDtmfProvider : public DtmfProviderInterface {
75 public:
76 struct DtmfInfo {
77 DtmfInfo(int code, int duration, int gap)
78 : code(code),
79 duration(duration),
80 gap(gap) {}
81 int code;
82 int duration;
83 int gap;
84 };
85
86 FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
87
88 ~FakeDtmfProvider() {
89 SignalDestroyed();
90 }
91
92 // Implements DtmfProviderInterface.
93 virtual bool CanInsertDtmf(const std::string& track_label) OVERRIDE {
94 return (can_insert_dtmf_tracks_.count(track_label) != 0);
95 }
96
97 virtual bool InsertDtmf(const std::string& track_label,
98 int code, int duration) OVERRIDE {
99 int gap = 0;
100 // TODO(ronghuawu): Make the timer (basically the talk_base::TimeNanos)
101 // mockable and use a fake timer in the unit tests.
102 if (last_insert_dtmf_call_ > 0) {
103 gap = static_cast<int>(talk_base::Time() - last_insert_dtmf_call_);
104 }
105 last_insert_dtmf_call_ = talk_base::Time();
106
107 LOG(LS_VERBOSE) << "FakeDtmfProvider::InsertDtmf code=" << code
108 << " duration=" << duration
109 << " gap=" << gap << ".";
110 dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
111 return true;
112 }
113
114 virtual sigslot::signal0<>* GetOnDestroyedSignal() {
115 return &SignalDestroyed;
116 }
117
118 // getter and setter
119 const std::vector<DtmfInfo>& dtmf_info_queue() const {
120 return dtmf_info_queue_;
121 }
122
123 // helper functions
124 void AddCanInsertDtmfTrack(const std::string& label) {
125 can_insert_dtmf_tracks_.insert(label);
126 }
127 void RemoveCanInsertDtmfTrack(const std::string& label) {
128 can_insert_dtmf_tracks_.erase(label);
129 }
130
131 private:
132 std::set<std::string> can_insert_dtmf_tracks_;
133 std::vector<DtmfInfo> dtmf_info_queue_;
134 int64 last_insert_dtmf_call_;
135 sigslot::signal0<> SignalDestroyed;
136};
137
138class DtmfSenderTest : public testing::Test {
139 protected:
140 DtmfSenderTest()
141 : track_(AudioTrack::Create(kTestAudioLabel, NULL)),
142 observer_(new talk_base::RefCountedObject<FakeDtmfObserver>()),
143 provider_(new FakeDtmfProvider()) {
144 provider_->AddCanInsertDtmfTrack(kTestAudioLabel);
145 dtmf_ = DtmfSender::Create(track_, talk_base::Thread::Current(),
146 provider_.get());
147 dtmf_->RegisterObserver(observer_.get());
148 }
149
150 ~DtmfSenderTest() {
151 if (dtmf_.get()) {
152 dtmf_->UnregisterObserver();
153 }
154 }
155
156 // Constructs a list of DtmfInfo from |tones|, |duration| and
157 // |inter_tone_gap|.
158 void GetDtmfInfoFromString(const std::string& tones, int duration,
159 int inter_tone_gap,
160 std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs) {
161 // Init extra_delay as -inter_tone_gap - duration to ensure the first
162 // DtmfInfo's gap field will be 0.
163 int extra_delay = -1 * (inter_tone_gap + duration);
164
165 std::string::const_iterator it = tones.begin();
166 for (; it != tones.end(); ++it) {
167 char tone = *it;
168 int code = 0;
169 webrtc::GetDtmfCode(tone, &code);
170 if (tone == ',') {
171 extra_delay = 2000; // 2 seconds
172 } else {
173 dtmfs->push_back(FakeDtmfProvider::DtmfInfo(code, duration,
174 duration + inter_tone_gap + extra_delay));
175 extra_delay = 0;
176 }
177 }
178 }
179
180 void VerifyExpectedState(AudioTrackInterface* track,
181 const std::string& tones,
182 int duration, int inter_tone_gap) {
183 EXPECT_EQ(track, dtmf_->track());
184 EXPECT_EQ(tones, dtmf_->tones());
185 EXPECT_EQ(duration, dtmf_->duration());
186 EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
187 }
188
189 // Verify the provider got all the expected calls.
190 void VerifyOnProvider(const std::string& tones, int duration,
191 int inter_tone_gap) {
192 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
193 GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref);
194 VerifyOnProvider(dtmf_queue_ref);
195 }
196
197 void VerifyOnProvider(
198 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
199 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
200 provider_->dtmf_info_queue();
201 ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
202 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
203 dtmf_queue_ref.begin();
204 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
205 dtmf_queue.begin();
206 while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
207 EXPECT_EQ(it_ref->code, it->code);
208 EXPECT_EQ(it_ref->duration, it->duration);
wu@webrtc.org8d1e4d62013-09-18 18:01:07 +0000209 // Allow ~100ms error.
210 EXPECT_GE(it_ref->gap, it->gap - 100);
211 EXPECT_LE(it_ref->gap, it->gap + 100);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000212 ++it_ref;
213 ++it;
214 }
215 }
216
217 // Verify the observer got all the expected callbacks.
218 void VerifyOnObserver(const std::string& tones_ref) {
219 const std::vector<std::string>& tones = observer_->tones();
220 // The observer will get an empty string at the end.
221 EXPECT_EQ(tones_ref.size() + 1, tones.size());
222 EXPECT_TRUE(tones.back().empty());
223 std::string::const_iterator it_ref = tones_ref.begin();
224 std::vector<std::string>::const_iterator it = tones.begin();
225 while (it_ref != tones_ref.end() && it != tones.end()) {
226 EXPECT_EQ(*it_ref, it->at(0));
227 ++it_ref;
228 ++it;
229 }
230 }
231
232 talk_base::scoped_refptr<AudioTrackInterface> track_;
233 talk_base::scoped_ptr<FakeDtmfObserver> observer_;
234 talk_base::scoped_ptr<FakeDtmfProvider> provider_;
235 talk_base::scoped_refptr<DtmfSender> dtmf_;
236};
237
238TEST_F(DtmfSenderTest, CanInsertDtmf) {
239 EXPECT_TRUE(dtmf_->CanInsertDtmf());
240 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
241 EXPECT_FALSE(dtmf_->CanInsertDtmf());
242}
243
244TEST_F(DtmfSenderTest, InsertDtmf) {
245 std::string tones = "@1%a&*$";
246 int duration = 100;
247 int inter_tone_gap = 50;
248 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
249 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
250
251 // The unrecognized characters should be ignored.
252 std::string known_tones = "1a*";
253 VerifyOnProvider(known_tones, duration, inter_tone_gap);
254 VerifyOnObserver(known_tones);
255}
256
257TEST_F(DtmfSenderTest, InsertDtmfTwice) {
258 std::string tones1 = "12";
259 std::string tones2 = "ab";
260 int duration = 100;
261 int inter_tone_gap = 50;
262 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
263 VerifyExpectedState(track_, tones1, duration, inter_tone_gap);
264 // Wait until the first tone got sent.
265 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
266 VerifyExpectedState(track_, "2", duration, inter_tone_gap);
267 // Insert with another tone buffer.
268 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
269 VerifyExpectedState(track_, tones2, duration, inter_tone_gap);
270 // Wait until it's completed.
271 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
272
273 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
274 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
275 GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
276 VerifyOnProvider(dtmf_queue_ref);
277 VerifyOnObserver("1ab");
278}
279
280TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
281 std::string tones = "@1%a&*$";
282 int duration = 100;
283 int inter_tone_gap = 50;
284 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
285 // Wait until the first tone got sent.
286 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
287 // Delete provider.
288 provider_.reset();
289 // The queue should be discontinued so no more tone callbacks.
290 WAIT(false, 200);
291 EXPECT_EQ(1U, observer_->tones().size());
292}
293
294TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
295 std::string tones = "@1%a&*$";
296 int duration = 100;
297 int inter_tone_gap = 50;
298 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
299 // Wait until the first tone got sent.
300 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
301 // Delete the sender.
302 dtmf_ = NULL;
303 // The queue should be discontinued so no more tone callbacks.
304 WAIT(false, 200);
305 EXPECT_EQ(1U, observer_->tones().size());
306}
307
308TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
309 std::string tones1 = "12";
310 std::string tones2 = "";
311 int duration = 100;
312 int inter_tone_gap = 50;
313 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
314 // Wait until the first tone got sent.
315 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
316 // Insert with another tone buffer.
317 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
318 // Wait until it's completed.
319 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
320
321 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
322 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
323 VerifyOnProvider(dtmf_queue_ref);
324 VerifyOnObserver("1");
325}
326
327TEST_F(DtmfSenderTest, InsertDtmfWithCommaAsDelay) {
328 std::string tones = "3,4";
329 int duration = 100;
330 int inter_tone_gap = 50;
331 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
332 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
333
334 VerifyOnProvider(tones, duration, inter_tone_gap);
335 VerifyOnObserver(tones);
336}
337
338TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
339 std::string tones = "3,4";
340 int duration = 100;
341 int inter_tone_gap = 50;
342 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
343 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
344}
345
346TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
347 std::string tones = "3,4";
348 int duration = 100;
349 int inter_tone_gap = 50;
350
351 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
352 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 69, inter_tone_gap));
353 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 49));
354
355 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
356}