1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "sync/internal_api/public/util/immutable.h" 6 7#include <algorithm> 8#include <cstddef> 9#include <deque> 10#include <list> 11#include <set> 12#include <string> 13#include <vector> 14 15#include "base/basictypes.h" 16#include "base/memory/ref_counted.h" 17#include "testing/gtest/include/gtest/gtest.h" 18 19namespace syncer { 20 21// Helper class that keeps track of the token passed in at 22// construction and how many times that token is copied. 23class TokenCore : public base::RefCounted<TokenCore> { 24 public: 25 explicit TokenCore(const char* token) : token_(token), copy_count_(0) {} 26 27 const char* GetToken() const { return token_; } 28 29 void RecordCopy() { ++copy_count_; } 30 31 int GetCopyCount() const { return copy_count_; } 32 33 private: 34 friend class base::RefCounted<TokenCore>; 35 36 ~TokenCore() {} 37 38 const char* const token_; 39 int copy_count_; 40}; 41 42enum SwapBehavior { 43 USE_DEFAULT_SWAP, 44 USE_FAST_SWAP_VIA_ADL, 45 USE_FAST_SWAP_VIA_SPECIALIZATION 46}; 47 48const char kEmptyToken[] = "<empty token>"; 49 50// Base class for various token classes, differing in swap behavior. 51template <SwapBehavior> 52class TokenBase { 53 public: 54 TokenBase() : core_(new TokenCore(kEmptyToken)) {} 55 56 explicit TokenBase(const char* token) : core_(new TokenCore(token)) {} 57 58 TokenBase(const TokenBase& other) : core_(other.core_) { 59 core_->RecordCopy(); 60 } 61 62 TokenBase& operator=(const TokenBase& other) { 63 core_ = other.core_; 64 core_->RecordCopy(); 65 return *this; 66 } 67 68 const char* GetToken() const { 69 return core_->GetToken(); 70 } 71 72 int GetCopyCount() const { 73 return core_->GetCopyCount(); 74 } 75 76 // For associative containers. 77 bool operator<(const TokenBase& other) const { 78 return std::string(GetToken()) < std::string(other.GetToken()); 79 } 80 81 // STL-style swap. 82 void swap(TokenBase& other) { 83 using std::swap; 84 swap(other.core_, core_); 85 } 86 87 // Google-style swap. 88 void Swap(TokenBase* other) { 89 using std::swap; 90 swap(other->core_, core_); 91 } 92 93 private: 94 scoped_refptr<TokenCore> core_; 95}; 96 97typedef TokenBase<USE_DEFAULT_SWAP> Token; 98typedef TokenBase<USE_FAST_SWAP_VIA_ADL> ADLToken; 99typedef TokenBase<USE_FAST_SWAP_VIA_SPECIALIZATION> SpecializationToken; 100 101void swap(ADLToken& t1, ADLToken& t2) { 102 t1.Swap(&t2); 103} 104 105} // namespace syncer 106 107// Allowed by the standard (17.4.3.1/1). 108namespace std { 109 110template <> 111void swap(syncer::SpecializationToken& t1, 112 syncer::SpecializationToken& t2) { 113 t1.Swap(&t2); 114} 115 116} // namespace 117 118namespace syncer { 119namespace { 120 121class ImmutableTest : public ::testing::Test {}; 122 123TEST_F(ImmutableTest, Int) { 124 int x = 5; 125 Immutable<int> ix(&x); 126 EXPECT_EQ(5, ix.Get()); 127 EXPECT_EQ(0, x); 128} 129 130TEST_F(ImmutableTest, IntCopy) { 131 int x = 5; 132 Immutable<int> ix = Immutable<int>(&x); 133 EXPECT_EQ(5, ix.Get()); 134 EXPECT_EQ(0, x); 135} 136 137TEST_F(ImmutableTest, IntAssign) { 138 int x = 5; 139 Immutable<int> ix; 140 EXPECT_EQ(0, ix.Get()); 141 ix = Immutable<int>(&x); 142 EXPECT_EQ(5, ix.Get()); 143 EXPECT_EQ(0, x); 144} 145 146TEST_F(ImmutableTest, IntMakeImmutable) { 147 int x = 5; 148 Immutable<int> ix = MakeImmutable(&x); 149 EXPECT_EQ(5, ix.Get()); 150 EXPECT_EQ(0, x); 151} 152 153template <typename T, typename ImmutableT> 154void RunTokenTest(const char* token, bool expect_copies) { 155 SCOPED_TRACE(token); 156 T t(token); 157 EXPECT_EQ(token, t.GetToken()); 158 EXPECT_EQ(0, t.GetCopyCount()); 159 160 ImmutableT immutable_t(&t); 161 EXPECT_EQ(token, immutable_t.Get().GetToken()); 162 EXPECT_EQ(kEmptyToken, t.GetToken()); 163 EXPECT_EQ(expect_copies, immutable_t.Get().GetCopyCount() > 0); 164 EXPECT_EQ(expect_copies, t.GetCopyCount() > 0); 165} 166 167TEST_F(ImmutableTest, Token) { 168 RunTokenTest<Token, Immutable<Token> >("Token", true /* expect_copies */); 169} 170 171TEST_F(ImmutableTest, TokenSwapMemFnByRef) { 172 RunTokenTest<Token, Immutable<Token, HasSwapMemFnByRef<Token> > >( 173 "TokenSwapMemFnByRef", false /* expect_copies */); 174} 175 176TEST_F(ImmutableTest, TokenSwapMemFnByPtr) { 177 RunTokenTest<Token, Immutable<Token, HasSwapMemFnByPtr<Token> > >( 178 "TokenSwapMemFnByPtr", false /* expect_copies */); 179} 180 181TEST_F(ImmutableTest, ADLToken) { 182 RunTokenTest<ADLToken, Immutable<ADLToken> >( 183 "ADLToken", false /* expect_copies */); 184} 185 186TEST_F(ImmutableTest, SpecializationToken) { 187 RunTokenTest<SpecializationToken, Immutable<SpecializationToken> >( 188 "SpecializationToken", false /* expect_copies */); 189} 190 191template <typename C, typename ImmutableC> 192void RunTokenContainerTest(const char* token) { 193 SCOPED_TRACE(token); 194 const Token tokens[] = { Token(), Token(token) }; 195 const size_t token_count = arraysize(tokens); 196 C c(tokens, tokens + token_count); 197 const int copy_count = c.begin()->GetCopyCount(); 198 EXPECT_GT(copy_count, 0); 199 for (typename C::const_iterator it = c.begin(); it != c.end(); ++it) { 200 EXPECT_EQ(copy_count, it->GetCopyCount()); 201 } 202 203 // Make sure that making the container immutable doesn't incur any 204 // copies of the tokens. 205 ImmutableC immutable_c(&c); 206 EXPECT_TRUE(c.empty()); 207 ASSERT_EQ(token_count, immutable_c.Get().size()); 208 int i = 0; 209 for (typename C::const_iterator it = c.begin(); it != c.end(); ++it) { 210 EXPECT_EQ(tokens[i].GetToken(), it->GetToken()); 211 EXPECT_EQ(copy_count, it->GetCopyCount()); 212 ++i; 213 } 214} 215 216TEST_F(ImmutableTest, Vector) { 217 RunTokenContainerTest<std::vector<Token>, Immutable<std::vector<Token> > >( 218 "Vector"); 219} 220 221TEST_F(ImmutableTest, VectorSwapMemFnByRef) { 222 RunTokenContainerTest< 223 std::vector<Token>, 224 Immutable<std::vector<Token>, HasSwapMemFnByRef<std::vector<Token> > > >( 225 "VectorSwapMemFnByRef"); 226} 227 228// http://crbug.com/129128 229#if defined(OS_WIN) 230#define MAYBE_Deque DISABLED_Deque 231#else 232#define MAYBE_Deque Deque 233#endif 234TEST_F(ImmutableTest, MAYBE_Deque) { 235 RunTokenContainerTest<std::deque<Token>, Immutable<std::deque<Token> > >( 236 "Deque"); 237} 238 239TEST_F(ImmutableTest, List) { 240 RunTokenContainerTest<std::list<Token>, Immutable<std::list<Token> > >( 241 "List"); 242} 243 244TEST_F(ImmutableTest, Set) { 245 RunTokenContainerTest<std::set<Token>, Immutable<std::set<Token> > >( 246 "Set"); 247} 248 249} // namespace 250} // namespace syncer 251