Add custom matchers for ProxiedWithPresence.
We continue our foray into googletest-rust [1], this time with homegrown matchers.
Our unit tests will now be able to check is_unset() and is_set() for all types that implement ProxiedWithPresence. In practice, this boils down to [u8] and ProtoStr.
Note that we've broken out matchers_upb and matchers_cpp, similar to what was done with aliasing here [2].
[1] https://github.com/google/googletest-rust
[2] https://github.com/protocolbuffers/protobuf/commit/9a0bc392b3011cabc214f01b41314acab33658c2#diff-08e5182ff36ad340a3bfb628995524a2a36a89b59a514ba027b0f25e048dd5c3R90
PiperOrigin-RevId: 573895179
diff --git a/rust/test/shared/BUILD b/rust/test/shared/BUILD
index 32fd7c1..e5be7b7 100644
--- a/rust/test/shared/BUILD
+++ b/rust/test/shared/BUILD
@@ -14,7 +14,31 @@
# Once we have a couple of these tests we will investigate ways to remove boilerplate (for example
# by introducing a build macro that registers 2 rust_test targets).
-load("@rules_rust//rust:defs.bzl", "rust_test")
+load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
+
+rust_library(
+ name = "matchers_upb",
+ srcs = ["matchers.rs"],
+ aliases = {
+ "//rust:protobuf_upb": "protobuf",
+ },
+ deps = [
+ "//third_party/gtest_rust/googletest",
+ "//rust:protobuf_upb",
+ ],
+)
+
+rust_library(
+ name = "matchers_cpp",
+ srcs = ["matchers.rs"],
+ aliases = {
+ "//rust:protobuf_cpp": "protobuf",
+ },
+ deps = [
+ "//third_party/gtest_rust/googletest",
+ "//rust:protobuf_cpp",
+ ],
+)
rust_test(
name = "child_parent_upb_test",
@@ -125,6 +149,7 @@
srcs = ["accessors_test.rs"],
aliases = {
"//rust:protobuf_cpp": "protobuf",
+ "//rust/test/shared:matchers_cpp": "matchers",
},
tags = [
# TODO: Enable testing on arm once we support sanitizers for Rust on Arm.
@@ -134,6 +159,7 @@
"//third_party/gtest_rust/googletest",
"//rust:protobuf_cpp",
"//rust/test:unittest_cc_rust_proto",
+ "//rust/test/shared:matchers_cpp",
],
)
@@ -142,6 +168,7 @@
srcs = ["accessors_test.rs"],
aliases = {
"//rust:protobuf_upb": "protobuf",
+ "//rust/test/shared:matchers_upb": "matchers",
},
tags = [
# TODO: Enable testing on arm once we support sanitizers for Rust on Arm.
@@ -151,6 +178,7 @@
"//third_party/gtest_rust/googletest",
"//rust:protobuf_upb",
"//rust/test:unittest_upb_rust_proto",
+ "//rust/test/shared:matchers_upb",
],
)
diff --git a/rust/test/shared/accessors_test.rs b/rust/test/shared/accessors_test.rs
index 7f2db2c..40098b1 100644
--- a/rust/test/shared/accessors_test.rs
+++ b/rust/test/shared/accessors_test.rs
@@ -8,6 +8,7 @@
//! Tests covering accessors for singular bool, int32, int64, and bytes fields.
use googletest::prelude::*;
+use matchers::{is_set, is_unset};
use protobuf::Optional;
use unittest_proto::proto2_unittest::{TestAllTypes, TestAllTypes_};
@@ -199,10 +200,10 @@
#[test]
fn test_optional_bytes_accessors() {
let mut msg = TestAllTypes::new();
- assert_that!(msg.optional_bytes(), eq(b""));
+ assert_that!(*msg.optional_bytes(), empty());
assert_that!(msg.optional_bytes_opt(), eq(Optional::Unset(&b""[..])));
- assert_that!(msg.optional_bytes_mut().get(), eq(b""));
- assert_that!(msg.optional_bytes_mut().is_unset(), eq(true));
+ assert_that!(*msg.optional_bytes_mut().get(), empty());
+ assert_that!(msg.optional_bytes_mut(), is_unset());
{
let s = Vec::from(&b"hello world"[..]);
@@ -210,34 +211,34 @@
}
assert_that!(msg.optional_bytes(), eq(b"hello world"));
assert_that!(msg.optional_bytes_opt(), eq(Optional::Set(&b"hello world"[..])));
- assert_that!(msg.optional_bytes_mut().is_set(), eq(true));
+ assert_that!(msg.optional_bytes_mut(), is_set());
assert_that!(msg.optional_bytes_mut().get(), eq(b"hello world"));
msg.optional_bytes_mut().or_default().set(b"accessors_test");
assert_that!(msg.optional_bytes(), eq(b"accessors_test"));
assert_that!(msg.optional_bytes_opt(), eq(Optional::Set(&b"accessors_test"[..])));
- assert_that!(msg.optional_bytes_mut().is_set(), eq(true));
+ assert_that!(msg.optional_bytes_mut(), is_set());
assert_that!(msg.optional_bytes_mut().get(), eq(b"accessors_test"));
assert_that!(msg.optional_bytes_mut().or_default().get(), eq(b"accessors_test"));
msg.optional_bytes_mut().clear();
- assert_that!(msg.optional_bytes(), eq(b""));
+ assert_that!(*msg.optional_bytes(), empty());
assert_that!(msg.optional_bytes_opt(), eq(Optional::Unset(&b""[..])));
- assert_that!(msg.optional_bytes_mut().is_unset(), eq(true));
+ assert_that!(msg.optional_bytes_mut(), is_unset());
msg.optional_bytes_mut().set(b"");
- assert_that!(msg.optional_bytes(), eq(b""));
+ assert_that!(*msg.optional_bytes(), empty());
assert_that!(msg.optional_bytes_opt(), eq(Optional::Set(&b""[..])));
msg.optional_bytes_mut().clear();
msg.optional_bytes_mut().or_default();
- assert_that!(msg.optional_bytes(), eq(b""));
+ assert_that!(*msg.optional_bytes(), empty());
assert_that!(msg.optional_bytes_opt(), eq(Optional::Set(&b""[..])));
msg.optional_bytes_mut().or_default().set(b"\xffbinary\x85non-utf8");
assert_that!(msg.optional_bytes(), eq(b"\xffbinary\x85non-utf8"));
assert_that!(msg.optional_bytes_opt(), eq(Optional::Set(&b"\xffbinary\x85non-utf8"[..])));
- assert_that!(msg.optional_bytes_mut().is_set(), eq(true));
+ assert_that!(msg.optional_bytes_mut(), is_set());
assert_that!(msg.optional_bytes_mut().get(), eq(b"\xffbinary\x85non-utf8"));
assert_that!(msg.optional_bytes_mut().or_default().get(), eq(b"\xffbinary\x85non-utf8"));
}
@@ -248,7 +249,7 @@
assert_that!(msg.default_bytes(), eq(b"world"));
assert_that!(msg.default_bytes_opt(), eq(Optional::Unset(&b"world"[..])));
assert_that!(msg.default_bytes_mut().get(), eq(b"world"));
- assert_that!(msg.default_bytes_mut().is_unset(), eq(true));
+ assert_that!(msg.default_bytes_mut(), is_unset());
{
let s = String::from("hello world");
@@ -256,23 +257,23 @@
}
assert_that!(msg.default_bytes(), eq(b"hello world"));
assert_that!(msg.default_bytes_opt(), eq(Optional::Set(&b"hello world"[..])));
- assert_that!(msg.default_bytes_mut().is_set(), eq(true));
+ assert_that!(msg.default_bytes_mut(), is_set());
assert_that!(msg.default_bytes_mut().get(), eq(b"hello world"));
msg.default_bytes_mut().or_default().set(b"accessors_test");
assert_that!(msg.default_bytes(), eq(b"accessors_test"));
assert_that!(msg.default_bytes_opt(), eq(Optional::Set(&b"accessors_test"[..])));
- assert_that!(msg.default_bytes_mut().is_set(), eq(true));
+ assert_that!(msg.default_bytes_mut(), is_set());
assert_that!(msg.default_bytes_mut().get(), eq(b"accessors_test"));
assert_that!(msg.default_bytes_mut().or_default().get(), eq(b"accessors_test"));
msg.default_bytes_mut().clear();
assert_that!(msg.default_bytes(), eq(b"world"));
assert_that!(msg.default_bytes_opt(), eq(Optional::Unset(&b"world"[..])));
- assert_that!(msg.default_bytes_mut().is_unset(), eq(true));
+ assert_that!(msg.default_bytes_mut(), is_unset());
msg.default_bytes_mut().set(b"");
- assert_that!(msg.default_bytes(), eq(b""));
+ assert_that!(*msg.default_bytes(), empty());
assert_that!(msg.default_bytes_opt(), eq(Optional::Set(&b""[..])));
msg.default_bytes_mut().clear();
@@ -283,7 +284,7 @@
msg.default_bytes_mut().or_default().set(b"\xffbinary\x85non-utf8");
assert_that!(msg.default_bytes(), eq(b"\xffbinary\x85non-utf8"));
assert_that!(msg.default_bytes_opt(), eq(Optional::Set(&b"\xffbinary\x85non-utf8"[..])));
- assert_that!(msg.default_bytes_mut().is_set(), eq(true));
+ assert_that!(msg.default_bytes_mut(), is_set());
assert_that!(msg.default_bytes_mut().get(), eq(b"\xffbinary\x85non-utf8"));
assert_that!(msg.default_bytes_mut().or_default().get(), eq(b"\xffbinary\x85non-utf8"));
}
@@ -294,7 +295,7 @@
assert_that!(msg.optional_string(), eq(""));
assert_that!(msg.optional_string_opt(), eq(Optional::Unset("".into())));
assert_that!(msg.optional_string_mut().get(), eq(""));
- assert_that!(msg.optional_string_mut().is_unset(), eq(true));
+ assert_that!(msg.optional_string_mut(), is_unset());
{
let s = String::from("hello world");
@@ -302,20 +303,20 @@
}
assert_that!(msg.optional_string(), eq("hello world"));
assert_that!(msg.optional_string_opt(), eq(Optional::Set("hello world".into())));
- assert_that!(msg.optional_string_mut().is_set(), eq(true));
+ assert_that!(msg.optional_string_mut(), is_set());
assert_that!(msg.optional_string_mut().get(), eq("hello world"));
msg.optional_string_mut().or_default().set("accessors_test");
assert_that!(msg.optional_string(), eq("accessors_test"));
assert_that!(msg.optional_string_opt(), eq(Optional::Set("accessors_test".into())));
- assert_that!(msg.optional_string_mut().is_set(), eq(true));
+ assert_that!(msg.optional_string_mut(), is_set());
assert_that!(msg.optional_string_mut().get(), eq("accessors_test"));
assert_that!(msg.optional_string_mut().or_default().get(), eq("accessors_test"));
msg.optional_string_mut().clear();
assert_that!(msg.optional_string(), eq(""));
assert_that!(msg.optional_string_opt(), eq(Optional::Unset("".into())));
- assert_that!(msg.optional_string_mut().is_unset(), eq(true));
+ assert_that!(msg.optional_string_mut(), is_unset());
msg.optional_string_mut().set("");
assert_that!(msg.optional_string(), eq(""));
@@ -333,7 +334,7 @@
assert_that!(msg.default_string(), eq("hello"));
assert_that!(msg.default_string_opt(), eq(Optional::Unset("hello".into())));
assert_that!(msg.default_string_mut().get(), eq("hello"));
- assert_that!(msg.default_string_mut().is_unset(), eq(true));
+ assert_that!(msg.default_string_mut(), is_unset());
{
let s = String::from("hello world");
@@ -341,20 +342,20 @@
}
assert_that!(msg.default_string(), eq("hello world"));
assert_that!(msg.default_string_opt(), eq(Optional::Set("hello world".into())));
- assert_that!(msg.default_string_mut().is_set(), eq(true));
+ assert_that!(msg.default_string_mut(), is_set());
assert_that!(msg.default_string_mut().get(), eq("hello world"));
msg.default_string_mut().or_default().set("accessors_test");
assert_that!(msg.default_string(), eq("accessors_test"));
assert_that!(msg.default_string_opt(), eq(Optional::Set("accessors_test".into())));
- assert_that!(msg.default_string_mut().is_set(), eq(true));
+ assert_that!(msg.default_string_mut(), is_set());
assert_that!(msg.default_string_mut().get(), eq("accessors_test"));
assert_that!(msg.default_string_mut().or_default().get(), eq("accessors_test"));
msg.default_string_mut().clear();
assert_that!(msg.default_string(), eq("hello"));
assert_that!(msg.default_string_opt(), eq(Optional::Unset("hello".into())));
- assert_that!(msg.default_string_mut().is_unset(), eq(true));
+ assert_that!(msg.default_string_mut(), is_unset());
msg.default_string_mut().set("");
assert_that!(msg.default_string(), eq(""));
diff --git a/rust/test/shared/matchers.rs b/rust/test/shared/matchers.rs
new file mode 100644
index 0000000..f89f1dd
--- /dev/null
+++ b/rust/test/shared/matchers.rs
@@ -0,0 +1,58 @@
+use googletest::matcher::MatcherResult;
+use googletest::prelude::*;
+use protobuf::{AbsentField, Optional, PresentField, ProxiedWithPresence};
+use std::marker::PhantomData;
+
+/// ===============================
+/// IS_UNSET
+/// ===============================
+pub fn is_unset<'a, T: std::fmt::Debug + ProxiedWithPresence + ?Sized + 'a>()
+-> impl Matcher<ActualT = Optional<PresentField<'a, T>, AbsentField<'a, T>>> {
+ UnsetMatcher::<T> { _phantom: PhantomData }
+}
+
+struct UnsetMatcher<'a, T: ProxiedWithPresence + ?Sized> {
+ _phantom: PhantomData<PresentField<'a, T>>,
+}
+
+impl<'a, T: std::fmt::Debug + ProxiedWithPresence + ?Sized> Matcher for UnsetMatcher<'a, T> {
+ type ActualT = Optional<PresentField<'a, T>, AbsentField<'a, T>>;
+
+ fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+ actual.is_unset().into()
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> String {
+ match matcher_result {
+ MatcherResult::Match => "is not set".to_string(),
+ MatcherResult::NoMatch => "is set".to_string(),
+ }
+ }
+}
+
+/// ===============================
+/// IS_SET
+/// ===============================
+pub fn is_set<'a, T: std::fmt::Debug + ProxiedWithPresence + ?Sized + 'a>()
+-> impl Matcher<ActualT = Optional<PresentField<'a, T>, AbsentField<'a, T>>> {
+ SetMatcher::<T> { _phantom: PhantomData }
+}
+
+struct SetMatcher<'a, T: ProxiedWithPresence + ?Sized> {
+ _phantom: PhantomData<PresentField<'a, T>>,
+}
+
+impl<'a, T: std::fmt::Debug + ProxiedWithPresence + ?Sized> Matcher for SetMatcher<'a, T> {
+ type ActualT = Optional<PresentField<'a, T>, AbsentField<'a, T>>;
+
+ fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+ actual.is_set().into()
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> String {
+ match matcher_result {
+ MatcherResult::Match => "is set".to_string(),
+ MatcherResult::NoMatch => "is not set".to_string(),
+ }
+ }
+}