1 /* 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 "webrtc/base/gunit.h" 36 #include "webrtc/base/logging.h" 37 #include "webrtc/base/timeutils.h" 38 39 using webrtc::AudioTrackInterface; 40 using webrtc::AudioTrack; 41 using webrtc::DtmfProviderInterface; 42 using webrtc::DtmfSender; 43 using webrtc::DtmfSenderObserverInterface; 44 45 static const char kTestAudioLabel[] = "test_audio_track"; 46 static const int kMaxWaitMs = 3000; 47 48 class FakeDtmfObserver : public DtmfSenderObserverInterface { 49 public: 50 FakeDtmfObserver() : completed_(false) {} 51 52 // Implements DtmfSenderObserverInterface. 53 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 74 class 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 bool CanInsertDtmf(const std::string& track_label) override { 94 return (can_insert_dtmf_tracks_.count(track_label) != 0); 95 } 96 97 bool InsertDtmf(const std::string& track_label, 98 int code, 99 int duration) override { 100 int gap = 0; 101 // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos) 102 // mockable and use a fake timer in the unit tests. 103 if (last_insert_dtmf_call_ > 0) { 104 gap = static_cast<int>(rtc::Time() - last_insert_dtmf_call_); 105 } 106 last_insert_dtmf_call_ = rtc::Time(); 107 108 LOG(LS_VERBOSE) << "FakeDtmfProvider::InsertDtmf code=" << code 109 << " duration=" << duration 110 << " gap=" << gap << "."; 111 dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap)); 112 return true; 113 } 114 115 virtual sigslot::signal0<>* GetOnDestroyedSignal() { 116 return &SignalDestroyed; 117 } 118 119 // getter and setter 120 const std::vector<DtmfInfo>& dtmf_info_queue() const { 121 return dtmf_info_queue_; 122 } 123 124 // helper functions 125 void AddCanInsertDtmfTrack(const std::string& label) { 126 can_insert_dtmf_tracks_.insert(label); 127 } 128 void RemoveCanInsertDtmfTrack(const std::string& label) { 129 can_insert_dtmf_tracks_.erase(label); 130 } 131 132 private: 133 std::set<std::string> can_insert_dtmf_tracks_; 134 std::vector<DtmfInfo> dtmf_info_queue_; 135 int64_t last_insert_dtmf_call_; 136 sigslot::signal0<> SignalDestroyed; 137 }; 138 139 class DtmfSenderTest : public testing::Test { 140 protected: 141 DtmfSenderTest() 142 : track_(AudioTrack::Create(kTestAudioLabel, NULL)), 143 observer_(new rtc::RefCountedObject<FakeDtmfObserver>()), 144 provider_(new FakeDtmfProvider()) { 145 provider_->AddCanInsertDtmfTrack(kTestAudioLabel); 146 dtmf_ = DtmfSender::Create(track_, rtc::Thread::Current(), 147 provider_.get()); 148 dtmf_->RegisterObserver(observer_.get()); 149 } 150 151 ~DtmfSenderTest() { 152 if (dtmf_.get()) { 153 dtmf_->UnregisterObserver(); 154 } 155 } 156 157 // Constructs a list of DtmfInfo from |tones|, |duration| and 158 // |inter_tone_gap|. 159 void GetDtmfInfoFromString(const std::string& tones, int duration, 160 int inter_tone_gap, 161 std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs) { 162 // Init extra_delay as -inter_tone_gap - duration to ensure the first 163 // DtmfInfo's gap field will be 0. 164 int extra_delay = -1 * (inter_tone_gap + duration); 165 166 std::string::const_iterator it = tones.begin(); 167 for (; it != tones.end(); ++it) { 168 char tone = *it; 169 int code = 0; 170 webrtc::GetDtmfCode(tone, &code); 171 if (tone == ',') { 172 extra_delay = 2000; // 2 seconds 173 } else { 174 dtmfs->push_back(FakeDtmfProvider::DtmfInfo(code, duration, 175 duration + inter_tone_gap + extra_delay)); 176 extra_delay = 0; 177 } 178 } 179 } 180 181 void VerifyExpectedState(AudioTrackInterface* track, 182 const std::string& tones, 183 int duration, int inter_tone_gap) { 184 EXPECT_EQ(track, dtmf_->track()); 185 EXPECT_EQ(tones, dtmf_->tones()); 186 EXPECT_EQ(duration, dtmf_->duration()); 187 EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap()); 188 } 189 190 // Verify the provider got all the expected calls. 191 void VerifyOnProvider(const std::string& tones, int duration, 192 int inter_tone_gap) { 193 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref; 194 GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref); 195 VerifyOnProvider(dtmf_queue_ref); 196 } 197 198 void VerifyOnProvider( 199 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) { 200 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue = 201 provider_->dtmf_info_queue(); 202 ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size()); 203 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref = 204 dtmf_queue_ref.begin(); 205 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it = 206 dtmf_queue.begin(); 207 while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) { 208 EXPECT_EQ(it_ref->code, it->code); 209 EXPECT_EQ(it_ref->duration, it->duration); 210 // Allow ~100ms error. 211 EXPECT_GE(it_ref->gap, it->gap - 100); 212 EXPECT_LE(it_ref->gap, it->gap + 100); 213 ++it_ref; 214 ++it; 215 } 216 } 217 218 // Verify the observer got all the expected callbacks. 219 void VerifyOnObserver(const std::string& tones_ref) { 220 const std::vector<std::string>& tones = observer_->tones(); 221 // The observer will get an empty string at the end. 222 EXPECT_EQ(tones_ref.size() + 1, tones.size()); 223 EXPECT_TRUE(tones.back().empty()); 224 std::string::const_iterator it_ref = tones_ref.begin(); 225 std::vector<std::string>::const_iterator it = tones.begin(); 226 while (it_ref != tones_ref.end() && it != tones.end()) { 227 EXPECT_EQ(*it_ref, it->at(0)); 228 ++it_ref; 229 ++it; 230 } 231 } 232 233 rtc::scoped_refptr<AudioTrackInterface> track_; 234 rtc::scoped_ptr<FakeDtmfObserver> observer_; 235 rtc::scoped_ptr<FakeDtmfProvider> provider_; 236 rtc::scoped_refptr<DtmfSender> dtmf_; 237 }; 238 239 TEST_F(DtmfSenderTest, CanInsertDtmf) { 240 EXPECT_TRUE(dtmf_->CanInsertDtmf()); 241 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel); 242 EXPECT_FALSE(dtmf_->CanInsertDtmf()); 243 } 244 245 TEST_F(DtmfSenderTest, InsertDtmf) { 246 std::string tones = "@1%a&*$"; 247 int duration = 100; 248 int inter_tone_gap = 50; 249 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); 250 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs); 251 252 // The unrecognized characters should be ignored. 253 std::string known_tones = "1a*"; 254 VerifyOnProvider(known_tones, duration, inter_tone_gap); 255 VerifyOnObserver(known_tones); 256 } 257 258 TEST_F(DtmfSenderTest, InsertDtmfTwice) { 259 std::string tones1 = "12"; 260 std::string tones2 = "ab"; 261 int duration = 100; 262 int inter_tone_gap = 50; 263 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap)); 264 VerifyExpectedState(track_, tones1, duration, inter_tone_gap); 265 // Wait until the first tone got sent. 266 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs); 267 VerifyExpectedState(track_, "2", duration, inter_tone_gap); 268 // Insert with another tone buffer. 269 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap)); 270 VerifyExpectedState(track_, tones2, duration, inter_tone_gap); 271 // Wait until it's completed. 272 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs); 273 274 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref; 275 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref); 276 GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref); 277 VerifyOnProvider(dtmf_queue_ref); 278 VerifyOnObserver("1ab"); 279 } 280 281 TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) { 282 std::string tones = "@1%a&*$"; 283 int duration = 100; 284 int inter_tone_gap = 50; 285 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); 286 // Wait until the first tone got sent. 287 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs); 288 // Delete provider. 289 provider_.reset(); 290 // The queue should be discontinued so no more tone callbacks. 291 WAIT(false, 200); 292 EXPECT_EQ(1U, observer_->tones().size()); 293 } 294 295 TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) { 296 std::string tones = "@1%a&*$"; 297 int duration = 100; 298 int inter_tone_gap = 50; 299 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); 300 // Wait until the first tone got sent. 301 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs); 302 // Delete the sender. 303 dtmf_ = NULL; 304 // The queue should be discontinued so no more tone callbacks. 305 WAIT(false, 200); 306 EXPECT_EQ(1U, observer_->tones().size()); 307 } 308 309 TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) { 310 std::string tones1 = "12"; 311 std::string tones2 = ""; 312 int duration = 100; 313 int inter_tone_gap = 50; 314 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap)); 315 // Wait until the first tone got sent. 316 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs); 317 // Insert with another tone buffer. 318 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap)); 319 // Wait until it's completed. 320 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs); 321 322 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref; 323 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref); 324 VerifyOnProvider(dtmf_queue_ref); 325 VerifyOnObserver("1"); 326 } 327 328 // Flaky when run in parallel. 329 // See https://code.google.com/p/webrtc/issues/detail?id=4219. 330 TEST_F(DtmfSenderTest, DISABLED_InsertDtmfWithCommaAsDelay) { 331 std::string tones = "3,4"; 332 int duration = 100; 333 int inter_tone_gap = 50; 334 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); 335 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs); 336 337 VerifyOnProvider(tones, duration, inter_tone_gap); 338 VerifyOnObserver(tones); 339 } 340 341 TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) { 342 std::string tones = "3,4"; 343 int duration = 100; 344 int inter_tone_gap = 50; 345 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel); 346 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); 347 } 348 349 TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) { 350 std::string tones = "3,4"; 351 int duration = 100; 352 int inter_tone_gap = 50; 353 354 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap)); 355 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 69, inter_tone_gap)); 356 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 49)); 357 358 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); 359 } 360