aboutsummaryrefslogtreecommitdiff
path: root/src/google/protobuf/arena_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/google/protobuf/arena_unittest.cc')
-rw-r--r--src/google/protobuf/arena_unittest.cc238
1 files changed, 189 insertions, 49 deletions
diff --git a/src/google/protobuf/arena_unittest.cc b/src/google/protobuf/arena_unittest.cc
index 6b67f446..eaaffce2 100644
--- a/src/google/protobuf/arena_unittest.cc
+++ b/src/google/protobuf/arena_unittest.cc
@@ -33,16 +33,13 @@
#include <algorithm>
#include <cstring>
#include <memory>
-#ifndef _SHARED_PTR_H
-#include <google/protobuf/stubs/shared_ptr.h>
-#endif
#include <string>
+#include <type_traits>
#include <typeinfo>
#include <vector>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
-#include <google/protobuf/stubs/scoped_ptr.h>
#include <google/protobuf/arena_test_util.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/unittest.pb.h>
@@ -68,7 +65,6 @@ using protobuf_unittest::TestOneof2;
using protobuf_unittest::TestEmptyMessage;
namespace protobuf {
-namespace {
class Notifier {
public:
@@ -152,14 +148,22 @@ class MustBeConstructedWithOneThroughEight {
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MustBeConstructedWithOneThroughEight);
};
-} // namespace
-
TEST(ArenaTest, ArenaConstructable) {
EXPECT_TRUE(Arena::is_arena_constructable<TestAllTypes>::type::value);
EXPECT_TRUE(Arena::is_arena_constructable<const TestAllTypes>::type::value);
+ EXPECT_FALSE(Arena::is_arena_constructable<
+ protobuf_unittest_no_arena::TestNoArenaMessage>::type::value);
EXPECT_FALSE(Arena::is_arena_constructable<Arena>::type::value);
}
+TEST(ArenaTest, DestructorSkippable) {
+ EXPECT_TRUE(Arena::is_destructor_skippable<TestAllTypes>::type::value);
+ EXPECT_TRUE(Arena::is_destructor_skippable<const TestAllTypes>::type::value);
+ EXPECT_FALSE(Arena::is_destructor_skippable<
+ protobuf_unittest_no_arena::TestNoArenaMessage>::type::value);
+ EXPECT_FALSE(Arena::is_destructor_skippable<Arena>::type::value);
+}
+
TEST(ArenaTest, BasicCreate) {
Arena arena;
EXPECT_TRUE(Arena::Create<int32>(&arena) != NULL);
@@ -183,6 +187,33 @@ TEST(ArenaTest, BasicCreate) {
EXPECT_EQ(2, notifier.GetCount());
}
+TEST(ArenaTest, CreateAndConstCopy) {
+ Arena arena;
+ const string s("foo");
+ const string* s_copy = Arena::Create<string>(&arena, s);
+ EXPECT_TRUE(s_copy != NULL);
+ EXPECT_EQ("foo", s);
+ EXPECT_EQ("foo", *s_copy);
+}
+
+TEST(ArenaTest, CreateAndNonConstCopy) {
+ Arena arena;
+ string s("foo");
+ const string* s_copy = Arena::Create<string>(&arena, s);
+ EXPECT_TRUE(s_copy != NULL);
+ EXPECT_EQ("foo", s);
+ EXPECT_EQ("foo", *s_copy);
+}
+
+TEST(ArenaTest, CreateAndMove) {
+ Arena arena;
+ string s("foo");
+ const string* s_move = Arena::Create<string>(&arena, std::move(s));
+ EXPECT_TRUE(s_move != NULL);
+ EXPECT_TRUE(s.empty()); // NOLINT
+ EXPECT_EQ("foo", *s_move);
+}
+
TEST(ArenaTest, CreateWithFourConstructorArguments) {
Arena arena;
const string three("3");
@@ -217,11 +248,32 @@ TEST(ArenaTest, CreateWithEightConstructorArguments) {
ASSERT_EQ("8", new_object->eight_);
}
+class PleaseMoveMe {
+ public:
+ explicit PleaseMoveMe(const string& value) : value_(value) {}
+ PleaseMoveMe(PleaseMoveMe&&) = default;
+ PleaseMoveMe(const PleaseMoveMe&) = delete;
+
+ const string& value() const { return value_; }
+
+ private:
+ string value_;
+};
+
+TEST(ArenaTest, CreateWithMoveArguments) {
+ Arena arena;
+ PleaseMoveMe one("1");
+ const PleaseMoveMe* new_object =
+ Arena::Create<PleaseMoveMe>(&arena, std::move(one));
+ EXPECT_TRUE(new_object);
+ ASSERT_EQ("1", new_object->value());
+}
+
TEST(ArenaTest, InitialBlockTooSmall) {
// Construct a small (64 byte) initial block of memory to be used by the
// arena allocator; then, allocate an object which will not fit in the
// initial block.
- std::vector<char> arena_block(64);
+ std::vector<char> arena_block(96);
ArenaOptions options;
options.initial_block = &arena_block[0];
options.initial_block_size = arena_block.size();
@@ -252,7 +304,7 @@ TEST(ArenaTest, Parsing) {
arena_message->ParseFromString(original.SerializeAsString());
TestUtil::ExpectAllFieldsSet(*arena_message);
- // Test that string fields have null terminator bytes (earlier bug).
+ // Test that string fields have nul terminator bytes (earlier bug).
EXPECT_EQ(strlen(original.optional_string().c_str()),
strlen(arena_message->optional_string().c_str()));
}
@@ -342,6 +394,64 @@ TEST(ArenaTest, Swap) {
EXPECT_EQ(42, arena2_message->unknown_fields().field(0).varint());
}
+TEST(ArenaTest, ReflectionSwapFields) {
+ Arena arena1;
+ Arena arena2;
+ TestAllTypes* arena1_message;
+ TestAllTypes* arena2_message;
+
+ // Case 1: messages on different arenas, only one message is set.
+ arena1_message = Arena::CreateMessage<TestAllTypes>(&arena1);
+ arena2_message = Arena::CreateMessage<TestAllTypes>(&arena2);
+ TestUtil::SetAllFields(arena1_message);
+ const Reflection* reflection = arena1_message->GetReflection();
+ std::vector<const FieldDescriptor*> fields;
+ reflection->ListFields(*arena1_message, &fields);
+ reflection->SwapFields(arena1_message, arena2_message, fields);
+ EXPECT_EQ(&arena1, arena1_message->GetArena());
+ EXPECT_EQ(&arena2, arena2_message->GetArena());
+ string output;
+ arena1_message->SerializeToString(&output);
+ EXPECT_EQ(0, output.size());
+ TestUtil::ExpectAllFieldsSet(*arena2_message);
+ reflection->SwapFields(arena1_message, arena2_message, fields);
+ arena2_message->SerializeToString(&output);
+ EXPECT_EQ(0, output.size());
+ TestUtil::ExpectAllFieldsSet(*arena1_message);
+
+ // Case 2: messages on different arenas, both messages are set.
+ arena1_message = Arena::CreateMessage<TestAllTypes>(&arena1);
+ arena2_message = Arena::CreateMessage<TestAllTypes>(&arena2);
+ TestUtil::SetAllFields(arena1_message);
+ TestUtil::SetAllFields(arena2_message);
+ reflection->SwapFields(arena1_message, arena2_message, fields);
+ EXPECT_EQ(&arena1, arena1_message->GetArena());
+ EXPECT_EQ(&arena2, arena2_message->GetArena());
+ TestUtil::ExpectAllFieldsSet(*arena1_message);
+ TestUtil::ExpectAllFieldsSet(*arena2_message);
+
+ // Case 3: messages on different arenas with different lifetimes.
+ arena1_message = Arena::CreateMessage<TestAllTypes>(&arena1);
+ {
+ Arena arena3;
+ TestAllTypes* arena3_message = Arena::CreateMessage<TestAllTypes>(&arena3);
+ TestUtil::SetAllFields(arena3_message);
+ reflection->SwapFields(arena1_message, arena3_message, fields);
+ }
+ TestUtil::ExpectAllFieldsSet(*arena1_message);
+
+ // Case 4: one message on arena, the other on heap.
+ arena1_message = Arena::CreateMessage<TestAllTypes>(&arena1);
+ TestAllTypes message;
+ TestUtil::SetAllFields(arena1_message);
+ reflection->SwapFields(arena1_message, &message, fields);
+ EXPECT_EQ(&arena1, arena1_message->GetArena());
+ EXPECT_EQ(nullptr, message.GetArena());
+ arena1_message->SerializeToString(&output);
+ EXPECT_EQ(0, output.size());
+ TestUtil::ExpectAllFieldsSet(message);
+}
+
TEST(ArenaTest, SetAllocatedMessage) {
Arena arena;
TestAllTypes *arena_message = Arena::CreateMessage<TestAllTypes>(&arena);
@@ -362,7 +472,7 @@ TEST(ArenaTest, ReleaseMessage) {
Arena arena;
TestAllTypes* arena_message = Arena::CreateMessage<TestAllTypes>(&arena);
arena_message->mutable_optional_nested_message()->set_bb(118);
- google::protobuf::scoped_ptr<TestAllTypes::NestedMessage> nested(
+ std::unique_ptr<TestAllTypes::NestedMessage> nested(
arena_message->release_optional_nested_message());
EXPECT_EQ(118, nested->bb());
@@ -383,7 +493,7 @@ TEST(ArenaTest, ReleaseString) {
Arena arena;
TestAllTypes* arena_message = Arena::CreateMessage<TestAllTypes>(&arena);
arena_message->set_optional_string("hello");
- google::protobuf::scoped_ptr<string> released_str(
+ std::unique_ptr<string> released_str(
arena_message->release_optional_string());
EXPECT_EQ("hello", *released_str);
@@ -494,25 +604,6 @@ TEST(ArenaTest, ReleaseFromArenaMessageUsingReflectionMakesCopy) {
}
#endif // !GOOGLE_PROTOBUF_NO_RTTI
-TEST(ArenaTest, UnsafeArenaReleaseDoesNotMakeCopy) {
- Arena arena;
- TestAllTypes* arena_message = Arena::CreateMessage<TestAllTypes>(&arena);
- TestAllTypes::NestedMessage* nested_msg = NULL;
- TestAllTypes::NestedMessage* orig_nested_msg = NULL;
- string* nested_string = NULL;
- string* orig_nested_string = NULL;
- arena_message->mutable_optional_nested_message()->set_bb(42);
- *arena_message->mutable_optional_string() = "Hello";
- orig_nested_msg = arena_message->mutable_optional_nested_message();
- orig_nested_string = arena_message->mutable_optional_string();
- nested_msg = arena_message->unsafe_arena_release_optional_nested_message();
- nested_string = arena_message->unsafe_arena_release_optional_string();
-
- EXPECT_EQ(orig_nested_msg, nested_msg);
- EXPECT_EQ(orig_nested_string, nested_string);
- // Released pointers still on arena; no 'delete' calls needed here.
-}
-
TEST(ArenaTest, SetAllocatedAcrossArenas) {
Arena arena1;
TestAllTypes* arena1_message = Arena::CreateMessage<TestAllTypes>(&arena1);
@@ -792,17 +883,18 @@ TEST(ArenaTest, ReleaseLastRepeatedField) {
TEST(ArenaTest, UnsafeArenaReleaseAdd) {
// Use unsafe_arena_release() and unsafe_arena_set_allocated() to transfer an
// arena-allocated string from one message to another.
+ const char kContent[] = "Test content";
+
Arena arena;
TestAllTypes* message1 = Arena::CreateMessage<TestAllTypes>(&arena);
TestAllTypes* message2 = Arena::CreateMessage<TestAllTypes>(&arena);
string* arena_string = Arena::Create<string>(&arena);
- *arena_string = "Test content";
+ *arena_string = kContent;
message1->unsafe_arena_set_allocated_optional_string(arena_string);
- EXPECT_EQ(arena_string, message1->mutable_optional_string());
message2->unsafe_arena_set_allocated_optional_string(
message1->unsafe_arena_release_optional_string());
- EXPECT_EQ(arena_string, message2->mutable_optional_string());
+ EXPECT_EQ(kContent, message2->optional_string());
}
TEST(ArenaTest, UnsafeArenaAddAllocated) {
@@ -836,6 +928,24 @@ TEST(ArenaTest, UnsafeArenaRelease) {
delete s;
}
+TEST(ArenaTest, OneofMerge) {
+ Arena arena;
+ TestAllTypes* message0 = Arena::CreateMessage<TestAllTypes>(&arena);
+ TestAllTypes* message1 = Arena::CreateMessage<TestAllTypes>(&arena);
+
+ message0->unsafe_arena_set_allocated_oneof_string(new string("x"));
+ ASSERT_TRUE(message0->has_oneof_string());
+ message1->unsafe_arena_set_allocated_oneof_string(new string("y"));
+ ASSERT_TRUE(message1->has_oneof_string());
+ EXPECT_EQ("x", message0->oneof_string());
+ EXPECT_EQ("y", message1->oneof_string());
+ message0->MergeFrom(*message1);
+ EXPECT_EQ("y", message0->oneof_string());
+ EXPECT_EQ("y", message1->oneof_string());
+ delete message0->unsafe_arena_release_oneof_string();
+ delete message1->unsafe_arena_release_oneof_string();
+}
+
TEST(ArenaTest, ArenaOneofReflection) {
Arena arena;
TestAllTypes* message = Arena::CreateMessage<TestAllTypes>(&arena);
@@ -866,7 +976,6 @@ TEST(ArenaTest, ArenaOneofReflection) {
delete submsg;
}
-namespace {
void TestSwapRepeatedField(Arena* arena1, Arena* arena2) {
// Test "safe" (copying) semantics for direct Swap() on RepeatedPtrField
// between arenas.
@@ -905,7 +1014,6 @@ void TestSwapRepeatedField(Arena* arena1, Arena* arena2) {
EXPECT_EQ(i, field2.Get(i).optional_int32());
}
}
-} // namespace
TEST(ArenaTest, SwapRepeatedField) {
Arena arena;
@@ -1047,8 +1155,6 @@ TEST(ArenaTest, MutableMessageReflection) {
#endif // !GOOGLE_PROTOBUF_NO_RTTI
-namespace {
-
void FillArenaAwareFields(TestAllTypes* message) {
string test_string = "hello world";
message->set_optional_int32(42);
@@ -1067,8 +1173,6 @@ void FillArenaAwareFields(TestAllTypes* message) {
message->mutable_optional_lazy_message()->set_bb(42);
}
-}
-
// Test: no allocations occur on heap while touching all supported field types.
TEST(ArenaTest, NoHeapAllocationsTest) {
// Allocate a large initial block to avoid mallocs during hooked test.
@@ -1087,6 +1191,13 @@ TEST(ArenaTest, NoHeapAllocationsTest) {
arena.Reset();
}
+TEST(ArenaTest, ParseCorruptedString) {
+ TestAllTypes message;
+ TestUtil::SetAllFields(&message);
+ TestParseCorruptedString<TestAllTypes, true>(message);
+ TestParseCorruptedString<TestAllTypes, false>(message);
+}
+
#ifndef GOOGLE_PROTOBUF_NO_RTTI
// Test construction on an arena via generic MessageLite interface. We should be
// able to successfully deserialize on the arena without incurring heap
@@ -1139,9 +1250,7 @@ TEST(ArenaTest, RepeatedFieldWithNonPODType) {
}
// Align n to next multiple of 8
-namespace {
uint64 Align8(uint64 n) { return (n + 7) & -8; }
-} // namespace
TEST(ArenaTest, SpaceAllocated_and_Used) {
ArenaOptions options;
@@ -1175,12 +1284,12 @@ TEST(ArenaTest, SpaceAllocated_and_Used) {
options.initial_block_size = 0;
Arena arena_3(options);
EXPECT_EQ(0, arena_3.SpaceUsed());
- ::google::protobuf::Arena::CreateArray<char>(&arena_3, 190);
+ ::google::protobuf::Arena::CreateArray<char>(&arena_3, 160);
EXPECT_EQ(256, arena_3.SpaceAllocated());
- EXPECT_EQ(Align8(190), arena_3.SpaceUsed());
+ EXPECT_EQ(Align8(160), arena_3.SpaceUsed());
::google::protobuf::Arena::CreateArray<char>(&arena_3, 70);
EXPECT_EQ(256 + 512, arena_3.SpaceAllocated());
- EXPECT_EQ(Align8(190) + Align8(70), arena_3.SpaceUsed());
+ EXPECT_EQ(Align8(160) + Align8(70), arena_3.SpaceUsed());
EXPECT_EQ(256 + 512, arena_3.Reset());
}
@@ -1192,6 +1301,22 @@ TEST(ArenaTest, Alignment) {
}
}
+TEST(ArenaTest, BlockSizeSmallerThanAllocation) {
+ for (size_t i = 0; i <= 8; ++i) {
+ ::google::protobuf::ArenaOptions opt;
+ opt.start_block_size = opt.max_block_size = i;
+ ::google::protobuf::Arena arena(opt);
+
+ *::google::protobuf::Arena::Create<int64>(&arena) = 42;
+ EXPECT_GE(arena.SpaceAllocated(), 8);
+ EXPECT_EQ(8, arena.SpaceUsed());
+
+ *::google::protobuf::Arena::Create<int64>(&arena) = 42;
+ EXPECT_GE(arena.SpaceAllocated(), 16);
+ EXPECT_EQ(16, arena.SpaceUsed());
+ }
+}
+
TEST(ArenaTest, GetArenaShouldReturnTheArenaForArenaAllocatedMessages) {
::google::protobuf::Arena arena;
ArenaMessage* message = Arena::CreateMessage<ArenaMessage>(&arena);
@@ -1207,6 +1332,13 @@ TEST(ArenaTest, GetArenaShouldReturnNullForNonArenaAllocatedMessages) {
EXPECT_EQ(NULL, Arena::GetArena(const_pointer_to_message));
}
+TEST(ArenaTest, AddCleanup) {
+ ::google::protobuf::Arena arena;
+ for (int i = 0; i < 100; i++) {
+ arena.Own(new int);
+ }
+}
+
TEST(ArenaTest, UnsafeSetAllocatedOnArena) {
::google::protobuf::Arena arena;
TestAllTypes* message = Arena::CreateMessage<TestAllTypes>(&arena);
@@ -1265,13 +1397,20 @@ uint32 ArenaHooksTestUtil::num_reset = 0;
uint32 ArenaHooksTestUtil::num_destruct = 0;
const int ArenaHooksTestUtil::kCookieValue;
+class ArenaOptionsTestFriend {
+ public:
+ static void Set(::google::protobuf::ArenaOptions* options) {
+ options->on_arena_init = ArenaHooksTestUtil::on_init;
+ options->on_arena_allocation = ArenaHooksTestUtil::on_allocation;
+ options->on_arena_reset = ArenaHooksTestUtil::on_reset;
+ options->on_arena_destruction = ArenaHooksTestUtil::on_destruction;
+ }
+};
+
// Test the hooks are correctly called and that the cookie is passed.
TEST(ArenaTest, ArenaHooksSanity) {
::google::protobuf::ArenaOptions options;
- options.on_arena_init = ArenaHooksTestUtil::on_init;
- options.on_arena_allocation = ArenaHooksTestUtil::on_allocation;
- options.on_arena_reset = ArenaHooksTestUtil::on_reset;
- options.on_arena_destruction = ArenaHooksTestUtil::on_destruction;
+ ArenaOptionsTestFriend::Set(&options);
// Scope for defining the arena
{
@@ -1279,7 +1418,7 @@ TEST(ArenaTest, ArenaHooksSanity) {
EXPECT_EQ(1, ArenaHooksTestUtil::num_init);
EXPECT_EQ(0, ArenaHooksTestUtil::num_allocations);
::google::protobuf::Arena::Create<uint64>(&arena);
- if (google::protobuf::internal::has_trivial_destructor<uint64>::value) {
+ if (std::is_trivially_destructible<uint64>::value) {
EXPECT_EQ(1, ArenaHooksTestUtil::num_allocations);
} else {
EXPECT_EQ(2, ArenaHooksTestUtil::num_allocations);
@@ -1292,5 +1431,6 @@ TEST(ArenaTest, ArenaHooksSanity) {
EXPECT_EQ(1, ArenaHooksTestUtil::num_destruct);
}
+
} // namespace protobuf
} // namespace google