mirror of
https://github.com/protocolbuffers/protobuf
synced 2026-08-26 02:23:14 -04:00
426 lines
17 KiB
C++
426 lines
17 KiB
C++
// Protocol Buffers - Google's data interchange format
|
|
// Copyright 2023 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
|
|
|
|
#include "upb/message/internal/compare_unknown.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <cstddef>
|
|
#include <initializer_list>
|
|
#include <string>
|
|
|
|
#include <gtest/gtest.h>
|
|
#include "absl/cleanup/cleanup.h"
|
|
#include "google/protobuf/test_messages_proto2.upb.h"
|
|
#include "upb/base/string_view.h"
|
|
#include "upb/base/upcast.h"
|
|
#include "upb/mem/alloc.h"
|
|
#include "upb/mem/arena.h"
|
|
#include "upb/mem/arena.hpp"
|
|
#include "upb/message/compare.h"
|
|
#include "upb/message/internal/accessors.h"
|
|
#include "upb/message/internal/message.h"
|
|
#include "upb/message/message.h"
|
|
#include "upb/message/test.upb.h"
|
|
#include "upb/message/test.upb_minitable.h"
|
|
#include "upb/message/unknown_fields.h"
|
|
#include "upb/test/test.upb.h"
|
|
#include "upb/test/test.upb_minitable.h"
|
|
#include "upb/wire/encode.h"
|
|
#include "upb/wire/test_util/wire_message.h"
|
|
|
|
// Must be last.
|
|
#include "upb/port/def.inc"
|
|
|
|
namespace upb {
|
|
namespace test {
|
|
|
|
namespace {
|
|
|
|
using ::upb::test::wire_types::Delimited;
|
|
using ::upb::test::wire_types::Fixed32;
|
|
using ::upb::test::wire_types::Fixed64;
|
|
using ::upb::test::wire_types::Group;
|
|
using ::upb::test::wire_types::Varint;
|
|
using ::upb::test::wire_types::WireMessage;
|
|
|
|
upb_UnknownCompareResult CompareUnknownWithMaxDepth(
|
|
WireMessage uf1, WireMessage uf2, int max_depth, int min_tag_length = 1,
|
|
int min_val_varint_length = 1) {
|
|
upb::Arena arena1;
|
|
upb::Arena arena2;
|
|
protobuf_test_messages_proto2_TestAllTypesProto2* msg1 =
|
|
protobuf_test_messages_proto2_TestAllTypesProto2_new(arena1.ptr());
|
|
protobuf_test_messages_proto2_TestAllTypesProto2* msg2 =
|
|
protobuf_test_messages_proto2_TestAllTypesProto2_new(arena2.ptr());
|
|
// Add the unknown fields to the messages.
|
|
std::string buf1 = ToBinaryPayloadWithLongVarints(uf1, min_tag_length,
|
|
min_val_varint_length);
|
|
std::string buf2 = ToBinaryPayloadWithLongVarints(uf2, min_tag_length,
|
|
min_val_varint_length);
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_AddUnknown)(
|
|
UPB_UPCAST(msg1), buf1.data(), buf1.size(), arena1.ptr(),
|
|
kUpb_AddUnknown_Copy));
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_AddUnknown)(
|
|
UPB_UPCAST(msg2), buf2.data(), buf2.size(), arena2.ptr(),
|
|
kUpb_AddUnknown_Copy));
|
|
return _upb_Message_UnknownFieldsAreEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
|
|
max_depth);
|
|
}
|
|
|
|
upb_UnknownCompareResult CompareUnknown(WireMessage uf1, WireMessage uf2) {
|
|
return CompareUnknownWithMaxDepth(uf1, uf2, 64);
|
|
}
|
|
|
|
TEST(CompareTest, UnknownFieldsReflexive) {
|
|
EXPECT_EQ(kUpb_UnknownCompareResult_Equal, CompareUnknown({}, {}));
|
|
EXPECT_EQ(kUpb_UnknownCompareResult_Equal,
|
|
CompareUnknown({{1, Varint(123)}, {2, Fixed32(456)}},
|
|
{{1, Varint(123)}, {2, Fixed32(456)}}));
|
|
EXPECT_EQ(
|
|
kUpb_UnknownCompareResult_Equal,
|
|
CompareUnknown(
|
|
{{1, Group({{2, Group({{3, Fixed32(456)}, {4, Fixed64(123)}})}})}},
|
|
{{1, Group({{2, Group({{3, Fixed32(456)}, {4, Fixed64(123)}})}})}}));
|
|
}
|
|
|
|
TEST(CompareTest, UnknownFieldsOrdering) {
|
|
EXPECT_EQ(kUpb_UnknownCompareResult_Equal,
|
|
CompareUnknown({{1, Varint(111)},
|
|
{2, Delimited("ABC")},
|
|
{3, Fixed32(456)},
|
|
{4, Fixed64(123)},
|
|
{5, Group({})}},
|
|
{{5, Group({})},
|
|
{4, Fixed64(123)},
|
|
{3, Fixed32(456)},
|
|
{2, Delimited("ABC")},
|
|
{1, Varint(111)}}));
|
|
EXPECT_EQ(kUpb_UnknownCompareResult_NotEqual,
|
|
CompareUnknown({{1, Varint(111)},
|
|
{2, Delimited("ABC")},
|
|
{3, Fixed32(456)},
|
|
{4, Fixed64(123)},
|
|
{5, Group({})}},
|
|
{{5, Group({})},
|
|
{4, Fixed64(123)},
|
|
{3, Fixed32(455)}, // Small difference.
|
|
{2, Delimited("ABC")},
|
|
{1, Varint(111)}}));
|
|
EXPECT_EQ(kUpb_UnknownCompareResult_Equal,
|
|
CompareUnknown({{3, Fixed32(456)}, {4, Fixed64(123)}},
|
|
{{4, Fixed64(123)}, {3, Fixed32(456)}}));
|
|
EXPECT_EQ(
|
|
kUpb_UnknownCompareResult_Equal,
|
|
CompareUnknown(
|
|
{{1, Group({{2, Group({{3, Fixed32(456)}, {4, Fixed64(123)}})}})}},
|
|
{{1, Group({{2, Group({{4, Fixed64(123)}, {3, Fixed32(456)}})}})}}));
|
|
}
|
|
|
|
TEST(CompareTest, LongVarint) {
|
|
EXPECT_EQ(kUpb_UnknownCompareResult_Equal,
|
|
CompareUnknownWithMaxDepth({{1, Varint(123)}, {2, Varint(456)}},
|
|
{{1, Varint(123)}, {2, Varint(456)}}, 64,
|
|
5, 10));
|
|
EXPECT_EQ(kUpb_UnknownCompareResult_Equal,
|
|
CompareUnknownWithMaxDepth({{2, Varint(456)}, {1, Varint(123)}},
|
|
{{1, Varint(123)}, {2, Varint(456)}}, 64,
|
|
5, 10));
|
|
}
|
|
|
|
TEST(CompareTest, MaxDepth) {
|
|
EXPECT_EQ(
|
|
kUpb_UnknownCompareResult_MaxDepthExceeded,
|
|
CompareUnknownWithMaxDepth(
|
|
{{1, Group({{2, Group({{3, Fixed32(456)}, {4, Fixed64(123)}})}})}},
|
|
{{1, Group({{2, Group({{4, Fixed64(123)}, {3, Fixed32(456)}})}})}},
|
|
1));
|
|
}
|
|
|
|
TEST(CompareTest, MessageIsEqualWithIdenticalNonCanonicalExtensions) {
|
|
upb::Arena arena;
|
|
|
|
// 1. Create base msg1 and msg2 which start empty
|
|
upb_test_ModelWithExtensions* msg1 =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
upb_test_ModelWithExtensions* msg2 =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
|
|
// 2. Create sub-message value ("World")
|
|
upb_test_ModelExtension1* extension1 =
|
|
upb_test_ModelExtension1_new(arena.ptr());
|
|
upb_test_ModelExtension1_set_str(extension1,
|
|
upb_StringView_FromString("World"));
|
|
|
|
upb_test_ModelExtension1* extension2 =
|
|
upb_test_ModelExtension1_new(arena.ptr());
|
|
upb_test_ModelExtension1_set_str(extension2,
|
|
upb_StringView_FromString("World"));
|
|
|
|
// 3. Attach identical non-canonical extensions to both messages
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(msg1), upb_test_ModelExtension1_model_ext_ext, &extension1,
|
|
arena.ptr()));
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(msg2), upb_test_ModelExtension1_model_ext_ext, &extension2,
|
|
arena.ptr()));
|
|
|
|
// 4. Verify upb_Message_IsEqual yields true
|
|
bool is_equal = upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
|
|
&upb_0test__ModelWithExtensions_msg_init,
|
|
kUpb_CompareOption_IncludeUnknownFields);
|
|
EXPECT_TRUE(is_equal);
|
|
}
|
|
|
|
TEST(CompareTest, MessageIsEqualWithDifferentNonCanonicalExtensions) {
|
|
upb::Arena arena;
|
|
|
|
// 1. Create base msg1 and msg2 which start empty
|
|
upb_test_ModelWithExtensions* msg1 =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
upb_test_ModelWithExtensions* msg2 =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
|
|
// 2. Create custom sub-message 1 ("World")
|
|
upb_test_ModelExtension1* extension1 =
|
|
upb_test_ModelExtension1_new(arena.ptr());
|
|
upb_test_ModelExtension1_set_str(extension1,
|
|
upb_StringView_FromString("World"));
|
|
|
|
// 3. Create custom sub-message 2 ("Hello")
|
|
upb_test_ModelExtension1* extension2 =
|
|
upb_test_ModelExtension1_new(arena.ptr());
|
|
upb_test_ModelExtension1_set_str(extension2,
|
|
upb_StringView_FromString("Hello"));
|
|
|
|
// 4. Attach different non-canonical extensions to the two messages
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(msg1), upb_test_ModelExtension1_model_ext_ext, &extension1,
|
|
arena.ptr()));
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(msg2), upb_test_ModelExtension1_model_ext_ext, &extension2,
|
|
arena.ptr()));
|
|
|
|
// Verify that NextUnknown2 successfully yields the non-canonical extension
|
|
upb_MessageUnknown udata;
|
|
uintptr_t uiter = kUpb_Message_UnknownBegin;
|
|
ASSERT_TRUE(upb_Message_NextUnknown2(UPB_UPCAST(msg1), &udata, &uiter));
|
|
EXPECT_EQ(udata.type, kUpb_MessageUnknownType_NonCanonicalExtension);
|
|
|
|
// 5. Verify upb_Message_IsEqual yields false because they are different!
|
|
bool is_equal = upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
|
|
&upb_0test__ModelWithExtensions_msg_init,
|
|
kUpb_CompareOption_IncludeUnknownFields);
|
|
EXPECT_FALSE(is_equal);
|
|
}
|
|
|
|
TEST(CompareTest, MessageIsEqualWithOnlyOneHavingNonCanonicalExtension) {
|
|
upb::Arena arena;
|
|
|
|
// 1. Create base msg1 and msg2
|
|
upb_test_ModelWithExtensions* msg1 =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
upb_test_ModelWithExtensions* msg2 =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
|
|
// 2. Create parsed submessage ("World")
|
|
upb_test_ModelExtension1* extension1 =
|
|
upb_test_ModelExtension1_new(arena.ptr());
|
|
upb_test_ModelExtension1_set_str(extension1,
|
|
upb_StringView_FromString("World"));
|
|
|
|
// 3. Attach only to msg1
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(msg1), upb_test_ModelExtension1_model_ext_ext, &extension1,
|
|
arena.ptr()));
|
|
|
|
// 4. Verify upb_Message_IsEqual yields false with IncludeUnknownFields
|
|
bool is_equal_with_unknowns =
|
|
upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
|
|
&upb_0test__ModelWithExtensions_msg_init,
|
|
kUpb_CompareOption_IncludeUnknownFields);
|
|
EXPECT_FALSE(is_equal_with_unknowns);
|
|
|
|
// 5. Without IncludeUnknownFields, they compare equal
|
|
bool is_equal_without_unknowns =
|
|
upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
|
|
&upb_0test__ModelWithExtensions_msg_init, 0);
|
|
EXPECT_TRUE(is_equal_without_unknowns);
|
|
}
|
|
|
|
TEST(CompareTest, MessageIsEqualWithNonCanonicalExtensionMatchingRawUnknown) {
|
|
upb::Arena arena;
|
|
|
|
// 1. Create custom sub-message ("World")
|
|
upb_test_ModelExtension1* extension1 =
|
|
upb_test_ModelExtension1_new(arena.ptr());
|
|
upb_test_ModelExtension1_set_str(extension1,
|
|
upb_StringView_FromString("World"));
|
|
|
|
// 2. Obtain encoded raw wire bytes of Extension A by serializing tmp_msg
|
|
upb_test_ModelWithExtensions* tmp_msg =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(tmp_msg), upb_test_ModelExtension1_model_ext_ext, &extension1,
|
|
arena.ptr()));
|
|
|
|
char* buf;
|
|
size_t size;
|
|
upb_EncodeStatus enc_status =
|
|
upb_Encode(UPB_UPCAST(tmp_msg), &upb_0test__ModelWithExtensions_msg_init,
|
|
0, arena.ptr(), &buf, &size);
|
|
ASSERT_EQ(enc_status, kUpb_EncodeStatus_Ok);
|
|
ASSERT_GT(size, 0u);
|
|
|
|
// 3. Create msg1 with non-canonical extension A
|
|
upb_test_ModelWithExtensions* msg1 =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(msg1), upb_test_ModelExtension1_model_ext_ext, &extension1,
|
|
arena.ptr()));
|
|
|
|
// 4. Create msg2 with raw unknown bytes representing A
|
|
upb_test_ModelWithExtensions* msg2 =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_AddUnknown)(
|
|
UPB_UPCAST(msg2), buf, size, arena.ptr(), kUpb_AddUnknown_Copy));
|
|
|
|
// 5. Verify they compare equal under IncludeUnknownFields
|
|
bool is_equal = upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
|
|
&upb_0test__ModelWithExtensions_msg_init,
|
|
kUpb_CompareOption_IncludeUnknownFields);
|
|
EXPECT_TRUE(is_equal);
|
|
}
|
|
|
|
TEST(CompareTest, MessageSetNonCanonicalExtMatchesUnknown) {
|
|
upb::Arena arena;
|
|
|
|
// 1. Create custom sub-message (MessageSetMember)
|
|
upb_test_MessageSetMember* extension1 =
|
|
upb_test_MessageSetMember_new(arena.ptr());
|
|
upb_test_MessageSetMember_set_optional_int32(extension1, 123);
|
|
|
|
// 2. Obtain encoded raw wire bytes of Extension A by serializing tmp_msg
|
|
upb_test_TestMessageSet* tmp_msg = upb_test_TestMessageSet_new(arena.ptr());
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(tmp_msg), upb_test_MessageSetMember_message_set_extension_ext,
|
|
&extension1, arena.ptr()));
|
|
|
|
char* buf;
|
|
size_t size;
|
|
upb_EncodeStatus enc_status =
|
|
upb_Encode(UPB_UPCAST(tmp_msg), &upb_0test__TestMessageSet_msg_init, 0,
|
|
arena.ptr(), &buf, &size);
|
|
ASSERT_EQ(enc_status, kUpb_EncodeStatus_Ok);
|
|
ASSERT_GT(size, 0u);
|
|
|
|
// 3. Create msg1 with non-canonical extension A
|
|
upb_test_TestMessageSet* msg1 = upb_test_TestMessageSet_new(arena.ptr());
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(msg1), upb_test_MessageSetMember_message_set_extension_ext,
|
|
&extension1, arena.ptr()));
|
|
|
|
// 4. Create msg2 with raw unknown bytes representing A
|
|
upb_test_TestMessageSet* msg2 = upb_test_TestMessageSet_new(arena.ptr());
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_AddUnknown)(
|
|
UPB_UPCAST(msg2), buf, size, arena.ptr(), kUpb_AddUnknown_Copy));
|
|
|
|
// 5. Verify they compare equal under IncludeUnknownFields
|
|
bool is_equal = upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
|
|
&upb_0test__TestMessageSet_msg_init,
|
|
kUpb_CompareOption_IncludeUnknownFields);
|
|
EXPECT_TRUE(is_equal);
|
|
}
|
|
|
|
TEST(CompareTest, MessageIsEqualWithCanonicalAndNonCanonicalExtensions) {
|
|
upb::Arena arena;
|
|
|
|
// 1. Create msg1 and set canonical extension
|
|
upb_test_ModelWithExtensions* msg1 =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
upb_test_ModelExtension1* ext_msg1 =
|
|
upb_test_ModelExtension1_new(arena.ptr());
|
|
upb_test_ModelExtension1_set_str(ext_msg1,
|
|
upb_StringView_FromString("World"));
|
|
upb_test_ModelExtension1_set_model_ext(msg1, ext_msg1, arena.ptr());
|
|
|
|
// 2. Create msg2 and set identical non-canonical extension
|
|
upb_test_ModelWithExtensions* msg2 =
|
|
upb_test_ModelWithExtensions_new(arena.ptr());
|
|
upb_test_ModelExtension1* ext_msg2 =
|
|
upb_test_ModelExtension1_new(arena.ptr());
|
|
upb_test_ModelExtension1_set_str(ext_msg2,
|
|
upb_StringView_FromString("World"));
|
|
EXPECT_TRUE(UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
|
|
UPB_UPCAST(msg2), upb_test_ModelExtension1_model_ext_ext, &ext_msg2,
|
|
arena.ptr()));
|
|
|
|
// 3. Verify upb_Message_IsEqual registers them as NOT equal
|
|
bool is_equal = upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
|
|
&upb_0test__ModelWithExtensions_msg_init,
|
|
kUpb_CompareOption_IncludeUnknownFields);
|
|
EXPECT_FALSE(is_equal);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(CompareTest, AllocationFailure) {
|
|
if (!upb_AllocationCount_IsAvailable()) return;
|
|
|
|
auto RunScenario = [&]() -> bool {
|
|
upb_Arena* arena1 = upb_Arena_New();
|
|
if (!arena1) return false;
|
|
auto cleanup1 = absl::MakeCleanup([arena1] { upb_Arena_Free(arena1); });
|
|
|
|
upb_Arena* arena2 = upb_Arena_New();
|
|
if (!arena2) return false;
|
|
auto cleanup2 = absl::MakeCleanup([arena2] { upb_Arena_Free(arena2); });
|
|
|
|
protobuf_test_messages_proto2_TestAllTypesProto2* msg1 =
|
|
protobuf_test_messages_proto2_TestAllTypesProto2_new(arena1);
|
|
if (!msg1) return false;
|
|
protobuf_test_messages_proto2_TestAllTypesProto2* msg2 =
|
|
protobuf_test_messages_proto2_TestAllTypesProto2_new(arena2);
|
|
if (!msg2) return false;
|
|
|
|
WireMessage uf1 = {{2, Fixed32(456)}, {1, Fixed64(123)}};
|
|
WireMessage uf2 = {{2, Fixed32(456)}, {1, Fixed64(123)}};
|
|
std::string buf1 = ToBinaryPayloadWithLongVarints(uf1, 1, 1);
|
|
std::string buf2 = ToBinaryPayloadWithLongVarints(uf2, 1, 1);
|
|
|
|
if (!UPB_PRIVATE(_upb_Message_AddUnknown)(UPB_UPCAST(msg1), buf1.data(),
|
|
buf1.size(), arena1,
|
|
kUpb_AddUnknown_Copy)) {
|
|
return false;
|
|
}
|
|
if (!UPB_PRIVATE(_upb_Message_AddUnknown)(UPB_UPCAST(msg2), buf2.data(),
|
|
buf2.size(), arena2,
|
|
kUpb_AddUnknown_Copy)) {
|
|
return false;
|
|
}
|
|
upb_UnknownCompareResult res = _upb_Message_UnknownFieldsAreEqual(
|
|
UPB_UPCAST(msg1), UPB_UPCAST(msg2), 64);
|
|
return res == kUpb_UnknownCompareResult_Equal;
|
|
};
|
|
|
|
upb_AllocationCount_Reset();
|
|
if (RunScenario()) {
|
|
size_t total_allocations = upb_AllocationCount_Get();
|
|
for (size_t i = 0; i < total_allocations; ++i) {
|
|
upb_AllocationCount_Reset();
|
|
upb_AllocationCount_FailOn(i);
|
|
bool success_with_fail = RunScenario();
|
|
EXPECT_FALSE(success_with_fail)
|
|
<< "Unknown comparison unexpectedly succeeded when allocation "
|
|
<< "number " << i << " was failed.";
|
|
}
|
|
}
|
|
upb_AllocationCount_Reset();
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace upb
|