| // 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 <cstring> | |
| #include <string> | |
| #include <string_view> | |
| #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/map.h" | |
| #include "upb/message/message.h" | |
| #include "upb/message/test.upb_minitable.h" | |
| #include "upb/message/unknown_fields.h" | |
| #include "upb/mini_table/extension.h" | |
| #include "upb/mini_table/extension_registry.h" | |
| #include "upb/mini_table/field.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" | |
| #include "upb/wire/encode.h" | |
| // 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, arena.ptr()); | |
| EXPECT_NE(dst, nullptr); | |
| // Dst should have unknown field 1 with value 999. | |
| size_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| upb_StringView data = unknown.value.bytes; | |
| 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_NextUnknown2(dst, &unknown, &iter)); | |
| } | |
| TEST(ConvertTest, MultipleDemotionContiguous) { | |
| 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, 111); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_int64(msg, 222); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_string( | |
| msg, upb_StringView_FromString("demoted_str")); | |
| // Convert to Empty message. All three fields 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, 0, 0, arena.ptr()); | |
| EXPECT_NE(dst, nullptr); | |
| // All demoted unknown fields must be contiguously packed into a SINGLE | |
| // StringView entry in dst, rather than multiple separate allocations. | |
| size_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| // There should be NO second unknown entry. | |
| EXPECT_FALSE(upb_Message_NextUnknown2(dst, &unknown, &iter)); | |
| // Verify that decoding dst back into TestAllTypesProto3 recovers all fields. | |
| const upb_Message* converted_back = | |
| upb_Message_Convert(dst, empty_mt, TEST_MT, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted_back, nullptr); | |
| const auto* rt = | |
| (const protobuf_test_messages_proto3_TestAllTypesProto3*)converted_back; | |
| EXPECT_EQ( | |
| 111, protobuf_test_messages_proto3_TestAllTypesProto3_optional_int32(rt)); | |
| EXPECT_EQ( | |
| 222, protobuf_test_messages_proto3_TestAllTypesProto3_optional_int64(rt)); | |
| upb_StringView str = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_optional_string(rt); | |
| EXPECT_EQ("demoted_str", std::string_view(str.data, str.size)); | |
| } | |
| TEST(ConvertTest, DemoteUnsetFieldsNoAlloc) { | |
| upb::Arena arena; | |
| // All fields unset in msg. | |
| protobuf_test_messages_proto3_TestAllTypesProto3* msg = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr()); | |
| 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, 0, 0, arena.ptr()); | |
| EXPECT_NE(dst, nullptr); | |
| // Dst should have ZERO unknown entries. | |
| size_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown; | |
| EXPECT_FALSE(upb_Message_NextUnknown2(dst, &unknown, &iter)); | |
| } | |
| TEST(ConvertTest, DemoteEmptyRepeatedAndMapFields) { | |
| upb::Arena arena; | |
| protobuf_test_messages_proto3_TestAllTypesProto3* msg = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr()); | |
| // Allocate a repeated field and a map field, but leave them empty. They count | |
| // as set but encode to nothing. | |
| protobuf_test_messages_proto3_TestAllTypesProto3_add_repeated_int32( | |
| msg, 1, arena.ptr()); | |
| ASSERT_NE( | |
| nullptr, | |
| protobuf_test_messages_proto3_TestAllTypesProto3_resize_repeated_int32( | |
| msg, 0, arena.ptr())); | |
| ASSERT_TRUE( | |
| protobuf_test_messages_proto3_TestAllTypesProto3_map_int32_int32_set( | |
| msg, 1, 1, arena.ptr())); | |
| ASSERT_TRUE( | |
| protobuf_test_messages_proto3_TestAllTypesProto3_map_int32_int32_delete( | |
| msg, 1)); | |
| const upb_MiniTable* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| // Only empty fields: no unknown chunk should be added. | |
| const upb_Message* dst = upb_Message_Convert( | |
| UPB_UPCAST(msg), TEST_MT, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(dst, nullptr); | |
| size_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown; | |
| EXPECT_FALSE(upb_Message_NextUnknown2(dst, &unknown, &iter)); | |
| // Empty fields alongside a non-empty one: a single chunk holding only the | |
| // non-empty field. | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_int32(msg, 5); | |
| dst = upb_Message_Convert(UPB_UPCAST(msg), TEST_MT, empty_mt, nullptr, 0, 0, | |
| arena.ptr()); | |
| ASSERT_NE(dst, nullptr); | |
| iter = kUpb_Message_UnknownBegin; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| EXPECT_EQ("\x08\x05", std::string_view(unknown.value.bytes.data, | |
| unknown.value.bytes.size)); | |
| EXPECT_FALSE(upb_Message_NextUnknown2(dst, &unknown, &iter)); | |
| } | |
| TEST(ConvertTest, DemoteBigFieldThenSmallFieldChunked) { | |
| upb::Arena arena; | |
| protobuf_test_messages_proto3_TestAllTypesProto3* msg = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr()); | |
| // Create a large 2KB string field and a small 4-byte int32 field. | |
| std::string large_string(2048, 'X'); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_string( | |
| msg, upb_StringView_FromString(large_string.c_str())); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_int32(msg, 777); | |
| 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, 0, 0, arena.ptr()); | |
| EXPECT_NE(dst, nullptr); | |
| // The large field should be isolated in its own chunk, and the small field in | |
| // its own chunk, avoiding reallocation and copying of the 2KB data. | |
| size_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown chunk1, chunk2; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst, &chunk1, &iter)); | |
| EXPECT_EQ(chunk1.type, kUpb_MessageUnknownType_StringView); | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst, &chunk2, &iter)); | |
| EXPECT_EQ(chunk2.type, kUpb_MessageUnknownType_StringView); | |
| EXPECT_FALSE(upb_Message_NextUnknown2(dst, &chunk2, &iter)); | |
| // Round-trip must recover both fields accurately. | |
| const upb_Message* converted_back = | |
| upb_Message_Convert(dst, empty_mt, TEST_MT, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted_back, nullptr); | |
| const auto* rt = | |
| (const protobuf_test_messages_proto3_TestAllTypesProto3*)converted_back; | |
| EXPECT_EQ( | |
| 777, protobuf_test_messages_proto3_TestAllTypesProto3_optional_int32(rt)); | |
| upb_StringView str = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_optional_string(rt); | |
| EXPECT_EQ(large_string, std::string_view(str.data, str.size)); | |
| } | |
| TEST(ConvertTest, DemoteSmallFieldThenBigFieldChunked) { | |
| upb::Arena arena; | |
| protobuf_test_messages_proto3_TestAllTypesProto3* msg = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr()); | |
| // Set field 21 (small enum), field 14 (large 2KB string), and field 1 (small | |
| // int32). | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_nested_enum( | |
| msg, protobuf_test_messages_proto3_TestAllTypesProto3_BAZ); | |
| std::string large_string(2048, 'Y'); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_string( | |
| msg, upb_StringView_FromString(large_string.c_str())); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_int32(msg, 999); | |
| 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, 0, 0, arena.ptr()); | |
| EXPECT_NE(dst, nullptr); | |
| // Field 21 (first in descending order) should be in chunk 1, field 14 (large) | |
| // in chunk 2, and field 1 in chunk 3. | |
| size_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown chunk1, chunk2, chunk3; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst, &chunk1, &iter)); | |
| EXPECT_EQ(chunk1.type, kUpb_MessageUnknownType_StringView); | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst, &chunk2, &iter)); | |
| EXPECT_EQ(chunk2.type, kUpb_MessageUnknownType_StringView); | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst, &chunk3, &iter)); | |
| EXPECT_EQ(chunk3.type, kUpb_MessageUnknownType_StringView); | |
| EXPECT_FALSE(upb_Message_NextUnknown2(dst, &chunk3, &iter)); | |
| // Round-trip must recover all three fields accurately. | |
| const upb_Message* converted_back = | |
| upb_Message_Convert(dst, empty_mt, TEST_MT, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted_back, nullptr); | |
| const auto* rt = | |
| (const protobuf_test_messages_proto3_TestAllTypesProto3*)converted_back; | |
| EXPECT_EQ( | |
| 999, protobuf_test_messages_proto3_TestAllTypesProto3_optional_int32(rt)); | |
| EXPECT_EQ( | |
| protobuf_test_messages_proto3_TestAllTypesProto3_BAZ, | |
| protobuf_test_messages_proto3_TestAllTypesProto3_optional_nested_enum( | |
| rt)); | |
| upb_StringView str = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_optional_string(rt); | |
| EXPECT_EQ(large_string, std::string_view(str.data, str.size)); | |
| } | |
| TEST(ConvertTest, DemoteMultiplePrimitivesSingleChunk) { | |
| upb::Arena arena; | |
| protobuf_test_messages_proto3_TestAllTypesProto3* msg = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr()); | |
| // Set several scalar primitive fields: bool, float, double, int32, int64, | |
| // enum. | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_bool(msg, true); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_float(msg, | |
| 1.5f); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_double(msg, | |
| 3.14); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_int32(msg, 42); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_int64( | |
| msg, 1000000000LL); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_nested_enum( | |
| msg, protobuf_test_messages_proto3_TestAllTypesProto3_BAZ); | |
| 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, 0, 0, arena.ptr()); | |
| EXPECT_NE(dst, nullptr); | |
| // All scalar primitive fields must fit contiguously into a SINGLE StringView | |
| // chunk. | |
| size_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown chunk; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst, &chunk, &iter)); | |
| EXPECT_EQ(chunk.type, kUpb_MessageUnknownType_StringView); | |
| EXPECT_FALSE(upb_Message_NextUnknown2(dst, &chunk, &iter)); | |
| // Round-trip recovery check. | |
| const upb_Message* converted_back = | |
| upb_Message_Convert(dst, empty_mt, TEST_MT, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted_back, nullptr); | |
| const auto* rt = | |
| (const protobuf_test_messages_proto3_TestAllTypesProto3*)converted_back; | |
| EXPECT_EQ(true, | |
| protobuf_test_messages_proto3_TestAllTypesProto3_optional_bool(rt)); | |
| EXPECT_EQ( | |
| 1.5f, | |
| protobuf_test_messages_proto3_TestAllTypesProto3_optional_float(rt)); | |
| EXPECT_EQ( | |
| 3.14, | |
| protobuf_test_messages_proto3_TestAllTypesProto3_optional_double(rt)); | |
| EXPECT_EQ( | |
| 42, protobuf_test_messages_proto3_TestAllTypesProto3_optional_int32(rt)); | |
| EXPECT_EQ( | |
| 1000000000LL, | |
| protobuf_test_messages_proto3_TestAllTypesProto3_optional_int64(rt)); | |
| EXPECT_EQ( | |
| protobuf_test_messages_proto3_TestAllTypesProto3_BAZ, | |
| protobuf_test_messages_proto3_TestAllTypesProto3_optional_nested_enum( | |
| rt)); | |
| } | |
| 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, 0, 0, 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, 0, 0, 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, 0, 0, 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); | |
| ASSERT_TRUE(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; | |
| EXPECT_TRUE(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, 0, 0, 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); | |
| ASSERT_TRUE(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; | |
| EXPECT_TRUE(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, 0, 0, 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; | |
| EXPECT_TRUE(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, 0, 0, | |
| 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, 0, 0, | |
| 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, 0, 0, | |
| 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, 0, 0, | |
| 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, 0, 0, | |
| 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, 0, 0, | |
| 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, 0, 0, | |
| 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, 0, 0, | |
| 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, 0, 0, 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, 0, 0, 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, 0, 0, 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; | |
| EXPECT_TRUE(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, 0, 0, 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); | |
| EXPECT_TRUE(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, 0, 0, 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); | |
| EXPECT_TRUE(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, 0, 0, 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; | |
| EXPECT_TRUE(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, 0, 0, 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; | |
| EXPECT_TRUE(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()); | |
| ASSERT_EQ(upb_ExtensionRegistry_Add( | |
| extreg, upb_test_convert_another_ext_field_int32_ext), | |
| kUpb_ExtensionRegistryStatus_Ok); | |
| const upb_Message* dst_msg = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, dst_mt, extreg, 0, 0, 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; | |
| EXPECT_TRUE(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, 0, 0, arena.ptr()); | |
| ASSERT_NE(dst_msg, nullptr); | |
| // Dst should have a non-canonical extension of field 1000 with value 123. | |
| uintptr_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst_msg, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_NonCanonicalExtension); | |
| const upb_Extension* ext_found = | |
| static_cast<const upb_Extension*>(unknown.value.extension); | |
| EXPECT_EQ(upb_MiniTableField_Number(upb_Extension_MiniTableField(ext_found)), | |
| 1000); | |
| EXPECT_EQ(upb_Extension_GetInt32(ext_found), 123); | |
| EXPECT_FALSE(upb_Message_NextUnknown2(dst_msg, &unknown, &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; | |
| EXPECT_TRUE(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, 0, 0, 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 handled/stored as a | |
| // non-canonical extension. | |
| uintptr_t iter2 = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown2; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst_msg, &unknown2, &iter2)); | |
| EXPECT_EQ(unknown2.type, kUpb_MessageUnknownType_NonCanonicalExtension); | |
| const upb_Extension* ext_found2 = | |
| static_cast<const upb_Extension*>(unknown2.value.extension); | |
| EXPECT_EQ(upb_MiniTableField_Number(upb_Extension_MiniTableField(ext_found2)), | |
| 1000); | |
| EXPECT_EQ(upb_Extension_GetInt32(ext_found2), 123); | |
| EXPECT_FALSE(upb_Message_NextUnknown2(dst_msg, &unknown2, &iter2)); | |
| } | |
| TEST(ConvertTest, NonCanonicalToNonCanonical) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_MessageValue ext_val; | |
| ext_val.int32_val = 123; | |
| EXPECT_TRUE(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; | |
| // Convert without registry to obtain a non-canonical extension source. | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| // Convert without registry again. It should remain non-canonical. | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, dst_mt, src_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| uintptr_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(converted, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_NonCanonicalExtension); | |
| const upb_Extension* ext_found = | |
| static_cast<const upb_Extension*>(unknown.value.extension); | |
| EXPECT_EQ(upb_MiniTableField_Number(upb_Extension_MiniTableField(ext_found)), | |
| 1000); | |
| EXPECT_EQ(upb_Extension_GetInt32(ext_found), 123); | |
| EXPECT_FALSE(upb_Message_NextUnknown2(converted, &unknown, &iter)); | |
| } | |
| TEST(ConvertTest, NonCanonicalToCanonical) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_MessageValue ext_val; | |
| ext_val.int32_val = 123; | |
| EXPECT_TRUE(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; | |
| // Convert without registry to obtain a non-canonical extension source. | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| // Convert with registry. It should become a canonical extension. | |
| upb_ExtensionRegistry* extreg = upb_ExtensionRegistry_New(arena.ptr()); | |
| ASSERT_NE(extreg, nullptr); | |
| EXPECT_EQ(upb_ExtensionRegistry_Add( | |
| extreg, upb_test_convert_another_ext_field_int32_ext), | |
| kUpb_ExtensionRegistryStatus_Ok); | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, dst_mt, dst_mt, extreg, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| const upb_test_convert_AnotherMessageWithExtension* dst = | |
| (const upb_test_convert_AnotherMessageWithExtension*)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); | |
| uintptr_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown; | |
| EXPECT_FALSE(upb_Message_NextUnknown2(converted, &unknown, &iter)); | |
| } | |
| TEST(ConvertTest, NonCanonicalToNormalField) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_MessageValue ext_val; | |
| ext_val.int32_val = 123; | |
| EXPECT_TRUE(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; | |
| // Convert without registry to obtain a non-canonical extension source. | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| // Convert to MessageWithKnown. Field 1000 is a normal field. | |
| const upb_MiniTable* known_mt = | |
| &upb__test__convert__MessageWithKnown_msg_init; | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, dst_mt, known_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| const upb_test_convert_MessageWithKnown* dst = | |
| (const upb_test_convert_MessageWithKnown*)converted; | |
| EXPECT_TRUE(upb_test_convert_MessageWithKnown_has_known_field_int32(dst)); | |
| EXPECT_EQ(123, upb_test_convert_MessageWithKnown_known_field_int32(dst)); | |
| uintptr_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown; | |
| EXPECT_FALSE(upb_Message_NextUnknown2(converted, &unknown, &iter)); | |
| } | |
| TEST(ConvertTest, NonCanonicalToUnknownBytes) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_MessageValue ext_val; | |
| ext_val.int32_val = 123; | |
| EXPECT_TRUE(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; | |
| // Convert without registry to obtain a non-canonical extension source. | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| // Serialize the non_canonical_src to wire bytes. | |
| char* wire_buf; | |
| size_t wire_size; | |
| upb_EncodeStatus encode_status = upb_Encode( | |
| non_canonical_src, dst_mt, 0, arena.ptr(), &wire_buf, &wire_size); | |
| ASSERT_EQ(encode_status, kUpb_EncodeStatus_Ok); | |
| // Decode the bytes back to AnotherMessageWithExtension without registry. | |
| // The decoder should decode the extension as unknown bytes (type Bytes). | |
| upb_Message* decoded = upb_Message_New(dst_mt, arena.ptr()); | |
| ASSERT_NE(decoded, nullptr); | |
| upb_DecodeStatus decode_status = | |
| upb_Decode(wire_buf, wire_size, decoded, dst_mt, nullptr, 0, arena.ptr()); | |
| ASSERT_EQ(decode_status, kUpb_DecodeStatus_Ok); | |
| uintptr_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(decoded, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| EXPECT_GT(unknown.value.bytes.size, 0); | |
| EXPECT_FALSE(upb_Message_NextUnknown2(decoded, &unknown, &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, 0, 0, arena.ptr()); | |
| EXPECT_NE(dst, nullptr); | |
| // Dst should have unknown field 2 with value "test". | |
| size_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown; | |
| EXPECT_TRUE(upb_Message_NextUnknown2(dst, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| upb_StringView data = unknown.value.bytes; | |
| // "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_NextUnknown2(dst, &unknown, &iter)); | |
| // Now convert back and verify. | |
| const upb_Message* dst2_msg = | |
| upb_Message_Convert(dst, empty_mt, src_mt, nullptr, 0, 0, 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, 0, 0, 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, 0, 0, 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_MessageUnknown unknown; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst_msg, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| upb_StringView data = unknown.value.bytes; | |
| 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, 0, 0, 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, 0, 0, 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_MessageUnknown unknown; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst_msg, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| upb_StringView data = unknown.value.bytes; | |
| 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, 0, 0, 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, 0, 0, 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_MessageUnknown unknown; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst_msg, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| upb_StringView data = unknown.value.bytes; | |
| 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, ClosedToOpenEnum) { | |
| upb::Arena arena; | |
| const upb_MiniTable* src_mt = &upb__test__convert__Proto2EnumMessage_msg_init; | |
| const upb_MiniTable* dst_mt = | |
| &protobuf_0test_0messages__proto3__TestAllTypesProto3_msg_init; | |
| // 1. Test valid value. | |
| { | |
| upb_test_convert_Proto2EnumMessage* msg = | |
| upb_test_convert_Proto2EnumMessage_new(arena.ptr()); | |
| upb_test_convert_Proto2EnumMessage_set_optional_nested_enum( | |
| msg, upb_test_convert_Proto2EnumMessage_BAR); | |
| const upb_Message* dst_msg = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(dst_msg, nullptr); | |
| const protobuf_test_messages_proto3_TestAllTypesProto3* dst = | |
| (const protobuf_test_messages_proto3_TestAllTypesProto3*)dst_msg; | |
| EXPECT_EQ( | |
| protobuf_test_messages_proto3_TestAllTypesProto3_BAR, | |
| protobuf_test_messages_proto3_TestAllTypesProto3_optional_nested_enum( | |
| dst)); | |
| } | |
| // 2. Test invalid value (should be promoted from unknown to known). | |
| { | |
| // We need to construct a wire format with the invalid value and decode it | |
| // into Proto2EnumMessage so it goes to unknown fields. | |
| // Field number for optional_nested_enum is 21. | |
| // Tag: 21 << 3 | 0 (varint) = 168 (0xa8), varint: 0xa8 0x01 | |
| // Value: 12345 = 0x3039, varint: 0xb9 0x60 | |
| char buf[32]; | |
| char* ptr = buf; | |
| *ptr++ = (char)0xa8; | |
| *ptr++ = (char)0x01; | |
| *ptr++ = (char)0xb9; | |
| *ptr++ = (char)0x60; | |
| size_t len = ptr - buf; | |
| upb_test_convert_Proto2EnumMessage* msg = | |
| upb_test_convert_Proto2EnumMessage_new(arena.ptr()); | |
| upb_DecodeStatus decode_status = | |
| upb_Decode(buf, len, UPB_UPCAST(msg), src_mt, nullptr, 0, arena.ptr()); | |
| ASSERT_EQ(kUpb_DecodeStatus_Ok, decode_status); | |
| ASSERT_FALSE( | |
| upb_test_convert_Proto2EnumMessage_has_optional_nested_enum(msg)); | |
| const upb_Message* dst_msg = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(dst_msg, nullptr); | |
| const protobuf_test_messages_proto3_TestAllTypesProto3* dst = | |
| (const protobuf_test_messages_proto3_TestAllTypesProto3*)dst_msg; | |
| EXPECT_EQ( | |
| 12345, | |
| protobuf_test_messages_proto3_TestAllTypesProto3_optional_nested_enum( | |
| dst)); | |
| } | |
| } | |
| TEST(ConvertTest, ClosedToOpenMapEnum) { | |
| upb::Arena arena; | |
| const upb_MiniTable* src_mt = &upb__test__convert__Proto2EnumMessage_msg_init; | |
| const upb_MiniTable* dst_mt = | |
| &protobuf_0test_0messages__proto3__TestAllTypesProto3_msg_init; | |
| // 1. Test valid values. | |
| { | |
| upb_test_convert_Proto2EnumMessage* msg = | |
| upb_test_convert_Proto2EnumMessage_new(arena.ptr()); | |
| upb_test_convert_Proto2EnumMessage_map_string_nested_enum_set( | |
| msg, upb_StringView_FromString("valid1"), | |
| upb_test_convert_Proto2EnumMessage_BAR, arena.ptr()); | |
| const upb_Message* dst_msg = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(dst_msg, nullptr); | |
| const protobuf_test_messages_proto3_TestAllTypesProto3* dst = | |
| (const protobuf_test_messages_proto3_TestAllTypesProto3*)dst_msg; | |
| int val; | |
| EXPECT_TRUE( | |
| protobuf_test_messages_proto3_TestAllTypesProto3_map_string_nested_enum_get( | |
| dst, upb_StringView_FromString("valid1"), &val)); | |
| EXPECT_EQ(protobuf_test_messages_proto3_TestAllTypesProto3_BAR, val); | |
| } | |
| // 2. Test invalid value (should be promoted from unknown map entry to known | |
| // map entry). | |
| { | |
| // Construct a wire format for a map entry with an invalid enum value. | |
| // map_string_nested_enum has field number 73. | |
| // MapEntry wire format: | |
| // Tag: 73 << 3 | 2 (length-delimited) = 586 (0x24a), varint: 0xca 0x04 | |
| // Length: 12 | |
| // MapEntry fields: key (1, string), value (2, enum) | |
| // Key "invalid": Tag: 10 (1 << 3 | 2), Length: 7, Value: "invalid" | |
| // Value 12345: Tag: 16 (2 << 3 | 0), Value: 12345 (varint: 0xb9 0x60) | |
| char buf[64]; | |
| char* ptr = buf; | |
| *ptr++ = (char)0xca; | |
| *ptr++ = (char)0x04; | |
| *ptr++ = 12; // Length of MapEntry | |
| *ptr++ = 0x0a; | |
| *ptr++ = 7; | |
| memcpy(ptr, "invalid", 7); | |
| ptr += 7; | |
| *ptr++ = 0x10; | |
| *ptr++ = (char)0xb9; | |
| *ptr++ = (char)0x60; | |
| size_t len = ptr - buf; | |
| upb_test_convert_Proto2EnumMessage* msg = | |
| upb_test_convert_Proto2EnumMessage_new(arena.ptr()); | |
| upb_DecodeStatus decode_status = | |
| upb_Decode(buf, len, UPB_UPCAST(msg), src_mt, nullptr, 0, arena.ptr()); | |
| ASSERT_EQ(kUpb_DecodeStatus_Ok, decode_status); | |
| // Verify it is not in the known map | |
| int val; | |
| EXPECT_FALSE(upb_test_convert_Proto2EnumMessage_map_string_nested_enum_get( | |
| msg, upb_StringView_FromString("invalid"), &val)); | |
| const upb_Message* dst_msg = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(dst_msg, nullptr); | |
| const protobuf_test_messages_proto3_TestAllTypesProto3* dst = | |
| (const protobuf_test_messages_proto3_TestAllTypesProto3*)dst_msg; | |
| EXPECT_TRUE( | |
| protobuf_test_messages_proto3_TestAllTypesProto3_map_string_nested_enum_get( | |
| dst, upb_StringView_FromString("invalid"), &val)); | |
| EXPECT_EQ(12345, val); | |
| } | |
| } | |
| TEST(ConvertTest, ClosedToOpenRepeatedEnum) { | |
| upb::Arena arena; | |
| const upb_MiniTable* src_mt = &upb__test__convert__Proto2EnumMessage_msg_init; | |
| const upb_MiniTable* dst_mt = | |
| &protobuf_0test_0messages__proto3__TestAllTypesProto3_msg_init; | |
| // 1. Test valid values. | |
| { | |
| upb_test_convert_Proto2EnumMessage* msg = | |
| upb_test_convert_Proto2EnumMessage_new(arena.ptr()); | |
| upb_test_convert_Proto2EnumMessage_add_repeated_nested_enum( | |
| msg, upb_test_convert_Proto2EnumMessage_BAR, arena.ptr()); | |
| const upb_Message* dst_msg = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(dst_msg, nullptr); | |
| const protobuf_test_messages_proto3_TestAllTypesProto3* dst = | |
| (const protobuf_test_messages_proto3_TestAllTypesProto3*)dst_msg; | |
| size_t count; | |
| const int* values = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_repeated_nested_enum( | |
| dst, &count); | |
| ASSERT_EQ(1, count); | |
| EXPECT_EQ(protobuf_test_messages_proto3_TestAllTypesProto3_BAR, values[0]); | |
| } | |
| // 2. Test invalid values (should be merged back in correct order, no | |
| // mutation). | |
| { | |
| // Construct a wire format with: [valid (BAR), invalid (12345), valid (FOO)] | |
| // repeated_nested_enum has field number 51. | |
| // Tag: 51 << 3 | 0 (varint) = 408 (0x198), varint: 0x98 0x03 | |
| // BAR (1): Tag, Value 1 | |
| // 12345: Tag, Value 12345 (varint: 0xb9 0x60) | |
| // FOO (0): Tag, Value 0 | |
| char buf[64]; | |
| char* ptr = buf; | |
| // BAR | |
| *ptr++ = (char)0x98; | |
| *ptr++ = (char)0x03; | |
| *ptr++ = 1; | |
| // 12345 | |
| *ptr++ = (char)0x98; | |
| *ptr++ = (char)0x03; | |
| *ptr++ = (char)0xb9; | |
| *ptr++ = (char)0x60; | |
| // FOO | |
| *ptr++ = (char)0x98; | |
| *ptr++ = (char)0x03; | |
| *ptr++ = 0; | |
| size_t len = ptr - buf; | |
| upb_test_convert_Proto2EnumMessage* msg = | |
| upb_test_convert_Proto2EnumMessage_new(arena.ptr()); | |
| upb_DecodeStatus decode_status = | |
| upb_Decode(buf, len, UPB_UPCAST(msg), src_mt, nullptr, 0, arena.ptr()); | |
| ASSERT_EQ(kUpb_DecodeStatus_Ok, decode_status); | |
| // Verify source state: known has [BAR, FOO], unknown has [12345] | |
| size_t count; | |
| const int* values = | |
| upb_test_convert_Proto2EnumMessage_repeated_nested_enum(msg, &count); | |
| ASSERT_EQ(2, count); | |
| EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR, values[0]); | |
| EXPECT_EQ(upb_test_convert_Proto2EnumMessage_FOO, values[1]); | |
| // Convert to open destination. | |
| const upb_Message* dst_msg = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(dst_msg, nullptr); | |
| const protobuf_test_messages_proto3_TestAllTypesProto3* dst = | |
| (const protobuf_test_messages_proto3_TestAllTypesProto3*)dst_msg; | |
| // Verify destination state: should have [BAR, 12345, FOO] in correct order! | |
| const int* dst_values = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_repeated_nested_enum( | |
| dst, &count); | |
| ASSERT_EQ(3, count); | |
| EXPECT_EQ(protobuf_test_messages_proto3_TestAllTypesProto3_BAR, | |
| dst_values[0]); | |
| EXPECT_EQ(protobuf_test_messages_proto3_TestAllTypesProto3_FOO, | |
| dst_values[1]); | |
| EXPECT_EQ(12345, dst_values[2]); | |
| // CRITICAL: Verify source was NOT mutated! | |
| const int* src_values_post = | |
| upb_test_convert_Proto2EnumMessage_repeated_nested_enum(msg, &count); | |
| ASSERT_EQ(2, count); | |
| EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR, src_values_post[0]); | |
| EXPECT_EQ(upb_test_convert_Proto2EnumMessage_FOO, src_values_post[1]); | |
| } | |
| } | |
| TEST(ConvertTest, ClosedToSameClosedRepeatedEnum_ShareArray) { | |
| upb::Arena arena; | |
| const upb_MiniTable* mt = &upb__test__convert__Proto2EnumMessage_msg_init; | |
| upb_test_convert_Proto2EnumMessage* msg = | |
| upb_test_convert_Proto2EnumMessage_new(arena.ptr()); | |
| upb_test_convert_Proto2EnumMessage_add_repeated_nested_enum( | |
| msg, upb_test_convert_Proto2EnumMessage_BAR, arena.ptr()); | |
| const upb_Message* dst_msg = | |
| upb_Message_Convert(UPB_UPCAST(msg), mt, mt, nullptr, 0, 0, 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* dst_values = | |
| upb_test_convert_Proto2EnumMessage_repeated_nested_enum(dst, &count); | |
| ASSERT_EQ(1, count); | |
| EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR, dst_values[0]); | |
| // Since both have the same closed enum minitable, they should share the same | |
| // underlying array pointer (shallow copy / alias). | |
| EXPECT_EQ( | |
| upb_test_convert_Proto2EnumMessage_repeated_nested_enum(msg, &count), | |
| dst_values); | |
| } | |
| TEST(ConvertTest, ClosedToSameClosedScalarEnum_CopyDirectly) { | |
| upb::Arena arena; | |
| const upb_MiniTable* mt = &upb__test__convert__Proto2EnumMessage_msg_init; | |
| upb_test_convert_Proto2EnumMessage* msg = | |
| upb_test_convert_Proto2EnumMessage_new(arena.ptr()); | |
| upb_test_convert_Proto2EnumMessage_set_optional_nested_enum( | |
| msg, upb_test_convert_Proto2EnumMessage_BAR); | |
| const upb_Message* dst_msg = | |
| upb_Message_Convert(UPB_UPCAST(msg), mt, mt, nullptr, 0, 0, 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, ClosedToSameClosedMapEnum_ShareMap) { | |
| upb::Arena arena; | |
| const upb_MiniTable* mt = &upb__test__convert__Proto2EnumMessage_msg_init; | |
| upb_test_convert_Proto2EnumMessage* msg = | |
| upb_test_convert_Proto2EnumMessage_new(arena.ptr()); | |
| upb_test_convert_Proto2EnumMessage_map_string_nested_enum_set( | |
| msg, upb_StringView_FromString("valid1"), | |
| upb_test_convert_Proto2EnumMessage_BAR, arena.ptr()); | |
| const upb_Message* dst_msg = | |
| upb_Message_Convert(UPB_UPCAST(msg), mt, mt, nullptr, 0, 0, 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("valid1"), &val)); | |
| EXPECT_EQ(upb_test_convert_Proto2EnumMessage_BAR, val); | |
| // Since both have the same closed enum map schema, they should share the same | |
| // underlying map pointer (shallow copy / alias). | |
| const upb_Map* src_map = upb_Message_GetMap( | |
| UPB_UPCAST(msg), upb_MiniTable_FindFieldByNumber(mt, 73)); | |
| const upb_Map* dst_map = | |
| upb_Message_GetMap(dst_msg, upb_MiniTable_FindFieldByNumber(mt, 73)); | |
| EXPECT_EQ(src_map, dst_map); | |
| } | |
| 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; | |
| EXPECT_TRUE(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, 0, 0, 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. | |
| EXPECT_TRUE(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, 0, 0, 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_MessageUnknown unknown; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst_msg, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| upb_StringView data = unknown.value.bytes; | |
| 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; | |
| ASSERT_TRUE(upb_Array_Append(ext_arr, elem_val, arena.ptr())); | |
| elem_val.int32_val = upb_test_convert_Proto2EnumMessage_BAR; | |
| ASSERT_TRUE(upb_Array_Append(ext_arr, elem_val, arena.ptr())); | |
| upb_MessageValue ext_val; | |
| ext_val.array_val = ext_arr; | |
| EXPECT_TRUE(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, 0, 0, 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. | |
| ASSERT_TRUE(upb_Array_Append(ext_arr, elem_val, arena.ptr())); | |
| elem_val.int32_val = upb_test_convert_Proto2EnumMessage_BAR; | |
| ASSERT_TRUE(upb_Array_Append(ext_arr, elem_val, arena.ptr())); | |
| upb_MessageValue ext_val; | |
| ext_val.array_val = ext_arr; | |
| EXPECT_TRUE(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, 0, 0, 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_MessageUnknown unknown; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(dst_msg, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| upb_StringView data = unknown.value.bytes; | |
| 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; | |
| EXPECT_TRUE(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, 0, 0, | |
| 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, 0, 0, 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)); | |
| } | |
| TEST(ConvertTest, NonCanonicalMessageToCanonical_SameSchema) { | |
| 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); | |
| EXPECT_TRUE(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* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| // Convert to carrier to make it non-canonical. | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| // Convert back to MessageWithExtension with registry. | |
| upb_ExtensionRegistry* extreg = upb_ExtensionRegistry_New(arena.ptr()); | |
| ASSERT_NE(extreg, nullptr); | |
| EXPECT_EQ( | |
| upb_ExtensionRegistry_Add(extreg, upb_test_convert_ext_field_msg_ext), | |
| kUpb_ExtensionRegistryStatus_Ok); | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, src_mt, extreg, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| // Verify canonical extension. | |
| EXPECT_TRUE( | |
| upb_Message_HasExtension(converted, upb_test_convert_ext_field_msg_ext)); | |
| upb_Message* dst_sub = upb_Message_GetExtensionMessage( | |
| converted, upb_test_convert_ext_field_msg_ext, nullptr); | |
| ASSERT_NE(dst_sub, nullptr); | |
| const upb_test_convert_MessageWithInt32* typed_dst_sub = | |
| (const upb_test_convert_MessageWithInt32*)dst_sub; | |
| EXPECT_EQ(123, upb_test_convert_MessageWithInt32_f1(typed_dst_sub)); | |
| // Should be shallow linked (pointer identity) since schemas match. | |
| EXPECT_EQ((const void*)dst_sub, (const void*)sub); | |
| } | |
| TEST(ConvertTest, NonCanonicalMessageToCanonical_DiffSchema) { | |
| 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); | |
| EXPECT_TRUE(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* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| // Convert to carrier to make it non-canonical. | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| // Convert to AnotherMessageWithExtension with registry containing | |
| // another_ext_field_msg. | |
| const upb_MiniTable* dst_mt = | |
| &upb__test__convert__AnotherMessageWithExtension_msg_init; | |
| upb_ExtensionRegistry* extreg = upb_ExtensionRegistry_New(arena.ptr()); | |
| ASSERT_NE(extreg, nullptr); | |
| EXPECT_EQ(upb_ExtensionRegistry_Add( | |
| extreg, upb_test_convert_another_ext_field_msg_ext), | |
| kUpb_ExtensionRegistryStatus_Ok); | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, dst_mt, extreg, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| // Verify canonical extension. | |
| EXPECT_TRUE(upb_Message_HasExtension( | |
| converted, upb_test_convert_another_ext_field_msg_ext)); | |
| upb_Message* dst_sub = upb_Message_GetExtensionMessage( | |
| converted, upb_test_convert_another_ext_field_msg_ext, nullptr); | |
| ASSERT_NE(dst_sub, nullptr); | |
| // Should be MessageWithInt32Clone. | |
| const upb_test_convert_MessageWithInt32Clone* typed_dst_sub = | |
| (const upb_test_convert_MessageWithInt32Clone*)dst_sub; | |
| EXPECT_EQ(123, upb_test_convert_MessageWithInt32Clone_f1(typed_dst_sub)); | |
| // Should be deep copied (different pointer) since schemas differ. | |
| EXPECT_NE((const void*)dst_sub, (const void*)sub); | |
| } | |
| TEST(ConvertTest, NonCanonicalMessageToNormalField_DiffSchema) { | |
| 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); | |
| EXPECT_TRUE(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* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| // Convert to carrier to make it non-canonical. | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| // Convert to MessageWithKnownMsgClone. | |
| const upb_MiniTable* dst_mt = | |
| &upb__test__convert__MessageWithKnownMsgClone_msg_init; | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| // Verify normal field. | |
| const upb_test_convert_MessageWithKnownMsgClone* dst = | |
| (const upb_test_convert_MessageWithKnownMsgClone*)converted; | |
| const upb_test_convert_MessageWithInt32Clone* dst_sub = | |
| upb_test_convert_MessageWithKnownMsgClone_known_msg(dst); | |
| ASSERT_NE(dst_sub, nullptr); | |
| EXPECT_EQ(123, upb_test_convert_MessageWithInt32Clone_f1(dst_sub)); | |
| // Should be deep copied. | |
| EXPECT_NE((const void*)dst_sub, (const void*)sub); | |
| } | |
| TEST(ConvertTest, NonCanonicalMessageToNonCanonical) { | |
| 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); | |
| EXPECT_TRUE(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* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| // Convert to carrier to make it non-canonical. | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| // Convert back to src_mt without registry. It should remain non-canonical. | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, src_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| uintptr_t iter = kUpb_Message_UnknownBegin; | |
| upb_MessageUnknown unknown; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(converted, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_NonCanonicalExtension); | |
| const upb_Extension* ext_found = | |
| static_cast<const upb_Extension*>(unknown.value.extension); | |
| EXPECT_EQ(upb_MiniTableField_Number(upb_Extension_MiniTableField(ext_found)), | |
| 1002); | |
| const upb_test_convert_MessageWithInt32* converted_sub = | |
| (const upb_test_convert_MessageWithInt32*)upb_Extension_GetMessage( | |
| ext_found); | |
| EXPECT_EQ(123, upb_test_convert_MessageWithInt32_f1(converted_sub)); | |
| EXPECT_FALSE(upb_Message_NextUnknown2(converted, &unknown, &iter)); | |
| } | |
| TEST(ConvertTest, NonCanonicalRepeatedInt32ToCanonical) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_Array* arr = upb_Array_New(arena.ptr(), kUpb_CType_Int32); | |
| upb_MessageValue val1, val2; | |
| val1.int32_val = 123; | |
| val2.int32_val = 456; | |
| ASSERT_TRUE(upb_Array_Append(arr, val1, arena.ptr())); | |
| ASSERT_TRUE(upb_Array_Append(arr, val2, arena.ptr())); | |
| upb_MessageValue ext_val; | |
| ext_val.array_val = arr; | |
| EXPECT_TRUE(upb_Message_SetExtension(UPB_UPCAST(msg), | |
| upb_test_convert_ext_repeated_int32_ext, | |
| &ext_val, arena.ptr())); | |
| const upb_MiniTable* src_mt = | |
| &upb__test__convert__MessageWithExtension_msg_init; | |
| const upb_MiniTable* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| upb_ExtensionRegistry* extreg = upb_ExtensionRegistry_New(arena.ptr()); | |
| ASSERT_NE(extreg, nullptr); | |
| EXPECT_EQ(upb_ExtensionRegistry_Add(extreg, | |
| upb_test_convert_ext_repeated_int32_ext), | |
| kUpb_ExtensionRegistryStatus_Ok); | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, src_mt, extreg, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| const upb_Array* dst_arr = upb_Message_GetExtensionArray( | |
| converted, upb_test_convert_ext_repeated_int32_ext); | |
| ASSERT_NE(dst_arr, nullptr); | |
| ASSERT_EQ(2, upb_Array_Size(dst_arr)); | |
| EXPECT_EQ(123, upb_Array_Get(dst_arr, 0).int32_val); | |
| EXPECT_EQ(456, upb_Array_Get(dst_arr, 1).int32_val); | |
| } | |
| TEST(ConvertTest, NonCanonicalRepeatedInt32ToNormalField) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_Array* arr = upb_Array_New(arena.ptr(), kUpb_CType_Int32); | |
| upb_MessageValue val1, val2; | |
| val1.int32_val = 123; | |
| val2.int32_val = 456; | |
| ASSERT_TRUE(upb_Array_Append(arr, val1, arena.ptr())); | |
| ASSERT_TRUE(upb_Array_Append(arr, val2, arena.ptr())); | |
| upb_MessageValue ext_val; | |
| ext_val.array_val = arr; | |
| EXPECT_TRUE(upb_Message_SetExtension(UPB_UPCAST(msg), | |
| upb_test_convert_ext_repeated_int32_ext, | |
| &ext_val, arena.ptr())); | |
| const upb_MiniTable* src_mt = | |
| &upb__test__convert__MessageWithExtension_msg_init; | |
| const upb_MiniTable* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| const upb_MiniTable* dst_mt = | |
| &upb__test__convert__MessageWithKnownRepeatedInt32_msg_init; | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| const upb_test_convert_MessageWithKnownRepeatedInt32* dst = | |
| (const upb_test_convert_MessageWithKnownRepeatedInt32*)converted; | |
| size_t len; | |
| const int32_t* dst_vals = | |
| upb_test_convert_MessageWithKnownRepeatedInt32_known_repeated_int32(dst, | |
| &len); | |
| ASSERT_EQ(2, len); | |
| EXPECT_EQ(123, dst_vals[0]); | |
| EXPECT_EQ(456, dst_vals[1]); | |
| } | |
| TEST(ConvertTest, NonCanonicalStringToCanonical) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_StringView str = upb_StringView_FromString("hello"); | |
| upb_MessageValue ext_val; | |
| ext_val.str_val = str; | |
| EXPECT_TRUE(upb_Message_SetExtension( | |
| UPB_UPCAST(msg), upb_test_convert_ext_string_ext, &ext_val, arena.ptr())); | |
| const upb_MiniTable* src_mt = | |
| &upb__test__convert__MessageWithExtension_msg_init; | |
| const upb_MiniTable* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| upb_ExtensionRegistry* extreg = upb_ExtensionRegistry_New(arena.ptr()); | |
| ASSERT_NE(extreg, nullptr); | |
| EXPECT_EQ(upb_ExtensionRegistry_Add(extreg, upb_test_convert_ext_string_ext), | |
| kUpb_ExtensionRegistryStatus_Ok); | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, src_mt, extreg, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| EXPECT_TRUE( | |
| upb_Message_HasExtension(converted, upb_test_convert_ext_string_ext)); | |
| upb_StringView dst_str = | |
| upb_Message_GetExtensionString(converted, upb_test_convert_ext_string_ext, | |
| upb_StringView_FromString("")); | |
| EXPECT_EQ(std::string("hello"), std::string(dst_str.data, dst_str.size)); | |
| } | |
| TEST(ConvertTest, NonCanonicalStringToNormalField) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_StringView str = upb_StringView_FromString("hello"); | |
| upb_MessageValue ext_val; | |
| ext_val.str_val = str; | |
| EXPECT_TRUE(upb_Message_SetExtension( | |
| UPB_UPCAST(msg), upb_test_convert_ext_string_ext, &ext_val, arena.ptr())); | |
| const upb_MiniTable* src_mt = | |
| &upb__test__convert__MessageWithExtension_msg_init; | |
| const upb_MiniTable* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| const upb_MiniTable* dst_mt = | |
| &upb__test__convert__MessageWithKnownString_msg_init; | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| const upb_test_convert_MessageWithKnownString* dst = | |
| (const upb_test_convert_MessageWithKnownString*)converted; | |
| upb_StringView dst_str = | |
| upb_test_convert_MessageWithKnownString_known_string(dst); | |
| EXPECT_EQ(std::string("hello"), std::string(dst_str.data, dst_str.size)); | |
| } | |
| TEST(ConvertTest, NonCanonicalEnumToCanonical_Valid) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_MessageValue ext_val; | |
| ext_val.int32_val = 2; // BAZ | |
| EXPECT_TRUE(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* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| upb_ExtensionRegistry* extreg = upb_ExtensionRegistry_New(arena.ptr()); | |
| ASSERT_NE(extreg, nullptr); | |
| EXPECT_EQ(upb_ExtensionRegistry_Add(extreg, upb_test_convert_ext_enum_ext), | |
| kUpb_ExtensionRegistryStatus_Ok); | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, src_mt, extreg, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| EXPECT_TRUE( | |
| upb_Message_HasExtension(converted, upb_test_convert_ext_enum_ext)); | |
| int32_t dst_val = upb_Message_GetExtensionInt32( | |
| converted, upb_test_convert_ext_enum_ext, 0); | |
| EXPECT_EQ(2, dst_val); | |
| } | |
| TEST(ConvertTest, NonCanonicalEnumToCanonical_InvalidClosed) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_MessageValue ext_val; | |
| ext_val.int32_val = 99; // Invalid for Proto2EnumMessage.NestedEnum | |
| EXPECT_TRUE(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* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| upb_ExtensionRegistry* extreg = upb_ExtensionRegistry_New(arena.ptr()); | |
| ASSERT_NE(extreg, nullptr); | |
| EXPECT_EQ(upb_ExtensionRegistry_Add(extreg, upb_test_convert_ext_enum_ext), | |
| kUpb_ExtensionRegistryStatus_Ok); | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, src_mt, extreg, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| // Should NOT be set as canonical extension. | |
| EXPECT_FALSE( | |
| upb_Message_HasExtension(converted, upb_test_convert_ext_enum_ext)); | |
| // Should be in unknowns. | |
| upb_MessageUnknown unknown; | |
| uintptr_t iter = kUpb_Message_UnknownBegin; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(converted, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| } | |
| TEST(ConvertTest, NonCanonicalRepeatedEnumToCanonical_Mixed) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_Array* arr = upb_Array_New(arena.ptr(), kUpb_CType_Enum); | |
| upb_MessageValue val1, val2, val3; | |
| val1.int32_val = 1; // BAR | |
| val2.int32_val = 99; // Invalid | |
| val3.int32_val = 2; // BAZ | |
| ASSERT_TRUE(upb_Array_Append(arr, val1, arena.ptr())); | |
| ASSERT_TRUE(upb_Array_Append(arr, val2, arena.ptr())); | |
| ASSERT_TRUE(upb_Array_Append(arr, val3, arena.ptr())); | |
| upb_MessageValue ext_val; | |
| ext_val.array_val = arr; | |
| EXPECT_TRUE(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* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| upb_ExtensionRegistry* extreg = upb_ExtensionRegistry_New(arena.ptr()); | |
| ASSERT_NE(extreg, nullptr); | |
| EXPECT_EQ( | |
| upb_ExtensionRegistry_Add(extreg, upb_test_convert_ext_repeated_enum_ext), | |
| kUpb_ExtensionRegistryStatus_Ok); | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, src_mt, extreg, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| // Canonical extension should have [1, 2]. | |
| const upb_Array* dst_arr = upb_Message_GetExtensionArray( | |
| converted, upb_test_convert_ext_repeated_enum_ext); | |
| ASSERT_NE(dst_arr, nullptr); | |
| ASSERT_EQ(2, upb_Array_Size(dst_arr)); | |
| EXPECT_EQ(1, upb_Array_Get(dst_arr, 0).int32_val); | |
| EXPECT_EQ(2, upb_Array_Get(dst_arr, 1).int32_val); | |
| // Unknowns should have 99. | |
| upb_MessageUnknown unknown; | |
| uintptr_t iter = kUpb_Message_UnknownBegin; | |
| ASSERT_TRUE(upb_Message_NextUnknown2(converted, &unknown, &iter)); | |
| EXPECT_EQ(unknown.type, kUpb_MessageUnknownType_StringView); | |
| } | |
| TEST(ConvertTest, NonCanonicalInt32ToMap_Incompatible) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| upb_MessageValue ext_val; | |
| ext_val.int32_val = 42; | |
| EXPECT_TRUE(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* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| const upb_MiniTable* dst_mt = | |
| &upb__test__convert__MessageWithMapAt1000_msg_init; | |
| // Conversion should FAIL (return nullptr) due to mode mismatch. | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, dst_mt, nullptr, 0, 0, arena.ptr()); | |
| EXPECT_EQ(converted, nullptr); | |
| } | |
| TEST(ConvertTest, NonCanonicalMixedToCanonical) { | |
| upb::Arena arena; | |
| upb_test_convert_MessageWithExtension* msg = | |
| upb_test_convert_MessageWithExtension_new(arena.ptr()); | |
| // Set int32 extension (1000). | |
| upb_MessageValue val_int32; | |
| val_int32.int32_val = 42; | |
| EXPECT_TRUE(upb_Message_SetExtension(UPB_UPCAST(msg), | |
| upb_test_convert_ext_field_int32_ext, | |
| &val_int32, arena.ptr())); | |
| // Set string extension (1004). | |
| upb_MessageValue val_str; | |
| val_str.str_val = upb_StringView_FromDataAndSize("hello", 5); | |
| EXPECT_TRUE(upb_Message_SetExtension( | |
| UPB_UPCAST(msg), upb_test_convert_ext_string_ext, &val_str, arena.ptr())); | |
| const upb_MiniTable* src_mt = | |
| &upb__test__convert__MessageWithExtension_msg_init; | |
| const upb_MiniTable* empty_mt = &upb_0test__EmptyMessage_msg_init; | |
| const upb_Message* non_canonical_src = upb_Message_Convert( | |
| UPB_UPCAST(msg), src_mt, empty_mt, nullptr, 0, 0, arena.ptr()); | |
| ASSERT_NE(non_canonical_src, nullptr); | |
| upb_ExtensionRegistry* extreg = upb_ExtensionRegistry_New(arena.ptr()); | |
| ASSERT_NE(extreg, nullptr); | |
| EXPECT_EQ( | |
| upb_ExtensionRegistry_Add(extreg, upb_test_convert_ext_field_int32_ext), | |
| kUpb_ExtensionRegistryStatus_Ok); | |
| EXPECT_EQ(upb_ExtensionRegistry_Add(extreg, upb_test_convert_ext_string_ext), | |
| kUpb_ExtensionRegistryStatus_Ok); | |
| const upb_Message* converted = upb_Message_Convert( | |
| non_canonical_src, empty_mt, src_mt, extreg, 0, 0, arena.ptr()); | |
| ASSERT_NE(converted, nullptr); | |
| // Verify both extensions are present. | |
| EXPECT_TRUE(upb_Message_HasExtension(converted, | |
| upb_test_convert_ext_field_int32_ext)); | |
| EXPECT_EQ(42, upb_Message_GetExtensionInt32( | |
| converted, upb_test_convert_ext_field_int32_ext, 0)); | |
| EXPECT_TRUE( | |
| upb_Message_HasExtension(converted, upb_test_convert_ext_string_ext)); | |
| upb_StringView dst_str = | |
| upb_Message_GetExtensionString(converted, upb_test_convert_ext_string_ext, | |
| upb_StringView_FromDataAndSize("", 0)); | |
| EXPECT_EQ(std::string("hello"), std::string(dst_str.data, dst_str.size)); | |
| } | |
| TEST(ConvertTest, EncodeFieldAsUnknownAbortOnError) { | |
| using TestMsg = protobuf_test_messages_proto3_TestAllTypesProto3; | |
| upb::Arena src_arena; | |
| TestMsg* msg = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_new(src_arena.ptr()); | |
| // Set a high-numbered field (oneof_uint32 = 111) so an initial | |
| // upb_Message_EncodeFieldAsUnknown call succeeds and finishes its BackAlloc. | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_oneof_uint32(msg, 42); | |
| // Set recursive_message (field 27) with a submessage that: | |
| // 1) Has a large repeated_string (field 44, encoded first) forcing | |
| // upb_BackAlloc to allocate a standalone block. | |
| // 2) Has another recursive_message (field 27, encoded after field 44) | |
| // that exceeds the max depth limit during _upb_Encode_Field. | |
| TestMsg* sub = | |
| protobuf_test_messages_proto3_TestAllTypesProto3_new(src_arena.ptr()); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_recursive_message(msg, | |
| sub); | |
| std::string large_str(2048, 'x'); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_add_repeated_string( | |
| sub, upb_StringView_FromDataAndSize(large_str.data(), large_str.size()), | |
| src_arena.ptr()); | |
| protobuf_test_messages_proto3_TestAllTypesProto3_mutable_recursive_message( | |
| sub, src_arena.ptr()); | |
| upb::Arena dst_arena; | |
| 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, 0, | |
| upb_EncodeOptions_MaxDepth(2), dst_arena.ptr()); | |
| EXPECT_EQ(dst, nullptr); | |
| // Also verify that if a standalone BackAlloc finishes successfully on a | |
| // dropped field (field 27) and a subsequent field (field 1) fails with a | |
| // non-encoder error (type mismatch), upb_BackAlloc_Abort does not double-free | |
| // the already-finished standalone block. | |
| protobuf_test_messages_proto3_TestAllTypesProto3_set_optional_int32(msg, 123); | |
| upb::Arena dst_arena2; | |
| const upb_MiniTable* incompatible_mt = | |
| &upb__test__convert__MessageWithString_msg_init; | |
| const upb_Message* dst2 = | |
| upb_Message_Convert(UPB_UPCAST(msg), TEST_MT, incompatible_mt, nullptr, 0, | |
| 0, dst_arena2.ptr()); | |
| EXPECT_EQ(dst2, nullptr); | |
| } |