Initial pass of a proto_eq gtest macro.

This version works where both sides are the same message, messageview or messagemut type, but not any mix of them (e.g. cannot compare a message against its corresponding view).

PiperOrigin-RevId: 671091099
pull/18099/head
Protobuf Team Bot 6 months ago committed by Copybara-Service
parent e1dbeb0e61
commit 5d341470da
  1. 42
      rust/BUILD
  2. 24
      rust/cpp.rs
  3. 35
      rust/gtest_matchers.rs
  4. 6
      rust/internal.rs
  5. 44
      rust/test/shared/BUILD
  6. 59
      rust/test/shared/gtest_matchers_test.rs
  7. 52
      src/google/protobuf/compiler/rust/message.cc

@ -162,6 +162,48 @@ rust_library(
deps = [":protobuf_cpp"],
)
alias(
name = "protobuf_gtest_matchers",
actual = select({
":use_upb_kernel": ":protobuf_gtest_matchers_upb",
"//conditions:default": ":protobuf_gtest_matchers_cpp",
}),
)
rust_library(
name = "protobuf_gtest_matchers_cpp",
testonly = True,
srcs = ["gtest_matchers.rs"],
aliases = {
"//rust:protobuf_cpp": "protobuf",
},
visibility = [
"//rust:__subpackages__",
"//src/google/protobuf:__subpackages__",
],
deps = [
":protobuf_cpp",
"@crate_index//:googletest",
],
)
rust_library(
name = "protobuf_gtest_matchers_upb",
testonly = True,
srcs = ["gtest_matchers.rs"],
aliases = {
"//rust:protobuf_upb": "protobuf",
},
visibility = [
"//rust:__subpackages__",
"//src/google/protobuf:__subpackages__",
],
deps = [
":protobuf_upb",
"@crate_index//:googletest",
],
)
rust_library(
name = "utf8",
srcs = ["utf8.rs"],

@ -9,19 +9,19 @@
use crate::__internal::{Enum, Private};
use crate::{
IntoProxied, Map, MapIter, Mut, ProtoBytes, ProtoStr, ProtoString, Proxied, ProxiedInMapValue,
ProxiedInRepeated, Repeated, RepeatedMut, RepeatedView, View, MapMut, MapView,
IntoProxied, Map, MapIter, MapMut, MapView, Mut, ProtoBytes, ProtoStr, ProtoString, Proxied,
ProxiedInMapValue, ProxiedInRepeated, Repeated, RepeatedMut, RepeatedView, View,
};
use core::fmt::Debug;
use paste::paste;
use std::fmt;
use std::slice;
use std::convert::identity;
use core::fmt::Debug;
use std::ffi::{c_int, c_void};
use std::fmt;
use std::marker::PhantomData;
use std::mem::{ManuallyDrop, MaybeUninit};
use std::ops::Deref;
use std::ptr::{self, NonNull};
use std::ffi::{c_int, c_void};
use std::mem::{ManuallyDrop, MaybeUninit};
use std::slice;
/// Defines a set of opaque, unique, non-accessible pointees.
///
@ -339,14 +339,8 @@ pub fn debug_string(msg: RawMessage, f: &mut fmt::Formatter<'_>) -> fmt::Result
extern "C" {
/// # Safety
/// - `msg1` and `msg2` legally dereferencable MessageLite* pointers.
fn proto2_rust_messagelite_equals(msg1: RawMessage, msg2: RawMessage) -> bool;
}
/// # Safety
/// - `msg1` and `msg2` legally dereferencable MessageLite* pointers.
pub unsafe fn raw_message_equals(msg1: RawMessage, msg2: RawMessage) -> bool {
// SAFETY: Same constraints placed on caller.
unsafe { proto2_rust_messagelite_equals(msg1, msg2) }
#[link_name = "proto2_rust_messagelite_equals"]
pub fn raw_message_equals(msg1: RawMessage, msg2: RawMessage) -> bool;
}
pub type RawMapIter = UntypedMapIterator;

@ -0,0 +1,35 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2024 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
use googletest::description::Description;
use googletest::matcher::{Matcher, MatcherBase, MatcherResult};
use protobuf::__internal::MatcherEq;
#[derive(MatcherBase)]
pub struct MessageMatcher<T: MatcherEq> {
expected: T,
}
impl<T> Matcher<&T> for MessageMatcher<T>
where
T: MatcherEq,
{
fn matches(&self, actual: &T) -> MatcherResult {
actual.matches(&self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
MatcherResult::NoMatch => format!("is not equal to {:?}", self.expected).into(),
}
}
}
pub fn proto_eq<T: MatcherEq>(expected: T) -> MessageMatcher<T> {
MessageMatcher { expected }
}

@ -14,6 +14,7 @@ pub use paste::paste;
pub use crate::r#enum::Enum;
pub use crate::ProtoStr;
pub use std::fmt::Debug;
// TODO: Temporarily re-export these symbols which are now under
// __runtime under __internal since some external callers using it through
@ -31,3 +32,8 @@ pub struct Private;
/// permit in other crates; this trait is intended to be available to crates
/// which were generated by protoc, but not to application code.
pub trait SealedInternal {}
/// A trait used by the proto_eq() gtest macro.
pub trait MatcherEq: SealedInternal + Debug {
fn matches(&self, o: &Self) -> bool;
}

@ -466,10 +466,54 @@ rust_test(
srcs = ["proto_macro_test.rs"],
aliases = {
"//rust:protobuf_upb": "protobuf",
"//rust:protobuf_gtest_matchers_upb": "protobuf_gtest_matchers",
},
deps = [
"//rust:protobuf_gtest_matchers_upb",
"//rust:protobuf_upb",
"//rust/test:unittest_upb_rust_proto",
"@crate_index//:googletest",
],
)
rust_test(
name = "gtest_matchers_cpp_test",
srcs = ["gtest_matchers_test.rs"],
aliases = {
"//rust:protobuf_cpp": "protobuf",
"//rust:protobuf_gtest_matchers_cpp": "protobuf_gtest_matchers",
},
proc_macro_deps = [
"@crate_index//:paste",
],
deps = [
"//rust:protobuf_cpp",
"//rust:protobuf_gtest_matchers_cpp",
"//rust/test:unittest_cpp_rust_proto",
"//rust/test:unittest_edition_cpp_rust_proto",
"//rust/test:unittest_proto3_cpp_rust_proto",
"//rust/test:unittest_proto3_optional_cpp_rust_proto",
"@crate_index//:googletest",
],
)
rust_test(
name = "gtest_matchers_upb_test",
srcs = ["gtest_matchers_test.rs"],
aliases = {
"//rust:protobuf_upb": "protobuf",
"//rust:protobuf_gtest_matchers_upb": "protobuf_gtest_matchers",
},
proc_macro_deps = [
"@crate_index//:paste",
],
deps = [
"//rust:protobuf_gtest_matchers_upb",
"//rust:protobuf_upb",
"//rust/test:unittest_edition_upb_rust_proto",
"//rust/test:unittest_proto3_optional_upb_rust_proto",
"//rust/test:unittest_proto3_upb_rust_proto",
"//rust/test:unittest_upb_rust_proto",
"@crate_index//:googletest",
],
)

@ -0,0 +1,59 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2024 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
use googletest::prelude::*;
use paste::paste;
use protobuf_gtest_matchers::proto_eq;
use unittest_edition_rust_proto::TestAllTypes as TestAllTypesEditions;
use unittest_proto3_rust_proto::TestAllTypes as TestAllTypesProto3;
use unittest_rust_proto::TestAllTypes as TestAllTypesProto2;
macro_rules! generate_eq_msgs_tests {
($(($type: ident, $name_ext: ident)),*) => {
paste! {$(
#[gtest]
fn [<expect_eq_msgs_ $name_ext>]() {
let mut msg = [< $type >]::new();
let mut msg2 = [< $type >]::new();
msg.set_optional_int32(1);
msg2.set_optional_int32(1);
assert_that!(&msg.as_view(), proto_eq(msg2.as_view()));
assert_that!(&msg.as_mut(), proto_eq(msg2.as_mut()));
assert_that!(msg, proto_eq(msg2));
}
)*}
}
}
macro_rules! generate_not_eq_msgs_tests {
($(($type: ident, $name_ext: ident)),*) => {
paste! {$(
#[gtest]
fn [<expect_not_eq_msgs_ $name_ext>]() {
let mut msg = [< $type >]::new();
let mut msg2 = [< $type >]::new();
msg.set_optional_int32(1);
msg2.set_optional_int32(0);
assert_that!(&msg.as_view(), not(proto_eq(msg2.as_view())));
assert_that!(&msg.as_mut(), not(proto_eq(msg2.as_mut())));
assert_that!(&msg, not(proto_eq(msg2)));
}
)*}
}
}
generate_eq_msgs_tests!(
(TestAllTypesEditions, editions),
(TestAllTypesProto3, proto3),
(TestAllTypesProto2, proto2)
);
generate_not_eq_msgs_tests!(
(TestAllTypesEditions, editions),
(TestAllTypesProto3, proto3),
(TestAllTypesProto2, proto2)
);

@ -1357,6 +1357,30 @@ void GenerateRs(Context& ctx, const Descriptor& msg) {
self.msg.as_ptr() as *const _
}
}
impl $pbi$::MatcherEq for $Msg$ {
fn matches(&self, o: &Self) -> bool {
$pbi$::MatcherEq::matches(
&$pb$::AsView::as_view(self),
&$pb$::AsView::as_view(o))
}
}
impl<'a> $pbi$::MatcherEq for $Msg$Mut<'a> {
fn matches(&self, o: &Self) -> bool {
$pbi$::MatcherEq::matches(
&$pb$::AsView::as_view(self),
&$pb$::AsView::as_view(o))
}
}
impl<'a> $pbi$::MatcherEq for $Msg$View<'a> {
fn matches(&self, o: &Self) -> bool {
unsafe {
$pbr$::raw_message_equals(self.msg, o.msg)
}
}
}
)rs");
} else {
ctx.Emit({{"Msg", RsSafeName(msg.name())}}, R"rs(
@ -1377,6 +1401,34 @@ void GenerateRs(Context& ctx, const Descriptor& msg) {
self.msg.as_ptr() as *const _
}
}
impl $pbi$::MatcherEq for $Msg$ {
fn matches(&self, o: &Self) -> bool {
$pbi$::MatcherEq::matches(
&$pb$::AsView::as_view(self),
&$pb$::AsView::as_view(o))
}
}
impl<'a> $pbi$::MatcherEq for $Msg$Mut<'a> {
fn matches(&self, o: &Self) -> bool {
$pbi$::MatcherEq::matches(
&$pb$::AsView::as_view(self),
&$pb$::AsView::as_view(o))
}
}
impl<'a> $pbi$::MatcherEq for $Msg$View<'a> {
fn matches(&self, o: &Self) -> bool {
unsafe {
$pbr$::upb_Message_IsEqual(
self.msg,
o.msg,
<$Msg$ as $pbr$::AssociatedMiniTable>::mini_table(),
0)
}
}
}
)rs");
}
} // NOLINT(readability/fn_size)

Loading…
Cancel
Save