From c6c1d08be093742060987e45cd341f86bd9faeae Mon Sep 17 00:00:00 2001 From: "blundell@chromium.org" Date: Tue, 25 Jun 2013 13:56:14 +0000 Subject: [PATCH] Introduce ios::SupportsUserData Internal implementation is done via base::SupportsUserData. This CL also introduces ios_consumer_unittests. Review URL: https://chromiumcodereview.appspot.com/17641002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@208485 0039d316-1c4b-4281-b951-d872f2087c98 --- ios/consumer/base/supports_user_data.cc | 90 ++++++++++++++ .../base/supports_user_data_unittest.cc | 116 ++++++++++++++++++ ios/consumer/ios_consumer.gyp | 17 +++ ios/consumer/public/base/supports_user_data.h | 50 ++++++++ 4 files changed, 273 insertions(+) create mode 100644 ios/consumer/base/supports_user_data.cc create mode 100644 ios/consumer/base/supports_user_data_unittest.cc create mode 100644 ios/consumer/public/base/supports_user_data.h diff --git a/ios/consumer/base/supports_user_data.cc b/ios/consumer/base/supports_user_data.cc new file mode 100644 index 00000000000000..f317fa2d8d0085 --- /dev/null +++ b/ios/consumer/base/supports_user_data.cc @@ -0,0 +1,90 @@ +// Copyright 2013 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 "ios/consumer/public/base/supports_user_data.h" + +#include "base/memory/scoped_ptr.h" +#include "base/supports_user_data.h" + +namespace ios { + +// Class that wraps a ios::SupportsUserData::Data object in a +// base::SupportsUserData::Data object. The wrapper object takes ownership of +// the wrapped object and will delete it on destruction. +class DataAdaptor : public base::SupportsUserData::Data { + public: + DataAdaptor(SupportsUserData::Data* data); + virtual ~DataAdaptor(); + + SupportsUserData::Data* data() { return data_.get(); } + + private: + scoped_ptr data_; +}; + +DataAdaptor::DataAdaptor(SupportsUserData::Data* data) + : data_(data) {} + +DataAdaptor::~DataAdaptor() {} + +// Class that subclasses base::SupportsUserData in order to enable it to +// support ios::SupportsUserData::Data objects. It accomplishes this by +// wrapping these objects internally in ios::DataAdaptor objects. +class SupportsUserDataInternal : public base::SupportsUserData { + public: + // Returns the data that is associated with |key|, or NULL if there is no + // such associated data. + ios::SupportsUserData::Data* GetIOSUserData(const void* key); + + // Associates |data| with |key|. Takes ownership of |data| and will delete it + // on either a call to |RemoveUserData(key)| or otherwise on destruction. + void SetIOSUserData(const void* key, ios::SupportsUserData::Data* data); + + private: + SupportsUserDataInternal() {} + virtual ~SupportsUserDataInternal() {} + + friend class ios::SupportsUserData; +}; + +ios::SupportsUserData::Data* SupportsUserDataInternal::GetIOSUserData( + const void* key) { + DataAdaptor* adaptor = static_cast( + base::SupportsUserData::GetUserData(key)); + if (!adaptor) + return NULL; + return adaptor->data(); +} + +void SupportsUserDataInternal::SetIOSUserData( + const void* key, ios::SupportsUserData::Data* data) { + base::SupportsUserData::SetUserData(key, new DataAdaptor(data)); +} + +// ios::SupportsUserData implementation. +SupportsUserData::SupportsUserData() + : internal_helper_(new SupportsUserDataInternal()) { +} + +SupportsUserData::~SupportsUserData() { + delete internal_helper_; +} + +SupportsUserData::Data* SupportsUserData::GetUserData(const void* key) const { + return internal_helper_->GetIOSUserData(key); +} + +void SupportsUserData::SetUserData(const void* key, Data* data) { + internal_helper_->SetIOSUserData(key, data); +} + +void SupportsUserData::RemoveUserData(const void* key) { + internal_helper_->RemoveUserData(key); +} + +void SupportsUserData::DetachUserDataThread() { + internal_helper_->DetachUserDataThread(); +} + +} // namespace ios diff --git a/ios/consumer/base/supports_user_data_unittest.cc b/ios/consumer/base/supports_user_data_unittest.cc new file mode 100644 index 00000000000000..b38c2db922d93d --- /dev/null +++ b/ios/consumer/base/supports_user_data_unittest.cc @@ -0,0 +1,116 @@ +// Copyright 2013 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 "ios/consumer/public/base/supports_user_data.h" + +#include "base/memory/scoped_ptr.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace ios { + +namespace { + const char* kTestData1Key = "test_data1"; + const char* kTestData2Key = "test_data2"; +} // namespace + +class TestData : public SupportsUserData::Data { + public: + TestData(bool* was_destroyed) + : was_destroyed_(was_destroyed) { + *was_destroyed_ = false; + } + virtual ~TestData() { + *was_destroyed_ = true; + } + + private: + bool* was_destroyed_; +}; + +class TestSupportsUserData : public SupportsUserData { + public: + TestSupportsUserData() {} + virtual ~TestSupportsUserData() {} +}; + +class SupportsUserDataTest : public PlatformTest { + public: + virtual void SetUp() OVERRIDE { + PlatformTest::SetUp(); + + test_data1_was_destroyed_ = false; + test_data1_ = new TestData(&test_data1_was_destroyed_); + test_data2_was_destroyed_ = false; + test_data2_ = new TestData(&test_data2_was_destroyed_); + supports_user_data_.reset(new TestSupportsUserData()); + } + + virtual void TearDown() OVERRIDE { + if (!test_data1_was_destroyed_ && + supports_user_data_ && + supports_user_data_->GetUserData(kTestData1Key) != test_data1_) + delete test_data1_; + if (!test_data2_was_destroyed_ && + supports_user_data_ && + supports_user_data_->GetUserData(kTestData2Key) != test_data2_) + delete test_data2_; + + PlatformTest::TearDown(); + } + + protected: + scoped_ptr supports_user_data_; + bool test_data1_was_destroyed_; + TestData* test_data1_; + bool test_data2_was_destroyed_; + TestData* test_data2_; +}; + +TEST_F(SupportsUserDataTest, SetAndGetData) { + EXPECT_FALSE(supports_user_data_->GetUserData(kTestData1Key)); + supports_user_data_->SetUserData(kTestData1Key, test_data1_); + EXPECT_EQ(supports_user_data_->GetUserData(kTestData1Key), test_data1_); +} + +TEST_F(SupportsUserDataTest, DataDestroyedOnDestruction) { + EXPECT_FALSE(supports_user_data_->GetUserData(kTestData1Key)); + supports_user_data_->SetUserData(kTestData1Key, test_data1_); + EXPECT_FALSE(test_data1_was_destroyed_); + supports_user_data_.reset(); + EXPECT_TRUE(test_data1_was_destroyed_); +} + +TEST_F(SupportsUserDataTest, DataDestroyedOnRemoval) { + EXPECT_FALSE(supports_user_data_->GetUserData(kTestData1Key)); + supports_user_data_->SetUserData(kTestData1Key, test_data1_); + EXPECT_FALSE(test_data1_was_destroyed_); + supports_user_data_->RemoveUserData(kTestData1Key); + EXPECT_TRUE(test_data1_was_destroyed_); +} + +TEST_F(SupportsUserDataTest, DistinctDataStoredSeparately) { + EXPECT_FALSE(supports_user_data_->GetUserData(kTestData2Key)); + supports_user_data_->SetUserData(kTestData1Key, test_data1_); + EXPECT_FALSE(supports_user_data_->GetUserData(kTestData2Key)); + supports_user_data_->SetUserData(kTestData2Key, test_data2_); + EXPECT_EQ(supports_user_data_->GetUserData(kTestData2Key), test_data2_); +} + +TEST_F(SupportsUserDataTest, DistinctDataDestroyedSeparately) { + supports_user_data_->SetUserData(kTestData1Key, test_data1_); + supports_user_data_->SetUserData(kTestData2Key, test_data2_); + EXPECT_FALSE(test_data1_was_destroyed_); + EXPECT_FALSE(test_data2_was_destroyed_); + + supports_user_data_->RemoveUserData(kTestData2Key); + EXPECT_FALSE(test_data1_was_destroyed_); + EXPECT_TRUE(test_data2_was_destroyed_); + + supports_user_data_.reset(); + EXPECT_TRUE(test_data1_was_destroyed_); +} + +} // namespace ios diff --git a/ios/consumer/ios_consumer.gyp b/ios/consumer/ios_consumer.gyp index 9e6f7b627004d0..3844e9b0b7ac4b 100644 --- a/ios/consumer/ios_consumer.gyp +++ b/ios/consumer/ios_consumer.gyp @@ -16,9 +16,26 @@ '../..', ], 'sources': [ + 'base/supports_user_data.cc', 'base/util.mm', + 'public/base/supports_user_data.h', 'public/base/util.h', ], }, + { + 'target_name': 'ios_consumer_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + '../../base/base.gyp:base', + '../../base/base.gyp:run_all_unittests', + '../../base/base.gyp:test_support_base', + '../../testing/gmock.gyp:gmock', + '../../testing/gtest.gyp:gtest', + 'ios_consumer_base', + ], + 'sources': [ + 'base/supports_user_data_unittest.cc', + ], + }, ], } diff --git a/ios/consumer/public/base/supports_user_data.h b/ios/consumer/public/base/supports_user_data.h new file mode 100644 index 00000000000000..16b5ebe75efc54 --- /dev/null +++ b/ios/consumer/public/base/supports_user_data.h @@ -0,0 +1,50 @@ +// Copyright 2013 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. + +#ifndef IOS_BASE_SUPPORTS_USER_DATA_H_ +#define IOS_BASE_SUPPORTS_USER_DATA_H_ + +namespace ios { + +class SupportsUserDataInternal; + +// This is a helper for classes that want to allow users to stash random data by +// key. At destruction all the objects will be destructed. +class SupportsUserData { + public: + SupportsUserData(); + + // Derive from this class and add your own data members to associate extra + // information with this object. Alternatively, add this as a public base + // class to any class with a virtual destructor. + class Data { + public: + virtual ~Data() {} + }; + + // The user data allows the clients to associate data with this object. + // Multiple user data values can be stored under different keys. + // This object will TAKE OWNERSHIP of the given data pointer, and will + // delete the object if it is changed or the object is destroyed. + Data* GetUserData(const void* key) const; + void SetUserData(const void* key, Data* data); + void RemoveUserData(const void* key); + + // SupportsUserData is not thread-safe, and on debug build will assert it is + // only used on one thread. Calling this method allows the caller to hand + // the SupportsUserData instance across threads. Use only if you are taking + // full control of the synchronization of that handover. + void DetachUserDataThread(); + + protected: + virtual ~SupportsUserData(); + + private: + // Owned by this object and scoped to its lifetime. + SupportsUserDataInternal* internal_helper_; +}; + +} // namespace ios + +#endif // IOS_BASE_SUPPORTS_USER_DATA_H_