From 7ade6591f738ba21504996936807363960323543 Mon Sep 17 00:00:00 2001 From: Artem Titov Date: Fri, 24 Jul 2020 21:32:38 +0200 Subject: [PATCH] Add time controller conformance test and fix conformance bug Bug: webrtc:11799 Change-Id: I13f79f3ab025c105e56dcb93da5b7631893850e2 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/180125 Commit-Queue: Artem Titov Reviewed-by: Tommi Cr-Commit-Position: refs/heads/master@{#31787} --- test/time_controller/BUILD.gn | 6 + test/time_controller/simulated_thread.cc | 1 + .../time_controller_conformance_test.cc | 169 ++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 test/time_controller/time_controller_conformance_test.cc diff --git a/test/time_controller/BUILD.gn b/test/time_controller/BUILD.gn index ac396b9997..c9fffe6853 100644 --- a/test/time_controller/BUILD.gn +++ b/test/time_controller/BUILD.gn @@ -52,13 +52,19 @@ if (rtc_include_tests) { sources = [ "external_time_controller_unittest.cc", "simulated_time_controller_unittest.cc", + "time_controller_conformance_test.cc", ] deps = [ ":time_controller", "../:test_support", + "../../api:time_controller", + "../../api/units:time_delta", + "../../rtc_base", "../../rtc_base:rtc_base_approved", "../../rtc_base:rtc_task_queue", + "../../rtc_base/synchronization:mutex", "../../rtc_base/task_utils:repeating_task", + "../../rtc_base/task_utils:to_queued_task", ] } } diff --git a/test/time_controller/simulated_thread.cc b/test/time_controller/simulated_thread.cc index 807126467a..aa8b9ac90d 100644 --- a/test/time_controller/simulated_thread.cc +++ b/test/time_controller/simulated_thread.cc @@ -83,6 +83,7 @@ void SimulatedThread::Send(const rtc::Location& posted_from, } else { TaskQueueBase* yielding_from = TaskQueueBase::Current(); handler_->StartYield(yielding_from); + RunReady(Timestamp::MinusInfinity()); CurrentThreadSetter set_current(this); msg.phandler->OnMessage(&msg); handler_->StopYield(yielding_from); diff --git a/test/time_controller/time_controller_conformance_test.cc b/test/time_controller/time_controller_conformance_test.cc new file mode 100644 index 0000000000..10f0e1d724 --- /dev/null +++ b/test/time_controller/time_controller_conformance_test.cc @@ -0,0 +1,169 @@ +/* + * 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/location.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/task_utils/to_queued_task.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_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(RTC_FROM_HERE, [&]() { execution_order.Executed(1); }); + thread->PostTask(RTC_FROM_HERE, [&]() { execution_order.Executed(2); }); + time_controller->AdvanceTime(TimeDelta::Millis(100)); + EXPECT_THAT(execution_order.order(), ElementsAreArray({1, 2})); +} + +TEST_P(SimulatedRealTimeControllerConformanceTest, ThreadPostDelayedOrderTest) { + std::unique_ptr time_controller = + CreateTimeController(GetParam()); + std::unique_ptr thread = time_controller->CreateThread("thread"); + + ExecutionOrderKeeper execution_order; + thread->PostDelayedTask(ToQueuedTask([&]() { execution_order.Executed(2); }), + /*milliseconds=*/500); + thread->PostTask(ToQueuedTask([&]() { execution_order.Executed(1); })); + time_controller->AdvanceTime(TimeDelta::Millis(600)); + EXPECT_THAT(execution_order.order(), ElementsAreArray({1, 2})); +} + +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(RTC_FROM_HERE, [&]() { execution_order.Executed(1); }); + thread->Invoke(RTC_FROM_HERE, [&]() { execution_order.Executed(2); }); + time_controller->AdvanceTime(TimeDelta::Millis(100)); + EXPECT_THAT(execution_order.order(), ElementsAreArray({1, 2})); +} + +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(RTC_FROM_HERE, [&]() { + thread->PostTask(RTC_FROM_HERE, [&]() { execution_order.Executed(2); }); + thread->Invoke(RTC_FROM_HERE, [&]() { execution_order.Executed(1); }); + }); + time_controller->AdvanceTime(TimeDelta::Millis(100)); + EXPECT_THAT(execution_order.order(), ElementsAreArray({1, 2})); +} + +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(ToQueuedTask([&]() { execution_order.Executed(1); })); + task_queue->PostTask(ToQueuedTask([&]() { + execution_order.Executed(2); + event.Set(); + })); + EXPECT_TRUE(event.Wait(/*give_up_after_ms=*/100, + /*warn_after_ms=*/10'000)); + time_controller->AdvanceTime(TimeDelta::Millis(100)); + EXPECT_THAT(execution_order.order(), ElementsAreArray({1, 2})); +} + +INSTANTIATE_TEST_SUITE_P(ConformanceTest, + SimulatedRealTimeControllerConformanceTest, + Values(TimeMode::kRealTime, TimeMode::kSimulated), + ParamsToString); + +} // namespace +} // namespace webrtc