Source code

Revision control

Copy as Markdown

Other Tools

/*
* 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(), [] {
rtc::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