xref: /aosp_15_r20/external/webrtc/pc/dtmf_sender_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 "pc/dtmf_sender.h"
12 
13 #include <stddef.h>
14 
15 #include <memory>
16 #include <string>
17 #include <vector>
18 
19 #include "rtc_base/fake_clock.h"
20 #include "rtc_base/gunit.h"
21 #include "rtc_base/time_utils.h"
22 #include "test/gtest.h"
23 
24 using webrtc::DtmfProviderInterface;
25 using webrtc::DtmfSender;
26 using webrtc::DtmfSenderObserverInterface;
27 
28 // TODO(deadbeef): Even though this test now uses a fake clock, it has a
29 // generous 3-second timeout for every test case. The timeout could be tuned
30 // to each test based on the tones sent, instead.
31 static const int kMaxWaitMs = 3000;
32 
33 class FakeDtmfObserver : public DtmfSenderObserverInterface {
34  public:
FakeDtmfObserver()35   FakeDtmfObserver() : completed_(false) {}
36 
37   // Implements DtmfSenderObserverInterface.
OnToneChange(const std::string & tone)38   void OnToneChange(const std::string& tone) override {
39     tones_from_single_argument_callback_.push_back(tone);
40     if (tone.empty()) {
41       completed_ = true;
42     }
43   }
OnToneChange(const std::string & tone,const std::string & tone_buffer)44   void OnToneChange(const std::string& tone,
45                     const std::string& tone_buffer) override {
46     tones_.push_back(tone);
47     tones_remaining_ = tone_buffer;
48     if (tone.empty()) {
49       completed_ = true;
50     }
51   }
52 
53   // getters
tones() const54   const std::vector<std::string>& tones() const { return tones_; }
tones_from_single_argument_callback() const55   const std::vector<std::string>& tones_from_single_argument_callback() const {
56     return tones_from_single_argument_callback_;
57   }
tones_remaining()58   const std::string tones_remaining() { return tones_remaining_; }
completed() const59   bool completed() const { return completed_; }
60 
61  private:
62   std::vector<std::string> tones_;
63   std::vector<std::string> tones_from_single_argument_callback_;
64   std::string tones_remaining_;
65   bool completed_;
66 };
67 
68 class FakeDtmfProvider : public DtmfProviderInterface {
69  public:
70   struct DtmfInfo {
DtmfInfoFakeDtmfProvider::DtmfInfo71     DtmfInfo(int code, int duration, int gap)
72         : code(code), duration(duration), gap(gap) {}
73     int code;
74     int duration;
75     int gap;
76   };
77 
FakeDtmfProvider()78   FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
79 
80   // Implements DtmfProviderInterface.
CanInsertDtmf()81   bool CanInsertDtmf() override { return can_insert_; }
82 
InsertDtmf(int code,int duration)83   bool InsertDtmf(int code, int duration) override {
84     int gap = 0;
85     // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
86     // mockable and use a fake timer in the unit tests.
87     if (last_insert_dtmf_call_ > 0) {
88       gap = static_cast<int>(rtc::TimeMillis() - last_insert_dtmf_call_);
89     }
90     last_insert_dtmf_call_ = rtc::TimeMillis();
91 
92     dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
93     return true;
94   }
95 
96   // getter and setter
dtmf_info_queue() const97   const std::vector<DtmfInfo>& dtmf_info_queue() const {
98     return dtmf_info_queue_;
99   }
100 
101   // helper functions
SetCanInsertDtmf(bool can_insert)102   void SetCanInsertDtmf(bool can_insert) { can_insert_ = can_insert; }
103 
104  private:
105   bool can_insert_ = false;
106   std::vector<DtmfInfo> dtmf_info_queue_;
107   int64_t last_insert_dtmf_call_;
108 };
109 
110 class DtmfSenderTest : public ::testing::Test {
111  protected:
DtmfSenderTest()112   DtmfSenderTest()
113       : observer_(new FakeDtmfObserver()), provider_(new FakeDtmfProvider()) {
114     provider_->SetCanInsertDtmf(true);
115     dtmf_ = DtmfSender::Create(rtc::Thread::Current(), provider_.get());
116     dtmf_->RegisterObserver(observer_.get());
117   }
118 
~DtmfSenderTest()119   ~DtmfSenderTest() {
120     if (dtmf_.get()) {
121       dtmf_->UnregisterObserver();
122     }
123   }
124 
125   // Constructs a list of DtmfInfo from `tones`, `duration` and
126   // `inter_tone_gap`.
GetDtmfInfoFromString(const std::string & tones,int duration,int inter_tone_gap,std::vector<FakeDtmfProvider::DtmfInfo> * dtmfs,int comma_delay=webrtc::DtmfSender::kDtmfDefaultCommaDelayMs)127   void GetDtmfInfoFromString(
128       const std::string& tones,
129       int duration,
130       int inter_tone_gap,
131       std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs,
132       int comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs) {
133     // Init extra_delay as -inter_tone_gap - duration to ensure the first
134     // DtmfInfo's gap field will be 0.
135     int extra_delay = -1 * (inter_tone_gap + duration);
136 
137     std::string::const_iterator it = tones.begin();
138     for (; it != tones.end(); ++it) {
139       char tone = *it;
140       int code = 0;
141       webrtc::GetDtmfCode(tone, &code);
142       if (tone == ',') {
143         extra_delay = comma_delay;
144       } else {
145         dtmfs->push_back(FakeDtmfProvider::DtmfInfo(
146             code, duration, duration + inter_tone_gap + extra_delay));
147         extra_delay = 0;
148       }
149     }
150   }
151 
VerifyExpectedState(const std::string & tones,int duration,int inter_tone_gap)152   void VerifyExpectedState(const std::string& tones,
153                            int duration,
154                            int inter_tone_gap) {
155     EXPECT_EQ(tones, dtmf_->tones());
156     EXPECT_EQ(duration, dtmf_->duration());
157     EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
158   }
159 
160   // Verify the provider got all the expected calls.
VerifyOnProvider(const std::string & tones,int duration,int inter_tone_gap,int comma_delay=webrtc::DtmfSender::kDtmfDefaultCommaDelayMs)161   void VerifyOnProvider(
162       const std::string& tones,
163       int duration,
164       int inter_tone_gap,
165       int comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs) {
166     std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
167     GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref,
168                           comma_delay);
169     VerifyOnProvider(dtmf_queue_ref);
170   }
171 
VerifyOnProvider(const std::vector<FakeDtmfProvider::DtmfInfo> & dtmf_queue_ref)172   void VerifyOnProvider(
173       const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
174     const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
175         provider_->dtmf_info_queue();
176     ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
177     std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
178         dtmf_queue_ref.begin();
179     std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
180         dtmf_queue.begin();
181     while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
182       EXPECT_EQ(it_ref->code, it->code);
183       EXPECT_EQ(it_ref->duration, it->duration);
184       // Allow ~10ms error (can be small since we're using a fake clock).
185       EXPECT_GE(it_ref->gap, it->gap - 10);
186       EXPECT_LE(it_ref->gap, it->gap + 10);
187       ++it_ref;
188       ++it;
189     }
190   }
191 
192   // Verify the observer got all the expected callbacks.
VerifyOnObserver(const std::string & tones_ref)193   void VerifyOnObserver(const std::string& tones_ref) {
194     const std::vector<std::string>& tones = observer_->tones();
195     // The observer will get an empty string at the end.
196     EXPECT_EQ(tones_ref.size() + 1, tones.size());
197     EXPECT_EQ(observer_->tones(),
198               observer_->tones_from_single_argument_callback());
199     EXPECT_TRUE(tones.back().empty());
200     EXPECT_TRUE(observer_->tones_remaining().empty());
201     std::string::const_iterator it_ref = tones_ref.begin();
202     std::vector<std::string>::const_iterator it = tones.begin();
203     while (it_ref != tones_ref.end() && it != tones.end()) {
204       EXPECT_EQ(*it_ref, it->at(0));
205       ++it_ref;
206       ++it;
207     }
208   }
209 
210   rtc::AutoThread main_thread_;
211   std::unique_ptr<FakeDtmfObserver> observer_;
212   std::unique_ptr<FakeDtmfProvider> provider_;
213   rtc::scoped_refptr<DtmfSender> dtmf_;
214   rtc::ScopedFakeClock fake_clock_;
215 };
216 
TEST_F(DtmfSenderTest,CanInsertDtmf)217 TEST_F(DtmfSenderTest, CanInsertDtmf) {
218   EXPECT_TRUE(dtmf_->CanInsertDtmf());
219   provider_->SetCanInsertDtmf(false);
220   EXPECT_FALSE(dtmf_->CanInsertDtmf());
221 }
222 
TEST_F(DtmfSenderTest,InsertDtmf)223 TEST_F(DtmfSenderTest, InsertDtmf) {
224   std::string tones = "@1%a&*$";
225   int duration = 100;
226   int inter_tone_gap = 50;
227   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
228   EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
229 
230   // The unrecognized characters should be ignored.
231   std::string known_tones = "1a*";
232   VerifyOnProvider(known_tones, duration, inter_tone_gap);
233   VerifyOnObserver(known_tones);
234 }
235 
TEST_F(DtmfSenderTest,InsertDtmfTwice)236 TEST_F(DtmfSenderTest, InsertDtmfTwice) {
237   std::string tones1 = "12";
238   std::string tones2 = "ab";
239   int duration = 100;
240   int inter_tone_gap = 50;
241   EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
242   VerifyExpectedState(tones1, duration, inter_tone_gap);
243   // Wait until the first tone got sent.
244   EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
245                              fake_clock_);
246   VerifyExpectedState("2", duration, inter_tone_gap);
247   // Insert with another tone buffer.
248   EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
249   VerifyExpectedState(tones2, duration, inter_tone_gap);
250   // Wait until it's completed.
251   EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
252 
253   std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
254   GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
255   GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
256   VerifyOnProvider(dtmf_queue_ref);
257   VerifyOnObserver("1ab");
258 }
259 
TEST_F(DtmfSenderTest,InsertDtmfWhileProviderIsDeleted)260 TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
261   std::string tones = "@1%a&*$";
262   int duration = 100;
263   int inter_tone_gap = 50;
264   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
265   // Wait until the first tone got sent.
266   EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
267                              fake_clock_);
268   // Delete provider.
269   dtmf_->OnDtmfProviderDestroyed();
270   provider_.reset();
271   // The queue should be discontinued so no more tone callbacks.
272   SIMULATED_WAIT(false, 200, fake_clock_);
273   EXPECT_EQ(1U, observer_->tones().size());
274 }
275 
TEST_F(DtmfSenderTest,InsertDtmfWhileSenderIsDeleted)276 TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
277   std::string tones = "@1%a&*$";
278   int duration = 100;
279   int inter_tone_gap = 50;
280   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
281   // Wait until the first tone got sent.
282   EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
283                              fake_clock_);
284   // Delete the sender.
285   dtmf_ = NULL;
286   // The queue should be discontinued so no more tone callbacks.
287   SIMULATED_WAIT(false, 200, fake_clock_);
288   EXPECT_EQ(1U, observer_->tones().size());
289 }
290 
TEST_F(DtmfSenderTest,InsertEmptyTonesToCancelPreviousTask)291 TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
292   std::string tones1 = "12";
293   std::string tones2 = "";
294   int duration = 100;
295   int inter_tone_gap = 50;
296   EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
297   // Wait until the first tone got sent.
298   EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
299                              fake_clock_);
300   // Insert with another tone buffer.
301   EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
302   // Wait until it's completed.
303   EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
304 
305   std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
306   GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
307   VerifyOnProvider(dtmf_queue_ref);
308   VerifyOnObserver("1");
309 }
310 
TEST_F(DtmfSenderTest,InsertDtmfWithDefaultCommaDelay)311 TEST_F(DtmfSenderTest, InsertDtmfWithDefaultCommaDelay) {
312   std::string tones = "3,4";
313   int duration = 100;
314   int inter_tone_gap = 50;
315   int default_comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs;
316   EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay);
317   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
318   EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
319 
320   VerifyOnProvider(tones, duration, inter_tone_gap);
321   VerifyOnObserver(tones);
322   EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay);
323 }
324 
TEST_F(DtmfSenderTest,InsertDtmfWithNonDefaultCommaDelay)325 TEST_F(DtmfSenderTest, InsertDtmfWithNonDefaultCommaDelay) {
326   std::string tones = "3,4";
327   int duration = 100;
328   int inter_tone_gap = 50;
329   int default_comma_delay = webrtc::DtmfSender::kDtmfDefaultCommaDelayMs;
330   int comma_delay = 500;
331   EXPECT_EQ(dtmf_->comma_delay(), default_comma_delay);
332   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap, comma_delay));
333   EXPECT_TRUE_SIMULATED_WAIT(observer_->completed(), kMaxWaitMs, fake_clock_);
334 
335   VerifyOnProvider(tones, duration, inter_tone_gap, comma_delay);
336   VerifyOnObserver(tones);
337   EXPECT_EQ(dtmf_->comma_delay(), comma_delay);
338 }
339 
TEST_F(DtmfSenderTest,TryInsertDtmfWhenItDoesNotWork)340 TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
341   std::string tones = "3,4";
342   int duration = 100;
343   int inter_tone_gap = 50;
344   provider_->SetCanInsertDtmf(false);
345   EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
346 }
347 
TEST_F(DtmfSenderTest,InsertDtmfWithInvalidDurationOrGap)348 TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
349   std::string tones = "3,4";
350   int duration = 40;
351   int inter_tone_gap = 50;
352 
353   EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
354   EXPECT_FALSE(dtmf_->InsertDtmf(tones, 39, inter_tone_gap));
355   EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 29));
356   EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap, 29));
357 
358   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
359 }
360 
TEST_F(DtmfSenderTest,InsertDtmfSendsAfterWait)361 TEST_F(DtmfSenderTest, InsertDtmfSendsAfterWait) {
362   std::string tones = "ABC";
363   int duration = 100;
364   int inter_tone_gap = 50;
365   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
366   VerifyExpectedState("ABC", duration, inter_tone_gap);
367   // Wait until the first tone got sent.
368   EXPECT_TRUE_SIMULATED_WAIT(observer_->tones().size() == 1, kMaxWaitMs,
369                              fake_clock_);
370   VerifyExpectedState("BC", duration, inter_tone_gap);
371 }
372