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