OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2012 The WebRTC project authors. All Rights Reserved. | |
3 * | |
4 * 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. | |
9 */ | |
10 | |
11 #include "webrtc/api/dtmfsender.h" | |
12 | |
13 #include <memory> | |
14 #include <set> | |
15 #include <string> | |
16 #include <vector> | |
17 | |
18 #include "webrtc/api/audiotrack.h" | |
19 #include "webrtc/base/fakeclock.h" | |
20 #include "webrtc/base/gunit.h" | |
21 #include "webrtc/base/logging.h" | |
22 #include "webrtc/base/timeutils.h" | |
23 | |
24 using webrtc::AudioTrackInterface; | |
25 using webrtc::AudioTrack; | |
26 using webrtc::DtmfProviderInterface; | |
27 using webrtc::DtmfSender; | |
28 using webrtc::DtmfSenderObserverInterface; | |
29 | |
30 static const char kTestAudioLabel[] = "test_audio_track"; | |
31 // 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. | |
34 static const int kMaxWaitMs = 3000; | |
35 | |
36 class FakeDtmfObserver : public DtmfSenderObserverInterface { | |
37 public: | |
38 FakeDtmfObserver() : completed_(false) {} | |
39 | |
40 // Implements DtmfSenderObserverInterface. | |
41 void OnToneChange(const std::string& tone) override { | |
42 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 | |
62 class 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. | |
81 bool CanInsertDtmf(const std::string& track_label) override { | |
82 return (can_insert_dtmf_tracks_.count(track_label) != 0); | |
83 } | |
84 | |
85 bool InsertDtmf(const std::string& track_label, | |
86 int code, | |
87 int duration) override { | |
88 int gap = 0; | |
89 // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos) | |
90 // mockable and use a fake timer in the unit tests. | |
91 if (last_insert_dtmf_call_ > 0) { | |
92 gap = static_cast<int>(rtc::TimeMillis() - last_insert_dtmf_call_); | |
93 } | |
94 last_insert_dtmf_call_ = rtc::TimeMillis(); | |
95 | |
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 | |
103 sigslot::signal0<>* GetOnDestroyedSignal() override { | |
104 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_; | |
123 int64_t last_insert_dtmf_call_; | |
124 sigslot::signal0<> SignalDestroyed; | |
125 }; | |
126 | |
127 class DtmfSenderTest : public testing::Test { | |
128 protected: | |
129 DtmfSenderTest() | |
130 : track_(AudioTrack::Create(kTestAudioLabel, NULL)), | |
131 observer_(new rtc::RefCountedObject<FakeDtmfObserver>()), | |
132 provider_(new FakeDtmfProvider()) { | |
133 provider_->AddCanInsertDtmfTrack(kTestAudioLabel); | |
134 dtmf_ = DtmfSender::Create(track_, rtc::Thread::Current(), | |
135 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); | |
198 // 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); | |
201 ++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 | |
221 rtc::scoped_refptr<AudioTrackInterface> track_; | |
222 std::unique_ptr<FakeDtmfObserver> observer_; | |
223 std::unique_ptr<FakeDtmfProvider> provider_; | |
224 rtc::scoped_refptr<DtmfSender> dtmf_; | |
225 rtc::ScopedFakeClock fake_clock_; | |
226 }; | |
227 | |
228 TEST_F(DtmfSenderTest, CanInsertDtmf) { | |
229 EXPECT_TRUE(dtmf_->CanInsertDtmf()); | |
230 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel); | |
231 EXPECT_FALSE(dtmf_->CanInsertDtmf()); | |
232 } | |
233 | |
234 TEST_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)); | |
239 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_); | |
240 | |
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 | |
247 TEST_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. | |
255 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs, | |
256 fake_clock_); | |
257 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. | |
262 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_); | |
263 | |
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 | |
271 TEST_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. | |
277 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs, | |
278 fake_clock_); | |
279 // Delete provider. | |
280 provider_.reset(); | |
281 // The queue should be discontinued so no more tone callbacks. | |
282 SIMULATED_WAIT(false, 200, fake_clock_); | |
283 EXPECT_EQ(1U, observer_->tones().size()); | |
284 } | |
285 | |
286 TEST_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. | |
292 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs, | |
293 fake_clock_); | |
294 // Delete the sender. | |
295 dtmf_ = NULL; | |
296 // The queue should be discontinued so no more tone callbacks. | |
297 SIMULATED_WAIT(false, 200, fake_clock_); | |
298 EXPECT_EQ(1U, observer_->tones().size()); | |
299 } | |
300 | |
301 TEST_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. | |
308 EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs, | |
309 fake_clock_); | |
310 // Insert with another tone buffer. | |
311 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap)); | |
312 // Wait until it's completed. | |
313 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_); | |
314 | |
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 | |
321 TEST_F(DtmfSenderTest, InsertDtmfWithCommaAsDelay) { | |
322 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)); | |
326 EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_); | |
327 | |
328 VerifyOnProvider(tones, duration, inter_tone_gap); | |
329 VerifyOnObserver(tones); | |
330 } | |
331 | |
332 TEST_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 | |
340 TEST_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 } | |
OLD | NEW |