/* * 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. */ #ifndef TEST_WAIT_UNTIL_H_ #define TEST_WAIT_UNTIL_H_ #include #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" #include "test/wait_until_internal.h" // IWYU pragma: private 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. ClockVariant clock = absl::monostate(); // Name of the result to be used in the error message. std::string result_name = "result"; }; // Runs a function `fn`, which returns a result, until `matcher` matches the // result. // // The function is called repeatedly until the result matches the matcher or the // timeout is reached. If the matcher matches the result, the result is // returned. Otherwise, an error is returned. // // Example: // // int counter = 0; // RTCErrorOr result = Waituntil([&] { return ++counter; }, Eq(3)) // EXPECT_THAT(result, IsOkAndHolds(3)); template [[nodiscard]] auto WaitUntil(const Fn& fn, Matcher matcher, WaitUntilSettings settings = {}) -> RTCErrorOr { if (absl::holds_alternative(settings.clock)) { RTC_CHECK(rtc::Thread::Current()) << "A current thread is required. An " "rtc::AutoThread can work for tests."; } Timestamp start = wait_until_internal::GetTimeFromClockVariant(settings.clock); do { auto result = fn(); if (::testing::Value(result, matcher)) { return result; } 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; if (wait_until_internal::ExplainMatchResult(matcher, result, &listener, settings.result_name)) { return result; } return RTCError(RTCErrorType::INTERNAL_ERROR, listener.str()); } } // namespace webrtc #endif // TEST_WAIT_UNTIL_H_