| Index: chrome/browser/sync/util/immutable_unittest.cc | 
| diff --git a/chrome/browser/sync/util/immutable_unittest.cc b/chrome/browser/sync/util/immutable_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..dc6553bbbec92f844016bdfc2bd246d2f267cf1e | 
| --- /dev/null | 
| +++ b/chrome/browser/sync/util/immutable_unittest.cc | 
| @@ -0,0 +1,244 @@ | 
| +// Copyright (c) 2011 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 "chrome/browser/sync/util/immutable.h" | 
| + | 
| +#include <algorithm> | 
| +#include <cstddef> | 
| +#include <deque> | 
| +#include <list> | 
| +#include <set> | 
| +#include <string> | 
| +#include <vector> | 
| + | 
| +#include "base/basictypes.h" | 
| +#include "base/memory/ref_counted.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| + | 
| +namespace browser_sync { | 
| + | 
| +// Helper class that keeps track of the token passed in at | 
| +// construction and how many times that token is copied. | 
| +class TokenCore : public base::RefCounted<TokenCore> { | 
| + public: | 
| +  explicit TokenCore(const char* token) : token_(token), copy_count_(0) {} | 
| + | 
| +  const char* GetToken() const { return token_; } | 
| + | 
| +  void RecordCopy() { ++copy_count_; } | 
| + | 
| +  int GetCopyCount() const { return copy_count_; } | 
| + | 
| + private: | 
| +  friend class base::RefCounted<TokenCore>; | 
| + | 
| +  ~TokenCore() {} | 
| + | 
| +  const char* const token_; | 
| +  int copy_count_; | 
| +}; | 
| + | 
| +enum SwapBehavior { | 
| +  USE_DEFAULT_SWAP, | 
| +  USE_FAST_SWAP_VIA_ADL, | 
| +  USE_FAST_SWAP_VIA_SPECIALIZATION | 
| +}; | 
| + | 
| +const char kEmptyToken[] = "<empty token>"; | 
| + | 
| +// Base class for various token classes, differing in swap behavior. | 
| +template <SwapBehavior> | 
| +class TokenBase { | 
| + public: | 
| +  TokenBase() : core_(new TokenCore(kEmptyToken)) {} | 
| + | 
| +  explicit TokenBase(const char* token) : core_(new TokenCore(token)) {} | 
| + | 
| +  TokenBase(const TokenBase& other) : core_(other.core_) { | 
| +    core_->RecordCopy(); | 
| +  } | 
| + | 
| +  TokenBase& operator=(const TokenBase& other) { | 
| +    core_ = other.core_; | 
| +    core_->RecordCopy(); | 
| +    return *this; | 
| +  } | 
| + | 
| +  const char* GetToken() const { | 
| +    return core_->GetToken(); | 
| +  } | 
| + | 
| +  int GetCopyCount() const { | 
| +    return core_->GetCopyCount(); | 
| +  } | 
| + | 
| +  // For associative containers. | 
| +  bool operator<(const TokenBase& other) const { | 
| +    return std::string(GetToken()) < std::string(other.GetToken()); | 
| +  } | 
| + | 
| +  // STL-style swap. | 
| +  void swap(TokenBase& other) { | 
| +    using std::swap; | 
| +    swap(other.core_, core_); | 
| +  } | 
| + | 
| +  // Google-style swap. | 
| +  void Swap(TokenBase* other) { | 
| +    using std::swap; | 
| +    swap(other->core_, core_); | 
| +  } | 
| + | 
| + private: | 
| +  scoped_refptr<TokenCore> core_; | 
| +}; | 
| + | 
| +typedef TokenBase<USE_DEFAULT_SWAP> Token; | 
| +typedef TokenBase<USE_FAST_SWAP_VIA_ADL> ADLToken; | 
| +typedef TokenBase<USE_FAST_SWAP_VIA_SPECIALIZATION> SpecializationToken; | 
| + | 
| +void swap(ADLToken& t1, ADLToken& t2) { | 
| +  t1.Swap(&t2); | 
| +} | 
| + | 
| +}  // namespace browser_sync | 
| + | 
| +// Allowed by the standard (17.4.3.1/1). | 
| +namespace std { | 
| + | 
| +template <> | 
| +void swap(browser_sync::SpecializationToken& t1, | 
| +          browser_sync::SpecializationToken& t2) { | 
| +  t1.Swap(&t2); | 
| +} | 
| + | 
| +}  // namespace | 
| + | 
| +namespace browser_sync { | 
| +namespace { | 
| + | 
| +class ImmutableTest : public ::testing::Test {}; | 
| + | 
| +TEST_F(ImmutableTest, Int) { | 
| +  int x = 5; | 
| +  Immutable<int> ix(&x); | 
| +  EXPECT_EQ(5, ix.Get()); | 
| +  EXPECT_EQ(0, x); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, IntCopy) { | 
| +  int x = 5; | 
| +  Immutable<int> ix = Immutable<int>(&x); | 
| +  EXPECT_EQ(5, ix.Get()); | 
| +  EXPECT_EQ(0, x); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, IntAssign) { | 
| +  int x = 5; | 
| +  Immutable<int> ix; | 
| +  EXPECT_EQ(0, ix.Get()); | 
| +  ix = Immutable<int>(&x); | 
| +  EXPECT_EQ(5, ix.Get()); | 
| +  EXPECT_EQ(0, x); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, IntMakeImmutable) { | 
| +  int x = 5; | 
| +  Immutable<int> ix = MakeImmutable(&x); | 
| +  EXPECT_EQ(5, ix.Get()); | 
| +  EXPECT_EQ(0, x); | 
| +} | 
| + | 
| +template <typename T, typename ImmutableT> | 
| +void RunTokenTest(const char* token, bool expect_copies) { | 
| +  SCOPED_TRACE(token); | 
| +  T t(token); | 
| +  EXPECT_EQ(token, t.GetToken()); | 
| +  EXPECT_EQ(0, t.GetCopyCount()); | 
| + | 
| +  ImmutableT immutable_t(&t); | 
| +  EXPECT_EQ(token, immutable_t.Get().GetToken()); | 
| +  EXPECT_EQ(kEmptyToken, t.GetToken()); | 
| +  EXPECT_EQ(expect_copies, immutable_t.Get().GetCopyCount() > 0); | 
| +  EXPECT_EQ(expect_copies, t.GetCopyCount() > 0); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, Token) { | 
| +  RunTokenTest<Token, Immutable<Token> >("Token", true /* expect_copies */); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, TokenSwapMemFnByRef) { | 
| +  RunTokenTest<Token, Immutable<Token, HasSwapMemFnByRef<Token> > >( | 
| +      "TokenSwapMemFnByRef", false /* expect_copies */); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, TokenSwapMemFnByPtr) { | 
| +  RunTokenTest<Token, Immutable<Token, HasSwapMemFnByPtr<Token> > >( | 
| +      "TokenSwapMemFnByPtr", false /* expect_copies */); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, ADLToken) { | 
| +  RunTokenTest<ADLToken, Immutable<ADLToken> >( | 
| +      "ADLToken", false /* expect_copies */); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, SpecializationToken) { | 
| +  RunTokenTest<SpecializationToken, Immutable<SpecializationToken> >( | 
| +      "SpecializationToken", false /* expect_copies */); | 
| +} | 
| + | 
| +template <typename C, typename ImmutableC> | 
| +void RunTokenContainerTest(const char* token) { | 
| +  SCOPED_TRACE(token); | 
| +  const Token tokens[] = { Token(), Token(token) }; | 
| +  const size_t token_count = arraysize(tokens); | 
| +  C c(tokens, tokens + token_count); | 
| +  const int copy_count = c.begin()->GetCopyCount(); | 
| +  EXPECT_GT(copy_count, 0); | 
| +  for (typename C::const_iterator it = c.begin(); it != c.end(); ++it) { | 
| +    EXPECT_EQ(copy_count, it->GetCopyCount()); | 
| +  } | 
| + | 
| +  // Make sure that making the container immutable doesn't incur any | 
| +  // copies of the tokens. | 
| +  ImmutableC immutable_c(&c); | 
| +  EXPECT_TRUE(c.empty()); | 
| +  ASSERT_EQ(token_count, immutable_c.Get().size()); | 
| +  int i = 0; | 
| +  for (typename C::const_iterator it = c.begin(); it != c.end(); ++it) { | 
| +    EXPECT_EQ(tokens[i].GetToken(), it->GetToken()); | 
| +    EXPECT_EQ(copy_count, it->GetCopyCount()); | 
| +    ++i; | 
| +  } | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, Vector) { | 
| +  RunTokenContainerTest<std::vector<Token>, Immutable<std::vector<Token> > >( | 
| +      "Vector"); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, VectorSwapMemFnByRef) { | 
| +  RunTokenContainerTest< | 
| +    std::vector<Token>, | 
| +    Immutable<std::vector<Token>, HasSwapMemFnByRef<std::vector<Token> > > >( | 
| +        "VectorSwapMemFnByRef"); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, Deque) { | 
| +  RunTokenContainerTest<std::deque<Token>, Immutable<std::deque<Token> > >( | 
| +      "Deque"); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, List) { | 
| +  RunTokenContainerTest<std::list<Token>, Immutable<std::list<Token> > >( | 
| +      "List"); | 
| +} | 
| + | 
| +TEST_F(ImmutableTest, Set) { | 
| +  RunTokenContainerTest<std::set<Token>, Immutable<std::set<Token> > >( | 
| +      "Set"); | 
| +} | 
| + | 
| +}  // namespace | 
| +}  // namespace browser_sync | 
|  |