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 <hta@webrtc.org>
Commit-Queue: Evan Shrubsole <eshr@webrtc.org>
Auto-Submit: Evan Shrubsole <eshr@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43580}
This commit is contained in:
Evan Shrubsole 2024-12-16 11:53:45 +00:00 committed by WebRTC LUCI CQ
parent 4b9cb69ae1
commit 17ad2f4af6
6 changed files with 144 additions and 28 deletions

View File

@ -715,6 +715,7 @@ if (rtc_include_tests) {
"../api:array_view", "../api:array_view",
"../api:create_frame_generator", "../api:create_frame_generator",
"../api:create_simulcast_test_fixture_api", "../api:create_simulcast_test_fixture_api",
"../api:create_time_controller",
"../api:frame_generator_api", "../api:frame_generator_api",
"../api:mock_video_codec_factory", "../api:mock_video_codec_factory",
"../api:mock_video_decoder", "../api:mock_video_decoder",
@ -723,6 +724,7 @@ if (rtc_include_tests) {
"../api:rtc_error_matchers", "../api:rtc_error_matchers",
"../api:scoped_refptr", "../api:scoped_refptr",
"../api:simulcast_test_fixture_api", "../api:simulcast_test_fixture_api",
"../api:time_controller",
"../api/environment", "../api/environment",
"../api/environment:environment_factory", "../api/environment:environment_factory",
"../api/task_queue", "../api/task_queue",
@ -753,6 +755,7 @@ if (rtc_include_tests) {
"../modules/video_coding:webrtc_vp9", "../modules/video_coding:webrtc_vp9",
"../modules/video_coding/svc:scalability_mode_util", "../modules/video_coding/svc:scalability_mode_util",
"../rtc_base:criticalsection", "../rtc_base:criticalsection",
"../rtc_base:rtc_base_tests_utils",
"../rtc_base:rtc_event", "../rtc_base:rtc_event",
"../rtc_base:threading", "../rtc_base:threading",
"../rtc_base/synchronization:mutex", "../rtc_base/synchronization:mutex",
@ -1422,21 +1425,27 @@ rtc_library("video_codec_tester") {
] ]
} }
rtc_source_set("wait_until") { rtc_library("wait_until") {
testonly = true testonly = true
sources = [ sources = [
"wait_until.cc",
"wait_until.h", "wait_until.h",
"wait_until_internal.h", "wait_until_internal.h",
] ]
deps = [ deps = [
":test_support", ":test_support",
"../api:rtc_error", "../api:rtc_error",
"../api:time_controller",
"../api/units:time_delta", "../api/units:time_delta",
"../api/units:timestamp", "../api/units:timestamp",
"../rtc_base:checks", "../rtc_base:checks",
"../rtc_base:rtc_base_tests_utils",
"../rtc_base:threading", "../rtc_base:threading",
"../rtc_base:timeutils",
"../system_wrappers", "../system_wrappers",
"//third_party/abseil-cpp/absl/base:nullability", "//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/strings:string_view",
"//third_party/abseil-cpp/absl/types:variant",
] ]
} }

View File

@ -89,5 +89,8 @@ specific_include_rules = {
], ],
"emulated_turn_server\.h": [ "emulated_turn_server\.h": [
"+p2p/base/turn_server.h", "+p2p/base/turn_server.h",
] ],
"wait_until\.cc": [
"+absl/functional/overload.h",
],
} }

54
test/wait_until.cc Normal file
View File

@ -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

View File

@ -13,11 +13,13 @@
#include <string> #include <string>
#include "absl/base/nullability.h" #include "absl/types/variant.h"
#include "api/rtc_error.h" #include "api/rtc_error.h"
#include "api/test/time_controller.h"
#include "api/units/time_delta.h" #include "api/units/time_delta.h"
#include "api/units/timestamp.h" #include "api/units/timestamp.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
#include "rtc_base/fake_clock.h"
#include "rtc_base/thread.h" #include "rtc_base/thread.h"
#include "system_wrappers/include/clock.h" #include "system_wrappers/include/clock.h"
#include "test/gmock.h" #include "test/gmock.h"
@ -25,14 +27,24 @@
namespace webrtc { namespace webrtc {
using ClockVariant = absl::variant<absl::monostate,
SimulatedClock*,
rtc::FakeClock*,
rtc::ThreadProcessingFakeClock*,
TimeController*>;
namespace wait_until_internal {
Timestamp GetTimeFromClockVariant(const ClockVariant& clock);
void AdvanceTimeOnClockVariant(ClockVariant& clock, TimeDelta delta);
} // namespace wait_until_internal
struct WaitUntilSettings { struct WaitUntilSettings {
// The maximum time to wait for the condition to be met. // The maximum time to wait for the condition to be met.
TimeDelta timeout = TimeDelta::Seconds(5); TimeDelta timeout = TimeDelta::Seconds(5);
// The interval between polling the condition. // The interval between polling the condition.
TimeDelta polling_interval = TimeDelta::Millis(1); TimeDelta polling_interval = TimeDelta::Millis(1);
// The clock to use for timing. // The clock to use for timing.
absl::Nullable<SimulatedClock*> clock = nullptr; ClockVariant clock = absl::monostate();
// Name of the result to be used in the error message. // Name of the result to be used in the error message.
std::string result_name = "result"; std::string result_name = "result";
}; };
@ -50,35 +62,31 @@ struct WaitUntilSettings {
// RTCErrorOr<int> result = Waituntil([&] { return ++counter; }, Eq(3)) // RTCErrorOr<int> result = Waituntil([&] { return ++counter; }, Eq(3))
// EXPECT_THAT(result, IsOkAndHolds(3)); // EXPECT_THAT(result, IsOkAndHolds(3));
template <typename Fn, typename Matcher> template <typename Fn, typename Matcher>
auto WaitUntil(const Fn& fn, Matcher matcher, WaitUntilSettings settings = {}) [[nodiscard]] auto WaitUntil(const Fn& fn,
Matcher matcher,
WaitUntilSettings settings = {})
-> RTCErrorOr<decltype(fn())> { -> RTCErrorOr<decltype(fn())> {
if (!settings.clock) { if (absl::holds_alternative<absl::monostate>(settings.clock)) {
RTC_CHECK(rtc::Thread::Current()) << "A current thread is required. An " RTC_CHECK(rtc::Thread::Current()) << "A current thread is required. An "
"rtc::AutoThread can work for tests."; "rtc::AutoThread can work for tests.";
} }
absl::Nonnull<Clock*> clock = Timestamp start =
settings.clock ? settings.clock : Clock::GetRealTimeClock(); wait_until_internal::GetTimeFromClockVariant(settings.clock);
Timestamp start = clock->CurrentTime();
do { do {
auto result = fn(); auto result = fn();
if (testing::Value(result, matcher)) { if (::testing::Value(result, matcher)) {
return result; return result;
} }
if (settings.clock) { wait_until_internal::AdvanceTimeOnClockVariant(settings.clock,
settings.clock->AdvanceTime(settings.polling_interval); settings.polling_interval);
} else { } while (wait_until_internal::GetTimeFromClockVariant(settings.clock) <
rtc::Thread::Current()->ProcessMessages(0); start + settings.timeout);
rtc::Thread::Current()->SleepMs(settings.polling_interval.ms());
}
} while (clock->CurrentTime() < start + settings.timeout);
// One more try after the last sleep. This failure will contain the error // One more try after the last sleep. This failure will contain the error
// message. // message.
auto result = fn(); auto result = fn();
testing::StringMatchResultListener listener; ::testing::StringMatchResultListener listener;
if (wait_until_internal::ExplainMatchResult(matcher, result, &listener, if (wait_until_internal::ExplainMatchResult(matcher, result, &listener,
settings.result_name)) { settings.result_name)) {
return result; return result;

View File

@ -29,23 +29,23 @@ template <typename T, typename M>
bool ExplainMatchResult( bool ExplainMatchResult(
const M& matcher, const M& matcher,
const T& value, const T& value,
absl::Nonnull<testing::StringMatchResultListener*> listener, absl::Nonnull<::testing::StringMatchResultListener*> listener,
absl::string_view value_name) { absl::string_view value_name) {
// SafeMatcherCast is required for matchers whose type does not match the // SafeMatcherCast is required for matchers whose type does not match the
// argument type. // argument type.
testing::Matcher<const T&> safe_matcher = ::testing::Matcher<const T&> safe_matcher =
testing::SafeMatcherCast<const T&>(matcher); ::testing::SafeMatcherCast<const T&>(matcher);
auto* ss = listener->stream(); auto* ss = listener->stream();
*ss << "Value of: " << value_name << "\n"; *ss << "Value of: " << value_name << "\n";
*ss << "Expected: "; *ss << "Expected: ";
safe_matcher.DescribeTo(ss); safe_matcher.DescribeTo(ss);
*ss << "\nActual: "; *ss << "\nActual: ";
testing::StringMatchResultListener inner_listener; ::testing::StringMatchResultListener inner_listener;
if (testing::ExplainMatchResult(safe_matcher, value, &inner_listener)) { if (::testing::ExplainMatchResult(safe_matcher, value, &inner_listener)) {
return true; return true;
} }
*ss << testing::PrintToString(value); *ss << ::testing::PrintToString(value);
if (const std::string& inner_message = inner_listener.str(); if (const std::string& inner_message = inner_listener.str();
!inner_message.empty()) { !inner_message.empty()) {
*ss << ", " << inner_message; *ss << ", " << inner_message;

View File

@ -10,10 +10,15 @@
#include "test/wait_until.h" #include "test/wait_until.h"
#include <memory>
#include "api/rtc_error.h" #include "api/rtc_error.h"
#include "api/test/create_time_controller.h"
#include "api/test/rtc_error_matchers.h" #include "api/test/rtc_error_matchers.h"
#include "api/test/time_controller.h"
#include "api/units/time_delta.h" #include "api/units/time_delta.h"
#include "api/units/timestamp.h" #include "api/units/timestamp.h"
#include "rtc_base/fake_clock.h"
#include "rtc_base/thread.h" #include "rtc_base/thread.h"
#include "system_wrappers/include/clock.h" #include "system_wrappers/include/clock.h"
#include "test/gmock.h" #include "test/gmock.h"
@ -73,7 +78,7 @@ TEST(WaitUntilTest, ErrorContainsMatcherExplanation) {
} }
TEST(WaitUntilTest, ReturnsWhenConditionIsMetWithSimulatedClock) { TEST(WaitUntilTest, ReturnsWhenConditionIsMetWithSimulatedClock) {
SimulatedClock fake_clock = SimulatedClock(Timestamp::Millis(1337)); SimulatedClock fake_clock(Timestamp::Millis(1337));
int counter = 0; int counter = 0;
RTCErrorOr<int> result = RTCErrorOr<int> result =
@ -83,5 +88,42 @@ TEST(WaitUntilTest, ReturnsWhenConditionIsMetWithSimulatedClock) {
EXPECT_THAT(fake_clock.CurrentTime(), Ge(Timestamp::Millis(1339))); EXPECT_THAT(fake_clock.CurrentTime(), Ge(Timestamp::Millis(1339)));
} }
TEST(WaitUntilTest, ReturnsWhenConditionIsMetWithThreadProcessingFakeClock) {
rtc::ScopedFakeClock fake_clock;
int counter = 0;
RTCErrorOr<int> 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<int> 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<TimeController> time_controller =
CreateSimulatedTimeController();
int counter = 0;
RTCErrorOr<int> 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
} // namespace webrtc } // namespace webrtc