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.
5 #include "sync/syncable/parent_child_index.h"
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"
20 static const std::string kCacheGuid
= "8HhNIHlEOCGQbIAALr9QEg==";
22 class ParentChildIndexTest
: public testing::Test
{
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_
);
30 // Unfortunately, we can't use the regular Entry factory methods, because the
31 // ParentChildIndex deals in EntryKernels.
33 static syncable::Id
GetBookmarkRootId() {
34 return syncable::Id::CreateFromServerId("bookmark_folder");
37 static syncable::Id
GetBookmarkId(int n
) {
38 return syncable::Id::CreateFromServerId("b" + base::IntToString(n
));
41 static syncable::Id
GetClientUniqueId(int n
) {
42 return syncable::Id::CreateFromServerId("c" + base::IntToString(n
));
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());
56 owned_entry_kernels_
.push_back(root
);
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");
72 owned_entry_kernels_
.push_back(folder
);
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());
87 bm
->put(UNIQUE_BOOKMARK_TAG
,
88 syncable::GenerateSyncableBookmarkHash(kCacheGuid
,
89 bm
->ref(ID
).GetServerId()));
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
);
96 owned_entry_kernels_
.push_back(bm
);
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
));
111 owned_entry_kernels_
.push_back(item
);
115 ParentChildIndex index_
;
118 std::list
<EntryKernel
*> owned_entry_kernels_
;
121 TEST_F(ParentChildIndexTest
, TestRootNode
) {
122 EntryKernel
* root
= MakeRoot();
123 EXPECT_FALSE(ParentChildIndex::ShouldInclude(root
));
126 TEST_F(ParentChildIndexTest
, TestBookmarkRootFolder
) {
127 EntryKernel
* bm_folder
= MakeBookmarkRoot();
128 EXPECT_TRUE(ParentChildIndex::ShouldInclude(bm_folder
));
131 // Tests iteration over a set of siblings.
132 TEST_F(ParentChildIndexTest
, ChildInsertionAndIteration
) {
133 EntryKernel
* bm_folder
= MakeBookmarkRoot();
134 index_
.Insert(bm_folder
);
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);
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.
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
));
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();
167 EXPECT_TRUE(it
== children
->end());
170 // Tests iteration when hierarchy is involved.
171 TEST_F(ParentChildIndexTest
, ChildInsertionAndIterationWithHierarchy
) {
172 EntryKernel
* bm_folder
= MakeBookmarkRoot();
173 index_
.Insert(bm_folder
);
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);
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));
186 // Under folder f2, there is one bookmark.
187 EntryKernel
* f2_b1
= MakeBookmark(201, 1, false);
188 f2_b1
->put(PARENT_ID
, GetBookmarkId(2));
190 // Under folder f3, there is nothing.
192 // Insert in a strange order, because we can.
193 index_
.Insert(f1_b2
);
195 index_
.Insert(f2_b1
);
197 index_
.Insert(f1_b1
);
200 OrderedChildSet::const_iterator it
;
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();
213 EXPECT_TRUE(it
== top_children
->end());
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
);
222 EXPECT_EQ(*it
, f1_b2
);
224 EXPECT_TRUE(it
== f1_children
->end());
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
);
233 EXPECT_TRUE(it
== f2_children
->end());
235 // Check for children of the third folder.
236 const OrderedChildSet
* f3_children
= index_
.GetChildren(GetBookmarkId(3));
237 EXPECT_FALSE(f3_children
);
240 // Tests removing items.
241 TEST_F(ParentChildIndexTest
, RemoveWithHierarchy
) {
242 EntryKernel
* bm_folder
= MakeBookmarkRoot();
243 index_
.Insert(bm_folder
);
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);
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));
256 // Under folder f2, there is one bookmark.
257 EntryKernel
* f2_b1
= MakeBookmark(201, 1, false);
258 f2_b1
->put(PARENT_ID
, GetBookmarkId(2));
260 // Under folder f3, there is nothing.
262 // Insert in any order.
263 index_
.Insert(f2_b1
);
265 index_
.Insert(f1_b2
);
268 index_
.Insert(f1_b1
);
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
));
278 // Remove them all in any order.
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
));
286 EXPECT_FALSE(index_
.Contains(f1
));
288 EXPECT_FALSE(index_
.Contains(f2
));
289 index_
.Remove(f1_b1
);
290 EXPECT_FALSE(index_
.Contains(f1_b1
));
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);
299 EXPECT_FALSE(u1
->ShouldMaintainPosition());
300 EXPECT_FALSE(u2
->ShouldMaintainPosition());
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);
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
);
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());
326 const OrderedChildSet
* children
= index_
.GetChildren(GetBookmarkRootId());
327 ASSERT_TRUE(children
);
328 EXPECT_EQ(children
->size(), 3UL);
330 // Ensure that the non-positionable item is moved to the far right.
331 OrderedChildSet::const_iterator it
= children
->begin();
338 EXPECT_TRUE(it
== children
->end());
342 } // namespace syncable
343 } // namespace syncer