blob: c36a1a7d4bc80065afce7ff8cc0954bd6567724e [file]
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "upb/message/map.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <gtest/gtest.h>
#include "upb/base/descriptor_constants.h"
#include "upb/base/string_view.h"
#include "upb/mem/arena.h"
#include "upb/mem/arena.hpp"
#include "upb/message/array.h"
#include "upb/message/internal/map.h"
TEST(MapTest, DeleteRegression) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_String);
ASSERT_NE(map, nullptr);
upb_MessageValue key;
key.int32_val = 0;
upb_MessageValue insert_value;
insert_value.str_val = upb_StringView_FromString("abcde");
upb_MapInsertStatus st = upb_Map_Insert(map, key, insert_value, arena.ptr());
EXPECT_EQ(kUpb_MapInsertStatus_Inserted, st);
upb_MessageValue delete_value;
bool removed = upb_Map_Delete(map, key, &delete_value);
EXPECT_TRUE(removed);
EXPECT_TRUE(
upb_StringView_IsEqual(insert_value.str_val, delete_value.str_val));
}
TEST(MapTest, DeleteMissingKeyStringValue) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_String);
ASSERT_NE(map, nullptr);
upb_MessageValue key;
key.int32_val = 42;
upb_MessageValue delete_value;
bool removed = upb_Map_Delete(map, key, &delete_value);
EXPECT_FALSE(removed);
}
TEST(MapTest, ReserveEmptyMap) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32);
ASSERT_NE(map, nullptr);
EXPECT_EQ(_upb_Map_Capacity(map), 0);
EXPECT_TRUE(upb_Map_Reserve(map, 20, arena.ptr()));
EXPECT_EQ(_upb_Map_Capacity(map), 32);
for (int32_t i = 1; i <= 20; i++) {
upb_MessageValue k, v;
k.int32_val = i;
v.int32_val = i * 10;
EXPECT_EQ(kUpb_MapInsertStatus_Inserted,
upb_Map_Insert(map, k, v, arena.ptr()));
EXPECT_EQ(_upb_Map_Capacity(map), 32);
}
EXPECT_EQ(upb_Map_Size(map), 20);
for (int32_t i = 1; i <= 20; i++) {
upb_MessageValue k;
k.int32_val = i;
upb_MessageValue got;
EXPECT_TRUE(upb_Map_Get(map, k, &got));
EXPECT_EQ(got.int32_val, i * 10);
}
}
TEST(MapTest, ReserveStringMap) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_String, kUpb_CType_Int32);
ASSERT_NE(map, nullptr);
EXPECT_EQ(_upb_Map_Capacity(map), 0);
EXPECT_TRUE(upb_Map_Reserve(map, 50, arena.ptr()));
EXPECT_EQ(_upb_Map_Capacity(map), 64);
for (int i = 0; i < 50; i++) {
std::string s = "key-" + std::to_string(i);
upb_MessageValue k, v;
k.str_val = upb_StringView_FromDataAndSize(s.data(), s.size());
v.int32_val = i;
EXPECT_EQ(kUpb_MapInsertStatus_Inserted,
upb_Map_Insert(map, k, v, arena.ptr()));
EXPECT_EQ(_upb_Map_Capacity(map), 64);
}
EXPECT_EQ(upb_Map_Size(map), 50);
}
TEST(MapTest, ReserveGrowExistingMap) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32);
ASSERT_NE(map, nullptr);
for (int32_t i = 1; i <= 3; i++) {
upb_MessageValue k, v;
k.int32_val = i;
v.int32_val = i * 10;
EXPECT_EQ(kUpb_MapInsertStatus_Inserted,
upb_Map_Insert(map, k, v, arena.ptr()));
}
EXPECT_EQ(_upb_Map_Capacity(map), 8);
EXPECT_TRUE(upb_Map_Reserve(map, 50, arena.ptr()));
EXPECT_GE(_upb_Map_Capacity(map), 64);
EXPECT_EQ(upb_Map_Size(map), 3);
for (int32_t i = 1; i <= 3; i++) {
upb_MessageValue k;
k.int32_val = i;
upb_MessageValue got;
EXPECT_TRUE(upb_Map_Get(map, k, &got));
EXPECT_EQ(got.int32_val, i * 10);
}
}
namespace {
// Walks `map` with the upb_MapIterator_* API used by the PHP, Ruby and elisp
// bindings. Returns the number of entries visited. Stops early (returning
// upb_Map_Size() + 1) if iteration fails to terminate.
size_t CountEntriesWithMapIterator(const upb_Map* map) {
size_t iter = kUpb_Map_Begin;
size_t n = 0;
while (upb_MapIterator_Next(map, &iter)) {
if (++n > upb_Map_Size(map)) break;
}
return n;
}
// Walks `map` with upb_Map_Next(). Returns the number of entries visited.
// Stops early (returning upb_Map_Size() + 1) if iteration fails to terminate.
size_t CountEntriesWithMapNext(const upb_Map* map) {
size_t iter = kUpb_Map_Begin;
upb_MessageValue k, v;
size_t n = 0;
while (upb_Map_Next(map, &k, &v, &iter)) {
if (++n > upb_Map_Size(map)) break;
}
return n;
}
void InsertInt32(upb_Map* map, int32_t key, int32_t val, upb_Arena* arena) {
upb_MessageValue k, v;
k.int32_val = key;
v.int32_val = val;
ASSERT_EQ(kUpb_MapInsertStatus_Inserted, upb_Map_Insert(map, k, v, arena));
}
void InsertString(upb_Map* map, const char* key, int32_t val,
upb_Arena* arena) {
upb_MessageValue k, v;
k.str_val = upb_StringView_FromString(key);
v.int32_val = val;
ASSERT_EQ(kUpb_MapInsertStatus_Inserted, upb_Map_Insert(map, k, v, arena));
}
} // namespace
TEST(MapIteratorTest, NeverPopulatedIntMap) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32);
ASSERT_NE(map, nullptr);
EXPECT_EQ(CountEntriesWithMapIterator(map), 0);
EXPECT_EQ(CountEntriesWithMapNext(map), 0);
}
TEST(MapIteratorTest, NeverPopulatedStringMap) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_String, kUpb_CType_Int32);
ASSERT_NE(map, nullptr);
EXPECT_EQ(CountEntriesWithMapIterator(map), 0);
EXPECT_EQ(CountEntriesWithMapNext(map), 0);
}
TEST(MapIteratorTest, ClearedMap) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32);
ASSERT_NE(map, nullptr);
for (int32_t i = 1; i <= 5; i++) InsertInt32(map, i, i, arena.ptr());
upb_Map_Clear(map);
EXPECT_EQ(CountEntriesWithMapIterator(map), 0);
EXPECT_EQ(CountEntriesWithMapNext(map), 0);
}
TEST(MapIteratorTest, AllEntriesDeleted) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_String, kUpb_CType_Int32);
ASSERT_NE(map, nullptr);
InsertString(map, "a", 1, arena.ptr());
InsertString(map, "b", 2, arena.ptr());
upb_MessageValue k;
k.str_val = upb_StringView_FromString("a");
EXPECT_TRUE(upb_Map_Delete(map, k, nullptr));
k.str_val = upb_StringView_FromString("b");
EXPECT_TRUE(upb_Map_Delete(map, k, nullptr));
EXPECT_EQ(CountEntriesWithMapIterator(map), 0);
EXPECT_EQ(CountEntriesWithMapNext(map), 0);
}
TEST(MapIteratorTest, PopulatedIntMapTerminates) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32);
ASSERT_NE(map, nullptr);
for (int32_t i = 1; i <= 20; i++) InsertInt32(map, i, i, arena.ptr());
EXPECT_EQ(CountEntriesWithMapIterator(map), 20);
EXPECT_EQ(CountEntriesWithMapNext(map), 20);
}
TEST(MapIteratorTest, PopulatedStringMapTerminates) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_String, kUpb_CType_Int32);
ASSERT_NE(map, nullptr);
InsertString(map, "x", 1, arena.ptr());
InsertString(map, "y", 2, arena.ptr());
InsertString(map, "z", 3, arena.ptr());
EXPECT_EQ(CountEntriesWithMapIterator(map), 3);
EXPECT_EQ(CountEntriesWithMapNext(map), 3);
}
TEST(MapIteratorTest, ZeroKeyOnly) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32);
ASSERT_NE(map, nullptr);
InsertInt32(map, 0, 42, arena.ptr());
EXPECT_EQ(CountEntriesWithMapIterator(map), 1);
EXPECT_EQ(CountEntriesWithMapNext(map), 1);
}
TEST(MapIteratorTest, ZeroKeyAndOtherKeys) {
upb::Arena arena;
upb_Map* map = upb_Map_New(arena.ptr(), kUpb_CType_Int32, kUpb_CType_Int32);
ASSERT_NE(map, nullptr);
for (int32_t i = 0; i < 10; i++) InsertInt32(map, i, i, arena.ptr());
EXPECT_EQ(CountEntriesWithMapIterator(map), 10);
EXPECT_EQ(CountEntriesWithMapNext(map), 10);
}