Add simplified WaitUntil variant to wait for a boolean condition

This allows makes test bodies cleaner for such common use case
eliminating extra IsTrue and IsRtcOk matchers.

Bug: None
Change-Id: I905143f5ccd0470e46437383e7fb5ec74e87a696
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/403160
Reviewed-by: Evan Shrubsole <eshr@webrtc.org>
Reviewed-by: Jeremy Leconte <jleconte@google.com>
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#45282}
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 9edc60a..75a3fbc 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -1487,6 +1487,7 @@
   ]
   deps = [
     ":test_support",
+    "../api:function_view",
     "../api:rtc_error",
     "../api:time_controller",
     "../api/units:time_delta",
diff --git a/test/network/network_emulation_pc_unittest.cc b/test/network/network_emulation_pc_unittest.cc
index 086e230..8310f3d 100644
--- a/test/network/network_emulation_pc_unittest.cc
+++ b/test/network/network_emulation_pc_unittest.cc
@@ -174,11 +174,8 @@
     ASSERT_THAT(WaitUntil([&] { return alice->signaling_state(); },
                           Eq(PeerConnectionInterface::kStable)),
                 IsRtcOk());
-    ASSERT_THAT(
-        WaitUntil([&] { return alice->IsIceGatheringDone(); }, IsTrue()),
-        IsRtcOk());
-    ASSERT_THAT(WaitUntil([&] { return bob->IsIceGatheringDone(); }, IsTrue()),
-                IsRtcOk());
+    ASSERT_TRUE(WaitUntil([&] { return alice->IsIceGatheringDone(); }));
+    ASSERT_TRUE(WaitUntil([&] { return bob->IsIceGatheringDone(); }));
 
     // Connect an ICE candidate pairs.
     ASSERT_TRUE(
@@ -186,10 +183,8 @@
     ASSERT_TRUE(
         AddIceCandidates(alice.get(), bob->observer()->GetAllCandidates()));
     // This means that ICE and DTLS are connected.
-    ASSERT_THAT(WaitUntil([&] { return bob->IsIceConnected(); }, IsTrue()),
-                IsRtcOk());
-    ASSERT_THAT(WaitUntil([&] { return alice->IsIceConnected(); }, IsTrue()),
-                IsRtcOk());
+    ASSERT_TRUE(WaitUntil([&] { return bob->IsIceConnected(); }));
+    ASSERT_TRUE(WaitUntil([&] { return alice->IsIceConnected(); }));
 
     // Close peer connections
     alice->pc()->Close();
@@ -285,11 +280,8 @@
     ASSERT_THAT(WaitUntil([&] { return alice->signaling_state(); },
                           Eq(PeerConnectionInterface::kStable)),
                 IsRtcOk());
-    ASSERT_THAT(
-        WaitUntil([&] { return alice->IsIceGatheringDone(); }, IsTrue()),
-        IsRtcOk());
-    ASSERT_THAT(WaitUntil([&] { return bob->IsIceGatheringDone(); }, IsTrue()),
-                IsRtcOk());
+    ASSERT_TRUE(WaitUntil([&] { return alice->IsIceGatheringDone(); }));
+    ASSERT_TRUE(WaitUntil([&] { return bob->IsIceGatheringDone(); }));
 
     // Connect an ICE candidate pairs.
     ASSERT_TRUE(
@@ -297,10 +289,8 @@
     ASSERT_TRUE(
         AddIceCandidates(alice.get(), bob->observer()->GetAllCandidates()));
     // This means that ICE and DTLS are connected.
-    ASSERT_THAT(WaitUntil([&] { return bob->IsIceConnected(); }, IsTrue()),
-                IsRtcOk());
-    ASSERT_THAT(WaitUntil([&] { return alice->IsIceConnected(); }, IsTrue()),
-                IsRtcOk());
+    ASSERT_TRUE(WaitUntil([&] { return bob->IsIceConnected(); }));
+    ASSERT_TRUE(WaitUntil([&] { return alice->IsIceConnected(); }));
 
     // Close peer connections
     alice->pc()->Close();
diff --git a/test/wait_until.cc b/test/wait_until.cc
index b92f32c..61cfebe 100644
--- a/test/wait_until.cc
+++ b/test/wait_until.cc
@@ -13,41 +13,61 @@
 #include <variant>
 
 #include "absl/functional/overload.h"
+#include "api/function_view.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/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 std::visit(
-      absl::Overload{
-          [](const std::monostate&) { return Timestamp::Micros(TimeMicros()); },
-          [](SimulatedClock* clock) { return clock->CurrentTime(); },
-          [](TimeController* time_controller) {
-            return time_controller->GetClock()->CurrentTime();
-          },
-          [](auto* clock) {
-            return Timestamp::Micros(clock->TimeNanos() / 1000);
-          },
-      },
-      clock);
+[[nodiscard]] bool WaitUntil(FunctionView<bool()> fn,
+                             WaitUntilSettings settings) {
+  if (std::holds_alternative<std::monostate>(settings.clock)) {
+    RTC_CHECK(Thread::Current()) << "A current thread is required. An "
+                                    "webrtc::AutoThread can work for tests.";
+  }
+
+  auto now = [&] {
+    return std::visit(
+        absl::Overload{
+            [](const std::monostate&) {
+              return Timestamp::Micros(TimeMicros());
+            },
+            [](SimulatedClock* clock) { return clock->CurrentTime(); },
+            [](TimeController* time_controller) {
+              return time_controller->GetClock()->CurrentTime();
+            },
+            [](auto* clock) {
+              return Timestamp::Micros(clock->TimeNanos() / 1000);
+            },
+        },
+        settings.clock);
+  };
+
+  auto sleep = [&](TimeDelta delta) {
+    std::visit(absl::Overload{
+                   [&](const std::monostate&) {
+                     Thread::Current()->ProcessMessages(0);
+                     Thread::Current()->SleepMs(delta.ms());
+                   },
+                   [&](auto* clock) { clock->AdvanceTime(delta); },
+               },
+               settings.clock);
+  };
+
+  Timestamp deadline = now() + settings.timeout;
+  for (;;) {
+    if (fn()) {
+      return true;
+    } else if (now() >= deadline) {
+      return false;
+    }
+    sleep(settings.polling_interval);
+  }
 }
 
-void AdvanceTimeOnClockVariant(ClockVariant& clock, TimeDelta delta) {
-  std::visit(absl::Overload{
-                 [&](const std::monostate&) {
-                   Thread::Current()->ProcessMessages(0);
-                   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 1d45fdc..c5e4f8f 100644
--- a/test/wait_until.h
+++ b/test/wait_until.h
@@ -11,18 +11,21 @@
 #ifndef TEST_WAIT_UNTIL_H_
 #define TEST_WAIT_UNTIL_H_
 
+#include <optional>
 #include <string>
+#include <type_traits>
+#include <utility>
 #include <variant>
 
+#include "api/function_view.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/gtest.h"
 #include "test/wait_until_internal.h"  // IWYU pragma: private
 
 namespace webrtc {
@@ -33,11 +36,6 @@
                                   ThreadProcessingFakeClock*,
                                   TimeController*>;
 
-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);
@@ -49,6 +47,16 @@
   std::string result_name = "result";
 };
 
+// Runs a function `fn`, until it returns true, or timeout from `settings`.
+// Calls `fn` at least once. Returns true when `fn` return true, returns false
+// after timeout if `fn` always returned false.
+//
+// Example:
+//
+//   EXPECT_TRUE(WaitUntil([&] { return client.IsConnected(); });
+[[nodiscard]] bool WaitUntil(FunctionView<bool()> fn,
+                             WaitUntilSettings settings = {});
+
 // Runs a function `fn`, which returns a result, until `matcher` matches the
 // result.
 //
@@ -59,39 +67,35 @@
 // Example:
 //
 //   int counter = 0;
-//   RTCErrorOr<int> result = Waituntil([&] { return ++counter; }, Eq(3))
+//   RTCErrorOr<int> result = WaitUntil([&] { return ++counter; }, Eq(3))
 //   EXPECT_THAT(result, IsOkAndHolds(3));
-template <typename Fn, typename Matcher>
-[[nodiscard]] auto WaitUntil(const Fn& fn,
-                             Matcher matcher,
-                             WaitUntilSettings settings = {})
-    -> RTCErrorOr<decltype(fn())> {
-  if (std::holds_alternative<std::monostate>(settings.clock)) {
-    RTC_CHECK(Thread::Current()) << "A current thread is required. An "
-                                    "webrtc::AutoThread can work for tests.";
+template <typename Fn>
+[[nodiscard]] RTCErrorOr<std::invoke_result_t<Fn>> WaitUntil(
+    const Fn& fn,
+    ::testing::Matcher<std::invoke_result_t<Fn>> matcher,
+    WaitUntilSettings settings = {}) {
+  // Wrap `result` into optional to support types that are not default
+  // constructable.
+  std::optional<std::invoke_result_t<Fn>> result;
+  bool ok = WaitUntil(
+      [&] {
+        // `emplace` instead of assigning to support return types that do not
+        // have an assign operator.
+        result.emplace(fn());
+        return ::testing::Value(*result, matcher);
+      },
+      settings);
+
+  // WaitUntil promise to call `fn` at least once and thus `result` is
+  // populated.
+  RTC_CHECK(result.has_value());
+  if (ok) {
+    return *std::move(result);
   }
 
-  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;
-  }
-
+  wait_until_internal::ExplainMatchResult(matcher, *result, &listener,
+                                          settings.result_name);
   return RTCError(RTCErrorType::INTERNAL_ERROR, listener.str());
 }
 
diff --git a/test/wait_until_unittest.cc b/test/wait_until_unittest.cc
index 6eff548..740ec0e 100644
--- a/test/wait_until_unittest.cc
+++ b/test/wait_until_unittest.cc
@@ -27,13 +27,24 @@
 namespace webrtc {
 namespace {
 
-using testing::_;
-using testing::AllOf;
-using testing::Eq;
-using testing::Ge;
-using testing::Gt;
-using testing::Lt;
-using testing::MatchesRegex;
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::Eq;
+using ::testing::Ge;
+using ::testing::Gt;
+using ::testing::Lt;
+using ::testing::MatchesRegex;
+using ::testing::Property;
+
+TEST(WaitUntilTest, ReturnsTrueWhenConditionIsMet) {
+  AutoThread thread;
+
+  int counter = 0;
+  EXPECT_TRUE(WaitUntil([&] { return ++counter == 3; }));
+
+  // Check that functor is not called after it returned true.
+  EXPECT_EQ(counter, 3);
+}
 
 TEST(WaitUntilTest, ReturnsWhenConditionIsMet) {
   AutoThread thread;
@@ -81,20 +92,22 @@
   SimulatedClock fake_clock(Timestamp::Millis(1337));
 
   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(fake_clock.CurrentTime(), Ge(Timestamp::Millis(1339)));
+  EXPECT_TRUE(WaitUntil(
+      [&] { return ++counter == 3; },
+      {.polling_interval = TimeDelta::Millis(10), .clock = &fake_clock}));
+  EXPECT_EQ(counter, 3);
+  // The fake clock should have advanced at least 2 polling intervals, 20ms.
+  EXPECT_THAT(fake_clock.CurrentTime(), Ge(Timestamp::Millis(1357)));
 }
 
 TEST(WaitUntilTest, ReturnsWhenConditionIsMetWithThreadProcessingFakeClock) {
   ScopedFakeClock fake_clock;
 
   int counter = 0;
-  RTCErrorOr<int> result =
-      WaitUntil([&] { return ++counter; }, Eq(3), {.clock = &fake_clock});
-  EXPECT_THAT(result, IsRtcOkAndHolds(3));
+  EXPECT_TRUE(WaitUntil(
+      [&] { return ++counter == 3; },
+      {.polling_interval = TimeDelta::Millis(1), .clock = &fake_clock}));
+  EXPECT_EQ(counter, 3);
   // The fake clock should have advanced at least 2ms.
   EXPECT_THAT(Timestamp::Micros(fake_clock.TimeNanos() * 1000),
               Ge(Timestamp::Millis(1339)));
@@ -104,22 +117,49 @@
   FakeClock fake_clock;
 
   int counter = 0;
-  RTCErrorOr<int> result =
-      WaitUntil([&] { return ++counter; }, Eq(3), {.clock = &fake_clock});
-  EXPECT_THAT(result, IsRtcOkAndHolds(3));
+  EXPECT_TRUE(WaitUntil(
+      [&] { return ++counter == 3; },
+      {.polling_interval = TimeDelta::Millis(1), .clock = &fake_clock}));
+  EXPECT_EQ(counter, 3);
   // The fake clock should have advanced at least 2ms.
   EXPECT_THAT(Timestamp::Micros(fake_clock.TimeNanos() * 1000),
               Ge(Timestamp::Millis(1339)));
 }
 
+// No default constuctor, not assignable, move-only type.
+class CustomType {
+ public:
+  explicit CustomType(int value) : value_(value) {}
+  CustomType(CustomType&&) = default;
+  CustomType& operator=(CustomType&&) = delete;
+  CustomType() = delete;
+
+  int value() const { return value_; }
+
+ private:
+  const int value_;
+};
+
+TEST(WaitUntilTest, RequiresOnlyMoveCopyConstructionForReturnedType) {
+  AutoThread thread;
+
+  int counter = 0;
+  RTCErrorOr<CustomType> result =
+      WaitUntil([&] { return CustomType(++counter); },
+                Property(&CustomType::value, Eq(3)));
+  EXPECT_THAT(result, IsRtcOkAndHolds(Property(&CustomType::value, Eq(3))));
+}
+
 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));
+  EXPECT_TRUE(WaitUntil([&] { return ++counter == 3; },
+                        {.polling_interval = TimeDelta::Millis(1),
+                         .clock = time_controller.get()}));
+  EXPECT_EQ(counter, 3);
+
   // The fake clock should have advanced at least 2ms.
   EXPECT_THAT(time_controller->GetClock()->CurrentTime(),
               Ge(Timestamp::Millis(1339)));