Skip to content

Commit

Permalink
Introduce ios::SupportsUserData
Browse files Browse the repository at this point in the history
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
  • Loading branch information
blundell@chromium.org committed Jun 25, 2013
1 parent 3b9ea9c commit c6c1d08
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 0 deletions.
90 changes: 90 additions & 0 deletions ios/consumer/base/supports_user_data.cc
Original file line number Diff line number Diff line change
@@ -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<SupportsUserData::Data> 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<DataAdaptor*>(
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
116 changes: 116 additions & 0 deletions ios/consumer/base/supports_user_data_unittest.cc
Original file line number Diff line number Diff line change
@@ -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<TestSupportsUserData> 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
17 changes: 17 additions & 0 deletions ios/consumer/ios_consumer.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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',
],
},
],
}
50 changes: 50 additions & 0 deletions ios/consumer/public/base/supports_user_data.h
Original file line number Diff line number Diff line change
@@ -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_

0 comments on commit c6c1d08

Please sign in to comment.