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 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 rtc::TimeNanos) 101 // mockable and use a fake timer in the unit tests. 102 if (last_insert_dtmf_call_ > 0) { 103 gap = static_cast<int>(rtc::Time() - last_insert_dtmf_call_); 104 } 105 last_insert_dtmf_call_ = rtc::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 rtc::RefCountedObject<FakeDtmfObserver>()), 143 provider_(new FakeDtmfProvider()) { 144 provider_->AddCanInsertDtmfTrack(kTestAudioLabel); 145 dtmf_ = DtmfSender::Create(track_, rtc::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); 209 // Allow ~100ms error. 210 EXPECT_GE(it_ref->gap, it->gap - 100); 211 EXPECT_LE(it_ref->gap, it->gap + 100); 212 ++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 rtc::scoped_refptr<AudioTrackInterface> track_; 233 rtc::scoped_ptr<FakeDtmfObserver> observer_; 234 rtc::scoped_ptr<FakeDtmfProvider> provider_; 235 rtc::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} 357