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 19 namespace syncer { 20 21 // Helper class that keeps track of the token passed in at 22 // construction and how many times that token is copied. 23 class 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 42 enum SwapBehavior { 43 USE_DEFAULT_SWAP, 44 USE_FAST_SWAP_VIA_ADL, 45 USE_FAST_SWAP_VIA_SPECIALIZATION 46 }; 47 48 const char kEmptyToken[] = "<empty token>"; 49 50 // Base class for various token classes, differing in swap behavior. 51 template <SwapBehavior> 52 class 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 97 typedef TokenBase<USE_DEFAULT_SWAP> Token; 98 typedef TokenBase<USE_FAST_SWAP_VIA_ADL> ADLToken; 99 typedef TokenBase<USE_FAST_SWAP_VIA_SPECIALIZATION> SpecializationToken; 100 101 void 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). 108 namespace std { 109 110 template <> 111 void swap(syncer::SpecializationToken& t1, 112 syncer::SpecializationToken& t2) { 113 t1.Swap(&t2); 114 } 115 116 } // namespace 117 118 namespace syncer { 119 namespace { 120 121 class ImmutableTest : public ::testing::Test {}; 122 123 TEST_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 130 TEST_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 137 TEST_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 146 TEST_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 153 template <typename T, typename ImmutableT> 154 void 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 167 TEST_F(ImmutableTest, Token) { 168 RunTokenTest<Token, Immutable<Token> >("Token", true /* expect_copies */); 169 } 170 171 TEST_F(ImmutableTest, TokenSwapMemFnByRef) { 172 RunTokenTest<Token, Immutable<Token, HasSwapMemFnByRef<Token> > >( 173 "TokenSwapMemFnByRef", false /* expect_copies */); 174 } 175 176 TEST_F(ImmutableTest, TokenSwapMemFnByPtr) { 177 RunTokenTest<Token, Immutable<Token, HasSwapMemFnByPtr<Token> > >( 178 "TokenSwapMemFnByPtr", false /* expect_copies */); 179 } 180 181 TEST_F(ImmutableTest, ADLToken) { 182 RunTokenTest<ADLToken, Immutable<ADLToken> >( 183 "ADLToken", false /* expect_copies */); 184 } 185 186 TEST_F(ImmutableTest, SpecializationToken) { 187 RunTokenTest<SpecializationToken, Immutable<SpecializationToken> >( 188 "SpecializationToken", false /* expect_copies */); 189 } 190 191 template <typename C, typename ImmutableC> 192 void 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 216 TEST_F(ImmutableTest, Vector) { 217 RunTokenContainerTest<std::vector<Token>, Immutable<std::vector<Token> > >( 218 "Vector"); 219 } 220 221 TEST_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 234 TEST_F(ImmutableTest, MAYBE_Deque) { 235 RunTokenContainerTest<std::deque<Token>, Immutable<std::deque<Token> > >( 236 "Deque"); 237 } 238 239 TEST_F(ImmutableTest, List) { 240 RunTokenContainerTest<std::list<Token>, Immutable<std::list<Token> > >( 241 "List"); 242 } 243 244 TEST_F(ImmutableTest, Set) { 245 RunTokenContainerTest<std::set<Token>, Immutable<std::set<Token> > >( 246 "Set"); 247 } 248 249 } // namespace 250 } // namespace syncer 251