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 }