tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestPacer.cpp (8204B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include <thread>
      8 
      9 #include "Pacer.h"
     10 #include "VideoUtils.h"
     11 #include "gmock/gmock-matchers.h"
     12 #include "gtest/gtest.h"
     13 #include "mozilla/gtest/WaitFor.h"
     14 
     15 using namespace mozilla;
     16 
     17 template <typename T>
     18 class PacerTest {
     19 protected:
     20  explicit PacerTest(TimeDuration aDuplicationInterval)
     21      : mTaskQueue(TaskQueue::Create(
     22            GetMediaThreadPool(MediaThreadType::WEBRTC_WORKER), "PacerTest")),
     23        mPacer(
     24            MakeRefPtr<Pacer<T>>(do_AddRef(mTaskQueue), aDuplicationInterval)),
     25        mInterval(aDuplicationInterval) {}
     26 
     27  // Helper for calling `mPacer->Enqueue(...)`. Dispatches an event to the
     28  // current thread which will enqueue the event to make sure that any listeners
     29  // registered by a call to `WaitFor(...)` have been registered before events
     30  // start being processed on a background queue.
     31  void EnqueueSoon(T aItem, TimeStamp aTime) {
     32    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(NS_NewRunnableFunction(
     33        "PacerTest::EnqueueSoon",
     34        [pacer = mPacer, aItem = std::move(aItem), aTime] {
     35          pacer->Enqueue(std::move(aItem), aTime);
     36        })));
     37  }
     38 
     39  void TearDown() {
     40    mPacer->Shutdown()->Then(mTaskQueue, __func__,
     41                             [tq = mTaskQueue] { tq->BeginShutdown(); });
     42  }
     43 
     44  TimeDuration Interval() const { return mInterval; }
     45 
     46  void SetInterval(TimeDuration aInterval) {
     47    MOZ_ASSERT(NS_IsMainThread());
     48    mInterval = aInterval;
     49    mPacer->SetDuplicationInterval(aInterval);
     50  }
     51 
     52  const RefPtr<TaskQueue> mTaskQueue;
     53  const RefPtr<Pacer<T>> mPacer;
     54 
     55 private:
     56  TimeDuration mInterval;
     57 };
     58 
     59 class PacerTestInt : public PacerTest<int>, public ::testing::Test {
     60 protected:
     61  explicit PacerTestInt(TimeDuration aDuplicationInterval)
     62      : PacerTest<int>(aDuplicationInterval) {}
     63 
     64  void TearDown() override { PacerTest::TearDown(); }
     65 };
     66 
     67 class PacerTestIntLongDuplication : public PacerTestInt {
     68 protected:
     69  PacerTestIntLongDuplication() : PacerTestInt(TimeDuration::FromSeconds(10)) {}
     70 };
     71 
     72 class PacerTestIntTenMsDuplication : public PacerTestInt {
     73 protected:
     74  PacerTestIntTenMsDuplication()
     75      : PacerTestInt(TimeDuration::FromMilliseconds(10)) {}
     76 };
     77 
     78 class PacerTestIntInfDuplication : public PacerTestInt {
     79 protected:
     80  PacerTestIntInfDuplication() : PacerTestInt(TimeDuration::Forever()) {}
     81 };
     82 
     83 MATCHER_P(IsDurationPositiveMultipleOf, aDenom,
     84          std::string(nsPrintfCString("%s a positive non-zero multiple of %s",
     85                                      negation ? "isn't" : "is",
     86                                      testing::PrintToString(aDenom).data())
     87                          .get())) {
     88  static_assert(std::is_same_v<std::decay_t<decltype(arg)>, TimeDuration>);
     89  static_assert(std::is_same_v<std::decay_t<decltype(aDenom)>, TimeDuration>);
     90  const double multiples = arg / aDenom;
     91  const TimeDuration remainder = arg % aDenom;
     92  return multiples > 0 && remainder.IsZero();
     93 }
     94 
     95 TEST_F(PacerTestIntLongDuplication, Single) {
     96  auto now = TimeStamp::Now();
     97  auto d1 = TimeDuration::FromMilliseconds(100);
     98  EnqueueSoon(1, now + d1);
     99 
    100  auto [i, time] = WaitFor(mPacer->PacedItemEvent());
    101  EXPECT_GE(TimeStamp::Now() - now, d1);
    102  EXPECT_EQ(i, 1);
    103  EXPECT_EQ(time - now, d1);
    104 }
    105 
    106 TEST_F(PacerTestIntLongDuplication, Past) {
    107  auto now = TimeStamp::Now();
    108  auto d1 = TimeDuration::FromMilliseconds(100);
    109  EnqueueSoon(1, now - d1);
    110 
    111  auto [i, time] = WaitFor(mPacer->PacedItemEvent());
    112  EXPECT_GE(TimeStamp::Now() - now, -d1);
    113  EXPECT_EQ(i, 1);
    114  EXPECT_EQ(time - now, -d1);
    115 }
    116 
    117 TEST_F(PacerTestIntLongDuplication, TimeReset) {
    118  auto now = TimeStamp::Now();
    119  auto d1 = TimeDuration::FromMilliseconds(100);
    120  auto d2 = TimeDuration::FromMilliseconds(200);
    121  auto d3 = TimeDuration::FromMilliseconds(300);
    122  EnqueueSoon(1, now + d1);
    123  EnqueueSoon(2, now + d3);
    124  EnqueueSoon(3, now + d2);
    125 
    126  auto items = WaitFor(TakeN(mPacer->PacedItemEvent(), 2)).unwrap();
    127 
    128  {
    129    auto [i, time] = items[0];
    130    EXPECT_GE(TimeStamp::Now() - now, d1);
    131    EXPECT_EQ(i, 1);
    132    EXPECT_EQ(time - now, d1);
    133  }
    134  {
    135    auto [i, time] = items[1];
    136    EXPECT_GE(TimeStamp::Now() - now, d2);
    137    EXPECT_EQ(i, 3);
    138    EXPECT_EQ(time - now, d2);
    139  }
    140 }
    141 
    142 TEST_F(PacerTestIntTenMsDuplication, SingleDuplication) {
    143  auto now = TimeStamp::Now();
    144  auto d1 = TimeDuration::FromMilliseconds(100);
    145  EnqueueSoon(1, now + d1);
    146 
    147  auto items = WaitFor(TakeN(mPacer->PacedItemEvent(), 2)).unwrap();
    148 
    149  {
    150    auto [i, time] = items[0];
    151    EXPECT_GE(TimeStamp::Now() - now, d1);
    152    EXPECT_EQ(i, 1);
    153    EXPECT_EQ(time - now, d1);
    154  }
    155  {
    156    auto [i, time] = items[1];
    157    EXPECT_GE(TimeStamp::Now() - now, d1 + Interval());
    158    EXPECT_EQ(i, 1);
    159    EXPECT_EQ(time - now, d1 + Interval());
    160  }
    161 }
    162 
    163 TEST_F(PacerTestIntTenMsDuplication, RacyDuplication1) {
    164  auto now = TimeStamp::Now();
    165  auto d1 = TimeDuration::FromMilliseconds(100);
    166  auto d2 = d1 + Interval() - TimeDuration::FromMicroseconds(1);
    167  EnqueueSoon(1, now + d1);
    168  EnqueueSoon(2, now + d2);
    169 
    170  auto items = WaitFor(TakeN(mPacer->PacedItemEvent(), 3)).unwrap();
    171 
    172  {
    173    auto [i, time] = items[0];
    174    EXPECT_GE(TimeStamp::Now() - now, d1);
    175    EXPECT_EQ(i, 1);
    176    EXPECT_EQ(time - now, d1);
    177  }
    178  {
    179    auto [i, time] = items[1];
    180    EXPECT_GE(TimeStamp::Now() - now, d2);
    181    EXPECT_EQ(i, 2);
    182    EXPECT_EQ(time - now, d2);
    183  }
    184  {
    185    auto [i, time] = items[2];
    186    EXPECT_GE(TimeStamp::Now() - now, d2 + Interval());
    187    EXPECT_EQ(i, 2);
    188    EXPECT_EQ(time - now, d2 + Interval());
    189  }
    190 }
    191 
    192 TEST_F(PacerTestIntTenMsDuplication, RacyDuplication2) {
    193  auto now = TimeStamp::Now();
    194  auto d1 = TimeDuration::FromMilliseconds(100);
    195  auto d2 = d1 + Interval() + TimeDuration::FromMicroseconds(1);
    196  EnqueueSoon(1, now + d1);
    197  EnqueueSoon(2, now + d2);
    198 
    199  auto items = WaitFor(TakeN(mPacer->PacedItemEvent(), 3)).unwrap();
    200 
    201  {
    202    auto [i, time] = items[0];
    203    EXPECT_GE(TimeStamp::Now() - now, d1);
    204    EXPECT_EQ(i, 1);
    205    EXPECT_EQ(time - now, d1);
    206  }
    207  {
    208    auto [i, time] = items[1];
    209    EXPECT_GE(TimeStamp::Now() - now, d1 + Interval());
    210    EXPECT_EQ(i, 1);
    211    EXPECT_EQ(time - now, d1 + Interval());
    212  }
    213  {
    214    auto [i, time] = items[2];
    215    EXPECT_GE(TimeStamp::Now() - now, d2);
    216    EXPECT_EQ(i, 2);
    217    EXPECT_EQ(time - now, d2);
    218  }
    219 }
    220 
    221 TEST_F(PacerTestIntInfDuplication, SetDuplicationInterval) {
    222  const auto now = TimeStamp::Now();
    223  const auto t1 = now;
    224  const auto noDuplication = TimeDuration::FromMilliseconds(250);
    225  const auto d1 = TimeDuration::FromMilliseconds(33);
    226 
    227  EnqueueSoon(1, t1);
    228  const auto first = WaitFor(mPacer->PacedItemEvent());
    229  const auto twoDupes = TakeN(mPacer->PacedItemEvent(), 2);
    230  while (TimeStamp::Now() < now + noDuplication) {
    231    if (!NS_ProcessNextEvent(nullptr, /* aMayWait = */ false)) {
    232      std::this_thread::sleep_for(std::chrono::milliseconds(1));
    233    }
    234  }
    235  SetInterval(d1);
    236 
    237  auto items = WaitFor(twoDupes).unwrap();
    238  const auto t2 =
    239      std::get<TimeStamp>(items.back()) + TimeDuration::FromMilliseconds(5);
    240  const auto d2 = TimeDuration::FromMilliseconds(50);
    241  EnqueueSoon(2, t2);
    242  SetInterval(d2);
    243  WaitUntil(mPacer->PacedItemEvent(), [&items](int aItem, TimeStamp aTime) {
    244    if (aItem == 2) {
    245      items.push_back({aItem, aTime});
    246      return true;
    247    }
    248    return false;
    249  });
    250  const auto last = WaitFor(mPacer->PacedItemEvent());
    251 
    252  items.insert(items.begin(), first);
    253  items.push_back(last);
    254  ASSERT_EQ(items.size(), 5U);
    255 
    256  auto [i1, time1] = items[0];
    257  EXPECT_EQ(i1, 1);
    258  EXPECT_EQ(time1 - now, t1 - now);
    259 
    260  auto [i2, time2] = items[1];
    261  EXPECT_EQ(i2, 1);
    262  EXPECT_GE(time2 - now, noDuplication);
    263 
    264  auto [i3, time3] = items[2];
    265  EXPECT_EQ(i3, 1);
    266  EXPECT_THAT(time3 - time2, IsDurationPositiveMultipleOf(d1));
    267 
    268  auto [i4, time4] = items[3];
    269  EXPECT_EQ(i4, 2);
    270  EXPECT_EQ(time4 - now, t2 - now);
    271 
    272  auto [i5, time5] = items[4];
    273  EXPECT_EQ(i5, 2);
    274  EXPECT_THAT(time5 - time4, IsDurationPositiveMultipleOf(d2));
    275 }