// Copyright (c) 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "sync/syncable/proto_value_ptr.h" #include "testing/gtest/include/gtest/gtest.h" namespace syncer { namespace syncable { namespace { // TestValue class is used as a template argument with ProtoValuePtr class TestValue { public: TestValue() : value_(0), is_initialized_(false), is_default_(false) {} explicit TestValue(int value) : value_(value), is_initialized_(true), is_default_(false) {} ~TestValue() { g_delete_count++; } static void ResetCounters() { g_copy_count = 0; g_delete_count = 0; } static int copy_count() { return g_copy_count; } static int delete_count() { return g_delete_count; } int value() const { return value_; } bool is_initialized() const { return is_initialized_; } bool is_default() const { return is_default_; } // TestValue uses the default traits struct with ProtoValuePtr. // The following 3 functions are expected by the traits struct to exist // in this class. void CopyFrom(const TestValue& from) { // Expected to always copy from an initialized instance // to an uninitialized one. // Not expected either value to be default. ASSERT_FALSE(is_initialized()); ASSERT_FALSE(is_default()); ASSERT_TRUE(from.is_initialized()); ASSERT_FALSE(from.is_default()); value_ = from.value(); is_initialized_ = false; g_copy_count++; } int ByteSize() const { return is_initialized() ? sizeof(int) : 0; } static const TestValue& default_instance() { static TestValue default_instance; default_instance.is_default_ = true; return default_instance; } private: static int g_copy_count; static int g_delete_count; int value_; bool is_initialized_; bool is_default_; DISALLOW_COPY_AND_ASSIGN(TestValue); }; // Static initializers. int TestValue::g_copy_count = 0; int TestValue::g_delete_count = 0; } // namespace typedef ProtoValuePtr TestPtr; class ProtoValuePtrTest : public testing::Test { public: void SetUp() override { TestValue::ResetCounters(); } static bool WrappedValuesAreShared(const TestPtr& ptr1, const TestPtr& ptr2) { const TestValue& wrapped_value_1 = ptr1.value(); const TestValue& wrapped_value_2 = ptr2.value(); // Compare addresses. return &wrapped_value_1 == &wrapped_value_2; } }; TEST_F(ProtoValuePtrTest, BasicTest) { // Basic assignment and default value. TestValue t1(1); { TestPtr ptr1; EXPECT_TRUE(ptr1->is_default()); ptr1.set_value(t1); EXPECT_FALSE(ptr1->is_default()); EXPECT_EQ(1, ptr1->value()); } EXPECT_EQ(1, TestValue::copy_count()); EXPECT_EQ(1, TestValue::delete_count()); } TEST_F(ProtoValuePtrTest, SharingTest) { // Sharing between two pointers. TestValue empty; TestValue t2(2); TestValue t3(3); { TestPtr ptr2; TestPtr ptr3; EXPECT_TRUE(ptr2->is_default()); EXPECT_TRUE(ptr3->is_default()); EXPECT_EQ(0, TestValue::copy_count()); EXPECT_EQ(0, TestValue::delete_count()); ptr2.set_value(t2); EXPECT_EQ(1, TestValue::copy_count()); EXPECT_EQ(0, TestValue::delete_count()); ptr3 = ptr2; // Both |ptr2| and |ptr3| now share the same value "2". // No additional copies expected. EXPECT_EQ(1, TestValue::copy_count()); EXPECT_EQ(0, TestValue::delete_count()); EXPECT_FALSE(ptr3->is_default()); EXPECT_EQ(2, ptr3->value()); EXPECT_TRUE(WrappedValuesAreShared(ptr2, ptr3)); // Stop sharing - |ptr2| is "3" and |ptr3| is still "2". ptr2.set_value(t3); EXPECT_FALSE(WrappedValuesAreShared(ptr2, ptr3)); EXPECT_EQ(3, ptr2->value()); EXPECT_EQ(2, ptr3->value()); // No extra copies or deletions expected. EXPECT_EQ(2, TestValue::copy_count()); EXPECT_EQ(0, TestValue::delete_count()); // |ptr3| still has the old value. EXPECT_EQ(2, ptr3->value()); // Share again. Both values are "3". ptr3 = ptr2; EXPECT_EQ(3, ptr3->value()); // This should have resulted in deleting the wrapper for the value "2". EXPECT_EQ(1, TestValue::delete_count()); // No extra copies expected. EXPECT_EQ(2, TestValue::copy_count()); // Set default value to one of the pointers. ptr2.set_value(empty); EXPECT_TRUE(ptr2->is_default()); // The other one is still intact. EXPECT_FALSE(ptr3->is_default()); EXPECT_EQ(3, ptr3->value()); // No extra copies or deletions expected. EXPECT_EQ(1, TestValue::delete_count()); EXPECT_EQ(2, TestValue::copy_count()); // Copy the default value between the pointers. ptr3 = ptr2; EXPECT_TRUE(ptr3->is_default()); // The wrapper for "3" is now deleted. EXPECT_EQ(2, TestValue::delete_count()); } // No extra deletions expected upon leaving the scope. EXPECT_EQ(2, TestValue::delete_count()); } } // namespace syncable } // namespace syncer