From 17ad2f4af656e4acd73bf89f68852506aac79e00 Mon Sep 17 00:00:00 2001 From: Evan Shrubsole Date: Mon, 16 Dec 2024 11:53:45 +0000 Subject: [PATCH] Add more clocks for WaitUntil support There are many different clocks used for testing. One day there will only be one but for now this function needs to support them all. Bug: webrtc:381524905 Change-Id: I8e240167af2ada2494420c751722f8e0dc97f0d2 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/371303 Reviewed-by: Harald Alvestrand Commit-Queue: Evan Shrubsole Auto-Submit: Evan Shrubsole Cr-Commit-Position: refs/heads/main@{#43580} --- test/BUILD.gn | 11 +++++++- test/DEPS | 5 +++- test/wait_until.cc | 54 +++++++++++++++++++++++++++++++++++++ test/wait_until.h | 46 ++++++++++++++++++------------- test/wait_until_internal.h | 12 ++++----- test/wait_until_unittest.cc | 44 +++++++++++++++++++++++++++++- 6 files changed, 144 insertions(+), 28 deletions(-) create mode 100644 test/wait_until.cc diff --git a/test/BUILD.gn b/test/BUILD.gn index bfe3cc3279..598c196bad 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -715,6 +715,7 @@ if (rtc_include_tests) { "../api:array_view", "../api:create_frame_generator", "../api:create_simulcast_test_fixture_api", + "../api:create_time_controller", "../api:frame_generator_api", "../api:mock_video_codec_factory", "../api:mock_video_decoder", @@ -723,6 +724,7 @@ if (rtc_include_tests) { "../api:rtc_error_matchers", "../api:scoped_refptr", "../api:simulcast_test_fixture_api", + "../api:time_controller", "../api/environment", "../api/environment:environment_factory", "../api/task_queue", @@ -753,6 +755,7 @@ if (rtc_include_tests) { "../modules/video_coding:webrtc_vp9", "../modules/video_coding/svc:scalability_mode_util", "../rtc_base:criticalsection", + "../rtc_base:rtc_base_tests_utils", "../rtc_base:rtc_event", "../rtc_base:threading", "../rtc_base/synchronization:mutex", @@ -1422,21 +1425,27 @@ rtc_library("video_codec_tester") { ] } -rtc_source_set("wait_until") { +rtc_library("wait_until") { testonly = true sources = [ + "wait_until.cc", "wait_until.h", "wait_until_internal.h", ] deps = [ ":test_support", "../api:rtc_error", + "../api:time_controller", "../api/units:time_delta", "../api/units:timestamp", "../rtc_base:checks", + "../rtc_base:rtc_base_tests_utils", "../rtc_base:threading", + "../rtc_base:timeutils", "../system_wrappers", "//third_party/abseil-cpp/absl/base:nullability", + "//third_party/abseil-cpp/absl/functional:overload", "//third_party/abseil-cpp/absl/strings:string_view", + "//third_party/abseil-cpp/absl/types:variant", ] } diff --git a/test/DEPS b/test/DEPS index 497c79ab9c..1b0b340d96 100644 --- a/test/DEPS +++ b/test/DEPS @@ -89,5 +89,8 @@ specific_include_rules = { ], "emulated_turn_server\.h": [ "+p2p/base/turn_server.h", - ] + ], + "wait_until\.cc": [ + "+absl/functional/overload.h", + ], } diff --git a/test/wait_until.cc b/test/wait_until.cc new file mode 100644 index 0000000000..cd9eacace1 --- /dev/null +++ b/test/wait_until.cc @@ -0,0 +1,54 @@ +/* + * Copyright 2024 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/wait_until.h" + +#include "absl/functional/overload.h" +#include "absl/types/variant.h" +#include "api/test/time_controller.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "rtc_base/thread.h" +#include "rtc_base/time_utils.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { +namespace wait_until_internal { + +Timestamp GetTimeFromClockVariant(const ClockVariant& clock) { + return absl::visit( + absl::Overload{ + [](const absl::monostate&) { + return Timestamp::Micros(rtc::TimeMicros()); + }, + [](SimulatedClock* clock) { return clock->CurrentTime(); }, + [](TimeController* time_controller) { + return time_controller->GetClock()->CurrentTime(); + }, + [](auto* clock) { + return Timestamp::Micros(clock->TimeNanos() / 1000); + }, + }, + clock); +} + +void AdvanceTimeOnClockVariant(ClockVariant& clock, TimeDelta delta) { + absl::visit(absl::Overload{ + [&](const absl::monostate&) { + rtc::Thread::Current()->ProcessMessages(0); + rtc::Thread::Current()->SleepMs(delta.ms()); + }, + [&](auto* clock) { clock->AdvanceTime(delta); }, + }, + clock); +} + +} // namespace wait_until_internal +} // namespace webrtc diff --git a/test/wait_until.h b/test/wait_until.h index a8f7ac2a9c..f6d5729539 100644 --- a/test/wait_until.h +++ b/test/wait_until.h @@ -13,11 +13,13 @@ #include -#include "absl/base/nullability.h" +#include "absl/types/variant.h" #include "api/rtc_error.h" +#include "api/test/time_controller.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "rtc_base/checks.h" +#include "rtc_base/fake_clock.h" #include "rtc_base/thread.h" #include "system_wrappers/include/clock.h" #include "test/gmock.h" @@ -25,14 +27,24 @@ namespace webrtc { +using ClockVariant = absl::variant; + +namespace wait_until_internal { +Timestamp GetTimeFromClockVariant(const ClockVariant& clock); +void AdvanceTimeOnClockVariant(ClockVariant& clock, TimeDelta delta); +} // namespace wait_until_internal + struct WaitUntilSettings { // The maximum time to wait for the condition to be met. TimeDelta timeout = TimeDelta::Seconds(5); // The interval between polling the condition. TimeDelta polling_interval = TimeDelta::Millis(1); // The clock to use for timing. - absl::Nullable clock = nullptr; - + ClockVariant clock = absl::monostate(); // Name of the result to be used in the error message. std::string result_name = "result"; }; @@ -50,35 +62,31 @@ struct WaitUntilSettings { // RTCErrorOr result = Waituntil([&] { return ++counter; }, Eq(3)) // EXPECT_THAT(result, IsOkAndHolds(3)); template -auto WaitUntil(const Fn& fn, Matcher matcher, WaitUntilSettings settings = {}) +[[nodiscard]] auto WaitUntil(const Fn& fn, + Matcher matcher, + WaitUntilSettings settings = {}) -> RTCErrorOr { - if (!settings.clock) { + if (absl::holds_alternative(settings.clock)) { RTC_CHECK(rtc::Thread::Current()) << "A current thread is required. An " "rtc::AutoThread can work for tests."; } - absl::Nonnull clock = - settings.clock ? settings.clock : Clock::GetRealTimeClock(); - - Timestamp start = clock->CurrentTime(); - + Timestamp start = + wait_until_internal::GetTimeFromClockVariant(settings.clock); do { auto result = fn(); - if (testing::Value(result, matcher)) { + if (::testing::Value(result, matcher)) { return result; } - if (settings.clock) { - settings.clock->AdvanceTime(settings.polling_interval); - } else { - rtc::Thread::Current()->ProcessMessages(0); - rtc::Thread::Current()->SleepMs(settings.polling_interval.ms()); - } - } while (clock->CurrentTime() < start + settings.timeout); + wait_until_internal::AdvanceTimeOnClockVariant(settings.clock, + settings.polling_interval); + } while (wait_until_internal::GetTimeFromClockVariant(settings.clock) < + start + settings.timeout); // One more try after the last sleep. This failure will contain the error // message. auto result = fn(); - testing::StringMatchResultListener listener; + ::testing::StringMatchResultListener listener; if (wait_until_internal::ExplainMatchResult(matcher, result, &listener, settings.result_name)) { return result; diff --git a/test/wait_until_internal.h b/test/wait_until_internal.h index 5065e965b3..86ebd279e3 100644 --- a/test/wait_until_internal.h +++ b/test/wait_until_internal.h @@ -29,23 +29,23 @@ template bool ExplainMatchResult( const M& matcher, const T& value, - absl::Nonnull listener, + absl::Nonnull<::testing::StringMatchResultListener*> listener, absl::string_view value_name) { // SafeMatcherCast is required for matchers whose type does not match the // argument type. - testing::Matcher safe_matcher = - testing::SafeMatcherCast(matcher); + ::testing::Matcher safe_matcher = + ::testing::SafeMatcherCast(matcher); auto* ss = listener->stream(); *ss << "Value of: " << value_name << "\n"; *ss << "Expected: "; safe_matcher.DescribeTo(ss); *ss << "\nActual: "; - testing::StringMatchResultListener inner_listener; - if (testing::ExplainMatchResult(safe_matcher, value, &inner_listener)) { + ::testing::StringMatchResultListener inner_listener; + if (::testing::ExplainMatchResult(safe_matcher, value, &inner_listener)) { return true; } - *ss << testing::PrintToString(value); + *ss << ::testing::PrintToString(value); if (const std::string& inner_message = inner_listener.str(); !inner_message.empty()) { *ss << ", " << inner_message; diff --git a/test/wait_until_unittest.cc b/test/wait_until_unittest.cc index 96fec94a15..5f4ecf1696 100644 --- a/test/wait_until_unittest.cc +++ b/test/wait_until_unittest.cc @@ -10,10 +10,15 @@ #include "test/wait_until.h" +#include + #include "api/rtc_error.h" +#include "api/test/create_time_controller.h" #include "api/test/rtc_error_matchers.h" +#include "api/test/time_controller.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" +#include "rtc_base/fake_clock.h" #include "rtc_base/thread.h" #include "system_wrappers/include/clock.h" #include "test/gmock.h" @@ -73,7 +78,7 @@ TEST(WaitUntilTest, ErrorContainsMatcherExplanation) { } TEST(WaitUntilTest, ReturnsWhenConditionIsMetWithSimulatedClock) { - SimulatedClock fake_clock = SimulatedClock(Timestamp::Millis(1337)); + SimulatedClock fake_clock(Timestamp::Millis(1337)); int counter = 0; RTCErrorOr result = @@ -83,5 +88,42 @@ TEST(WaitUntilTest, ReturnsWhenConditionIsMetWithSimulatedClock) { EXPECT_THAT(fake_clock.CurrentTime(), Ge(Timestamp::Millis(1339))); } +TEST(WaitUntilTest, ReturnsWhenConditionIsMetWithThreadProcessingFakeClock) { + rtc::ScopedFakeClock fake_clock; + + int counter = 0; + RTCErrorOr result = + WaitUntil([&] { return ++counter; }, Eq(3), {.clock = &fake_clock}); + EXPECT_THAT(result, IsRtcOkAndHolds(3)); + // The fake clock should have advanced at least 2ms. + EXPECT_THAT(Timestamp::Micros(fake_clock.TimeNanos() * 1000), + Ge(Timestamp::Millis(1339))); +} + +TEST(WaitUntilTest, ReturnsWhenConditionIsMetWithFakeClock) { + rtc::FakeClock fake_clock; + + int counter = 0; + RTCErrorOr result = + WaitUntil([&] { return ++counter; }, Eq(3), {.clock = &fake_clock}); + EXPECT_THAT(result, IsRtcOkAndHolds(3)); + // The fake clock should have advanced at least 2ms. + EXPECT_THAT(Timestamp::Micros(fake_clock.TimeNanos() * 1000), + Ge(Timestamp::Millis(1339))); +} + +TEST(WaitUntilTest, ReturnsWhenConditionIsMetWithSimulatedTimeController) { + std::unique_ptr time_controller = + CreateSimulatedTimeController(); + + int counter = 0; + RTCErrorOr result = WaitUntil([&] { return ++counter; }, Eq(3), + {.clock = time_controller.get()}); + EXPECT_THAT(result, IsRtcOkAndHolds(3)); + // The fake clock should have advanced at least 2ms. + EXPECT_THAT(time_controller->GetClock()->CurrentTime(), + Ge(Timestamp::Millis(1339))); +} + } // namespace } // namespace webrtc