blob: 4514679b989e733d8a2a2b536154f32f25254e53 [file]
// 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/convert.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <gtest/gtest.h>
#include "google/protobuf/test_messages_proto3.upb.h"
#include "google/protobuf/test_messages_proto3.upb_minitable.h"
#include "upb/base/descriptor_constants.h"
#include "upb/base/string_view.h"
#include "upb/base/upcast.h"
#include "upb/mem/arena.hpp"
#include "upb/message/accessors.h"
#include "upb/message/array.h"
#include "upb/message/convert_test.upb.h"
#include "upb/message/convert_test.upb_minitable.h"
#include "upb/message/internal/message.h"
#include "upb/message/message.h"
#include "upb/message/test.upb_minitable.h"
#include "upb/mini_table/extension_registry.h"
#include "upb/mini_table/message.h"
#include "upb/wire/decode.h"
// Must be last to ensure UPB_PRIVATE is defined.
#include "upb/port/def.inc"
// We use the generated upb_MiniTables from test_messages_proto3.
#define TEST_MT &protobuf_0test_0messages__proto3__TestAllTypesProto3_msg_init
TEST(ConvertTest, Identity) {
upb::Arena arena;
protobuf_test_messages_proto3_TestAllTypesProto3* msg =
protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_int32(msg, 123);
protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_string(
msg, upb_StringView_FromString("hello"));
const upb_MiniTable* mt = TEST_MT;
const upb_Message* dst_msg =
upb_Message_Convert(UPB_UPCAST(msg), mt, mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const protobuf_test_messages_proto3_TestAllTypesProto3* dst =
(const protobuf_test_messages_proto3_TestAllTypesProto3*)dst_msg;
EXPECT_EQ(
123,
protobuf_test_messages_proto3_TestAllTypesProto3_optional_int32(dst));
upb_StringView str =
protobuf_test_messages_proto3_TestAllTypesProto3_optional_string(dst);
EXPECT_EQ(std::string("hello"), std::string(str.data, str.size));
}
TEST(ConvertTest, AliasSubMessage) {
upb::Arena arena;
protobuf_test_messages_proto3_TestAllTypesProto3* msg =
protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_NestedMessage* sub =
protobuf_test_messages_proto3_TestAllTypesProto3_mutable_optional_nested_message(
msg, arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_NestedMessage_set_a(sub,
456);
const upb_MiniTable* mt = TEST_MT;
const upb_Message* dst_msg =
upb_Message_Convert(UPB_UPCAST(msg), mt, mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const protobuf_test_messages_proto3_TestAllTypesProto3* dst =
(const protobuf_test_messages_proto3_TestAllTypesProto3*)dst_msg;
const protobuf_test_messages_proto3_TestAllTypesProto3_NestedMessage* dst_sub =
protobuf_test_messages_proto3_TestAllTypesProto3_optional_nested_message(
dst);
// Verify value
EXPECT_EQ(456,
protobuf_test_messages_proto3_TestAllTypesProto3_NestedMessage_a(
dst_sub));
// Verify shallow copy (pointer identity)
EXPECT_EQ(sub, dst_sub);
}
TEST(ConvertTest, UnknownPromotion) {
upb::Arena arena;
// 1. Create a serialized buffer with just the unknown field.
// Field `optional_int32` has number 1.
// Encode 789 as field 1.
char buf[32];
char* ptr = buf;
// Tag: 1 << 3 | 0 (varint) = 8
*ptr++ = 8;
// Value: 789 = 0x315. Varint: 0x95 0x06
*ptr++ = (char)0x95;
*ptr++ = (char)0x06;
size_t len = ptr - buf;
// 2. Parse this buffer into Message with NO fields.
const upb_MiniTable* empty_mt = &upb_0test__EmptyMessage_msg_init;
upb_Message* msg_empty = upb_Message_New(empty_mt, arena.ptr());
upb_DecodeStatus status =
upb_Decode(buf, len, msg_empty, empty_mt, nullptr, 0, arena.ptr());
EXPECT_EQ(kUpb_DecodeStatus_Ok, status);
// 3. Convert `msg_empty` to `dst` (TestAllTypesProto3).
// `dst` HAS field 1. So it should promote the unknown from `msg_empty`.
const upb_MiniTable* mt = TEST_MT;
const upb_Message* dst_msg =
upb_Message_Convert(msg_empty, empty_mt, mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const protobuf_test_messages_proto3_TestAllTypesProto3* dst =
(const protobuf_test_messages_proto3_TestAllTypesProto3*)dst_msg;
EXPECT_EQ(
789,
protobuf_test_messages_proto3_TestAllTypesProto3_optional_int32(dst));
}
TEST(ConvertTest, Demotion) {
upb::Arena arena;
protobuf_test_messages_proto3_TestAllTypesProto3* msg =
protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_int32(msg, 999);
// Convert to Empty message. Field 1 should become unknown.
const upb_MiniTable* empty_mt = &upb_0test__EmptyMessage_msg_init;
const upb_Message* dst = upb_Message_Convert(UPB_UPCAST(msg), TEST_MT,
empty_mt, nullptr, arena.ptr());
EXPECT_NE(dst, nullptr);
// Dst should have unknown field 1 with value 999.
size_t iter = kUpb_Message_UnknownBegin;
upb_StringView data;
ASSERT_TRUE(upb_Message_NextUnknown(dst, &data, &iter));
EXPECT_GE(data.size, 3);
EXPECT_EQ((uint8_t)data.data[0], 8);
EXPECT_EQ((uint8_t)data.data[1], 0xE7);
EXPECT_EQ((uint8_t)data.data[2], 0x07);
EXPECT_FALSE(upb_Message_NextUnknown(dst, &data, &iter));
}
TEST(ConvertTest, DeepConvertMap) {
upb::Arena arena;
protobuf_test_messages_proto3_TestAllTypesProto3* msg =
protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_map_int32_int32_set(
msg, 10, 20, arena.ptr());
const upb_MiniTable* mt = TEST_MT;
// Self-conversion should work (shallow).
const upb_Message* dst_msg =
upb_Message_Convert(UPB_UPCAST(msg), mt, mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const protobuf_test_messages_proto3_TestAllTypesProto3* dst =
(const protobuf_test_messages_proto3_TestAllTypesProto3*)dst_msg;
int32_t val;
EXPECT_TRUE(
protobuf_test_messages_proto3_TestAllTypesProto3_map_int32_int32_get(
dst, 10, &val));
EXPECT_EQ(20, val);
}
TEST(ConvertTest, DeepConvertMapMessage) {
upb::Arena arena;
upb_test_convert_MessageWithMapMessage* msg =
upb_test_convert_MessageWithMapMessage_new(arena.ptr());
upb_test_convert_MessageWithInt32* val =
upb_test_convert_MessageWithInt32_new(arena.ptr());
upb_test_convert_MessageWithInt32_set_f1(val, 123);
upb_test_convert_MessageWithMapMessage_map_msg_set(msg, 10, val, arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithMapMessage_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithMapMessageClone_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithMapMessageClone* dst =
(const upb_test_convert_MessageWithMapMessageClone*)dst_msg;
upb_test_convert_MessageWithInt32Clone* dst_val;
EXPECT_TRUE(upb_test_convert_MessageWithMapMessageClone_map_msg_get(
dst, 10, &dst_val));
EXPECT_EQ(123, upb_test_convert_MessageWithInt32Clone_f1(dst_val));
// It should be a deep copy, not the same pointer.
EXPECT_NE((const void*)dst_val, (const void*)val);
}
TEST(ConvertTest, DeepConvertScalarMap) {
upb::Arena arena;
upb_test_convert_MessageWithMapInt32Int32* msg =
upb_test_convert_MessageWithMapInt32Int32_new(arena.ptr());
upb_test_convert_MessageWithMapInt32Int32_m_set(msg, 123, 456, arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithMapInt32Int32_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithMapInt32Int32Clone_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithMapInt32Int32Clone* dst =
(const upb_test_convert_MessageWithMapInt32Int32Clone*)dst_msg;
int32_t val;
EXPECT_TRUE(
upb_test_convert_MessageWithMapInt32Int32Clone_m_get(dst, 123, &val));
EXPECT_EQ(456, val);
}
TEST(ConvertTest, ExtensionArrayShallowConversion) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_test_convert_MessageWithInt32* val =
upb_test_convert_MessageWithInt32_new(arena.ptr());
upb_test_convert_MessageWithInt32_set_f1(val, 456);
upb_Array* ext_arr = upb_Array_New(arena.ptr(), kUpb_CType_Message);
upb_Array_Resize(ext_arr, 1, arena.ptr());
upb_MessageValue elem_val;
elem_val.msg_val = (const upb_Message*)val;
upb_Array_Set(ext_arr, 0, elem_val);
upb_MessageValue ext_val;
ext_val.array_val = ext_arr;
upb_Message_SetExtension(UPB_UPCAST(msg),
upb_test_convert_ext_field_repeated_msg_ext,
&ext_val, arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithKnownRepeatedMsg_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithKnownRepeatedMsg* dst =
(const upb_test_convert_MessageWithKnownRepeatedMsg*)dst_msg;
size_t size;
const upb_test_convert_MessageWithInt32* const* dst_arr =
upb_test_convert_MessageWithKnownRepeatedMsg_known_repeated_msg(dst,
&size);
EXPECT_EQ(1, size);
EXPECT_EQ(456, upb_test_convert_MessageWithInt32_f1(dst_arr[0]));
// Due to minitable identity match, elements are shallow-copied.
EXPECT_EQ((const void*)dst_arr[0], (const void*)val);
}
TEST(ConvertTest, ExtensionArrayDeepConversion) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_test_convert_MessageWithInt32* val =
upb_test_convert_MessageWithInt32_new(arena.ptr());
upb_test_convert_MessageWithInt32_set_f1(val, 789);
upb_Array* ext_arr = upb_Array_New(arena.ptr(), kUpb_CType_Message);
upb_Array_Resize(ext_arr, 1, arena.ptr());
upb_MessageValue elem_val;
elem_val.msg_val = (const upb_Message*)val;
upb_Array_Set(ext_arr, 0, elem_val);
upb_MessageValue ext_val;
ext_val.array_val = ext_arr;
upb_Message_SetExtension(UPB_UPCAST(msg),
upb_test_convert_ext_field_repeated_msg_ext,
&ext_val, arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithKnownRepeatedMsgClone_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithKnownRepeatedMsgClone* dst =
(const upb_test_convert_MessageWithKnownRepeatedMsgClone*)dst_msg;
size_t size;
const upb_test_convert_MessageWithInt32Clone* const* dst_arr =
upb_test_convert_MessageWithKnownRepeatedMsgClone_known_repeated_msg(
dst, &size);
EXPECT_EQ(1, size);
EXPECT_EQ(789, upb_test_convert_MessageWithInt32Clone_f1(dst_arr[0]));
// Deep conversion means the pointers are not equal.
EXPECT_NE((const void*)dst_arr[0], (const void*)val);
}
TEST(ConvertTest, MismatchedExtensionFails) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
// Set extension field 1000 to an int32
upb_MessageValue ext_val;
ext_val.int32_val = 12345;
upb_Message_SetExtension(UPB_UPCAST(msg),
upb_test_convert_ext_field_int32_ext, &ext_val,
arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
// Convert to a message where field 1000 is an int64 instead of int32.
// The type mismatch should cause it to skip setting the regular field,
// but it should preserve the int32 wire data into the unknown fields.
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithKnownInt64_msg_init;
ASSERT_EQ(upb_Message_Convert(UPB_UPCAST(msg), src_mt, dst_mt, nullptr,
arena.ptr()),
nullptr);
}
TEST(ConvertTest, ConvertField_IncompatibleCType) {
upb::Arena arena;
upb_test_convert_MessageWithString* msg =
upb_test_convert_MessageWithString_new(arena.ptr());
upb_test_convert_MessageWithString_set_f1(msg,
upb_StringView_FromString("hello"));
const upb_MiniTable* src_mt = &upb__test__convert__MessageWithString_msg_init;
const upb_MiniTable* dst_mt = &upb__test__convert__MessageWithInt32_msg_init;
ASSERT_EQ(upb_Message_Convert(UPB_UPCAST(msg), src_mt, dst_mt, nullptr,
arena.ptr()),
nullptr);
}
TEST(ConvertTest, ConvertField_ArrayIncompatibleCType) {
upb::Arena arena;
upb_test_convert_MessageWithRepeatedString* msg =
upb_test_convert_MessageWithRepeatedString_new(arena.ptr());
upb_StringView* arr =
upb_test_convert_MessageWithRepeatedString_resize_r(msg, 1, arena.ptr());
arr[0] = upb_StringView_FromString("hello");
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithRepeatedString_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithRepeatedInt32_msg_init;
ASSERT_EQ(upb_Message_Convert(UPB_UPCAST(msg), src_mt, dst_mt, nullptr,
arena.ptr()),
nullptr);
}
TEST(ConvertTest, ConvertField_TypeMismatch) {
upb::Arena arena;
upb_test_convert_MessageWithInt32* msg =
upb_test_convert_MessageWithInt32_new(arena.ptr());
upb_test_convert_MessageWithInt32_set_f1(msg, 12345);
const upb_MiniTable* src_mt = &upb__test__convert__MessageWithInt32_msg_init;
const upb_MiniTable* dst_mt = &upb__test__convert__MessageWithInt64_msg_init;
ASSERT_EQ(upb_Message_Convert(UPB_UPCAST(msg), src_mt, dst_mt, nullptr,
arena.ptr()),
nullptr);
}
TEST(ConvertTest, ConvertField_ModeMismatch_ScalarToArray) {
upb::Arena arena;
upb_test_convert_MessageWithInt32* msg =
upb_test_convert_MessageWithInt32_new(arena.ptr());
upb_test_convert_MessageWithInt32_set_f1(msg, 12345);
const upb_MiniTable* src_mt = &upb__test__convert__MessageWithInt32_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithRepeatedInt32_msg_init;
ASSERT_EQ(upb_Message_Convert(UPB_UPCAST(msg), src_mt, dst_mt, nullptr,
arena.ptr()),
nullptr);
}
TEST(ConvertTest, ConvertField_ModeMismatch_ArrayToScalar) {
upb::Arena arena;
upb_test_convert_MessageWithRepeatedInt32* msg =
upb_test_convert_MessageWithRepeatedInt32_new(arena.ptr());
upb_test_convert_MessageWithRepeatedInt32_add_r(msg, 12345, arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithRepeatedInt32_msg_init;
const upb_MiniTable* dst_mt = &upb__test__convert__MessageWithInt32_msg_init;
ASSERT_EQ(upb_Message_Convert(UPB_UPCAST(msg), src_mt, dst_mt, nullptr,
arena.ptr()),
nullptr);
}
TEST(ConvertTest, ConvertField_ModeMismatch_ScalarToMap) {
upb::Arena arena;
upb_test_convert_MessageWithInt32* msg =
upb_test_convert_MessageWithInt32_new(arena.ptr());
upb_test_convert_MessageWithInt32_set_f1(msg, 12345);
const upb_MiniTable* src_mt = &upb__test__convert__MessageWithInt32_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithMapInt32Int32_msg_init;
ASSERT_EQ(upb_Message_Convert(UPB_UPCAST(msg), src_mt, dst_mt, nullptr,
arena.ptr()),
nullptr);
}
TEST(ConvertTest, ConvertField_MapTypeMismatch) {
upb::Arena arena;
upb_test_convert_MessageWithMapInt32Int32* msg =
upb_test_convert_MessageWithMapInt32Int32_new(arena.ptr());
upb_test_convert_MessageWithMapInt32Int32_m_set(msg, 123, 456, arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithMapInt32Int32_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithMapInt32Int64_msg_init;
ASSERT_EQ(upb_Message_Convert(UPB_UPCAST(msg), src_mt, dst_mt, nullptr,
arena.ptr()),
nullptr);
}
TEST(ConvertTest, ConvertField_SingularMessageDeep) {
upb::Arena arena;
upb_test_convert_MessageWithMsg* msg =
upb_test_convert_MessageWithMsg_new(arena.ptr());
upb_test_convert_MessageWithInt32* sub =
upb_test_convert_MessageWithInt32_new(arena.ptr());
upb_test_convert_MessageWithInt32_set_f1(sub, 123);
upb_test_convert_MessageWithMsg_set_msg(msg, sub);
const upb_MiniTable* src_mt = &upb__test__convert__MessageWithMsg_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithMsgClone_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithMsgClone* dst =
(const upb_test_convert_MessageWithMsgClone*)dst_msg;
const upb_test_convert_MessageWithInt32Clone* dst_sub =
upb_test_convert_MessageWithMsgClone_msg(dst);
EXPECT_NE(dst_sub, nullptr);
EXPECT_EQ(123, upb_test_convert_MessageWithInt32Clone_f1(dst_sub));
// Verify deep copy logic occurred correctly
EXPECT_NE((const void*)dst_sub, (const void*)sub);
}
TEST(ConvertTest, ConvertField_ArrayMessageShallow) {
upb::Arena arena;
upb_test_convert_MessageWithRepeatedMsg* msg =
upb_test_convert_MessageWithRepeatedMsg_new(arena.ptr());
upb_test_convert_MessageWithInt32* sub =
upb_test_convert_MessageWithInt32_new(arena.ptr());
upb_test_convert_MessageWithInt32_set_f1(sub, 123);
upb_test_convert_MessageWithInt32** arr =
upb_test_convert_MessageWithRepeatedMsg_resize_msgs(msg, 1, arena.ptr());
arr[0] = sub;
const upb_MiniTable* mt =
&upb__test__convert__MessageWithRepeatedMsg_msg_init;
const upb_Message* dst_msg =
upb_Message_Convert(UPB_UPCAST(msg), mt, mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithRepeatedMsg* dst =
(const upb_test_convert_MessageWithRepeatedMsg*)dst_msg;
size_t size;
const upb_test_convert_MessageWithInt32* const* dst_arr =
upb_test_convert_MessageWithRepeatedMsg_msgs(dst, &size);
EXPECT_EQ(1, size);
EXPECT_EQ(123, upb_test_convert_MessageWithInt32_f1(dst_arr[0]));
// Shallow copy because the destination MiniTable array element is identical
EXPECT_EQ((const void*)dst_arr[0], (const void*)sub);
}
TEST(ConvertTest, ConvertField_ArrayMessageDeep) {
upb::Arena arena;
upb_test_convert_MessageWithRepeatedMsg* msg =
upb_test_convert_MessageWithRepeatedMsg_new(arena.ptr());
upb_test_convert_MessageWithInt32* sub =
upb_test_convert_MessageWithInt32_new(arena.ptr());
upb_test_convert_MessageWithInt32_set_f1(sub, 123);
upb_test_convert_MessageWithInt32** arr =
upb_test_convert_MessageWithRepeatedMsg_resize_msgs(msg, 1, arena.ptr());
arr[0] = sub;
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithRepeatedMsg_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithRepeatedMsgClone_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithRepeatedMsgClone* dst =
(const upb_test_convert_MessageWithRepeatedMsgClone*)dst_msg;
size_t size;
const upb_test_convert_MessageWithInt32Clone* const* dst_arr =
upb_test_convert_MessageWithRepeatedMsgClone_msgs(dst, &size);
EXPECT_EQ(1, size);
EXPECT_EQ(123, upb_test_convert_MessageWithInt32Clone_f1(dst_arr[0]));
// Deep copy expected
EXPECT_NE((const void*)dst_arr[0], (const void*)sub);
}
TEST(ConvertTest, ConvertExtensions_ScalarMatch) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_MessageValue ext_val;
ext_val.int32_val = 123;
upb_Message_SetExtension(UPB_UPCAST(msg),
upb_test_convert_ext_field_int32_ext, &ext_val,
arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt = &upb__test__convert__MessageWithKnown_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithKnown* dst =
(const upb_test_convert_MessageWithKnown*)dst_msg;
EXPECT_TRUE(upb_test_convert_MessageWithKnown_has_known_field_int32(dst));
EXPECT_EQ(123, upb_test_convert_MessageWithKnown_known_field_int32(dst));
}
TEST(ConvertTest, ConvertExtensions_SingularMessageShallow) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_test_convert_MessageWithInt32* sub =
upb_test_convert_MessageWithInt32_new(arena.ptr());
upb_test_convert_MessageWithInt32_set_f1(sub, 123);
upb_MessageValue ext_val;
ext_val.msg_val = UPB_UPCAST(sub);
upb_Message_SetExtension(UPB_UPCAST(msg), upb_test_convert_ext_field_msg_ext,
&ext_val, arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithKnownMsg_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithKnownMsg* dst =
(const upb_test_convert_MessageWithKnownMsg*)dst_msg;
const upb_test_convert_MessageWithInt32* dst_sub =
upb_test_convert_MessageWithKnownMsg_known_msg(dst);
EXPECT_NE(dst_sub, nullptr);
EXPECT_EQ(123, upb_test_convert_MessageWithInt32_f1(dst_sub));
// Shallow copy expected since we are using the exact same minitable
// underlying message
EXPECT_EQ((const void*)dst_sub, (const void*)sub);
}
TEST(ConvertTest, ConvertExtensions_SingularMessageDeep) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_test_convert_MessageWithInt32* sub =
upb_test_convert_MessageWithInt32_new(arena.ptr());
upb_test_convert_MessageWithInt32_set_f1(sub, 123);
upb_MessageValue ext_val;
ext_val.msg_val = UPB_UPCAST(sub);
upb_Message_SetExtension(UPB_UPCAST(msg), upb_test_convert_ext_field_msg_ext,
&ext_val, arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithKnownMsgClone_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithKnownMsgClone* dst =
(const upb_test_convert_MessageWithKnownMsgClone*)dst_msg;
const upb_test_convert_MessageWithInt32Clone* dst_sub =
upb_test_convert_MessageWithKnownMsgClone_known_msg(dst);
EXPECT_NE(dst_sub, nullptr);
EXPECT_EQ(123, upb_test_convert_MessageWithInt32Clone_f1(dst_sub));
// Deep copy expected because of mismatched minitables
EXPECT_NE((const void*)dst_sub, (const void*)sub);
}
TEST(ConvertTest, ConvertExtensions_RemainsExtension) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_MessageValue ext_val;
ext_val.int32_val = 123;
upb_Message_SetExtension(UPB_UPCAST(msg),
upb_test_convert_ext_field_int32_ext, &ext_val,
arena.ptr());
const upb_MiniTable* mt = &upb__test__convert__MessageWithExtension_msg_init;
const upb_Message* dst_msg =
upb_Message_Convert(UPB_UPCAST(msg), mt, mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithExtension* dst =
(const upb_test_convert_MessageWithExtension*)dst_msg;
// Extension should still be perfectly preserved on the destination message.
EXPECT_TRUE(upb_Message_HasExtension(UPB_UPCAST(dst),
upb_test_convert_ext_field_int32_ext));
int32_t out_val = upb_Message_GetExtensionInt32(
UPB_UPCAST(dst), upb_test_convert_ext_field_int32_ext, 0);
EXPECT_EQ(123, out_val);
}
TEST(ConvertTest, ConvertExtensions_LookupExtensionInRegistry) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_MessageValue ext_val;
ext_val.int32_val = 123;
upb_Message_SetExtension(UPB_UPCAST(msg),
upb_test_convert_ext_field_int32_ext, &ext_val,
arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__AnotherMessageWithExtension_msg_init;
upb_ExtensionRegistry* extreg = upb_ExtensionRegistry_New(arena.ptr());
upb_ExtensionRegistry_Add(extreg,
upb_test_convert_another_ext_field_int32_ext);
const upb_Message* dst_msg =
upb_Message_Convert(UPB_UPCAST(msg), src_mt, dst_mt, extreg, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_AnotherMessageWithExtension* dst =
(const upb_test_convert_AnotherMessageWithExtension*)dst_msg;
// Extension should be found via registry and converted.
EXPECT_TRUE(upb_Message_HasExtension(
UPB_UPCAST(dst), upb_test_convert_another_ext_field_int32_ext));
int32_t out_val = upb_Message_GetExtensionInt32(
UPB_UPCAST(dst), upb_test_convert_another_ext_field_int32_ext, 0);
EXPECT_EQ(123, out_val);
}
TEST(ConvertTest, ConvertExtensionToNonExtendable) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_MessageValue ext_val;
ext_val.int32_val = 123;
upb_Message_SetExtension(UPB_UPCAST(msg),
upb_test_convert_ext_field_int32_ext, &ext_val,
arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt = &upb_0test__EmptyMessage_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
// Dst should have unknown field 1000 with value 123.
size_t iter = kUpb_Message_UnknownBegin;
upb_StringView data;
ASSERT_TRUE(upb_Message_NextUnknown(dst_msg, &data, &iter));
EXPECT_EQ(data.size, 3);
EXPECT_EQ((uint8_t)data.data[0], 0xC0);
EXPECT_EQ((uint8_t)data.data[1], 0x3E);
EXPECT_EQ((uint8_t)data.data[2], 0x7B);
EXPECT_FALSE(upb_Message_NextUnknown(dst_msg, &data, &iter));
}
TEST(ConvertTest, ConvertExtensionToExtendableButUnknown) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_MessageValue ext_val;
ext_val.int32_val = 123;
upb_Message_SetExtension(UPB_UPCAST(msg),
upb_test_convert_ext_field_int32_ext, &ext_val,
arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__AnotherMessageWithExtension_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
// The destination message (AnotherMessageWithExtension) supports extensions,
// but since we did not provide an extension registry containing field 1000 to
// the conversion, the extension is treated as unknown/non-canonical in the
// destination schemas. Therefore, it should be encoded as an unknown field
// (field 1000 with value 123).
size_t iter = kUpb_Message_UnknownBegin;
upb_StringView data;
ASSERT_TRUE(upb_Message_NextUnknown(dst_msg, &data, &iter));
EXPECT_EQ(data.size, 3);
EXPECT_EQ((uint8_t)data.data[0], 0xC0);
EXPECT_EQ((uint8_t)data.data[1], 0x3E);
EXPECT_EQ((uint8_t)data.data[2], 0x7B);
EXPECT_FALSE(upb_Message_NextUnknown(dst_msg, &data, &iter));
}
TEST(ConvertTest, OneofDemotion) {
upb::Arena arena;
upb_test_convert_SrcWithOneof* msg =
upb_test_convert_SrcWithOneof_new(arena.ptr());
// Set both fields of the oneof. The last one should win and the first should
// be dropped.
upb_test_convert_SrcWithOneof_set_oneof_int32(msg, 54321);
upb_test_convert_SrcWithOneof_set_oneof_string(
msg, upb_StringView_FromString("test"));
// Convert to Empty message. Field 2 should become unknown.
const upb_MiniTable* empty_mt = &upb_0test__EmptyMessage_msg_init;
const upb_MiniTable* src_mt = &upb__test__convert__SrcWithOneof_msg_init;
const upb_Message* dst = upb_Message_Convert(UPB_UPCAST(msg), src_mt,
empty_mt, nullptr, arena.ptr());
EXPECT_NE(dst, nullptr);
// Dst should have unknown field 2 with value "test".
size_t iter = kUpb_Message_UnknownBegin;
upb_StringView data;
EXPECT_TRUE(upb_Message_NextUnknown(dst, &data, &iter));
// "test" string field 2.
// Tag 2, type 2: 2<<3 | 2 = 18
EXPECT_EQ(data.size, 6);
EXPECT_EQ((uint8_t)data.data[0], 18);
EXPECT_EQ((uint8_t)data.data[1], 4);
EXPECT_EQ((uint8_t)data.data[2], 't');
EXPECT_EQ((uint8_t)data.data[3], 'e');
EXPECT_EQ((uint8_t)data.data[4], 's');
EXPECT_EQ((uint8_t)data.data[5], 't');
EXPECT_FALSE(upb_Message_NextUnknown(dst, &data, &iter));
// Now convert back and verify.
const upb_Message* dst2_msg =
upb_Message_Convert(dst, empty_mt, src_mt, nullptr, arena.ptr());
EXPECT_NE(dst2_msg, nullptr);
const upb_test_convert_SrcWithOneof* dst2 =
(const upb_test_convert_SrcWithOneof*)dst2_msg;
EXPECT_EQ(upb_test_convert_SrcWithOneof_my_oneof_case(dst2),
upb_test_convert_SrcWithOneof_my_oneof_oneof_string);
upb_StringView str = upb_test_convert_SrcWithOneof_oneof_string(dst2);
EXPECT_EQ(std::string("test"), std::string(str.data, str.size));
}
TEST(ConvertTest, OpenToClosedEnum) {
upb::Arena arena;
const upb_MiniTable* src_mt =
&protobuf_0test_0messages__proto3__TestAllTypesProto3_msg_init;
const upb_MiniTable* dst_mt = &upb__test__convert__Proto2EnumMessage_msg_init;
// Set a valid enum value that exists in range of the destination enum.
protobuf_test_messages_proto3_TestAllTypesProto3* msg =
protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_nested_enum(
msg, protobuf_test_messages_proto3_TestAllTypesProto3_BAR);
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_Proto2EnumMessage* dst =
(const upb_test_convert_Proto2EnumMessage*)dst_msg;
EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR,
upb_test_convert_Proto2EnumMessage_optional_nested_enum(dst));
}
TEST(ConvertTest, OpenToClosedEnum_InvalidValueInClosedEnum) {
upb::Arena arena;
const upb_MiniTable* src_mt =
&protobuf_0test_0messages__proto3__TestAllTypesProto3_msg_init;
const upb_MiniTable* dst_mt = &upb__test__convert__Proto2EnumMessage_msg_init;
// Set an invalid enum value for the destination enum.
protobuf_test_messages_proto3_TestAllTypesProto3* msg =
protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_nested_enum(
msg, 12345); // Invalid value.
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_Proto2EnumMessage* dst =
(const upb_test_convert_Proto2EnumMessage*)dst_msg;
EXPECT_FALSE(
upb_test_convert_Proto2EnumMessage_has_optional_nested_enum(dst));
size_t iter = kUpb_Message_UnknownBegin;
upb_StringView data;
ASSERT_TRUE(upb_Message_NextUnknown(dst_msg, &data, &iter));
EXPECT_GT(data.size, 0);
protobuf_test_messages_proto3_TestAllTypesProto3* check_msg =
protobuf_test_messages_proto3_TestAllTypesProto3_parse(
data.data, data.size, arena.ptr());
ASSERT_NE(check_msg, nullptr);
EXPECT_EQ(
12345,
protobuf_test_messages_proto3_TestAllTypesProto3_optional_nested_enum(
check_msg));
}
TEST(ConvertTest, OpenToClosedMapEnum) {
upb::Arena arena;
protobuf_test_messages_proto3_TestAllTypesProto3* msg =
protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_map_string_nested_enum_set(
msg, upb_StringView_FromString("valid1"),
protobuf_test_messages_proto3_TestAllTypesProto3_BAR, arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_map_string_nested_enum_set(
msg, upb_StringView_FromString("valid2"),
protobuf_test_messages_proto3_TestAllTypesProto3_BAZ, arena.ptr());
const upb_MiniTable* src_mt =
&protobuf_0test_0messages__proto3__TestAllTypesProto3_msg_init;
const upb_MiniTable* dst_mt = &upb__test__convert__Proto2EnumMessage_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_Proto2EnumMessage* dst =
(const upb_test_convert_Proto2EnumMessage*)dst_msg;
// Valid enum should be present
int val1;
EXPECT_TRUE(upb_test_convert_Proto2EnumMessage_map_string_nested_enum_get(
dst, upb_StringView_FromString("valid1"), &val1));
EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR, val1);
int val2;
EXPECT_TRUE(upb_test_convert_Proto2EnumMessage_map_string_nested_enum_get(
dst, upb_StringView_FromString("valid2"), &val2));
EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAZ, val2);
}
TEST(ConvertTest, OpenToClosedMapEnum_InvalidValueInClosedEnum) {
upb::Arena arena;
protobuf_test_messages_proto3_TestAllTypesProto3* msg =
protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_map_string_nested_enum_set(
msg, upb_StringView_FromString("valid"),
protobuf_test_messages_proto3_TestAllTypesProto3_BAR, arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_map_string_nested_enum_set(
msg, upb_StringView_FromString("invalid"), 12345,
arena.ptr()); // Invalid value.
const upb_MiniTable* src_mt =
&protobuf_0test_0messages__proto3__TestAllTypesProto3_msg_init;
const upb_MiniTable* dst_mt = &upb__test__convert__Proto2EnumMessage_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_Proto2EnumMessage* dst =
(const upb_test_convert_Proto2EnumMessage*)dst_msg;
int val;
EXPECT_TRUE(upb_test_convert_Proto2EnumMessage_map_string_nested_enum_get(
dst, upb_StringView_FromString("valid"), &val));
EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR, val);
EXPECT_FALSE(upb_test_convert_Proto2EnumMessage_map_string_nested_enum_get(
dst, upb_StringView_FromString("invalid"), &val));
size_t iter = kUpb_Message_UnknownBegin;
upb_StringView data;
ASSERT_TRUE(upb_Message_NextUnknown(dst_msg, &data, &iter));
EXPECT_GT(data.size, 0);
// Verify that the unknown field contains the complete invalid map entry.
// We can parse the unknown field data back into the source proto3 message,
// which treats the enum as open, to verify both the key and the invalid value
// were perfectly preserved.
protobuf_test_messages_proto3_TestAllTypesProto3* check_msg =
protobuf_test_messages_proto3_TestAllTypesProto3_parse(
data.data, data.size, arena.ptr());
ASSERT_NE(check_msg, nullptr);
int check_val;
EXPECT_TRUE(
protobuf_test_messages_proto3_TestAllTypesProto3_map_string_nested_enum_get(
check_msg, upb_StringView_FromString("invalid"), &check_val));
EXPECT_EQ(12345, check_val);
// The "valid" map entry should NOT be in the unknown fields.
EXPECT_FALSE(
protobuf_test_messages_proto3_TestAllTypesProto3_map_string_nested_enum_get(
check_msg, upb_StringView_FromString("valid"), &check_val));
}
TEST(ConvertTest, OpenToClosedRepeatedEnum) {
upb::Arena arena;
protobuf_test_messages_proto3_TestAllTypesProto3* msg =
protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_add_repeated_nested_enum(
msg, protobuf_test_messages_proto3_TestAllTypesProto3_FOO, arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_add_repeated_nested_enum(
msg, protobuf_test_messages_proto3_TestAllTypesProto3_BAR, arena.ptr());
const upb_MiniTable* src_mt =
&protobuf_0test_0messages__proto3__TestAllTypesProto3_msg_init;
const upb_MiniTable* dst_mt = &upb__test__convert__Proto2EnumMessage_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_Proto2EnumMessage* dst =
(const upb_test_convert_Proto2EnumMessage*)dst_msg;
size_t count;
const int* values =
upb_test_convert_Proto2EnumMessage_repeated_nested_enum(dst, &count);
EXPECT_EQ(2, count);
EXPECT_EQ(upb_test_convert_Proto2EnumMessage_FOO, values[0]);
EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR, values[1]);
}
TEST(ConvertTest, OpenToClosedRepeatedEnum_ContainsInvalidValue) {
upb::Arena arena;
protobuf_test_messages_proto3_TestAllTypesProto3* msg =
protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr());
protobuf_test_messages_proto3_TestAllTypesProto3_add_repeated_nested_enum(
msg, 12345, arena.ptr()); // Invalid value.
protobuf_test_messages_proto3_TestAllTypesProto3_add_repeated_nested_enum(
msg, protobuf_test_messages_proto3_TestAllTypesProto3_BAR, arena.ptr());
const upb_MiniTable* src_mt =
&protobuf_0test_0messages__proto3__TestAllTypesProto3_msg_init;
const upb_MiniTable* dst_mt = &upb__test__convert__Proto2EnumMessage_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_Proto2EnumMessage* dst =
(const upb_test_convert_Proto2EnumMessage*)dst_msg;
size_t count;
const int* values =
upb_test_convert_Proto2EnumMessage_repeated_nested_enum(dst, &count);
ASSERT_EQ(1, count);
EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR, values[0]);
size_t iter = kUpb_Message_UnknownBegin;
upb_StringView data;
ASSERT_TRUE(upb_Message_NextUnknown(dst_msg, &data, &iter));
EXPECT_GT(data.size, 0);
protobuf_test_messages_proto3_TestAllTypesProto3* check_msg =
protobuf_test_messages_proto3_TestAllTypesProto3_parse(
data.data, data.size, arena.ptr());
ASSERT_NE(check_msg, nullptr);
size_t check_count;
const int* check_values =
protobuf_test_messages_proto3_TestAllTypesProto3_repeated_nested_enum(
check_msg, &check_count);
EXPECT_EQ(1, check_count);
EXPECT_EQ(12345, check_values[0]);
}
TEST(ConvertTest, OpenToClosedExtensionEnum) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_MessageValue ext_val;
ext_val.int32_val = upb_test_convert_Proto2EnumMessage_BAR;
upb_Message_SetExtension(UPB_UPCAST(msg), upb_test_convert_ext_enum_ext,
&ext_val, arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithKnownEnum_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithKnownEnum* dst =
(const upb_test_convert_MessageWithKnownEnum*)dst_msg;
EXPECT_TRUE(upb_test_convert_MessageWithKnownEnum_has_ext_enum(dst));
EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR,
upb_test_convert_MessageWithKnownEnum_ext_enum(dst));
}
TEST(ConvertTest, OpenToClosedExtensionEnum_InvalidValue) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_MessageValue ext_val;
ext_val.int32_val = 12345; // Invalid value.
upb_Message_SetExtension(UPB_UPCAST(msg), upb_test_convert_ext_enum_ext,
&ext_val, arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithKnownEnum_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithKnownEnum* dst =
(const upb_test_convert_MessageWithKnownEnum*)dst_msg;
EXPECT_FALSE(upb_test_convert_MessageWithKnownEnum_has_ext_enum(dst));
size_t iter = kUpb_Message_UnknownBegin;
upb_StringView data;
ASSERT_TRUE(upb_Message_NextUnknown(dst_msg, &data, &iter));
EXPECT_GT(data.size, 0);
// We cannot parse it back using the extension registry because the extension
// itself is a closed enum, so the parser would just put it into unknown
// fields again. Instead, we verify the raw bytes.
// Tag: 2000 << 3 | 0 (varint) = 16000 = 0x3E80 -> 0x80 0x7D
// Value: 12345 = 0x3039 -> 0xB9 0x60
EXPECT_EQ(data.size, 4);
EXPECT_EQ((uint8_t)data.data[0], 0x80);
EXPECT_EQ((uint8_t)data.data[1], 0x7D);
EXPECT_EQ((uint8_t)data.data[2], 0xB9);
EXPECT_EQ((uint8_t)data.data[3], 0x60);
}
TEST(ConvertTest, OpenToClosedExtensionRepeatedEnum) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_Array* ext_arr = upb_Array_New(arena.ptr(), kUpb_CType_Enum);
upb_MessageValue elem_val;
elem_val.int32_val = upb_test_convert_Proto2EnumMessage_FOO;
upb_Array_Append(ext_arr, elem_val, arena.ptr());
elem_val.int32_val = upb_test_convert_Proto2EnumMessage_BAR;
upb_Array_Append(ext_arr, elem_val, arena.ptr());
upb_MessageValue ext_val;
ext_val.array_val = ext_arr;
upb_Message_SetExtension(UPB_UPCAST(msg),
upb_test_convert_ext_repeated_enum_ext, &ext_val,
arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithKnownEnum_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithKnownEnum* dst =
(const upb_test_convert_MessageWithKnownEnum*)dst_msg;
size_t count;
const int* values =
upb_test_convert_MessageWithKnownEnum_ext_repeated_enum(dst, &count);
EXPECT_EQ(2, count);
EXPECT_EQ(upb_test_convert_Proto2EnumMessage_FOO, values[0]);
EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR, values[1]);
}
TEST(ConvertTest, OpenToClosedExtensionRepeatedEnum_InvalidValue) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_Array* ext_arr = upb_Array_New(arena.ptr(), kUpb_CType_Enum);
upb_MessageValue elem_val;
elem_val.int32_val = 12345; // Invalid value.
upb_Array_Append(ext_arr, elem_val, arena.ptr());
elem_val.int32_val = upb_test_convert_Proto2EnumMessage_BAR;
upb_Array_Append(ext_arr, elem_val, arena.ptr());
upb_MessageValue ext_val;
ext_val.array_val = ext_arr;
upb_Message_SetExtension(UPB_UPCAST(msg),
upb_test_convert_ext_repeated_enum_ext, &ext_val,
arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithKnownEnum_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
const upb_test_convert_MessageWithKnownEnum* dst =
(const upb_test_convert_MessageWithKnownEnum*)dst_msg;
size_t count = 0;
const int* values =
upb_test_convert_MessageWithKnownEnum_ext_repeated_enum(dst, &count);
EXPECT_EQ(1, count);
EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR, values[0]);
size_t iter = kUpb_Message_UnknownBegin;
upb_StringView data;
ASSERT_TRUE(upb_Message_NextUnknown(dst_msg, &data, &iter));
EXPECT_GT(data.size, 0);
// We cannot parse it back using the extension registry because the extension
// itself is a closed enum, so the parser would just put it into unknown
// fields again. Instead, we verify the raw bytes.
// Tag: 2001 << 3 | 0 (varint) = 16008 = 0x3E88 -> 0x88 0x7D
// Value: 12345 = 0x3039 -> 0xB9 0x60
EXPECT_EQ(data.size, 4);
EXPECT_EQ((uint8_t)data.data[0], 0x88);
EXPECT_EQ((uint8_t)data.data[1], 0x7D);
EXPECT_EQ((uint8_t)data.data[2], 0xB9);
EXPECT_EQ((uint8_t)data.data[3], 0x60);
}
TEST(ConvertTest, ExtensionToMapMismatch) {
upb::Arena arena;
upb_test_convert_MessageWithExtension* msg =
upb_test_convert_MessageWithExtension_new(arena.ptr());
upb_MessageValue ext_val;
ext_val.int32_val = 123;
upb_Message_SetExtension(UPB_UPCAST(msg),
upb_test_convert_ext_field_int32_ext, &ext_val,
arena.ptr());
const upb_MiniTable* src_mt =
&upb__test__convert__MessageWithExtension_msg_init;
const upb_MiniTable* dst_mt =
&upb__test__convert__MessageWithMapAt1000_msg_init;
// Return NULL when the destination mini table has a map, which is not
// compatible with the source extension.
ASSERT_EQ(upb_Message_Convert(UPB_UPCAST(msg), src_mt, dst_mt, nullptr,
arena.ptr()),
nullptr);
}
TEST(ConvertTest, OneofPromotion) {
upb::Arena arena;
upb_test_convert_SrcWithoutOneof* msg =
upb_test_convert_SrcWithoutOneof_new(arena.ptr());
upb_test_convert_SrcWithoutOneof_set_oneof_int32(msg, 123);
upb_test_convert_SrcWithoutOneof_set_oneof_string(
msg, upb_StringView_FromString("abc"));
const upb_MiniTable* src_mt = &upb__test__convert__SrcWithoutOneof_msg_init;
const upb_MiniTable* dst_mt = &upb__test__convert__SrcWithOneof_msg_init;
const upb_Message* dst_msg = upb_Message_Convert(
UPB_UPCAST(msg), src_mt, dst_mt, nullptr, arena.ptr());
ASSERT_NE(dst_msg, nullptr);
// Since both fields in the source message are set (oneof_int32=123 and
// oneof_string="abc") and are moved into a oneof in the destination schema,
// only one of them can be set at the end. In this case, because fields are
// converted in descending order, the higher field number 2 (oneof_string)
// is converted first, and then field number 1 (oneof_int32) is ignored
// because the oneof already has a field set. This matches the roundtrip
// encoding-then-decoding path where field 2 is serialized last and wins.
const upb_test_convert_SrcWithOneof* dst =
(const upb_test_convert_SrcWithOneof*)dst_msg;
EXPECT_EQ(upb_test_convert_SrcWithOneof_my_oneof_case(dst),
upb_test_convert_SrcWithOneof_my_oneof_oneof_string);
upb_StringView str = upb_test_convert_SrcWithOneof_oneof_string(dst);
EXPECT_EQ(std::string("abc"), std::string(str.data, str.size));
}