Home | History | Annotate | Download | only in syncable
      1 // Copyright (c) 2013 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/syncable/parent_child_index.h"
      6 
      7 #include <list>
      8 
      9 #include "base/stl_util.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "sync/syncable/entry_kernel.h"
     12 #include "sync/syncable/syncable_util.h"
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 
     15 namespace syncer {
     16 namespace syncable {
     17 
     18 namespace {
     19 
     20 static const std::string kCacheGuid = "8HhNIHlEOCGQbIAALr9QEg==";
     21 
     22 class ParentChildIndexTest : public testing::Test {
     23  public:
     24   virtual void TearDown() {
     25     // To make memory management easier, we take ownership of all EntryKernels
     26     // returned by our factory methods and delete them here.
     27     STLDeleteElements(&owned_entry_kernels_);
     28   }
     29 
     30   // Unfortunately, we can't use the regular Entry factory methods, because the
     31   // ParentChildIndex deals in EntryKernels.
     32 
     33   static syncable::Id GetBookmarkRootId() {
     34     return syncable::Id::CreateFromServerId("bookmark_folder");
     35   }
     36 
     37   static syncable::Id GetBookmarkId(int n) {
     38     return syncable::Id::CreateFromServerId("b" + base::IntToString(n));
     39   }
     40 
     41   static syncable::Id GetClientUniqueId(int n) {
     42     return syncable::Id::CreateFromServerId("c" + base::IntToString(n));
     43   }
     44 
     45   EntryKernel* MakeRoot() {
     46     // Mimics the root node.
     47     EntryKernel* root = new EntryKernel();
     48     root->put(META_HANDLE, 1);
     49     root->put(BASE_VERSION, -1);
     50     root->put(SERVER_VERSION, 0);
     51     root->put(IS_DIR, true);
     52     root->put(ID, syncable::Id());
     53     root->put(PARENT_ID, syncable::Id());
     54     root->put(SERVER_PARENT_ID, syncable::Id());
     55 
     56     owned_entry_kernels_.push_back(root);
     57     return root;
     58   }
     59 
     60   EntryKernel* MakeBookmarkRoot() {
     61     // Mimics a server-created bookmark folder.
     62     EntryKernel* folder = new EntryKernel;
     63     folder->put(META_HANDLE, 1);
     64     folder->put(BASE_VERSION, 9);
     65     folder->put(SERVER_VERSION, 9);
     66     folder->put(IS_DIR, true);
     67     folder->put(ID, GetBookmarkRootId());
     68     folder->put(SERVER_PARENT_ID, syncable::Id());
     69     folder->put(PARENT_ID, syncable::Id());
     70     folder->put(UNIQUE_SERVER_TAG, "google_chrome_bookmarks");
     71 
     72     owned_entry_kernels_.push_back(folder);
     73     return folder;
     74   }
     75 
     76   EntryKernel* MakeBookmark(int n, int pos, bool is_dir) {
     77     // Mimics a regular bookmark or folder.
     78     EntryKernel* bm = new EntryKernel();
     79     bm->put(META_HANDLE, n);
     80     bm->put(BASE_VERSION, 10);
     81     bm->put(SERVER_VERSION, 10);
     82     bm->put(IS_DIR, is_dir);
     83     bm->put(ID, GetBookmarkId(n));
     84     bm->put(PARENT_ID, GetBookmarkRootId());
     85     bm->put(SERVER_PARENT_ID, GetBookmarkRootId());
     86 
     87     bm->put(UNIQUE_BOOKMARK_TAG,
     88             syncable::GenerateSyncableBookmarkHash(kCacheGuid,
     89                                                    bm->ref(ID).GetServerId()));
     90 
     91     UniquePosition unique_pos =
     92         UniquePosition::FromInt64(pos, bm->ref(UNIQUE_BOOKMARK_TAG));
     93     bm->put(UNIQUE_POSITION, unique_pos);
     94     bm->put(SERVER_UNIQUE_POSITION, unique_pos);
     95 
     96     owned_entry_kernels_.push_back(bm);
     97     return bm;
     98   }
     99 
    100   EntryKernel* MakeUniqueClientItem(int n) {
    101     EntryKernel* item = new EntryKernel();
    102     item->put(META_HANDLE, n);
    103     item->put(BASE_VERSION, 10);
    104     item->put(SERVER_VERSION, 10);
    105     item->put(IS_DIR, false);
    106     item->put(ID, GetClientUniqueId(n));
    107     item->put(PARENT_ID, syncable::Id());
    108     item->put(SERVER_PARENT_ID, syncable::Id());
    109     item->put(UNIQUE_CLIENT_TAG, base::IntToString(n));
    110 
    111     owned_entry_kernels_.push_back(item);
    112     return item;
    113   }
    114 
    115   ParentChildIndex index_;
    116 
    117  private:
    118   std::list<EntryKernel*> owned_entry_kernels_;
    119 };
    120 
    121 TEST_F(ParentChildIndexTest, TestRootNode) {
    122   EntryKernel* root = MakeRoot();
    123   EXPECT_FALSE(ParentChildIndex::ShouldInclude(root));
    124 }
    125 
    126 TEST_F(ParentChildIndexTest, TestBookmarkRootFolder) {
    127   EntryKernel* bm_folder = MakeBookmarkRoot();
    128   EXPECT_TRUE(ParentChildIndex::ShouldInclude(bm_folder));
    129 }
    130 
    131 // Tests iteration over a set of siblings.
    132 TEST_F(ParentChildIndexTest, ChildInsertionAndIteration) {
    133   EntryKernel* bm_folder = MakeBookmarkRoot();
    134   index_.Insert(bm_folder);
    135 
    136   // Make some folder and non-folder entries.
    137   EntryKernel* b1 = MakeBookmark(1, 1, false);
    138   EntryKernel* b2 = MakeBookmark(2, 2, false);
    139   EntryKernel* b3 = MakeBookmark(3, 3, true);
    140   EntryKernel* b4 = MakeBookmark(4, 4, false);
    141 
    142   // Insert them out-of-order to test different cases.
    143   index_.Insert(b3); // Only child.
    144   index_.Insert(b4); // Right-most child.
    145   index_.Insert(b1); // Left-most child.
    146   index_.Insert(b2); // Between existing items.
    147 
    148   // Double-check they've been added.
    149   EXPECT_TRUE(index_.Contains(b1));
    150   EXPECT_TRUE(index_.Contains(b2));
    151   EXPECT_TRUE(index_.Contains(b3));
    152   EXPECT_TRUE(index_.Contains(b4));
    153 
    154   // Check the ordering.
    155   const OrderedChildSet* children = index_.GetChildren(GetBookmarkRootId());
    156   ASSERT_TRUE(children);
    157   ASSERT_EQ(children->size(), 4UL);
    158   OrderedChildSet::const_iterator it = children->begin();
    159   EXPECT_EQ(*it, b1);
    160   it++;
    161   EXPECT_EQ(*it, b2);
    162   it++;
    163   EXPECT_EQ(*it, b3);
    164   it++;
    165   EXPECT_EQ(*it, b4);
    166   it++;
    167   EXPECT_TRUE(it == children->end());
    168 }
    169 
    170 // Tests iteration when hierarchy is involved.
    171 TEST_F(ParentChildIndexTest, ChildInsertionAndIterationWithHierarchy) {
    172   EntryKernel* bm_folder = MakeBookmarkRoot();
    173   index_.Insert(bm_folder);
    174 
    175   // Just below the root, we have folders f1 and f2.
    176   EntryKernel* f1 = MakeBookmark(1, 1, false);
    177   EntryKernel* f2 = MakeBookmark(2, 2, false);
    178   EntryKernel* f3 = MakeBookmark(3, 3, false);
    179 
    180   // Under folder f1, we have two bookmarks.
    181   EntryKernel* f1_b1 = MakeBookmark(101, 1, false);
    182   f1_b1->put(PARENT_ID, GetBookmarkId(1));
    183   EntryKernel* f1_b2 = MakeBookmark(102, 2, false);
    184   f1_b2->put(PARENT_ID, GetBookmarkId(1));
    185 
    186   // Under folder f2, there is one bookmark.
    187   EntryKernel* f2_b1 = MakeBookmark(201, 1, false);
    188   f2_b1->put(PARENT_ID, GetBookmarkId(2));
    189 
    190   // Under folder f3, there is nothing.
    191 
    192   // Insert in a strange order, because we can.
    193   index_.Insert(f1_b2);
    194   index_.Insert(f2);
    195   index_.Insert(f2_b1);
    196   index_.Insert(f1);
    197   index_.Insert(f1_b1);
    198   index_.Insert(f3);
    199 
    200   OrderedChildSet::const_iterator it;
    201 
    202   // Iterate over children of the bookmark root.
    203   const OrderedChildSet* top_children = index_.GetChildren(GetBookmarkRootId());
    204   ASSERT_TRUE(top_children);
    205   ASSERT_EQ(top_children->size(), 3UL);
    206   it = top_children->begin();
    207   EXPECT_EQ(*it, f1);
    208   it++;
    209   EXPECT_EQ(*it, f2);
    210   it++;
    211   EXPECT_EQ(*it, f3);
    212   it++;
    213   EXPECT_TRUE(it == top_children->end());
    214 
    215   // Iterate over children of the first folder.
    216   const OrderedChildSet* f1_children = index_.GetChildren(GetBookmarkId(1));
    217   ASSERT_TRUE(f1_children);
    218   ASSERT_EQ(f1_children->size(), 2UL);
    219   it = f1_children->begin();
    220   EXPECT_EQ(*it, f1_b1);
    221   it++;
    222   EXPECT_EQ(*it, f1_b2);
    223   it++;
    224   EXPECT_TRUE(it == f1_children->end());
    225 
    226   // Iterate over children of the second folder.
    227   const OrderedChildSet* f2_children = index_.GetChildren(GetBookmarkId(2));
    228   ASSERT_TRUE(f2_children);
    229   ASSERT_EQ(f2_children->size(), 1UL);
    230   it = f2_children->begin();
    231   EXPECT_EQ(*it, f2_b1);
    232   it++;
    233   EXPECT_TRUE(it == f2_children->end());
    234 
    235   // Check for children of the third folder.
    236   const OrderedChildSet* f3_children = index_.GetChildren(GetBookmarkId(3));
    237   EXPECT_FALSE(f3_children);
    238 }
    239 
    240 // Tests removing items.
    241 TEST_F(ParentChildIndexTest, RemoveWithHierarchy) {
    242   EntryKernel* bm_folder = MakeBookmarkRoot();
    243   index_.Insert(bm_folder);
    244 
    245   // Just below the root, we have folders f1 and f2.
    246   EntryKernel* f1 = MakeBookmark(1, 1, false);
    247   EntryKernel* f2 = MakeBookmark(2, 2, false);
    248   EntryKernel* f3 = MakeBookmark(3, 3, false);
    249 
    250   // Under folder f1, we have two bookmarks.
    251   EntryKernel* f1_b1 = MakeBookmark(101, 1, false);
    252   f1_b1->put(PARENT_ID, GetBookmarkId(1));
    253   EntryKernel* f1_b2 = MakeBookmark(102, 2, false);
    254   f1_b2->put(PARENT_ID, GetBookmarkId(1));
    255 
    256   // Under folder f2, there is one bookmark.
    257   EntryKernel* f2_b1 = MakeBookmark(201, 1, false);
    258   f2_b1->put(PARENT_ID, GetBookmarkId(2));
    259 
    260   // Under folder f3, there is nothing.
    261 
    262   // Insert in any order.
    263   index_.Insert(f2_b1);
    264   index_.Insert(f3);
    265   index_.Insert(f1_b2);
    266   index_.Insert(f1);
    267   index_.Insert(f2);
    268   index_.Insert(f1_b1);
    269 
    270   // Check that all are in the index.
    271   EXPECT_TRUE(index_.Contains(f1));
    272   EXPECT_TRUE(index_.Contains(f2));
    273   EXPECT_TRUE(index_.Contains(f3));
    274   EXPECT_TRUE(index_.Contains(f1_b1));
    275   EXPECT_TRUE(index_.Contains(f1_b2));
    276   EXPECT_TRUE(index_.Contains(f2_b1));
    277 
    278   // Remove them all in any order.
    279   index_.Remove(f3);
    280   EXPECT_FALSE(index_.Contains(f3));
    281   index_.Remove(f1_b2);
    282   EXPECT_FALSE(index_.Contains(f1_b2));
    283   index_.Remove(f2_b1);
    284   EXPECT_FALSE(index_.Contains(f2_b1));
    285   index_.Remove(f1);
    286   EXPECT_FALSE(index_.Contains(f1));
    287   index_.Remove(f2);
    288   EXPECT_FALSE(index_.Contains(f2));
    289   index_.Remove(f1_b1);
    290   EXPECT_FALSE(index_.Contains(f1_b1));
    291 }
    292 
    293 // Test that involves two non-ordered items.
    294 TEST_F(ParentChildIndexTest, UnorderedChildren) {
    295   // Make two unique client tag items under the root node.
    296   EntryKernel* u1 = MakeUniqueClientItem(1);
    297   EntryKernel* u2 = MakeUniqueClientItem(2);
    298 
    299   EXPECT_FALSE(u1->ShouldMaintainPosition());
    300   EXPECT_FALSE(u2->ShouldMaintainPosition());
    301 
    302   index_.Insert(u1);
    303   index_.Insert(u2);
    304 
    305   const OrderedChildSet* children = index_.GetChildren(syncable::Id());
    306   EXPECT_EQ(children->count(u1), 1UL);
    307   EXPECT_EQ(children->count(u2), 1UL);
    308   EXPECT_EQ(children->size(), 2UL);
    309 }
    310 
    311 // Test ordered and non-ordered entries under the same parent.
    312 // TODO(rlarocque): We should not need to support this.
    313 TEST_F(ParentChildIndexTest, OrderedAndUnorderedChildren) {
    314   EntryKernel* bm_folder = MakeBookmarkRoot();
    315   index_.Insert(bm_folder);
    316 
    317   EntryKernel* b1 = MakeBookmark(1, 1, false);
    318   EntryKernel* b2 = MakeBookmark(2, 2, false);
    319   EntryKernel* u1 = MakeUniqueClientItem(1);
    320   u1->put(PARENT_ID, GetBookmarkRootId());
    321 
    322   index_.Insert(b1);
    323   index_.Insert(u1);
    324   index_.Insert(b2);
    325 
    326   const OrderedChildSet* children = index_.GetChildren(GetBookmarkRootId());
    327   ASSERT_TRUE(children);
    328   EXPECT_EQ(children->size(), 3UL);
    329 
    330   // Ensure that the non-positionable item is moved to the far right.
    331   OrderedChildSet::const_iterator it = children->begin();
    332   EXPECT_EQ(*it, b1);
    333   it++;
    334   EXPECT_EQ(*it, b2);
    335   it++;
    336   EXPECT_EQ(*it, u1);
    337   it++;
    338   EXPECT_TRUE(it == children->end());
    339 }
    340 
    341 }  // namespace
    342 }  // namespace syncable
    343 }  // namespace syncer
    344 
    345