Home | History | Annotate | Download | only in util
      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