1 /*
2 * Copyright 2019 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 "test/time_controller/external_time_controller.h"
12
13 #include <atomic>
14 #include <memory>
15 #include <utility>
16
17 #include "rtc_base/event.h"
18 #include "rtc_base/task_queue.h"
19 #include "rtc_base/task_utils/repeating_task.h"
20 #include "test/gmock.h"
21 #include "test/gtest.h"
22
23 // NOTE: Since these tests rely on real time behavior, they will be flaky
24 // if run on heavily loaded systems.
25 namespace webrtc {
26 namespace {
27 using ::testing::AtLeast;
28 using ::testing::Invoke;
29 using ::testing::MockFunction;
30 using ::testing::NiceMock;
31 using ::testing::Return;
32 constexpr Timestamp kStartTime = Timestamp::Seconds(1000);
33
34 class FakeAlarm : public ControlledAlarmClock {
35 public:
36 explicit FakeAlarm(Timestamp start_time);
37
38 Clock* GetClock() override;
39 bool ScheduleAlarmAt(Timestamp deadline) override;
40 void SetCallback(std::function<void()> callback) override;
41 void Sleep(TimeDelta duration) override;
42
43 private:
44 SimulatedClock clock_;
45 Timestamp deadline_;
46 std::function<void()> callback_;
47 };
48
FakeAlarm(Timestamp start_time)49 FakeAlarm::FakeAlarm(Timestamp start_time)
50 : clock_(start_time),
51 deadline_(Timestamp::PlusInfinity()),
52 callback_([] {}) {}
53
GetClock()54 Clock* FakeAlarm::GetClock() {
55 return &clock_;
56 }
57
ScheduleAlarmAt(Timestamp deadline)58 bool FakeAlarm::ScheduleAlarmAt(Timestamp deadline) {
59 if (deadline < deadline_) {
60 deadline_ = deadline;
61 return true;
62 }
63 return false;
64 }
65
SetCallback(std::function<void ()> callback)66 void FakeAlarm::SetCallback(std::function<void()> callback) {
67 callback_ = callback;
68 }
69
Sleep(TimeDelta duration)70 void FakeAlarm::Sleep(TimeDelta duration) {
71 Timestamp end_time = clock_.CurrentTime() + duration;
72
73 while (deadline_ <= end_time) {
74 clock_.AdvanceTime(deadline_ - clock_.CurrentTime());
75 deadline_ = Timestamp::PlusInfinity();
76 callback_();
77 }
78
79 clock_.AdvanceTime(end_time - clock_.CurrentTime());
80 }
81
82 } // namespace
83
TEST(ExternalTimeControllerTest,TaskIsStoppedOnStop)84 TEST(ExternalTimeControllerTest, TaskIsStoppedOnStop) {
85 const TimeDelta kShortInterval = TimeDelta::Millis(5);
86 const TimeDelta kLongInterval = TimeDelta::Millis(20);
87 const int kShortIntervalCount = 4;
88 const int kMargin = 1;
89 FakeAlarm alarm(kStartTime);
90 ExternalTimeController time_simulation(&alarm);
91 rtc::TaskQueue task_queue(
92 time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
93 "TestQueue", TaskQueueFactory::Priority::NORMAL));
94 std::atomic_int counter(0);
95 auto handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
96 if (++counter >= kShortIntervalCount)
97 return kLongInterval;
98 return kShortInterval;
99 });
100 // Sleep long enough to go through the initial phase.
101 time_simulation.AdvanceTime(kShortInterval * (kShortIntervalCount + kMargin));
102 EXPECT_EQ(counter.load(), kShortIntervalCount);
103
104 task_queue.PostTask(
105 [handle = std::move(handle)]() mutable { handle.Stop(); });
106
107 // Sleep long enough that the task would run at least once more if not
108 // stopped.
109 time_simulation.AdvanceTime(kLongInterval * 2);
110 EXPECT_EQ(counter.load(), kShortIntervalCount);
111 }
112
TEST(ExternalTimeControllerTest,TaskCanStopItself)113 TEST(ExternalTimeControllerTest, TaskCanStopItself) {
114 std::atomic_int counter(0);
115 FakeAlarm alarm(kStartTime);
116 ExternalTimeController time_simulation(&alarm);
117 rtc::TaskQueue task_queue(
118 time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
119 "TestQueue", TaskQueueFactory::Priority::NORMAL));
120
121 RepeatingTaskHandle handle;
122 task_queue.PostTask([&] {
123 handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
124 ++counter;
125 handle.Stop();
126 return TimeDelta::Millis(2);
127 });
128 });
129 time_simulation.AdvanceTime(TimeDelta::Millis(10));
130 EXPECT_EQ(counter.load(), 1);
131 }
132
TEST(ExternalTimeControllerTest,YieldForTask)133 TEST(ExternalTimeControllerTest, YieldForTask) {
134 FakeAlarm alarm(kStartTime);
135 ExternalTimeController time_simulation(&alarm);
136
137 rtc::TaskQueue task_queue(
138 time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
139 "TestQueue", TaskQueueFactory::Priority::NORMAL));
140
141 rtc::Event event;
142 task_queue.PostTask([&] { event.Set(); });
143 EXPECT_TRUE(event.Wait(TimeDelta::Millis(200)));
144 }
145
TEST(ExternalTimeControllerTest,TasksYieldToEachOther)146 TEST(ExternalTimeControllerTest, TasksYieldToEachOther) {
147 FakeAlarm alarm(kStartTime);
148 ExternalTimeController time_simulation(&alarm);
149
150 rtc::TaskQueue task_queue(
151 time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
152 "TestQueue", TaskQueueFactory::Priority::NORMAL));
153 rtc::TaskQueue other_queue(
154 time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
155 "OtherQueue", TaskQueueFactory::Priority::NORMAL));
156
157 task_queue.PostTask([&] {
158 rtc::Event event;
159 other_queue.PostTask([&] { event.Set(); });
160 EXPECT_TRUE(event.Wait(TimeDelta::Millis(200)));
161 });
162
163 time_simulation.AdvanceTime(TimeDelta::Millis(300));
164 }
165
TEST(ExternalTimeControllerTest,CurrentTaskQueue)166 TEST(ExternalTimeControllerTest, CurrentTaskQueue) {
167 FakeAlarm alarm(kStartTime);
168 ExternalTimeController time_simulation(&alarm);
169
170 rtc::TaskQueue task_queue(
171 time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
172 "TestQueue", TaskQueueFactory::Priority::NORMAL));
173
174 task_queue.PostTask([&] { EXPECT_TRUE(task_queue.IsCurrent()); });
175
176 time_simulation.AdvanceTime(TimeDelta::Millis(10));
177 }
178
179 } // namespace webrtc
180