blob: e03a32afc89ca09a7b5442e5482394c28b7cf6eb [file]
// Protocol Buffers - Google's data interchange format
// Copyright 2026 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
/* Test of unknown fields APIs.
*/
#include "upb/message/unknown_fields.h"
#include <string.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <gtest/gtest.h>
#include "upb/base/string_view.h"
#include "upb/base/upcast.h"
#include "upb/mem/arena.h"
#include "upb/message/internal/accessors.h"
#include "upb/message/internal/message.h"
#include "upb/message/message.h"
#include "upb/message/test.upb.h"
#include "upb/message/test.upb_minitable.h"
#include "upb/mini_table/extension.h"
#include "upb/test/test.upb.h"
#include "upb/test/test.upb_minitable.h"
#include "upb/wire/decode.h"
#include "upb/wire/encode_extension.h"
// Must be last.
#include "upb/port/def.inc"
#include "upb/wire/encode.h"
namespace {
TEST(GeneratedCode, FindUnknown2) {
upb_Arena* arena = upb_Arena_New();
upb_test_ModelWithExtensions* msg = upb_test_ModelWithExtensions_new(arena);
upb_test_ModelWithExtensions_set_random_int32(msg, 10);
upb_test_ModelWithExtensions_set_random_name(
msg, upb_StringView_FromString("Hello"));
upb_test_ModelExtension1* extension1 = upb_test_ModelExtension1_new(arena);
upb_test_ModelExtension1_set_str(extension1,
upb_StringView_FromString("World"));
upb_test_ModelExtension1_set_model_ext(msg, extension1, arena);
size_t serialized_size;
char* serialized =
upb_test_ModelWithExtensions_serialize(msg, arena, &serialized_size);
upb_test_EmptyMessageWithExtensions* base_msg =
upb_test_EmptyMessageWithExtensions_parse(serialized, serialized_size,
arena);
// Case 1: Find raw bytes unknown
upb_FindUnknownRet2 result = upb_Message_FindUnknown2(
UPB_UPCAST(base_msg),
upb_MiniTableExtension_Number(upb_test_ModelExtension1_model_ext_ext), 0);
EXPECT_EQ(kUpb_FindUnknown_Ok, result.status);
EXPECT_EQ(kUpb_MessageUnknownType_StringView, result.unknown.type);
// Case 2: Find non-canonical extension
upb_test_ModelExtension2* extension2 = upb_test_ModelExtension2_new(arena);
upb_test_ModelExtension2_set_i(extension2, 42);
bool set_ext_ok = UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
UPB_UPCAST(base_msg), upb_test_ModelExtension2_model_ext_ext, &extension2,
arena);
EXPECT_TRUE(set_ext_ok);
result = upb_Message_FindUnknown2(
UPB_UPCAST(base_msg),
upb_MiniTableExtension_Number(upb_test_ModelExtension2_model_ext_ext), 0);
EXPECT_EQ(kUpb_FindUnknown_Ok, result.status);
EXPECT_EQ(kUpb_MessageUnknownType_NonCanonicalExtension, result.unknown.type);
// Case 3: Not Present
result = upb_Message_FindUnknown2(UPB_UPCAST(base_msg), 12345, 0);
EXPECT_EQ(kUpb_FindUnknown_NotPresent, result.status);
upb_Arena_Free(arena);
}
TEST(GeneratedCode, FindUnknown2_NoUnknowns) {
upb_Arena* arena = upb_Arena_New();
upb_test_ModelWithExtensions* msg = upb_test_ModelWithExtensions_new(arena);
upb_FindUnknownRet2 result =
upb_Message_FindUnknown2(UPB_UPCAST(msg), 123, 0);
EXPECT_EQ(kUpb_FindUnknown_NotPresent, result.status);
upb_Arena_Free(arena);
}
TEST(GeneratedCode, FindUnknown2_MultipleMatchingValues) {
upb_Arena* arena = upb_Arena_New();
upb_test_ModelWithExtensions* msg = upb_test_ModelWithExtensions_new(arena);
upb_test_ModelWithExtensions_set_random_int32(msg, 10);
upb_test_ModelExtension1* extension1 = upb_test_ModelExtension1_new(arena);
upb_test_ModelExtension1_set_str(extension1,
upb_StringView_FromString("World"));
upb_test_ModelExtension1_set_model_ext(msg, extension1, arena);
size_t serialized_size;
char* serialized =
upb_test_ModelWithExtensions_serialize(msg, arena, &serialized_size);
uint32_t field_number =
upb_MiniTableExtension_Number(upb_test_ModelExtension1_model_ext_ext);
// Case A: StringView first, then NonCanonicalExtension
upb_test_EmptyMessageWithExtensions* base_msg =
upb_test_EmptyMessageWithExtensions_parse(serialized, serialized_size,
arena);
upb_test_ModelExtension1* extension1_non_canonical =
upb_test_ModelExtension1_new(arena);
upb_test_ModelExtension1_set_str(extension1_non_canonical,
upb_StringView_FromString("NonCanonical"));
bool set_ext_ok = UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
UPB_UPCAST(base_msg), upb_test_ModelExtension1_model_ext_ext,
&extension1_non_canonical, arena);
EXPECT_TRUE(set_ext_ok);
upb_FindUnknownRet2 result =
upb_Message_FindUnknown2(UPB_UPCAST(base_msg), field_number, 0);
EXPECT_EQ(kUpb_FindUnknown_Ok, result.status);
EXPECT_EQ(kUpb_MessageUnknownType_StringView, result.unknown.type);
// Case B: NonCanonicalExtension first, then StringView
upb_test_EmptyMessageWithExtensions* base_msg2 =
upb_test_EmptyMessageWithExtensions_new(arena);
bool set_ext_ok2 = UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
UPB_UPCAST(base_msg2), upb_test_ModelExtension1_model_ext_ext,
&extension1_non_canonical, arena);
EXPECT_TRUE(set_ext_ok2);
upb_DecodeStatus decode_status = upb_Decode(
serialized, serialized_size, UPB_UPCAST(base_msg2),
&upb_0test__EmptyMessageWithExtensions_msg_init, nullptr, 0, arena);
EXPECT_EQ(decode_status, kUpb_DecodeStatus_Ok);
result = upb_Message_FindUnknown2(UPB_UPCAST(base_msg2), field_number, 0);
EXPECT_EQ(kUpb_FindUnknown_Ok, result.status);
EXPECT_EQ(kUpb_MessageUnknownType_NonCanonicalExtension, result.unknown.type);
upb_Arena_Free(arena);
}
TEST(GeneratedCode, HasUnknownIgnoresTombstones) {
upb_Arena* arena = upb_Arena_New();
upb_test_ModelWithExtensions* msg = upb_test_ModelWithExtensions_new(arena);
// Add a non-canonical extension
upb_test_ModelExtension1* extension1 = upb_test_ModelExtension1_new(arena);
upb_test_ModelExtension1_set_str(extension1,
upb_StringView_FromString("World"));
bool set_ext_ok = UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
UPB_UPCAST(msg), upb_test_ModelExtension1_model_ext_ext, &extension1,
arena);
ASSERT_TRUE(set_ext_ok);
// Verify HasUnknown returns true (non-canonical are treated as unknowns)
EXPECT_TRUE(upb_Message_HasUnknown(UPB_UPCAST(msg)));
upb_Message_Internal* in =
UPB_PRIVATE(_upb_Message_GetInternal)(UPB_UPCAST(msg));
ASSERT_NE(in, nullptr);
uint32_t original_size = in->size;
EXPECT_GT(original_size, 0);
// Delete it using DeleteUnknown2 to create a tombstone
uintptr_t iter = kUpb_Message_UnknownBegin;
upb_MessageUnknown unknown;
ASSERT_TRUE(upb_Message_NextUnknown2(UPB_UPCAST(msg), &unknown, &iter));
upb_Message_DeleteUnknownStatus status =
upb_Message_DeleteUnknown2(UPB_UPCAST(msg), &unknown, &iter, arena);
EXPECT_EQ(status, kUpb_DeleteUnknown_DeletedLast);
// Verify HasUnknown now returns false (ignores tombstone)
EXPECT_FALSE(upb_Message_HasUnknown(UPB_UPCAST(msg)));
// Size should STILL be original_size (contains tombstone)
EXPECT_EQ(in->size, original_size);
// Call _upb_Message_DiscardUnknown_shallow
_upb_Message_DiscardUnknown_shallow(UPB_UPCAST(msg));
// Size should now be 0
EXPECT_EQ(in->size, 0);
// Verify HasUnknown still returns false
EXPECT_FALSE(upb_Message_HasUnknown(UPB_UPCAST(msg)));
upb_Arena_Free(arena);
}
TEST(GeneratedCode, HasUnknownMultipleUnknownsDeleteOne) {
upb_Arena* arena = upb_Arena_New();
upb_test_ModelWithExtensions* msg = upb_test_ModelWithExtensions_new(arena);
// Add non-canonical extension 1
upb_test_ModelExtension1* extension1 = upb_test_ModelExtension1_new(arena);
upb_test_ModelExtension1_set_str(extension1,
upb_StringView_FromString("Ext1"));
bool set_ext1_ok = UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
UPB_UPCAST(msg), upb_test_ModelExtension1_model_ext_ext, &extension1,
arena);
ASSERT_TRUE(set_ext1_ok);
// Add non-canonical extension 2
upb_test_ModelExtension2* extension2 = upb_test_ModelExtension2_new(arena);
upb_test_ModelExtension2_set_i(extension2, 42);
bool set_ext2_ok = UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
UPB_UPCAST(msg), upb_test_ModelExtension2_model_ext_ext, &extension2,
arena);
ASSERT_TRUE(set_ext2_ok);
// Verify HasUnknown returns true
EXPECT_TRUE(upb_Message_HasUnknown(UPB_UPCAST(msg)));
// Delete the first unknown
uintptr_t iter = kUpb_Message_UnknownBegin;
upb_MessageUnknown unknown;
ASSERT_TRUE(upb_Message_NextUnknown2(UPB_UPCAST(msg), &unknown, &iter));
upb_Message_DeleteUnknownStatus status =
upb_Message_DeleteUnknown2(UPB_UPCAST(msg), &unknown, &iter, arena);
// Deleting the first unknown should return IterUpdated
EXPECT_EQ(status, kUpb_DeleteUnknown_IterUpdated);
// Verify HasUnknown STILL returns true because one unknown remains
EXPECT_TRUE(upb_Message_HasUnknown(UPB_UPCAST(msg)));
upb_Arena_Free(arena);
}
TEST(GeneratedCode, MessageUnknown_Encode_NonCanonicalExtension) {
upb_Arena* arena = upb_Arena_New();
upb_test_ModelWithExtensions* msg = upb_test_ModelWithExtensions_new(arena);
upb_test_ModelExtension2* extension2 = upb_test_ModelExtension2_new(arena);
upb_test_ModelExtension2_set_i(extension2, 42);
bool set_ext_ok = UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
UPB_UPCAST(msg), upb_test_ModelExtension2_model_ext_ext, &extension2,
arena);
EXPECT_TRUE(set_ext_ok);
upb_FindUnknownRet2 result = upb_Message_FindUnknown2(
UPB_UPCAST(msg),
upb_MiniTableExtension_Number(upb_test_ModelExtension2_model_ext_ext), 0);
EXPECT_EQ(kUpb_FindUnknown_Ok, result.status);
EXPECT_EQ(kUpb_MessageUnknownType_NonCanonicalExtension, result.unknown.type);
upb_StringView view;
upb_EncodeStatus status =
upb_EncodeExtension(result.unknown.value.extension, arena, &view,
/*encode_options=*/0);
EXPECT_EQ(kUpb_EncodeStatus_Ok, status);
EXPECT_GT(view.size, 0);
upb_Arena_Free(arena);
}
TEST(GeneratedCode, MessageUnknown_Encode_NonCanonicalMessageSetExtension) {
upb_Arena* arena = upb_Arena_New();
upb_test_TestMessageSet* mset = upb_test_TestMessageSet_new(arena);
upb_test_MessageSetMember* member = upb_test_MessageSetMember_new(arena);
upb_test_MessageSetMember_set_optional_int32(member, 42);
bool set_ext_ok = UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
UPB_UPCAST(mset), upb_test_MessageSetMember_message_set_extension_ext,
&member, arena);
EXPECT_TRUE(set_ext_ok);
upb_FindUnknownRet2 result = upb_Message_FindUnknown2(
UPB_UPCAST(mset),
upb_MiniTableExtension_Number(
upb_test_MessageSetMember_message_set_extension_ext),
0);
EXPECT_EQ(kUpb_FindUnknown_Ok, result.status);
EXPECT_EQ(kUpb_MessageUnknownType_NonCanonicalExtension, result.unknown.type);
upb_StringView view;
upb_EncodeStatus status =
upb_EncodeExtension(result.unknown.value.extension, arena, &view,
/*encode_options=*/0);
EXPECT_EQ(kUpb_EncodeStatus_Ok, status);
EXPECT_GT(view.size, 0);
upb_Arena_Free(arena);
}
} // namespace