"../rtc_base:logging",
"../rtc_base:macromagic",
"../rtc_base/system:rtc_export",
+ "//third_party/abseil-cpp/absl/meta:type_traits",
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/strings:str_format",
"//third_party/abseil-cpp/absl/strings:string_view",
]
}
+rtc_source_set("rtc_error_matchers") {
+ testonly = true
+ sources = [ "test/rtc_error_matchers.h" ]
+ deps = [
+ ":rtc_error",
+ "../test:test_support",
+ "//third_party/abseil-cpp/absl/strings",
+ ]
+}
+
rtc_source_set("packet_socket_factory") {
visibility = [ "*" ]
sources = [ "packet_socket_factory.h" ]
"rtc_error\.h": [
"+rtc_base/logging.h",
+ "+absl/strings/has_absl_stringify.h",
+ "+absl/strings/str_format.h",
],
"rtc_event_log_output_file.h": [
# For private member and constructor.
#include <optional>
#include <string>
+#include <type_traits>
#include <utility> // For std::move.
+#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
HARDWARE_ENCODER_ERROR,
};
+// Outputs the error as a friendly string. Update this method when adding a new
+// error type.
+//
+// Only intended to be used for logging/diagnostics. The returned char* points
+// to literal strings that live for the whole duration of the program.
+RTC_EXPORT absl::string_view ToString(RTCErrorType error);
+RTC_EXPORT absl::string_view ToString(RTCErrorDetailType error);
+
// Roughly corresponds to RTCError in the web api. Holds an error type, a
// message, and possibly additional information specific to that error.
//
// error occurred.
bool ok() const { return type_ == RTCErrorType::NONE; }
+ template <typename Sink>
+ friend void AbslStringify(Sink& sink, const RTCError& error) {
+ sink.Append(ToString(error.type_));
+ if (!error.message_.empty()) {
+ sink.Append(" with message: \"");
+ sink.Append(error.message_);
+ sink.Append("\"");
+ }
+ }
+
private:
RTCErrorType type_ = RTCErrorType::NONE;
std::string message_;
std::optional<uint16_t> sctp_cause_code_;
};
-// Outputs the error as a friendly string. Update this method when adding a new
-// error type.
-//
-// Only intended to be used for logging/diagnostics. The returned char* points
-// to literal string that lives for the whole duration of the program.
-RTC_EXPORT absl::string_view ToString(RTCErrorType error);
-RTC_EXPORT absl::string_view ToString(RTCErrorDetailType error);
-
// Helper macro that can be used by implementations to create an error with a
// message and log it. `message` should be a string literal or movable
// std::string.
return std::move(*value_);
}
+ template <typename Sink>
+ friend void AbslStringify(Sink& sink, const RTCErrorOr<T>& error_or) {
+ if (error_or.ok()) {
+ sink.Append("OK");
+ if constexpr (std::is_convertible_v<T, absl::AlphaNum>) {
+ sink.Append(" with value: ");
+ sink.Append(absl::StrCat(error_or.value()));
+ }
+ } else {
+ sink.Append(absl::StrCat(error_or.error()));
+ }
+ }
+
private:
RTCError error_;
std::optional<T> value_;
#include <string>
#include <utility>
+#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "rtc_base/checks.h"
#include "test/gtest.h"
EXPECT_STREQ(e.message(), "string");
}
+TEST(RTCErrorTest, Stringify) {
+ RTCError e(RTCErrorType::INVALID_PARAMETER, "foo");
+ EXPECT_EQ(absl::StrCat(e), "INVALID_PARAMETER with message: \"foo\"");
+}
+
// Test that the default constructor creates an "INTERNAL_ERROR".
TEST(RTCErrorOrTest, DefaultConstructor) {
RTCErrorOr<MoveOnlyInt> e;
EXPECT_EQ(value.value, 88);
}
+TEST(RTCErrorOrTest, StringifyWithUnprintableValue) {
+ RTCErrorOr<MoveOnlyInt> e(MoveOnlyInt(1337));
+ EXPECT_EQ(absl::StrCat(e), "OK");
+}
+
+TEST(RTCErrorOrTest, StringifyWithStringValue) {
+ RTCErrorOr<absl::string_view> e("foo");
+ EXPECT_EQ(absl::StrCat(e), "OK with value: foo");
+}
+
+TEST(RTCErrorOrTest, StringifyWithPrintableValue) {
+ RTCErrorOr<int> e(1337);
+ EXPECT_EQ(absl::StrCat(e), "OK with value: 1337");
+}
+
+TEST(RTCErrorOrTest, StringifyWithError) {
+ RTCErrorOr<int> e({RTCErrorType::SYNTAX_ERROR, "message"});
+ EXPECT_EQ(absl::StrCat(e), "SYNTAX_ERROR with message: \"message\"");
+}
+
// Death tests.
// Disabled on Android because death tests misbehave on Android, see
// base/test/gtest_util.h.
"videocodec_test_fixture\.h": [
"+modules/video_coding/codecs/h264/include/h264_globals.h",
],
+ "rtc_error_matchers\.h": [
+ "+test/gmock.h",
+ ],
}
--- /dev/null
+/*
+ * 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 API_TEST_RTC_ERROR_MATCHERS_H_
+#define API_TEST_RTC_ERROR_MATCHERS_H_
+
+#include <string>
+
+#include "absl/strings/str_cat.h"
+#include "api/rtc_error.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+MATCHER(IsRtcOk, "") {
+ if (!arg.ok()) {
+ *result_listener << "Expected OK, got " << absl::StrCat(arg);
+ return false;
+ }
+ return true;
+}
+
+MATCHER_P(IsRtcOkAndHolds,
+ matcher,
+ "RtcErrorOr that is holding an OK status and ") {
+ if (!arg.ok()) {
+ *result_listener << "Expected OK, got " << absl::StrCat(arg);
+ return false;
+ }
+ return testing::ExplainMatchResult(matcher, arg.value(), result_listener);
+}
+
+MATCHER_P(IsRtcErrorWithType, error_type, ToString(error_type)) {
+ if (arg.ok()) {
+ *result_listener << "Expected " << ToString(error_type) << ", got OK.";
+ return false;
+ }
+ if (arg.type() != error_type) {
+ *result_listener << "Expected " << ToString(error_type) << ", got "
+ << ToString(arg.type());
+ return false;
+ }
+ return true;
+}
+
+MATCHER_P2(IsRtcErrorWithTypeAndMessage,
+ error_type,
+ message,
+ ToString(error_type)) {
+ if (arg.ok()) {
+ *result_listener << "Expected " << ToString(error_type) << ", got OK.";
+ return false;
+ }
+ if (arg.type() != error_type) {
+ *result_listener << "Expected " << ToString(error_type) << ", got "
+ << ToString(arg.type());
+ return false;
+ }
+ if (std::string(arg.message()) != message) {
+ *result_listener << "Expected message \"" << message << "\", got \""
+ << arg.message() << "\"";
+ return false;
+ }
+ return true;
+}
+
+MATCHER_P2(IsRtcErrorOrWithMessage,
+ error_matcher,
+ message_matcher,
+ "RtcErrorOr that is holding an error that " +
+ testing::DescribeMatcher<RTCError>(error_matcher, negation) +
+ (negation ? " or " : " and ") + " with a message that " +
+ testing::DescribeMatcher<std::string>(message_matcher,
+ negation)) {
+ if (arg.ok()) {
+ *result_listener << "Expected error, got " << absl::StrCat(arg);
+ return false;
+ }
+ return testing::ExplainMatchResult(error_matcher, arg.error(),
+ result_listener) &&
+ testing::ExplainMatchResult(message_matcher, arg.error().message(),
+ result_listener);
+}
+
+} // namespace webrtc
+
+#endif // API_TEST_RTC_ERROR_MATCHERS_H_
video_->SetSendCodecs(codecs);
video_->SetRecvCodecs(codecs);
}
+void FakeMediaEngine::SetVideoRecvCodecs(const std::vector<Codec>& codecs) {
+ video_->SetRecvCodecs(codecs);
+}
+void FakeMediaEngine::SetVideoSendCodecs(const std::vector<Codec>& codecs) {
+ video_->SetSendCodecs(codecs);
+}
+
void FakeMediaEngine::set_fail_create_channel(bool fail) {
voice_->fail_create_channel_ = fail;
video_->fail_create_channel_ = fail;
void SetAudioRecvCodecs(const std::vector<Codec>& codecs);
void SetAudioSendCodecs(const std::vector<Codec>& codecs);
void SetVideoCodecs(const std::vector<Codec>& codecs);
+ void SetVideoRecvCodecs(const std::vector<Codec>& codecs);
+ void SetVideoSendCodecs(const std::vector<Codec>& codecs);
void set_fail_create_channel(bool fail);
return true;
}
-int32_t FakeWebRtcVideoDecoder::Decode(const webrtc::EncodedImage&,
- int64_t) {
+int32_t FakeWebRtcVideoDecoder::Decode(const webrtc::EncodedImage&, int64_t) {
num_frames_received_++;
return WEBRTC_VIDEO_CODEC_OK;
}
decoders_.end());
}
+void FakeWebRtcVideoDecoderFactory::AddSupportedVideoCodec(
+ const webrtc::SdpVideoFormat& format) {
+ supported_codec_formats_.push_back(format);
+}
+
void FakeWebRtcVideoDecoderFactory::AddSupportedVideoCodecType(
const std::string& name) {
// This is to match the default H264 params of cricket::Codec.
const webrtc::SdpVideoFormat& format) override;
void DecoderDestroyed(FakeWebRtcVideoDecoder* decoder);
+ void AddSupportedVideoCodec(const webrtc::SdpVideoFormat& format);
void AddSupportedVideoCodecType(const std::string& name);
int GetNumCreatedDecoders();
const std::vector<FakeWebRtcVideoDecoder*>& decoders();
}
if (tizen_rtc_unittests) {
- deps -= [
- "../system_wrappers:metrics",
- ]
+ deps -= [ "../system_wrappers:metrics" ]
deps += [
"../rtc_base/synchronization:yield_policy",
"../api:packet_socket_factory",
"../api:priority",
"../api:rtc_error",
+ "../api:rtc_error_matchers",
"../api:rtp_sender_interface",
"../api:rtp_transceiver_direction",
"../api:scoped_refptr",
"../test:rtc_expect_death",
"../test:run_loop",
"../test:scoped_key_value_config",
+ "../test:wait_until",
"../test/pc/sctp:fake_sctp_transport",
"//testing/gtest",
"//third_party/abseil-cpp/absl/algorithm:container",
]
if (tizen_rtc_unittests) {
- deps -= [
- "../system_wrappers:metrics",
- ]
+ deps -= [ "../system_wrappers:metrics" ]
deps += [
"../rtc_base/synchronization:yield_policy",
":video_frame_writer",
":video_test_common",
":video_test_support",
+ ":wait_until",
":y4m_frame_generator",
"../api:array_view",
"../api:create_frame_generator",
"../api:mock_video_codec_factory",
"../api:mock_video_decoder",
"../api:mock_video_encoder",
+ "../api:rtc_error",
+ "../api:rtc_error_matchers",
"../api:scoped_refptr",
"../api:simulcast_test_fixture_api",
"../api/environment",
"../api/units:data_size",
"../api/units:frequency",
"../api/units:time_delta",
+ "../api/units:timestamp",
"../api/video:encoded_image",
"../api/video:video_frame",
"../api/video_codecs:builtin_video_decoder_factory",
"../modules/video_coding/svc:scalability_mode_util",
"../rtc_base:criticalsection",
"../rtc_base:rtc_event",
+ "../rtc_base:threading",
"../rtc_base/synchronization:mutex",
"../rtc_base/system:file_wrapper",
+ "../system_wrappers",
"jitter:jitter_unittests",
"pc/e2e:e2e_unittests",
"pc/e2e/analyzer/video:video_analyzer_unittests",
"testsupport/yuv_frame_reader_unittest.cc",
"testsupport/yuv_frame_writer_unittest.cc",
"video_codec_tester_unittest.cc",
+ "wait_until_unittest.cc",
]
if (rtc_enable_protobuf) {
"//third_party/libyuv",
]
}
+
+rtc_source_set("wait_until") {
+ testonly = true
+ sources = [
+ "wait_until.h",
+ "wait_until_internal.h",
+ ]
+ deps = [
+ ":test_support",
+ "../api:rtc_error",
+ "../api/units:time_delta",
+ "../api/units:timestamp",
+ "../rtc_base:checks",
+ "../rtc_base:threading",
+ "../system_wrappers",
+ "//third_party/abseil-cpp/absl/base:nullability",
+ "//third_party/abseil-cpp/absl/strings:string_view",
+ ]
+}
--- /dev/null
+/*
+ * 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 <string>
+
+#include "absl/base/nullability.h"
+#include "api/rtc_error.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/checks.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 {
+
+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<SimulatedClock*> clock = nullptr;
+
+ // 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<int> result = Waituntil([&] { return ++counter; }, Eq(3))
+// EXPECT_THAT(result, IsOkAndHolds(3));
+template <typename Fn, typename Matcher>
+auto WaitUntil(const Fn& fn, Matcher matcher, WaitUntilSettings settings = {})
+ -> RTCErrorOr<decltype(fn())> {
+ if (!settings.clock) {
+ RTC_CHECK(rtc::Thread::Current()) << "A current thread is required. An "
+ "rtc::AutoThread can work for tests.";
+ }
+
+ absl::Nonnull<Clock*> clock =
+ settings.clock ? settings.clock : Clock::GetRealTimeClock();
+
+ Timestamp start = clock->CurrentTime();
+
+ do {
+ auto result = fn();
+ 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);
+
+ // 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_
--- /dev/null
+/*
+ * 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_INTERNAL_H_
+#define TEST_WAIT_UNTIL_INTERNAL_H_
+
+#include <string>
+
+#include "absl/base/nullability.h"
+#include "absl/strings/string_view.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace wait_until_internal {
+
+// Explains the match result of `matcher` against `value` to `listener`.
+// `value_name` is the name of the value to be used in the error message.
+// This is inspired by testing::ExplainMatchResult and
+// testing::internal::MatchPrintAndExplain.
+template <typename T, typename M>
+bool ExplainMatchResult(
+ const M& matcher,
+ const T& value,
+ 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<const T&> safe_matcher =
+ testing::SafeMatcherCast<const T&>(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)) {
+ return true;
+ }
+ *ss << testing::PrintToString(value);
+ if (const std::string& inner_message = inner_listener.str();
+ !inner_message.empty()) {
+ *ss << ", " << inner_message;
+ }
+ return false;
+}
+
+} // namespace wait_until_internal
+} // namespace webrtc
+
+#endif // TEST_WAIT_UNTIL_INTERNAL_H_
--- /dev/null
+/*
+ * 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 "api/rtc_error.h"
+#include "api/test/rtc_error_matchers.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "rtc_base/thread.h"
+#include "system_wrappers/include/clock.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+using testing::_;
+using testing::AllOf;
+using testing::Eq;
+using testing::Ge;
+using testing::Gt;
+using testing::Lt;
+using testing::MatchesRegex;
+
+TEST(WaitUntilTest, ReturnsWhenConditionIsMet) {
+ rtc::AutoThread thread;
+
+ int counter = 0;
+ RTCErrorOr<int> result = WaitUntil([&] { return ++counter; }, Eq(3));
+ EXPECT_THAT(result, IsRtcOkAndHolds(3));
+}
+
+TEST(WaitUntilTest, ReturnsErrorWhenTimeoutIsReached) {
+ rtc::AutoThread thread;
+ int counter = 0;
+ RTCErrorOr<int> result =
+ WaitUntil([&] { return --counter; }, Eq(1),
+ {.timeout = TimeDelta::Millis(10), .result_name = "counter"});
+ // Only returns the last error. Note we only are checking that the error
+ // message ends with a negative number rather than a specific number to avoid
+ // flakiness.
+ EXPECT_THAT(
+ result,
+ IsRtcErrorOrWithMessage(
+ _, MatchesRegex(
+ "Value of: counter\nExpected: is equal to 1\nActual: -\\d+")));
+}
+
+TEST(WaitUntilTest, ErrorContainsMatcherExplanation) {
+ rtc::AutoThread thread;
+ int counter = 0;
+ auto matcher = AllOf(Gt(0), Lt(10));
+ RTCErrorOr<int> result =
+ WaitUntil([&] { return --counter; }, matcher,
+ {.timeout = TimeDelta::Millis(10), .result_name = "counter"});
+ // Only returns the last error. Note we only are checking that the error
+ // message ends with a negative number rather than a specific number to avoid
+ // flakiness.
+ EXPECT_THAT(
+ result,
+ IsRtcErrorOrWithMessage(
+ _, MatchesRegex("Value of: counter\nExpected: \\(is > 0\\) and "
+ "\\(is < 10\\)\nActual: -\\d+, which doesn't match "
+ "\\(is > 0\\)")));
+}
+
+TEST(WaitUntilTest, ReturnsWhenConditionIsMetWithSimulatedClock) {
+ SimulatedClock fake_clock = SimulatedClock(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)));
+}
+
+} // namespace
+} // namespace webrtc
\ No newline at end of file