From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- third_party/libwebrtc/pc/dtmf_sender_unittest.cc | 371 +++++++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 third_party/libwebrtc/pc/dtmf_sender_unittest.cc (limited to 'third_party/libwebrtc/pc/dtmf_sender_unittest.cc') diff --git a/third_party/libwebrtc/pc/dtmf_sender_unittest.cc b/third_party/libwebrtc/pc/dtmf_sender_unittest.cc new file mode 100644 index 0000000000..ab5ba9eaee --- /dev/null +++ b/third_party/libwebrtc/pc/dtmf_sender_unittest.cc @@ -0,0 +1,371 @@ +/* + * Copyright 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "pc/dtmf_sender.h" + +#include + +#include +#include +#include + +#include "rtc_base/fake_clock.h" +#include "rtc_base/gunit.h" +#include "rtc_base/time_utils.h" +#include "test/gtest.h" + +using webrtc::DtmfProviderInterface; +using webrtc::DtmfSender; +using webrtc::DtmfSenderObserverInterface; + +// TODO(deadbeef): Even though this test now uses a fake clock, it has a +// generous 3-second timeout for every test case. The timeout could be tuned +// to each test based on the tones sent, instead. +static const int kMaxWaitMs = 3000; + +class FakeDtmfObserver : public DtmfSenderObserverInterface { + public: + FakeDtmfObserver() : completed_(false) {} + + // Implements DtmfSenderObserverInterface. + void OnToneChange(const std::string& tone) override { + tones_from_single_argument_callback_.push_back(tone); + if (tone.empty()) { + completed_ = true; + } + } + void OnToneChange(const std::string& tone, + const std::string& tone_buffer) override { + tones_.push_back(tone); + tones_remaining_ = tone_buffer; + if (tone.empty()) { + completed_ = true; + } + } + + // getters + const std::vector& tones() const { return tones_; } + const std::vector& tones_from_single_argument_callback() const { + return tones_from_single_argument_callback_; + } + const std::string tones_remaining() { return tones_remaining_; } + bool completed() const { return completed_; } + + private: + std::vector tones_; + std::vector tones_from_single_argument_callback_; + std::string tones_remaining_; + bool completed_; +}; + +class FakeDtmfProvider : public DtmfProviderInterface { + public: + struct DtmfInfo { + DtmfInfo(int code, int duration, int gap) + : code(code), duration(duration), gap(gap) {} + int code; + int duration; + int gap; + }; + + FakeDtmfProvider() : last_insert_dtmf_call_(0) {} + + // Implements DtmfProviderInterface. + bool CanInsertDtmf() override { return can_insert_; } + + bool InsertDtmf(int code, int duration) override { + int gap = 0; + // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos) + // mockable and use a fake timer in the unit tests. + if (last_insert_dtmf_call_ > 0) { + gap = static_cast(rtc::TimeMillis() - last_insert_dtmf_call_); + } + last_insert_dtmf_call_ = rtc::TimeMillis(); + + dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap)); + return true; + } + + // getter and setter + const std::vector& dtmf_info_queue() const { + return dtmf_info_queue_; + } + + // helper functions + void SetCanInsertDtmf(bool can_insert) { can_insert_ = can_insert; } + + private: + bool can_insert_ = false; + std::vector dtmf_info_queue_; + int64_t last_insert_dtmf_call_; +}; + +class DtmfSenderTest : public ::testing::Test { + protected: + DtmfSenderTest() + : observer_(new FakeDtmfObserver()), provider_(new FakeDtmfProvider()) { + provider_->SetCanInsertDtmf(true); + dtmf_ = DtmfSender::Create(rtc::Thread::Current(), provider_.get()); + dtmf_->RegisterObserver(observer_.get()); + } + + ~DtmfSenderTest() { + if (dtmf_.get()) { + dtmf_->UnregisterObserver(); + } + } + + // Constructs a list of DtmfInfo from `tones`, `duration` and + // `inter_tone_gap`. + void GetDtmfInfoFromString( + const std::string& tones, + int duration, + int inter_tone_gap, + std::vector* dtmfs, + int comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs) { + // Init extra_delay as -inter_tone_gap - duration to ensure the first + // DtmfInfo's gap field will be 0. + int extra_delay = -1 * (inter_tone_gap + duration); + + std::string::const_iterator it = tones.begin(); + for (; it != tones.end(); ++it) { + char tone = *it; + int code = 0; + webrtc::GetDtmfCode(tone, &code); + if (tone == ',') { + extra_delay = comma_delay; + } else { + dtmfs->push_back(FakeDtmfProvider::DtmfInfo( + code, duration, duration + inter_tone_gap + extra_delay)); + extra_delay = 0; + } + } + } + + void VerifyExpectedState(const std::string& tones, + int duration, + int inter_tone_gap) { + EXPECT_EQ(tones, dtmf_->tones()); + EXPECT_EQ(duration, dtmf_->duration()); + EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap()); + } + + // Verify the provider got all the expected calls. + void VerifyOnProvider( + const std::string& tones, + int duration, + int inter_tone_gap, + int comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs) { + std::vector dtmf_queue_ref; + GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref, + comma_delay); + VerifyOnProvider(dtmf_queue_ref); + } + + void VerifyOnProvider( + const std::vector& dtmf_queue_ref) { + const std::vector& dtmf_queue = + provider_->dtmf_info_queue(); + ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size()); + std::vector::const_iterator it_ref = + dtmf_queue_ref.begin(); + std::vector::const_iterator it = + dtmf_queue.begin(); + while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) { + EXPECT_EQ(it_ref->code, it->code); + EXPECT_EQ(it_ref->duration, it->duration); + // Allow ~10ms error (can be small since we're using a fake clock). + EXPECT_GE(it_ref->gap, it->gap - 10); + EXPECT_LE(it_ref->gap, it->gap + 10); + ++it_ref; + ++it; + } + } + + // Verify the observer got all the expected callbacks. + void VerifyOnObserver(const std::string& tones_ref) { + const std::vector& tones = observer_->tones(); + // The observer will get an empty string at the end. + EXPECT_EQ(tones_ref.size() + 1, tones.size()); + EXPECT_EQ(observer_->tones(), + observer_->tones_from_single_argument_callback()); + EXPECT_TRUE(tones.back().empty()); + EXPECT_TRUE(observer_->tones_remaining().empty()); + std::string::const_iterator it_ref = tones_ref.begin(); + std::vector::const_iterator it = tones.begin(); + while (it_ref != tones_ref.end() && it != tones.end()) { + EXPECT_EQ(*it_ref, it->at(0)); + ++it_ref; + ++it; + } + } + + rtc::AutoThread main_thread_; + std::unique_ptr observer_; + std::unique_ptr provider_; + rtc::scoped_refptr dtmf_; + rtc::ScopedFakeClock fake_clock_; +}; + +TEST_F(DtmfSenderTest, CanInsertDtmf) { + EXPECT_TRUE(dtmf_->CanInsertDtmf()); + provider_->SetCanInsertDtmf(false); + EXPECT_FALSE(dtmf_->CanInsertDtmf()); +} + +TEST_F(DtmfSenderTest, InsertDtmf) { + std::string tones = "@1%a&*$"; + int duration = 100; + int inter_tone_gap = 50; + EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); + EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_); + + // The unrecognized characters should be ignored. + std::string known_tones = "1a*"; + VerifyOnProvider(known_tones, duration, inter_tone_gap); + VerifyOnObserver(known_tones); +} + +TEST_F(DtmfSenderTest, InsertDtmfTwice) { + std::string tones1 = "12"; + std::string tones2 = "ab"; + int duration = 100; + int inter_tone_gap = 50; + EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap)); + VerifyExpectedState(tones1, duration, inter_tone_gap); + // Wait until the first tone got sent. + EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs, + fake_clock_); + VerifyExpectedState("2", duration, inter_tone_gap); + // Insert with another tone buffer. + EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap)); + VerifyExpectedState(tones2, duration, inter_tone_gap); + // Wait until it's completed. + EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_); + + std::vector dtmf_queue_ref; + GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref); + GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref); + VerifyOnProvider(dtmf_queue_ref); + VerifyOnObserver("1ab"); +} + +TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) { + std::string tones = "@1%a&*$"; + int duration = 100; + int inter_tone_gap = 50; + EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); + // Wait until the first tone got sent. + EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs, + fake_clock_); + // Delete provider. + dtmf_->OnDtmfProviderDestroyed(); + provider_.reset(); + // The queue should be discontinued so no more tone callbacks. + SIMULATED_WAIT(false, 200, fake_clock_); + EXPECT_EQ(1U, observer_->tones().size()); +} + +TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) { + std::string tones = "@1%a&*$"; + int duration = 100; + int inter_tone_gap = 50; + EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); + // Wait until the first tone got sent. + EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs, + fake_clock_); + // Delete the sender. + dtmf_ = NULL; + // The queue should be discontinued so no more tone callbacks. + SIMULATED_WAIT(false, 200, fake_clock_); + EXPECT_EQ(1U, observer_->tones().size()); +} + +TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) { + std::string tones1 = "12"; + std::string tones2 = ""; + int duration = 100; + int inter_tone_gap = 50; + EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap)); + // Wait until the first tone got sent. + EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs, + fake_clock_); + // Insert with another tone buffer. + EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap)); + // Wait until it's completed. + EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_); + + std::vector dtmf_queue_ref; + GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref); + VerifyOnProvider(dtmf_queue_ref); + VerifyOnObserver("1"); +} + +TEST_F(DtmfSenderTest, InsertDtmfWithDefaultCommaDelay) { + std::string tones = "3,4"; + int duration = 100; + int inter_tone_gap = 50; + int default_comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs; + EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay); + EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); + EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_); + + VerifyOnProvider(tones, duration, inter_tone_gap); + VerifyOnObserver(tones); + EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay); +} + +TEST_F(DtmfSenderTest, InsertDtmfWithNonDefaultCommaDelay) { + std::string tones = "3,4"; + int duration = 100; + int inter_tone_gap = 50; + int default_comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs; + int comma_delay = 500; + EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay); + EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap, comma_delay)); + EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_); + + VerifyOnProvider(tones, duration, inter_tone_gap, comma_delay); + VerifyOnObserver(tones); + EXPECT_EQ(dtmf_->comma_delay(), comma_delay); +} + +TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) { + std::string tones = "3,4"; + int duration = 100; + int inter_tone_gap = 50; + provider_->SetCanInsertDtmf(false); + EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); +} + +TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) { + std::string tones = "3,4"; + int duration = 40; + int inter_tone_gap = 50; + + EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap)); + EXPECT_FALSE(dtmf_->InsertDtmf(tones, 39, inter_tone_gap)); + EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 29)); + EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap, 29)); + + EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); +} + +TEST_F(DtmfSenderTest, InsertDtmfSendsAfterWait) { + std::string tones = "ABC"; + int duration = 100; + int inter_tone_gap = 50; + EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap)); + VerifyExpectedState("ABC", duration, inter_tone_gap); + // Wait until the first tone got sent. + EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs, + fake_clock_); + VerifyExpectedState("BC", duration, inter_tone_gap); +} -- cgit v1.2.3