From 9e46cf5cc5dbd24e0602767a72b211eb23d46233 Mon Sep 17 00:00:00 2001 From: Tommi Date: Mon, 4 May 2020 16:43:05 +0200 Subject: [PATCH] Introduce a RunLoop class that supports the TaskQueue interface on the current thread. This simplifies writing async tests that use TaskQueue and doesn't require spinning up a new thread for simple things. The implementation is currently based on rtc::Thread, which could also be useful in some circumstances while migrating code over to TQ. Remove PressEnterToContinue from the test_common files since it's very specific and only used from one file. Bug: none Change-Id: I8b2c6c40809271a109ec17cf7e1120847645d58a Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/174260 Reviewed-by: Mirko Bonadei Commit-Queue: Tommi Cr-Commit-Position: refs/heads/master@{#31160} --- test/BUILD.gn | 21 +++++-------- test/run_loop.cc | 60 ++++++++++++++++++++++++++++++++--- test/run_loop.h | 62 +++++++++++++++++++++++++++++++++++-- test/run_loop_unittest.cc | 61 ++++++++++++++++++++++++++++++++++++ test/win/run_loop_win.cc | 36 --------------------- video/video_quality_test.cc | 30 ++++++++++++++++-- 6 files changed, 211 insertions(+), 59 deletions(-) create mode 100644 test/run_loop_unittest.cc delete mode 100644 test/win/run_loop_win.cc diff --git a/test/BUILD.gn b/test/BUILD.gn index 58129bbdbf..34da8894f7 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -458,6 +458,7 @@ if (rtc_include_tests) { ":perf_test", ":rtc_expect_death", ":rtp_test_utils", + ":test_common", ":test_main", ":test_support", ":test_support_test_artifacts", @@ -487,7 +488,9 @@ if (rtc_include_tests) { "../modules/video_coding:webrtc_vp9", "../rtc_base:criticalsection", "../rtc_base:rtc_event", + "../rtc_base:rtc_task_queue", "../rtc_base/system:file_wrapper", + "../rtc_base/task_utils:to_queued_task", "pc/e2e:e2e_unittests", "peer_scenario/tests", "scenario:scenario_unittests", @@ -505,6 +508,7 @@ if (rtc_include_tests) { "frame_generator_unittest.cc", "rtp_file_reader_unittest.cc", "rtp_file_writer_unittest.cc", + "run_loop_unittest.cc", "testsupport/ivf_video_frame_generator_unittest.cc", "testsupport/perf_test_unittest.cc", "testsupport/test_artifacts_unittest.cc", @@ -784,22 +788,11 @@ rtc_library("test_common") { "layer_filtering_transport.cc", "layer_filtering_transport.h", "rtp_rtcp_observer.h", + "run_loop.cc", + "run_loop.h", "video_decoder_proxy_factory.h", "video_encoder_proxy_factory.h", ] - if (current_os != "winuwp") { - # The filtering of *_win.cc is not done for WinUWP (intentionally) as - # most _win.cc files are compatible with WinUWP. However, the - # peek/dispatch Win32 runloops are entirely WinUWP incompatible thus - # WinUWP uses the generic runloop as defined for non-Windows targets. - sources += [ "win/run_loop_win.cc" ] - } - if (!is_win || current_os == "winuwp") { - sources += [ - "run_loop.cc", - "run_loop.h", - ] - } deps = [ ":direct_transport", @@ -840,8 +833,10 @@ rtc_library("test_common") { "../modules/video_coding:codec_globals_headers", "../rtc_base:checks", "../rtc_base:criticalsection", + "../rtc_base:rtc_base", "../rtc_base:rtc_event", "../rtc_base:task_queue_for_test", + "../rtc_base/task_utils:to_queued_task", "../system_wrappers", "../system_wrappers:field_trial", "//third_party/abseil-cpp/absl/types:optional", diff --git a/test/run_loop.cc b/test/run_loop.cc index 1fc200f929..643da5d56e 100644 --- a/test/run_loop.cc +++ b/test/run_loop.cc @@ -9,15 +9,65 @@ */ #include "test/run_loop.h" -#include +#include "rtc_base/task_utils/to_queued_task.h" namespace webrtc { namespace test { -void PressEnterToContinue(TaskQueueBase* /*task_queue*/) { - puts(">> Press ENTER to continue..."); - while (getc(stdin) != '\n' && !feof(stdin)) - ; +RunLoop::RunLoop() { + worker_thread_.WrapCurrent(); } + +RunLoop::~RunLoop() { + worker_thread_.UnwrapCurrent(); +} + +TaskQueueBase* RunLoop::task_queue() { + return &worker_thread_; +} + +void RunLoop::Run() { + worker_thread_.ProcessMessages(WorkerThread::kForever); +} + +void RunLoop::Quit() { + socket_server_.FailNextWait(); +} + +void RunLoop::Flush() { + worker_thread_.PostTask( + ToQueuedTask([this]() { socket_server_.FailNextWait(); })); + worker_thread_.ProcessMessages(1000); +} + +RunLoop::FakeSocketServer::FakeSocketServer() = default; +RunLoop::FakeSocketServer::~FakeSocketServer() = default; + +void RunLoop::FakeSocketServer::FailNextWait() { + fail_next_wait_ = true; +} + +bool RunLoop::FakeSocketServer::Wait(int cms, bool process_io) { + if (fail_next_wait_) { + fail_next_wait_ = false; + return false; + } + return true; +} + +void RunLoop::FakeSocketServer::WakeUp() {} + +rtc::Socket* RunLoop::FakeSocketServer::CreateSocket(int family, int type) { + return nullptr; +} + +rtc::AsyncSocket* RunLoop::FakeSocketServer::CreateAsyncSocket(int family, + int type) { + return nullptr; +} + +RunLoop::WorkerThread::WorkerThread(rtc::SocketServer* ss) + : rtc::Thread(ss), tq_setter_(this) {} + } // namespace test } // namespace webrtc diff --git a/test/run_loop.h b/test/run_loop.h index 414e72c65b..f350b2ce93 100644 --- a/test/run_loop.h +++ b/test/run_loop.h @@ -10,13 +10,69 @@ #ifndef TEST_RUN_LOOP_H_ #define TEST_RUN_LOOP_H_ -#include "api/task_queue/task_queue_base.h" +#include "rtc_base/task_utils/to_queued_task.h" +#include "rtc_base/thread.h" namespace webrtc { namespace test { -// Blocks until the user presses enter. -void PressEnterToContinue(TaskQueueBase* task_queue); +// This utility class allows you to run a TaskQueue supported interface on the +// main test thread, call Run() while doing things asynchonously and break +// the loop (from the same thread) from a callback by calling Quit(). +class RunLoop { + public: + RunLoop(); + ~RunLoop(); + + TaskQueueBase* task_queue(); + + void Run(); + void Quit(); + + void Flush(); + + // Convenience methods since TaskQueueBase doesn't support this sort of magic. + template + void PostTask(Closure&& task) { + task_queue()->PostTask(ToQueuedTask(std::forward(task))); + } + + template + void PostDelayedTask(Closure&& task, uint32_t milliseconds) { + task_queue()->PostDelayedTask(ToQueuedTask(std::forward(task)), + milliseconds); + } + + private: + class FakeSocketServer : public rtc::SocketServer { + public: + FakeSocketServer(); + ~FakeSocketServer(); + + void FailNextWait(); + + private: + bool Wait(int cms, bool process_io) override; + void WakeUp() override; + + rtc::Socket* CreateSocket(int family, int type) override; + rtc::AsyncSocket* CreateAsyncSocket(int family, int type) override; + + private: + bool fail_next_wait_ = false; + }; + + class WorkerThread : public rtc::Thread { + public: + explicit WorkerThread(rtc::SocketServer* ss); + + private: + CurrentTaskQueueSetter tq_setter_; + }; + + FakeSocketServer socket_server_; + WorkerThread worker_thread_{&socket_server_}; +}; } // namespace test } // namespace webrtc diff --git a/test/run_loop_unittest.cc b/test/run_loop_unittest.cc new file mode 100644 index 0000000000..a356cc265a --- /dev/null +++ b/test/run_loop_unittest.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 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 "test/run_loop.h" + +#include "rtc_base/task_queue.h" +#include "rtc_base/task_utils/to_queued_task.h" +#include "test/gtest.h" + +namespace webrtc { + +TEST(RunLoopTest, TaskQueueOnThread) { + EXPECT_EQ(TaskQueueBase::Current(), nullptr); + test::RunLoop loop; + EXPECT_EQ(TaskQueueBase::Current(), loop.task_queue()); + EXPECT_TRUE(loop.task_queue()->IsCurrent()); +} + +TEST(RunLoopTest, Flush) { + test::RunLoop loop; + int counter = 0; + loop.PostTask([&counter]() { ++counter; }); + EXPECT_EQ(counter, 0); + loop.Flush(); + EXPECT_EQ(counter, 1); +} + +TEST(RunLoopTest, Delayed) { + test::RunLoop loop; + bool ran = false; + loop.PostDelayedTask( + [&ran, &loop]() { + ran = true; + loop.Quit(); + }, + 100); + loop.Flush(); + EXPECT_FALSE(ran); + loop.Run(); + EXPECT_TRUE(ran); +} + +TEST(RunLoopTest, PostAndQuit) { + test::RunLoop loop; + bool ran = false; + loop.PostTask([&ran, &loop]() { + ran = true; + loop.Quit(); + }); + loop.Run(); + EXPECT_TRUE(ran); +} + +} // namespace webrtc diff --git a/test/win/run_loop_win.cc b/test/win/run_loop_win.cc deleted file mode 100644 index 95de16bf24..0000000000 --- a/test/win/run_loop_win.cc +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2013 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 -#include - -#include "rtc_base/task_queue_for_test.h" -#include "test/run_loop.h" - -namespace webrtc { -namespace test { - -void PressEnterToContinue(TaskQueueBase* task_queue) { - puts(">> Press ENTER to continue..."); - - while (!_kbhit() || _getch() != '\r') { - // Drive the message loop for the thread running the task_queue - SendTask(RTC_FROM_HERE, task_queue, [&]() { - MSG msg; - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - }); - } -} -} // namespace test -} // namespace webrtc diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc index 0482cb0730..a8f475abdf 100644 --- a/video/video_quality_test.cc +++ b/video/video_quality_test.cc @@ -11,6 +11,10 @@ #include +#if defined(WEBRTC_WIN) +#include +#endif + #include #include #include @@ -43,7 +47,6 @@ #include "rtc_base/strings/string_builder.h" #include "rtc_base/task_queue_for_test.h" #include "test/platform_video_capturer.h" -#include "test/run_loop.h" #include "test/testsupport/file_utils.h" #include "test/video_renderer.h" #include "video/frame_dumping_decoder.h" @@ -270,6 +273,29 @@ class QualityTestVideoEncoder : public VideoEncoder, VideoCodec codec_settings_; }; +#if defined(WEBRTC_WIN) && !defined(WINUWP) +void PressEnterToContinue(TaskQueueBase* task_queue) { + puts(">> Press ENTER to continue..."); + + while (!_kbhit() || _getch() != '\r') { + // Drive the message loop for the thread running the task_queue + SendTask(RTC_FROM_HERE, task_queue, [&]() { + MSG msg; + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + }); + } +} +#else +void PressEnterToContinue(TaskQueueBase* /*task_queue*/) { + puts(">> Press ENTER to continue..."); + while (getc(stdin) != '\n' && !feof(stdin)) + ; // NOLINT +} +#endif + } // namespace std::unique_ptr VideoQualityTest::CreateVideoDecoder( @@ -1570,7 +1596,7 @@ void VideoQualityTest::RunWithRenderers(const Params& params) { Start(); }); - test::PressEnterToContinue(task_queue()); + PressEnterToContinue(task_queue()); SendTask(RTC_FROM_HERE, task_queue(), [&]() { Stop();