| /* |
| * 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 <memory> |
| #include <vector> |
| |
| #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<TimeController> CreateTimeController(TimeMode mode) { |
| switch (mode) { |
| case TimeMode::kRealTime: |
| return std::make_unique<RealTimeController>(); |
| 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<GlobalSimulatedTimeController>( |
| kSimulatedStartTime); |
| } |
| } |
| |
| std::string ParamsToString(const TestParamInfo<webrtc::TimeMode>& 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<int> order() const { |
| MutexLock lock(&mutex_); |
| return order_; |
| } |
| |
| private: |
| mutable Mutex mutex_; |
| std::vector<int> order_ RTC_GUARDED_BY(mutex_); |
| }; |
| |
| // Tests conformance between real time and simulated time time controller. |
| class SimulatedRealTimeControllerConformanceTest |
| : public TestWithParam<webrtc::TimeMode> {}; |
| |
| TEST_P(SimulatedRealTimeControllerConformanceTest, ThreadPostOrderTest) { |
| std::unique_ptr<TimeController> time_controller = |
| CreateTimeController(GetParam()); |
| std::unique_ptr<rtc::Thread> 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<TimeController> time_controller = |
| CreateTimeController(GetParam()); |
| std::unique_ptr<rtc::Thread> 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<TimeController> time_controller = |
| CreateTimeController(GetParam()); |
| std::unique_ptr<rtc::Thread> 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<TimeController> time_controller = |
| CreateTimeController(GetParam()); |
| std::unique_ptr<rtc::Thread> 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<TimeController> 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 |