1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
/*
* Copyright 2019 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 "test/time_controller/simulated_time_controller.h"
#include <atomic>
#include <memory>
#include "api/task_queue/task_queue_base.h"
#include "api/units/time_delta.h"
#include "rtc_base/event.h"
#include "rtc_base/task_queue_for_test.h"
#include "rtc_base/task_utils/repeating_task.h"
#include "test/gmock.h"
#include "test/gtest.h"
// NOTE: Since these tests rely on real time behavior, they will be flaky
// if run on heavily loaded systems.
namespace webrtc {
namespace {
using ::testing::AtLeast;
using ::testing::Invoke;
using ::testing::MockFunction;
using ::testing::NiceMock;
using ::testing::Return;
constexpr Timestamp kStartTime = Timestamp::Seconds(1000);
} // namespace
TEST(SimulatedTimeControllerTest, TaskIsStoppedOnStop) {
const TimeDelta kShortInterval = TimeDelta::Millis(5);
const TimeDelta kLongInterval = TimeDelta::Millis(20);
const int kShortIntervalCount = 4;
const int kMargin = 1;
GlobalSimulatedTimeController time_simulation(kStartTime);
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> task_queue =
time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
"TestQueue", TaskQueueFactory::Priority::NORMAL);
std::atomic_int counter(0);
auto handle = RepeatingTaskHandle::Start(task_queue.get(), [&] {
if (++counter >= kShortIntervalCount)
return kLongInterval;
return kShortInterval;
});
// Sleep long enough to go through the initial phase.
time_simulation.AdvanceTime(kShortInterval * (kShortIntervalCount + kMargin));
EXPECT_EQ(counter.load(), kShortIntervalCount);
task_queue->PostTask(
[handle = std::move(handle)]() mutable { handle.Stop(); });
// Sleep long enough that the task would run at least once more if not
// stopped.
time_simulation.AdvanceTime(kLongInterval * 2);
EXPECT_EQ(counter.load(), kShortIntervalCount);
}
TEST(SimulatedTimeControllerTest, TaskCanStopItself) {
std::atomic_int counter(0);
GlobalSimulatedTimeController time_simulation(kStartTime);
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> task_queue =
time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
"TestQueue", TaskQueueFactory::Priority::NORMAL);
RepeatingTaskHandle handle;
task_queue->PostTask([&] {
handle = RepeatingTaskHandle::Start(task_queue.get(), [&] {
++counter;
handle.Stop();
return TimeDelta::Millis(2);
});
});
time_simulation.AdvanceTime(TimeDelta::Millis(10));
EXPECT_EQ(counter.load(), 1);
}
TEST(SimulatedTimeControllerTest, Example) {
class ObjectOnTaskQueue {
public:
void DoPeriodicTask() {}
TimeDelta TimeUntilNextRun() { return TimeDelta::Millis(100); }
void StartPeriodicTask(RepeatingTaskHandle* handle,
TaskQueueBase* task_queue) {
*handle = RepeatingTaskHandle::Start(task_queue, [this] {
DoPeriodicTask();
return TimeUntilNextRun();
});
}
};
GlobalSimulatedTimeController time_simulation(kStartTime);
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> task_queue =
time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
"TestQueue", TaskQueueFactory::Priority::NORMAL);
auto object = std::make_unique<ObjectOnTaskQueue>();
// Create and start the periodic task.
RepeatingTaskHandle handle;
object->StartPeriodicTask(&handle, task_queue.get());
// Restart the task
task_queue->PostTask(
[handle = std::move(handle)]() mutable { handle.Stop(); });
object->StartPeriodicTask(&handle, task_queue.get());
task_queue->PostTask(
[handle = std::move(handle)]() mutable { handle.Stop(); });
task_queue->PostTask([object = std::move(object)] {});
}
TEST(SimulatedTimeControllerTest, DelayTaskRunOnTime) {
GlobalSimulatedTimeController time_simulation(kStartTime);
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> task_queue =
time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
"TestQueue", TaskQueueFactory::Priority::NORMAL);
bool delay_task_executed = false;
task_queue->PostDelayedTask([&] { delay_task_executed = true; },
TimeDelta::Millis(10));
time_simulation.AdvanceTime(TimeDelta::Millis(10));
EXPECT_TRUE(delay_task_executed);
}
TEST(SimulatedTimeControllerTest, ThreadYeildsOnSynchronousCall) {
GlobalSimulatedTimeController sim(kStartTime);
auto main_thread = sim.GetMainThread();
auto t2 = sim.CreateThread("thread", nullptr);
bool task_has_run = false;
// Posting a task to the main thread, this should not run until AdvanceTime is
// called.
main_thread->PostTask([&] { task_has_run = true; });
SendTask(t2.get(), [] {
Event yield_event;
// Wait() triggers YieldExecution() which will runs message processing on
// all threads that are not in the yielded set.
yield_event.Wait(TimeDelta::Zero());
});
// Since we are doing an invoke from the main thread, we don't expect the main
// thread message loop to be processed.
EXPECT_FALSE(task_has_run);
sim.AdvanceTime(TimeDelta::Seconds(1));
ASSERT_TRUE(task_has_run);
}
TEST(SimulatedTimeControllerTest, SkipsDelayedTaskForward) {
GlobalSimulatedTimeController sim(kStartTime);
auto main_thread = sim.GetMainThread();
constexpr auto duration_during_which_nothing_runs = TimeDelta::Seconds(2);
constexpr auto shorter_duration = TimeDelta::Seconds(1);
MockFunction<void()> fun;
EXPECT_CALL(fun, Call).WillOnce(Invoke([&] {
ASSERT_EQ(sim.GetClock()->CurrentTime(),
kStartTime + duration_during_which_nothing_runs);
}));
main_thread->PostDelayedTask(fun.AsStdFunction(), shorter_duration);
sim.SkipForwardBy(duration_during_which_nothing_runs);
// Run tasks that were pending during the skip.
sim.AdvanceTime(TimeDelta::Zero());
}
} // namespace webrtc
|