blob: e5f704c43f3decd94a31b3e05a7d844f82578267 [file] [log] [blame]
/*
* 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 "rtc_base/task_queue.h"
#include "rtc_base/task_utils/repeating_task.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "rtc_base/event.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);
rtc::TaskQueue 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);
rtc::TaskQueue 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,
rtc::TaskQueue* task_queue) {
*handle = RepeatingTaskHandle::Start(task_queue->Get(), [this] {
DoPeriodicTask();
return TimeUntilNextRun();
});
}
};
GlobalSimulatedTimeController time_simulation(kStartTime);
rtc::TaskQueue 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);
// Restart the task
task_queue.PostTask(
[handle = std::move(handle)]() mutable { handle.Stop(); });
object->StartPeriodicTask(&handle, &task_queue);
task_queue.PostTask(
[handle = std::move(handle)]() mutable { handle.Stop(); });
struct Destructor {
void operator()() { object.reset(); }
std::unique_ptr<ObjectOnTaskQueue> object;
};
task_queue.PostTask(Destructor{std::move(object)});
}
TEST(SimulatedTimeControllerTest, DelayTaskRunOnTime) {
GlobalSimulatedTimeController time_simulation(kStartTime);
rtc::TaskQueue task_queue(
time_simulation.GetTaskQueueFactory()->CreateTaskQueue(
"TestQueue", TaskQueueFactory::Priority::NORMAL));
bool delay_task_executed = false;
task_queue.PostDelayedTask([&] { delay_task_executed = true; }, 10);
time_simulation.AdvanceTime(TimeDelta::Millis(10));
EXPECT_TRUE(delay_task_executed);
}
TEST(SimulatedTimeControllerTest, ThreadYeildsOnInvoke) {
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(RTC_FROM_HERE, [&] { task_has_run = true; });
t2->Invoke<void>(RTC_FROM_HERE, [] {
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(0);
});
// 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);
}
} // namespace webrtc