From f157a5651c79a7a36e242af216a5d5b383ba8af2 Mon Sep 17 00:00:00 2001 From: Feng Xiao Date: Fri, 14 Nov 2014 11:50:31 -0800 Subject: Down-integrate from internal code base (C++ maps support). --- src/google/protobuf/map_field_test.cc | 470 ++++++++++++++++++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100644 src/google/protobuf/map_field_test.cc (limited to 'src/google/protobuf/map_field_test.cc') diff --git a/src/google/protobuf/map_field_test.cc b/src/google/protobuf/map_field_test.cc new file mode 100644 index 00000000..98551839 --- /dev/null +++ b/src/google/protobuf/map_field_test.cc @@ -0,0 +1,470 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#ifndef _SHARED_PTR_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace internal { + +using unittest::TestAllTypes; + +class MapFieldBaseStub : public MapFieldBase { + public: + void SyncRepeatedFieldWithMap() const { + MapFieldBase::SyncRepeatedFieldWithMap(); + } + void SyncMapWithRepeatedField() const { + MapFieldBase::SyncMapWithRepeatedField(); + } + // Get underlined repeated field without synchronizing map. + RepeatedPtrField* InternalRepeatedField() { + return repeated_field_; + } + // Get underlined map without synchronizing repeated field. + template + const MapType& GetMap() { + return *reinterpret_cast(base_map_); + } + // Get underlined map without synchronizing repeated field. + template + MapType* MutableMap() { + return reinterpret_cast(base_map_); + } + bool IsMapClean() { return state_ != 0; } + bool IsRepeatedClean() { return state_ != 1; } + void SetMapDirty() { state_ = 0; } + void SetRepeatedDirty() { state_ = 1; } +}; + +class MapFieldBasePrimitiveTest : public ::testing::Test { + protected: + typedef MapField MapFieldType; + + MapFieldBasePrimitiveTest() { + // Get descriptors + map_descriptor_ = unittest::TestMap::descriptor() + ->FindFieldByName("map_int32_int32") + ->message_type(); + key_descriptor_ = map_descriptor_->FindFieldByName("key"); + value_descriptor_ = map_descriptor_->FindFieldByName("value"); + + // Build map field + default_entry_ = + MessageFactory::generated_factory()->GetPrototype(map_descriptor_); + map_field_.reset(new MapFieldType(default_entry_)); + map_field_base_ = map_field_.get(); + map_ = map_field_->MutableMap(); + initial_value_map_[0] = 100; + initial_value_map_[1] = 101; + map_->insert(initial_value_map_.begin(), initial_value_map_.end()); + EXPECT_EQ(2, map_->size()); + } + + google::protobuf::scoped_ptr map_field_; + MapFieldBase* map_field_base_; + Map* map_; + const Descriptor* map_descriptor_; + const FieldDescriptor* key_descriptor_; + const FieldDescriptor* value_descriptor_; + const Message* default_entry_; + std::map initial_value_map_; // copy of initial values inserted +}; + +TEST_F(MapFieldBasePrimitiveTest, SpaceUsedExcludingSelf) { + EXPECT_LT(0, map_field_base_->SpaceUsedExcludingSelf()); +} + +TEST_F(MapFieldBasePrimitiveTest, GetRepeatedField) { + const RepeatedPtrField& repeated = + reinterpret_cast&>( + map_field_base_->GetRepeatedField()); + EXPECT_EQ(2, repeated.size()); + for (int i = 0; i < repeated.size(); i++) { + const Message& message = repeated.Get(i); + int key = message.GetReflection()->GetInt32(message, key_descriptor_); + int value = message.GetReflection()->GetInt32(message, value_descriptor_); + EXPECT_EQ(value, initial_value_map_[key]); + } +} + +TEST_F(MapFieldBasePrimitiveTest, MutableRepeatedField) { + RepeatedPtrField* repeated = + reinterpret_cast*>( + map_field_base_->MutableRepeatedField()); + EXPECT_EQ(2, repeated->size()); + for (int i = 0; i < repeated->size(); i++) { + const Message& message = repeated->Get(i); + int key = message.GetReflection()->GetInt32(message, key_descriptor_); + int value = message.GetReflection()->GetInt32(message, value_descriptor_); + EXPECT_EQ(value, initial_value_map_[key]); + } +} + +namespace { +enum State { CLEAN, MAP_DIRTY, REPEATED_DIRTY }; +} // anonymous namespace + +class MapFieldStateTest + : public testing::TestWithParam { + public: + protected: + typedef MapField MapFieldType; + MapFieldStateTest() : state_(GetParam()) { + // Build map field + const Descriptor* map_descriptor = + unittest::TestMap::descriptor() + ->FindFieldByName("map_int32_int32") + ->message_type(); + default_entry_ = + MessageFactory::generated_factory()->GetPrototype(map_descriptor); + map_field_.reset(new MapFieldType(default_entry_)); + map_field_base_ = map_field_.get(); + + Expect(map_field_.get(), MAP_DIRTY, 0, 0, true); + switch (state_) { + case CLEAN: + AddOneStillClean(map_field_.get()); + break; + case MAP_DIRTY: + MakeMapDirty(map_field_.get()); + break; + case REPEATED_DIRTY: + MakeRepeatedDirty(map_field_.get()); + break; + default: + break; + } + } + + void AddOneStillClean(MapFieldType* map_field) { + MapFieldBase* map_field_base = map_field; + Map* map = map_field->MutableMap(); + (*map)[0] = 0; + map_field_base->GetRepeatedField(); + Expect(map_field, CLEAN, 1, 1, false); + } + + void MakeMapDirty(MapFieldType* map_field) { + Map* map = map_field->MutableMap(); + (*map)[0] = 0; + Expect(map_field, MAP_DIRTY, 1, 0, true); + } + + void MakeRepeatedDirty(MapFieldType* map_field) { + MakeMapDirty(map_field); + MapFieldBase* map_field_base = map_field; + map_field_base->MutableRepeatedField(); + MapFieldBaseStub* stub = + reinterpret_cast(map_field_base); + Map* map = stub->MutableMap >(); + map->clear(); + + Expect(map_field, REPEATED_DIRTY, 0, 1, false); + } + + void Expect(MapFieldType* map_field, State state, int map_size, + int repeated_size, bool is_repeated_null) { + MapFieldBase* map_field_base = map_field; + MapFieldBaseStub* stub = + reinterpret_cast(map_field_base); + + Map* map = stub->MutableMap >(); + RepeatedPtrField* repeated_field = stub->InternalRepeatedField(); + + switch (state) { + case MAP_DIRTY: + EXPECT_FALSE(stub->IsMapClean()); + EXPECT_TRUE(stub->IsRepeatedClean()); + break; + case REPEATED_DIRTY: + EXPECT_TRUE(stub->IsMapClean()); + EXPECT_FALSE(stub->IsRepeatedClean()); + break; + case CLEAN: + EXPECT_TRUE(stub->IsMapClean()); + EXPECT_TRUE(stub->IsRepeatedClean()); + break; + default: + FAIL(); + } + + EXPECT_EQ(map_size, map->size()); + if (is_repeated_null) { + EXPECT_TRUE(repeated_field == NULL); + } else { + EXPECT_EQ(repeated_size, repeated_field->size()); + } + } + + google::protobuf::scoped_ptr map_field_; + MapFieldBase* map_field_base_; + State state_; + const Message* default_entry_; +}; + +INSTANTIATE_TEST_CASE_P(MapFieldStateTestInstance, MapFieldStateTest, + ::testing::Values(CLEAN, MAP_DIRTY, REPEATED_DIRTY)); + +TEST_P(MapFieldStateTest, GetMap) { + map_field_->GetMap(); + if (state_ != MAP_DIRTY) { + Expect(map_field_.get(), CLEAN, 1, 1, false); + } else { + Expect(map_field_.get(), MAP_DIRTY, 1, 0, true); + } +} + +TEST_P(MapFieldStateTest, MutableMap) { + map_field_->MutableMap(); + if (state_ != MAP_DIRTY) { + Expect(map_field_.get(), MAP_DIRTY, 1, 1, false); + } else { + Expect(map_field_.get(), MAP_DIRTY, 1, 0, true); + } +} + +TEST_P(MapFieldStateTest, MergeFromClean) { + MapFieldType other(default_entry_); + AddOneStillClean(&other); + + map_field_->MergeFrom(other); + + if (state_ != MAP_DIRTY) { + Expect(map_field_.get(), MAP_DIRTY, 1, 1, false); + } else { + Expect(map_field_.get(), MAP_DIRTY, 1, 0, true); + } + + Expect(&other, CLEAN, 1, 1, false); +} + +TEST_P(MapFieldStateTest, MergeFromMapDirty) { + MapFieldType other(default_entry_); + MakeMapDirty(&other); + + map_field_->MergeFrom(other); + + if (state_ != MAP_DIRTY) { + Expect(map_field_.get(), MAP_DIRTY, 1, 1, false); + } else { + Expect(map_field_.get(), MAP_DIRTY, 1, 0, true); + } + + Expect(&other, MAP_DIRTY, 1, 0, true); +} + +TEST_P(MapFieldStateTest, MergeFromRepeatedDirty) { + MapFieldType other(default_entry_); + MakeRepeatedDirty(&other); + + map_field_->MergeFrom(other); + + if (state_ != MAP_DIRTY) { + Expect(map_field_.get(), MAP_DIRTY, 1, 1, false); + } else { + Expect(map_field_.get(), MAP_DIRTY, 1, 0, true); + } + + Expect(&other, CLEAN, 1, 1, false); +} + +TEST_P(MapFieldStateTest, SwapClean) { + MapFieldType other(default_entry_); + AddOneStillClean(&other); + + map_field_->Swap(&other); + + Expect(map_field_.get(), CLEAN, 1, 1, false); + + switch (state_) { + case CLEAN: + Expect(&other, CLEAN, 1, 1, false); + break; + case MAP_DIRTY: + Expect(&other, MAP_DIRTY, 1, 0, true); + break; + case REPEATED_DIRTY: + Expect(&other, REPEATED_DIRTY, 0, 1, false); + break; + default: + break; + } +} + +TEST_P(MapFieldStateTest, SwapMapDirty) { + MapFieldType other(default_entry_); + MakeMapDirty(&other); + + map_field_->Swap(&other); + + Expect(map_field_.get(), MAP_DIRTY, 1, 0, true); + + switch (state_) { + case CLEAN: + Expect(&other, CLEAN, 1, 1, false); + break; + case MAP_DIRTY: + Expect(&other, MAP_DIRTY, 1, 0, true); + break; + case REPEATED_DIRTY: + Expect(&other, REPEATED_DIRTY, 0, 1, false); + break; + default: + break; + } +} + +TEST_P(MapFieldStateTest, SwapRepeatedDirty) { + MapFieldType other(default_entry_); + MakeRepeatedDirty(&other); + + map_field_->Swap(&other); + + Expect(map_field_.get(), REPEATED_DIRTY, 0, 1, false); + + switch (state_) { + case CLEAN: + Expect(&other, CLEAN, 1, 1, false); + break; + case MAP_DIRTY: + Expect(&other, MAP_DIRTY, 1, 0, true); + break; + case REPEATED_DIRTY: + Expect(&other, REPEATED_DIRTY, 0, 1, false); + break; + default: + break; + } +} + +TEST_P(MapFieldStateTest, Clear) { + map_field_->Clear(); + + if (state_ != MAP_DIRTY) { + Expect(map_field_.get(), MAP_DIRTY, 0, 1, false); + } else { + Expect(map_field_.get(), MAP_DIRTY, 0, 0, true); + } +} + +TEST_P(MapFieldStateTest, SpaceUsedExcludingSelf) { + map_field_base_->SpaceUsedExcludingSelf(); + + switch (state_) { + case CLEAN: + Expect(map_field_.get(), CLEAN, 1, 1, false); + break; + case MAP_DIRTY: + Expect(map_field_.get(), MAP_DIRTY, 1, 0, true); + break; + case REPEATED_DIRTY: + Expect(map_field_.get(), REPEATED_DIRTY, 0, 1, false); + break; + default: + break; + } +} + +TEST_P(MapFieldStateTest, GetMapField) { + map_field_base_->GetRepeatedField(); + + if (state_ != REPEATED_DIRTY) { + Expect(map_field_.get(), CLEAN, 1, 1, false); + } else { + Expect(map_field_.get(), REPEATED_DIRTY, 0, 1, false); + } +} + +TEST_P(MapFieldStateTest, MutableMapField) { + map_field_base_->MutableRepeatedField(); + + if (state_ != REPEATED_DIRTY) { + Expect(map_field_.get(), REPEATED_DIRTY, 1, 1, false); + } else { + Expect(map_field_.get(), REPEATED_DIRTY, 0, 1, false); + } +} + +class MapFieldBaseStateStub : public MapFieldBaseStub { + public: + MapFieldBaseStateStub(Mutex* mutex, int* clean_counter, + int* completed_counter) + : mutex_(mutex), + clean_counter_(clean_counter), + completed_counter_(completed_counter) {} + ~MapFieldBaseStateStub() {} + + protected: + void SyncRepeatedFieldWithMapNoLock() const { Clean(); } + void SyncMapWithRepeatedFieldNoLock() const { Clean(); } + + private: + void Clean() const { + { + MutexLock lock(mutex_); + ++(*clean_counter_); + } + struct timespec tm; + tm.tv_sec = 0; + tm.tv_nsec = 100000000; // 100ms + nanosleep(&tm, NULL); + { + MutexLock lock(mutex_); + // No other thread should have completed while this one was initializing. + EXPECT_EQ(0, *completed_counter_); + } + } + Mutex* mutex_; + int* clean_counter_; + int* completed_counter_; +}; + + +} // namespace internal +} // namespace protobuf +} // namespace google -- cgit v1.2.3