/* * Copyright 2020 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 #include #include "api/test/time_controller.h" #include "api/units/time_delta.h" #include "rtc_base/event.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" #include "test/gmock.h" #include "test/gtest.h" #include "test/time_controller/real_time_controller.h" #include "test/time_controller/simulated_time_controller.h" namespace webrtc { namespace { using ::testing::ElementsAreArray; using ::testing::TestParamInfo; using ::testing::TestWithParam; using ::testing::Values; enum class TimeMode { kRealTime, kSimulated }; std::unique_ptr CreateTimeController(TimeMode mode) { switch (mode) { case TimeMode::kRealTime: return std::make_unique(); case TimeMode::kSimulated: // Using an offset of 100000 to get nice fixed width and readable // timestamps in typical test scenarios. constexpr Timestamp kSimulatedStartTime = Timestamp::Seconds(100000); return std::make_unique( kSimulatedStartTime); } } std::string ParamsToString(const TestParamInfo& param) { switch (param.param) { case webrtc::TimeMode::kRealTime: return "RealTime"; case webrtc::TimeMode::kSimulated: return "SimulatedTime"; default: RTC_DCHECK_NOTREACHED() << "Time mode not supported"; } } // Keeps order of executions. May be called from different threads. class ExecutionOrderKeeper { public: void Executed(int execution_id) { MutexLock lock(&mutex_); order_.push_back(execution_id); } std::vector order() const { MutexLock lock(&mutex_); return order_; } private: mutable Mutex mutex_; std::vector order_ RTC_GUARDED_BY(mutex_); }; // Tests conformance between real time and simulated time time controller. class SimulatedRealTimeControllerConformanceTest : public TestWithParam {}; TEST_P(SimulatedRealTimeControllerConformanceTest, ThreadPostOrderTest) { std::unique_ptr time_controller = CreateTimeController(GetParam()); std::unique_ptr thread = time_controller->CreateThread("thread"); // Tasks on thread have to be executed in order in which they were // posted. ExecutionOrderKeeper execution_order; thread->PostTask([&]() { execution_order.Executed(1); }); thread->PostTask([&]() { execution_order.Executed(2); }); time_controller->AdvanceTime(TimeDelta::Millis(100)); EXPECT_THAT(execution_order.order(), ElementsAreArray({1, 2})); // Destroy `thread` before `execution_order` to be sure `execution_order` // is not accessed on the posted task after it is destroyed. thread = nullptr; } TEST_P(SimulatedRealTimeControllerConformanceTest, ThreadPostDelayedOrderTest) { std::unique_ptr time_controller = CreateTimeController(GetParam()); std::unique_ptr thread = time_controller->CreateThread("thread"); ExecutionOrderKeeper execution_order; thread->PostDelayedTask([&]() { execution_order.Executed(2); }, TimeDelta::Millis(500)); thread->PostTask([&]() { execution_order.Executed(1); }); time_controller->AdvanceTime(TimeDelta::Millis(600)); EXPECT_THAT(execution_order.order(), ElementsAreArray({1, 2})); // Destroy `thread` before `execution_order` to be sure `execution_order` // is not accessed on the posted task after it is destroyed. thread = nullptr; } TEST_P(SimulatedRealTimeControllerConformanceTest, ThreadPostInvokeOrderTest) { std::unique_ptr time_controller = CreateTimeController(GetParam()); std::unique_ptr thread = time_controller->CreateThread("thread"); // Tasks on thread have to be executed in order in which they were // posted/invoked. ExecutionOrderKeeper execution_order; thread->PostTask([&]() { execution_order.Executed(1); }); thread->BlockingCall([&]() { execution_order.Executed(2); }); time_controller->AdvanceTime(TimeDelta::Millis(100)); EXPECT_THAT(execution_order.order(), ElementsAreArray({1, 2})); // Destroy `thread` before `execution_order` to be sure `execution_order` // is not accessed on the posted task after it is destroyed. thread = nullptr; } TEST_P(SimulatedRealTimeControllerConformanceTest, ThreadPostInvokeFromThreadOrderTest) { std::unique_ptr time_controller = CreateTimeController(GetParam()); std::unique_ptr thread = time_controller->CreateThread("thread"); // If task is invoked from thread X on thread X it has to be executed // immediately. ExecutionOrderKeeper execution_order; thread->PostTask([&]() { thread->PostTask([&]() { execution_order.Executed(2); }); thread->BlockingCall([&]() { execution_order.Executed(1); }); }); time_controller->AdvanceTime(TimeDelta::Millis(100)); EXPECT_THAT(execution_order.order(), ElementsAreArray({1, 2})); // Destroy `thread` before `execution_order` to be sure `execution_order` // is not accessed on the posted task after it is destroyed. thread = nullptr; } TEST_P(SimulatedRealTimeControllerConformanceTest, TaskQueuePostEventWaitOrderTest) { std::unique_ptr time_controller = CreateTimeController(GetParam()); auto task_queue = time_controller->GetTaskQueueFactory()->CreateTaskQueue( "task_queue", webrtc::TaskQueueFactory::Priority::NORMAL); // Tasks on thread have to be executed in order in which they were // posted/invoked. ExecutionOrderKeeper execution_order; rtc::Event event; task_queue->PostTask([&]() { execution_order.Executed(1); }); task_queue->PostTask([&]() { execution_order.Executed(2); event.Set(); }); EXPECT_TRUE(event.Wait(/*give_up_after=*/TimeDelta::Millis(100))); time_controller->AdvanceTime(TimeDelta::Millis(100)); EXPECT_THAT(execution_order.order(), ElementsAreArray({1, 2})); // Destroy `task_queue` before `execution_order` to be sure `execution_order` // is not accessed on the posted task after it is destroyed. task_queue = nullptr; } INSTANTIATE_TEST_SUITE_P(ConformanceTest, SimulatedRealTimeControllerConformanceTest, Values(TimeMode::kRealTime, TimeMode::kSimulated), ParamsToString); } // namespace } // namespace webrtc