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.
5 // TODO(akalin): This file is basically just a unit test for
6 // BookmarkChangeProcessor. Write unit tests for
7 // BookmarkModelAssociator separately.
14 #include "base/command_line.h"
15 #include "base/files/file_path.h"
16 #include "base/location.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/strings/string16.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/time/time.h"
25 #include "chrome/browser/bookmarks/base_bookmark_model_observer.h"
26 #include "chrome/browser/bookmarks/bookmark_model.h"
27 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
28 #include "chrome/browser/bookmarks/bookmark_test_helpers.h"
29 #include "chrome/browser/sync/glue/bookmark_change_processor.h"
30 #include "chrome/browser/sync/glue/bookmark_model_associator.h"
31 #include "chrome/browser/sync/glue/data_type_error_handler.h"
32 #include "chrome/browser/sync/glue/data_type_error_handler_mock.h"
33 #include "chrome/common/chrome_switches.h"
34 #include "chrome/test/base/testing_profile.h"
35 #include "content/public/test/test_browser_thread.h"
36 #include "sync/api/sync_error.h"
37 #include "sync/internal_api/public/change_record.h"
38 #include "sync/internal_api/public/read_node.h"
39 #include "sync/internal_api/public/read_transaction.h"
40 #include "sync/internal_api/public/test/test_user_share.h"
41 #include "sync/internal_api/public/write_node.h"
42 #include "sync/internal_api/public/write_transaction.h"
43 #include "sync/syncable/mutable_entry.h" // TODO(tim): Remove. Bug 131130.
44 #include "testing/gmock/include/gmock/gmock.h"
45 #include "testing/gtest/include/gtest/gtest.h"
47 namespace browser_sync
{
49 using content::BrowserThread
;
50 using syncer::BaseNode
;
52 using testing::InvokeWithoutArgs
;
54 using testing::StrictMock
;
56 #if defined(OS_ANDROID)
57 static const bool kExpectMobileBookmarks
= true;
59 static const bool kExpectMobileBookmarks
= false;
60 #endif // defined(OS_ANDROID)
64 // FakeServerChange constructs a list of syncer::ChangeRecords while modifying
65 // the sync model, and can pass the ChangeRecord list to a
66 // syncer::SyncObserver (i.e., the ProfileSyncService) to test the client
67 // change-application behavior.
68 // Tests using FakeServerChange should be careful to avoid back-references,
69 // since FakeServerChange will send the edits in the order specified.
70 class FakeServerChange
{
72 explicit FakeServerChange(syncer::WriteTransaction
* trans
) : trans_(trans
) {
75 // Pretend that the server told the syncer to add a bookmark object.
76 int64
AddWithMetaInfo(const std::wstring
& title
,
77 const std::string
& url
,
78 const BookmarkNode::MetaInfoMap
* meta_info_map
,
81 int64 predecessor_id
) {
82 syncer::ReadNode
parent(trans_
);
83 EXPECT_EQ(BaseNode::INIT_OK
, parent
.InitByIdLookup(parent_id
));
84 syncer::WriteNode
node(trans_
);
85 if (predecessor_id
== 0) {
86 EXPECT_TRUE(node
.InitBookmarkByCreation(parent
, NULL
));
88 syncer::ReadNode
predecessor(trans_
);
89 EXPECT_EQ(BaseNode::INIT_OK
, predecessor
.InitByIdLookup(predecessor_id
));
90 EXPECT_EQ(predecessor
.GetParentId(), parent
.GetId());
91 EXPECT_TRUE(node
.InitBookmarkByCreation(parent
, &predecessor
));
93 EXPECT_EQ(node
.GetPredecessorId(), predecessor_id
);
94 EXPECT_EQ(node
.GetParentId(), parent_id
);
95 node
.SetIsFolder(is_folder
);
98 sync_pb::BookmarkSpecifics
specifics(node
.GetBookmarkSpecifics());
100 specifics
.set_url(url
);
102 SetNodeMetaInfo(*meta_info_map
, &specifics
);
103 node
.SetBookmarkSpecifics(specifics
);
105 syncer::ChangeRecord record
;
106 record
.action
= syncer::ChangeRecord::ACTION_ADD
;
107 record
.id
= node
.GetId();
108 changes_
.push_back(record
);
112 int64
Add(const std::wstring
& title
,
113 const std::string
& url
,
116 int64 predecessor_id
) {
117 return AddWithMetaInfo(title
, url
, NULL
, is_folder
, parent_id
,
121 // Add a bookmark folder.
122 int64
AddFolder(const std::wstring
& title
,
124 int64 predecessor_id
) {
125 return Add(title
, std::string(), true, parent_id
, predecessor_id
);
127 int64
AddFolderWithMetaInfo(const std::wstring
& title
,
128 const BookmarkNode::MetaInfoMap
* meta_info_map
,
130 int64 predecessor_id
) {
131 return AddWithMetaInfo(title
, std::string(), meta_info_map
, true, parent_id
,
136 int64
AddURL(const std::wstring
& title
,
137 const std::string
& url
,
139 int64 predecessor_id
) {
140 return Add(title
, url
, false, parent_id
, predecessor_id
);
142 int64
AddURLWithMetaInfo(const std::wstring
& title
,
143 const std::string
& url
,
144 const BookmarkNode::MetaInfoMap
* meta_info_map
,
146 int64 predecessor_id
) {
147 return AddWithMetaInfo(title
, url
, meta_info_map
, false, parent_id
,
151 // Pretend that the server told the syncer to delete an object.
152 void Delete(int64 id
) {
154 // Delete the sync node.
155 syncer::WriteNode
node(trans_
);
156 EXPECT_EQ(BaseNode::INIT_OK
, node
.InitByIdLookup(id
));
157 if (node
.GetIsFolder())
158 EXPECT_FALSE(node
.GetFirstChildId());
159 node
.GetMutableEntryForTest()->PutServerIsDel(true);
163 // Verify the deletion.
164 syncer::ReadNode
node(trans_
);
165 EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_IS_DEL
, node
.InitByIdLookup(id
));
168 syncer::ChangeRecord record
;
169 record
.action
= syncer::ChangeRecord::ACTION_DELETE
;
171 // Deletions are always first in the changelist, but we can't actually do
172 // WriteNode::Remove() on the node until its children are moved. So, as
173 // a practical matter, users of FakeServerChange must move or delete
174 // children before parents. Thus, we must insert the deletion record
175 // at the front of the vector.
176 changes_
.insert(changes_
.begin(), record
);
179 // Set a new title value, and return the old value.
180 std::wstring
ModifyTitle(int64 id
, const std::wstring
& new_title
) {
181 syncer::WriteNode
node(trans_
);
182 EXPECT_EQ(BaseNode::INIT_OK
, node
.InitByIdLookup(id
));
183 std::string old_title
= node
.GetTitle();
184 node
.SetTitle(new_title
);
186 return base::UTF8ToWide(old_title
);
189 // Set a new parent and predecessor value. Return the old parent id.
190 // We could return the old predecessor id, but it turns out not to be
191 // very useful for assertions.
192 int64
ModifyPosition(int64 id
, int64 parent_id
, int64 predecessor_id
) {
193 syncer::ReadNode
parent(trans_
);
194 EXPECT_EQ(BaseNode::INIT_OK
, parent
.InitByIdLookup(parent_id
));
195 syncer::WriteNode
node(trans_
);
196 EXPECT_EQ(BaseNode::INIT_OK
, node
.InitByIdLookup(id
));
197 int64 old_parent_id
= node
.GetParentId();
198 if (predecessor_id
== 0) {
199 EXPECT_TRUE(node
.SetPosition(parent
, NULL
));
201 syncer::ReadNode
predecessor(trans_
);
202 EXPECT_EQ(BaseNode::INIT_OK
, predecessor
.InitByIdLookup(predecessor_id
));
203 EXPECT_EQ(predecessor
.GetParentId(), parent
.GetId());
204 EXPECT_TRUE(node
.SetPosition(parent
, &predecessor
));
207 return old_parent_id
;
210 void ModifyCreationTime(int64 id
, int64 creation_time_us
) {
211 syncer::WriteNode
node(trans_
);
212 ASSERT_EQ(BaseNode::INIT_OK
, node
.InitByIdLookup(id
));
213 sync_pb::BookmarkSpecifics specifics
= node
.GetBookmarkSpecifics();
214 specifics
.set_creation_time_us(creation_time_us
);
215 node
.SetBookmarkSpecifics(specifics
);
219 void ModifyMetaInfo(int64 id
,
220 const BookmarkNode::MetaInfoMap
& meta_info_map
) {
221 syncer::WriteNode
node(trans_
);
222 ASSERT_EQ(BaseNode::INIT_OK
, node
.InitByIdLookup(id
));
223 sync_pb::BookmarkSpecifics specifics
= node
.GetBookmarkSpecifics();
224 SetNodeMetaInfo(meta_info_map
, &specifics
);
225 node
.SetBookmarkSpecifics(specifics
);
229 // Pass the fake change list to |service|.
230 void ApplyPendingChanges(ChangeProcessor
* processor
) {
231 processor
->ApplyChangesFromSyncModel(
232 trans_
, 0, syncer::ImmutableChangeRecordList(&changes_
));
235 const syncer::ChangeRecordList
& changes() {
240 // Helper function to push an ACTION_UPDATE record onto the back
241 // of the changelist.
242 void SetModified(int64 id
) {
243 // Coalesce multi-property edits.
244 if (!changes_
.empty() && changes_
.back().id
== id
&&
245 changes_
.back().action
==
246 syncer::ChangeRecord::ACTION_UPDATE
)
248 syncer::ChangeRecord record
;
249 record
.action
= syncer::ChangeRecord::ACTION_UPDATE
;
251 changes_
.push_back(record
);
254 void SetNodeMetaInfo(const BookmarkNode::MetaInfoMap
& meta_info_map
,
255 sync_pb::BookmarkSpecifics
* specifics
) {
256 specifics
->clear_meta_info();
257 for (BookmarkNode::MetaInfoMap::const_iterator it
=
258 meta_info_map
.begin(); it
!= meta_info_map
.end(); ++it
) {
259 sync_pb::MetaInfo
* meta_info
= specifics
->add_meta_info();
260 meta_info
->set_key(it
->first
);
261 meta_info
->set_value(it
->second
);
266 // The transaction on which everything happens.
267 syncer::WriteTransaction
*trans_
;
269 // The change list we construct.
270 syncer::ChangeRecordList changes_
;
273 class ExtensiveChangesBookmarkModelObserver
: public BaseBookmarkModelObserver
{
275 explicit ExtensiveChangesBookmarkModelObserver()
277 completed_count_at_started_(0),
278 completed_count_(0) {}
280 virtual void ExtensiveBookmarkChangesBeginning(
281 BookmarkModel
* model
) OVERRIDE
{
283 completed_count_at_started_
= completed_count_
;
286 virtual void ExtensiveBookmarkChangesEnded(BookmarkModel
* model
) OVERRIDE
{
290 virtual void BookmarkModelChanged() OVERRIDE
{}
292 int get_started() const {
293 return started_count_
;
296 int get_completed_count_at_started() const {
297 return completed_count_at_started_
;
300 int get_completed() const {
301 return completed_count_
;
306 int completed_count_at_started_
;
307 int completed_count_
;
309 DISALLOW_COPY_AND_ASSIGN(ExtensiveChangesBookmarkModelObserver
);
313 class ProfileSyncServiceBookmarkTest
: public testing::Test
{
315 enum LoadOption
{ LOAD_FROM_STORAGE
, DELETE_EXISTING_STORAGE
};
316 enum SaveOption
{ SAVE_TO_STORAGE
, DONT_SAVE_TO_STORAGE
};
318 ProfileSyncServiceBookmarkTest()
320 ui_thread_(BrowserThread::UI
, &message_loop_
),
321 file_thread_(BrowserThread::FILE, &message_loop_
),
322 local_merge_result_(syncer::BOOKMARKS
),
323 syncer_merge_result_(syncer::BOOKMARKS
) {
326 virtual ~ProfileSyncServiceBookmarkTest() {
328 UnloadBookmarkModel();
331 virtual void SetUp() {
332 test_user_share_
.SetUp();
335 virtual void TearDown() {
336 test_user_share_
.TearDown();
339 // Inserts a folder directly to the share.
340 // Do not use this after model association is complete.
342 // This function differs from the AddFolder() function declared elsewhere in
343 // this file in that it only affects the sync model. It would be invalid to
344 // change the sync model directly after ModelAssociation. This function can
345 // be invoked prior to model association to set up first-time sync model
346 // association scenarios.
347 int64
AddFolderToShare(syncer::WriteTransaction
* trans
, std::string title
) {
348 EXPECT_FALSE(model_associator_
);
350 // Be sure to call CreatePermanentBookmarkNodes(), otherwise this will fail.
351 syncer::ReadNode
bookmark_bar(trans
);
352 EXPECT_EQ(BaseNode::INIT_OK
, bookmark_bar
.InitByTagLookup("bookmark_bar"));
354 syncer::WriteNode
node(trans
);
355 EXPECT_TRUE(node
.InitBookmarkByCreation(bookmark_bar
, NULL
));
356 node
.SetIsFolder(true);
357 node
.SetTitle(base::ASCIIToWide(title
));
362 // Inserts a bookmark directly to the share.
363 // Do not use this after model association is complete.
365 // This function differs from the AddURL() function declared elsewhere in this
366 // file in that it only affects the sync model. It would be invalid to change
367 // the sync model directly after ModelAssociation. This function can be
368 // invoked prior to model association to set up first-time sync model
369 // association scenarios.
370 int64
AddBookmarkToShare(syncer::WriteTransaction
*trans
,
373 EXPECT_FALSE(model_associator_
);
375 syncer::ReadNode
parent(trans
);
376 EXPECT_EQ(BaseNode::INIT_OK
, parent
.InitByIdLookup(parent_id
));
378 sync_pb::BookmarkSpecifics specifics
;
379 specifics
.set_url("http://www.google.com/search?q=" + title
);
380 specifics
.set_title(title
);
382 syncer::WriteNode
node(trans
);
383 EXPECT_TRUE(node
.InitBookmarkByCreation(parent
, NULL
));
384 node
.SetIsFolder(false);
385 node
.SetTitle(base::ASCIIToWide(title
));
386 node
.SetBookmarkSpecifics(specifics
);
391 // Load (or re-load) the bookmark model. |load| controls use of the
392 // bookmarks file on disk. |save| controls whether the newly loaded
393 // bookmark model will write out a bookmark file as it goes.
394 void LoadBookmarkModel(LoadOption load
, SaveOption save
) {
395 bool delete_bookmarks
= load
== DELETE_EXISTING_STORAGE
;
396 profile_
.CreateBookmarkModel(delete_bookmarks
);
397 model_
= BookmarkModelFactory::GetForProfile(&profile_
);
398 test::WaitForBookmarkModelToLoad(model_
);
399 // This noticeably speeds up the unit tests that request it.
400 if (save
== DONT_SAVE_TO_STORAGE
)
401 model_
->ClearStore();
402 message_loop_
.RunUntilIdle();
405 int GetSyncBookmarkCount() {
406 syncer::ReadTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
407 syncer::ReadNode
node(&trans
);
408 if (node
.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::BOOKMARKS
)) !=
409 syncer::BaseNode::INIT_OK
)
411 return node
.GetTotalNodeCount();
414 // Creates the bookmark root node and the permanent nodes if they don't
416 bool CreatePermanentBookmarkNodes() {
417 bool root_exists
= false;
418 syncer::ModelType type
= syncer::BOOKMARKS
;
420 syncer::WriteTransaction
trans(FROM_HERE
,
421 test_user_share_
.user_share());
422 syncer::ReadNode
uber_root(&trans
);
423 uber_root
.InitByRootLookup();
425 syncer::ReadNode
root(&trans
);
426 root_exists
= (root
.InitByTagLookup(syncer::ModelTypeToRootTag(type
)) ==
431 if (!syncer::TestUserShare::CreateRoot(type
,
432 test_user_share_
.user_share()))
436 const int kNumPermanentNodes
= 3;
437 const std::string permanent_tags
[kNumPermanentNodes
] = {
438 "bookmark_bar", "other_bookmarks", "synced_bookmarks"
440 syncer::WriteTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
441 syncer::ReadNode
root(&trans
);
442 EXPECT_EQ(BaseNode::INIT_OK
, root
.InitByTagLookup(
443 syncer::ModelTypeToRootTag(type
)));
445 // Loop through creating permanent nodes as necessary.
446 int64 last_child_id
= syncer::kInvalidId
;
447 for (int i
= 0; i
< kNumPermanentNodes
; ++i
) {
448 // First check if the node already exists. This is for tests that involve
449 // persistence and set up sync more than once.
450 syncer::ReadNode
lookup(&trans
);
451 if (lookup
.InitByTagLookup(permanent_tags
[i
]) ==
452 syncer::ReadNode::INIT_OK
) {
453 last_child_id
= lookup
.GetId();
457 // If it doesn't exist, create the permanent node at the end of the
459 syncer::ReadNode
predecessor_node(&trans
);
460 syncer::ReadNode
* predecessor
= NULL
;
461 if (last_child_id
!= syncer::kInvalidId
) {
462 EXPECT_EQ(BaseNode::INIT_OK
,
463 predecessor_node
.InitByIdLookup(last_child_id
));
464 predecessor
= &predecessor_node
;
466 syncer::WriteNode
node(&trans
);
467 if (!node
.InitBookmarkByCreation(root
, predecessor
))
469 node
.SetIsFolder(true);
470 node
.GetMutableEntryForTest()->PutUniqueServerTag(permanent_tags
[i
]);
471 node
.SetTitle(base::UTF8ToWide(permanent_tags
[i
]));
472 node
.SetExternalId(0);
473 last_child_id
= node
.GetId();
478 bool AssociateModels() {
479 DCHECK(!model_associator_
);
481 // Set up model associator.
482 model_associator_
.reset(new BookmarkModelAssociator(
483 BookmarkModelFactory::GetForProfile(&profile_
),
485 test_user_share_
.user_share(),
486 &mock_error_handler_
,
487 kExpectMobileBookmarks
));
489 local_merge_result_
= syncer::SyncMergeResult(syncer::BOOKMARKS
);
490 syncer_merge_result_
= syncer::SyncMergeResult(syncer::BOOKMARKS
);
491 int local_count_before
= model_
->root_node()->GetTotalNodeCount();
492 int syncer_count_before
= GetSyncBookmarkCount();
494 syncer::SyncError error
= model_associator_
->AssociateModels(
495 &local_merge_result_
,
496 &syncer_merge_result_
);
500 base::MessageLoop::current()->RunUntilIdle();
502 // Verify the merge results were calculated properly.
503 EXPECT_EQ(local_count_before
,
504 local_merge_result_
.num_items_before_association());
505 EXPECT_EQ(syncer_count_before
,
506 syncer_merge_result_
.num_items_before_association());
507 EXPECT_EQ(local_merge_result_
.num_items_after_association(),
508 local_merge_result_
.num_items_before_association() +
509 local_merge_result_
.num_items_added() -
510 local_merge_result_
.num_items_deleted());
511 EXPECT_EQ(syncer_merge_result_
.num_items_after_association(),
512 syncer_merge_result_
.num_items_before_association() +
513 syncer_merge_result_
.num_items_added() -
514 syncer_merge_result_
.num_items_deleted());
515 EXPECT_EQ(model_
->root_node()->GetTotalNodeCount(),
516 local_merge_result_
.num_items_after_association());
517 EXPECT_EQ(GetSyncBookmarkCount(),
518 syncer_merge_result_
.num_items_after_association());
523 test_user_share_
.Reload();
525 ASSERT_TRUE(CreatePermanentBookmarkNodes());
526 ASSERT_TRUE(AssociateModels());
528 // Set up change processor.
529 change_processor_
.reset(
530 new BookmarkChangeProcessor(model_associator_
.get(),
531 &mock_error_handler_
));
532 change_processor_
->Start(&profile_
, test_user_share_
.user_share());
536 change_processor_
.reset();
537 if (model_associator_
) {
538 syncer::SyncError error
= model_associator_
->DisassociateModels();
539 EXPECT_FALSE(error
.IsSet());
541 model_associator_
.reset();
543 message_loop_
.RunUntilIdle();
545 // TODO(akalin): Actually close the database and flush it to disk
546 // (and make StartSync reload from disk). This would require
547 // refactoring TestUserShare.
550 void UnloadBookmarkModel() {
551 profile_
.CreateBookmarkModel(false /* delete_bookmarks */);
553 message_loop_
.RunUntilIdle();
556 bool InitSyncNodeFromChromeNode(const BookmarkNode
* bnode
,
557 syncer::BaseNode
* sync_node
) {
558 return model_associator_
->InitSyncNodeFromChromeId(bnode
->id(),
562 void ExpectSyncerNodeMatching(syncer::BaseTransaction
* trans
,
563 const BookmarkNode
* bnode
) {
564 syncer::ReadNode
gnode(trans
);
565 ASSERT_TRUE(InitSyncNodeFromChromeNode(bnode
, &gnode
));
566 // Non-root node titles and parents must match.
567 if (!model_
->is_permanent_node(bnode
)) {
568 EXPECT_EQ(bnode
->GetTitle(), base::UTF8ToUTF16(gnode
.GetTitle()));
570 model_associator_
->GetChromeNodeFromSyncId(gnode
.GetParentId()),
573 EXPECT_EQ(bnode
->is_folder(), gnode
.GetIsFolder());
575 EXPECT_EQ(bnode
->url(), GURL(gnode
.GetBookmarkSpecifics().url()));
577 // Check that meta info matches.
578 const BookmarkNode::MetaInfoMap
* meta_info_map
= bnode
->GetMetaInfoMap();
579 sync_pb::BookmarkSpecifics specifics
= gnode
.GetBookmarkSpecifics();
580 if (!meta_info_map
) {
581 EXPECT_EQ(0, specifics
.meta_info_size());
583 EXPECT_EQ(meta_info_map
->size(),
584 static_cast<size_t>(specifics
.meta_info_size()));
585 for (int i
= 0; i
< specifics
.meta_info_size(); i
++) {
586 BookmarkNode::MetaInfoMap::const_iterator it
=
587 meta_info_map
->find(specifics
.meta_info(i
).key());
588 EXPECT_TRUE(it
!= meta_info_map
->end());
589 EXPECT_EQ(it
->second
, specifics
.meta_info(i
).value());
593 // Check for position matches.
594 int browser_index
= bnode
->parent()->GetIndexOf(bnode
);
595 if (browser_index
== 0) {
596 EXPECT_EQ(gnode
.GetPredecessorId(), 0);
598 const BookmarkNode
* bprev
=
599 bnode
->parent()->GetChild(browser_index
- 1);
600 syncer::ReadNode
gprev(trans
);
601 ASSERT_TRUE(InitSyncNodeFromChromeNode(bprev
, &gprev
));
602 EXPECT_EQ(gnode
.GetPredecessorId(), gprev
.GetId());
603 EXPECT_EQ(gnode
.GetParentId(), gprev
.GetParentId());
605 if (browser_index
== bnode
->parent()->child_count() - 1) {
606 EXPECT_EQ(gnode
.GetSuccessorId(), 0);
608 const BookmarkNode
* bnext
=
609 bnode
->parent()->GetChild(browser_index
+ 1);
610 syncer::ReadNode
gnext(trans
);
611 ASSERT_TRUE(InitSyncNodeFromChromeNode(bnext
, &gnext
));
612 EXPECT_EQ(gnode
.GetSuccessorId(), gnext
.GetId());
613 EXPECT_EQ(gnode
.GetParentId(), gnext
.GetParentId());
616 EXPECT_TRUE(gnode
.GetFirstChildId());
619 void ExpectSyncerNodeMatching(const BookmarkNode
* bnode
) {
620 syncer::ReadTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
621 ExpectSyncerNodeMatching(&trans
, bnode
);
624 void ExpectBrowserNodeMatching(syncer::BaseTransaction
* trans
,
626 EXPECT_TRUE(sync_id
);
627 const BookmarkNode
* bnode
=
628 model_associator_
->GetChromeNodeFromSyncId(sync_id
);
630 int64 id
= model_associator_
->GetSyncIdFromChromeId(bnode
->id());
631 EXPECT_EQ(id
, sync_id
);
632 ExpectSyncerNodeMatching(trans
, bnode
);
635 void ExpectBrowserNodeUnknown(int64 sync_id
) {
636 EXPECT_FALSE(model_associator_
->GetChromeNodeFromSyncId(sync_id
));
639 void ExpectBrowserNodeKnown(int64 sync_id
) {
640 EXPECT_TRUE(model_associator_
->GetChromeNodeFromSyncId(sync_id
));
643 void ExpectSyncerNodeKnown(const BookmarkNode
* node
) {
644 int64 sync_id
= model_associator_
->GetSyncIdFromChromeId(node
->id());
645 EXPECT_NE(sync_id
, syncer::kInvalidId
);
648 void ExpectSyncerNodeUnknown(const BookmarkNode
* node
) {
649 int64 sync_id
= model_associator_
->GetSyncIdFromChromeId(node
->id());
650 EXPECT_EQ(sync_id
, syncer::kInvalidId
);
653 void ExpectBrowserNodeTitle(int64 sync_id
, const std::wstring
& title
) {
654 const BookmarkNode
* bnode
=
655 model_associator_
->GetChromeNodeFromSyncId(sync_id
);
657 EXPECT_EQ(bnode
->GetTitle(), base::WideToUTF16Hack(title
));
660 void ExpectBrowserNodeURL(int64 sync_id
, const std::string
& url
) {
661 const BookmarkNode
* bnode
=
662 model_associator_
->GetChromeNodeFromSyncId(sync_id
);
664 EXPECT_EQ(GURL(url
), bnode
->url());
667 void ExpectBrowserNodeParent(int64 sync_id
, int64 parent_sync_id
) {
668 const BookmarkNode
* node
=
669 model_associator_
->GetChromeNodeFromSyncId(sync_id
);
671 const BookmarkNode
* parent
=
672 model_associator_
->GetChromeNodeFromSyncId(parent_sync_id
);
674 EXPECT_EQ(node
->parent(), parent
);
677 void ExpectModelMatch(syncer::BaseTransaction
* trans
) {
678 const BookmarkNode
* root
= model_
->root_node();
679 EXPECT_EQ(root
->GetIndexOf(model_
->bookmark_bar_node()), 0);
680 EXPECT_EQ(root
->GetIndexOf(model_
->other_node()), 1);
681 EXPECT_EQ(root
->GetIndexOf(model_
->mobile_node()), 2);
683 std::stack
<int64
> stack
;
684 stack
.push(bookmark_bar_id());
685 while (!stack
.empty()) {
686 int64 id
= stack
.top();
690 ExpectBrowserNodeMatching(trans
, id
);
692 syncer::ReadNode
gnode(trans
);
693 ASSERT_EQ(BaseNode::INIT_OK
, gnode
.InitByIdLookup(id
));
694 stack
.push(gnode
.GetSuccessorId());
695 if (gnode
.GetIsFolder())
696 stack
.push(gnode
.GetFirstChildId());
700 void ExpectModelMatch() {
701 syncer::ReadTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
702 ExpectModelMatch(&trans
);
705 int64
mobile_bookmarks_id() {
707 model_associator_
->GetSyncIdFromChromeId(model_
->mobile_node()->id());
710 int64
other_bookmarks_id() {
712 model_associator_
->GetSyncIdFromChromeId(model_
->other_node()->id());
715 int64
bookmark_bar_id() {
716 return model_associator_
->GetSyncIdFromChromeId(
717 model_
->bookmark_bar_node()->id());
721 BookmarkModel
* model_
;
722 syncer::TestUserShare test_user_share_
;
723 scoped_ptr
<BookmarkChangeProcessor
> change_processor_
;
724 StrictMock
<DataTypeErrorHandlerMock
> mock_error_handler_
;
725 scoped_ptr
<BookmarkModelAssociator
> model_associator_
;
728 // Used by both |ui_thread_| and |file_thread_|.
729 base::MessageLoop message_loop_
;
730 content::TestBrowserThread ui_thread_
;
731 // Needed by |model_|.
732 content::TestBrowserThread file_thread_
;
734 syncer::SyncMergeResult local_merge_result_
;
735 syncer::SyncMergeResult syncer_merge_result_
;
737 TestingProfile profile_
;
740 TEST_F(ProfileSyncServiceBookmarkTest
, InitialState
) {
741 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
744 EXPECT_TRUE(other_bookmarks_id());
745 EXPECT_TRUE(bookmark_bar_id());
746 EXPECT_TRUE(mobile_bookmarks_id());
751 // Populate the sync database then start model association. Sync's bookmarks
752 // should end up being copied into the native model, resulting in a successful
753 // "ExpectModelMatch()".
755 // This code has some use for verifying correctness. It's also a very useful
756 // for profiling bookmark ModelAssociation, an important part of some first-time
757 // sync scenarios. Simply increase the kNumFolders and kNumBookmarksPerFolder
758 // as desired, then run the test under a profiler to find hot spots in the model
760 TEST_F(ProfileSyncServiceBookmarkTest
, InitialModelAssociate
) {
761 const int kNumBookmarksPerFolder
= 10;
762 const int kNumFolders
= 10;
764 CreatePermanentBookmarkNodes();
767 syncer::WriteTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
768 for (int i
= 0; i
< kNumFolders
; ++i
) {
769 int64 folder_id
= AddFolderToShare(&trans
,
770 base::StringPrintf("folder%05d", i
));
771 for (int j
= 0; j
< kNumBookmarksPerFolder
; ++j
) {
772 AddBookmarkToShare(&trans
,
774 base::StringPrintf("bookmark%05d", j
));
779 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
786 TEST_F(ProfileSyncServiceBookmarkTest
, BookmarkModelOperations
) {
787 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
791 const BookmarkNode
* folder
=
792 model_
->AddFolder(model_
->other_node(), 0, base::ASCIIToUTF16("foobar"));
793 ExpectSyncerNodeMatching(folder
);
795 const BookmarkNode
* folder2
=
796 model_
->AddFolder(folder
, 0, base::ASCIIToUTF16("nested"));
797 ExpectSyncerNodeMatching(folder2
);
799 const BookmarkNode
* url1
= model_
->AddURL(
800 folder
, 0, base::ASCIIToUTF16("Internets #1 Pies Site"),
801 GURL("http://www.easypie.com/"));
802 ExpectSyncerNodeMatching(url1
);
804 const BookmarkNode
* url2
= model_
->AddURL(
805 folder
, 1, base::ASCIIToUTF16("Airplanes"),
806 GURL("http://www.easyjet.com/"));
807 ExpectSyncerNodeMatching(url2
);
810 const BookmarkNode
* mobile_folder
=
811 model_
->AddFolder(model_
->mobile_node(), 0, base::ASCIIToUTF16("pie"));
812 ExpectSyncerNodeMatching(mobile_folder
);
815 // Test modification.
816 model_
->SetTitle(url2
, base::ASCIIToUTF16("EasyJet"));
818 model_
->Move(url1
, folder2
, 0);
820 model_
->Move(folder2
, model_
->bookmark_bar_node(), 0);
822 model_
->SetTitle(folder2
, base::ASCIIToUTF16("Not Nested"));
824 model_
->Move(folder
, folder2
, 0);
826 model_
->SetTitle(folder
, base::ASCIIToUTF16("who's nested now?"));
828 model_
->Copy(url2
, model_
->bookmark_bar_node(), 0);
830 model_
->SetTitle(mobile_folder
, base::ASCIIToUTF16("strawberry"));
834 // Delete a single item.
835 model_
->Remove(url2
->parent(), url2
->parent()->GetIndexOf(url2
));
837 // Delete an item with several children.
838 model_
->Remove(folder2
->parent(),
839 folder2
->parent()->GetIndexOf(folder2
));
841 model_
->Remove(model_
->mobile_node(), 0);
845 TEST_F(ProfileSyncServiceBookmarkTest
, ServerChangeProcessing
) {
846 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
849 syncer::WriteTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
851 FakeServerChange
adds(&trans
);
852 int64 f1
= adds
.AddFolder(L
"Server Folder B", bookmark_bar_id(), 0);
853 int64 f2
= adds
.AddFolder(L
"Server Folder A", bookmark_bar_id(), f1
);
854 int64 u1
= adds
.AddURL(L
"Some old site", "ftp://nifty.andrew.cmu.edu/",
855 bookmark_bar_id(), f2
);
856 int64 u2
= adds
.AddURL(L
"Nifty", "ftp://nifty.andrew.cmu.edu/", f1
, 0);
857 // u3 is a duplicate URL
858 int64 u3
= adds
.AddURL(L
"Nifty2", "ftp://nifty.andrew.cmu.edu/", f1
, u2
);
859 // u4 is a duplicate title, different URL.
860 adds
.AddURL(L
"Some old site", "http://slog.thestranger.com/",
861 bookmark_bar_id(), u1
);
862 // u5 tests an empty-string title.
863 std::string
javascript_url(
864 "javascript:(function(){var w=window.open(" \
865 "'about:blank','gnotesWin','location=0,menubar=0," \
866 "scrollbars=0,status=0,toolbar=0,width=300," \
867 "height=300,resizable');});");
868 adds
.AddURL(std::wstring(), javascript_url
, other_bookmarks_id(), 0);
869 int64 u6
= adds
.AddURL(
870 L
"Sync1", "http://www.syncable.edu/", mobile_bookmarks_id(), 0);
872 syncer::ChangeRecordList::const_iterator it
;
873 // The bookmark model shouldn't yet have seen any of the nodes of |adds|.
874 for (it
= adds
.changes().begin(); it
!= adds
.changes().end(); ++it
)
875 ExpectBrowserNodeUnknown(it
->id
);
877 adds
.ApplyPendingChanges(change_processor_
.get());
879 // Make sure the bookmark model received all of the nodes in |adds|.
880 for (it
= adds
.changes().begin(); it
!= adds
.changes().end(); ++it
)
881 ExpectBrowserNodeMatching(&trans
, it
->id
);
882 ExpectModelMatch(&trans
);
884 // Part two: test modifications.
885 FakeServerChange
mods(&trans
);
886 // Mess with u2, and move it into empty folder f2
887 // TODO(ncarter): Determine if we allow ModifyURL ops or not.
888 /* std::wstring u2_old_url = mods.ModifyURL(u2, L"http://www.google.com"); */
889 std::wstring u2_old_title
= mods
.ModifyTitle(u2
, L
"The Google");
890 int64 u2_old_parent
= mods
.ModifyPosition(u2
, f2
, 0);
892 // Now move f1 after u2.
893 std::wstring f1_old_title
= mods
.ModifyTitle(f1
, L
"Server Folder C");
894 int64 f1_old_parent
= mods
.ModifyPosition(f1
, f2
, u2
);
896 // Then add u3 after f1.
897 int64 u3_old_parent
= mods
.ModifyPosition(u3
, f2
, f1
);
899 std::wstring u6_old_title
= mods
.ModifyTitle(u6
, L
"Mobile Folder A");
901 // Test that the property changes have not yet taken effect.
902 ExpectBrowserNodeTitle(u2
, u2_old_title
);
903 /* ExpectBrowserNodeURL(u2, u2_old_url); */
904 ExpectBrowserNodeParent(u2
, u2_old_parent
);
906 ExpectBrowserNodeTitle(f1
, f1_old_title
);
907 ExpectBrowserNodeParent(f1
, f1_old_parent
);
909 ExpectBrowserNodeParent(u3
, u3_old_parent
);
911 ExpectBrowserNodeTitle(u6
, u6_old_title
);
913 // Apply the changes.
914 mods
.ApplyPendingChanges(change_processor_
.get());
916 // Check for successful application.
917 for (it
= mods
.changes().begin(); it
!= mods
.changes().end(); ++it
)
918 ExpectBrowserNodeMatching(&trans
, it
->id
);
919 ExpectModelMatch(&trans
);
921 // Part 3: Test URL deletion.
922 FakeServerChange
dels(&trans
);
927 ExpectBrowserNodeKnown(u2
);
928 ExpectBrowserNodeKnown(u3
);
930 dels
.ApplyPendingChanges(change_processor_
.get());
932 ExpectBrowserNodeUnknown(u2
);
933 ExpectBrowserNodeUnknown(u3
);
934 ExpectBrowserNodeUnknown(u6
);
935 ExpectModelMatch(&trans
);
938 // Tests a specific case in ApplyModelChanges where we move the
939 // children out from under a parent, and then delete the parent
940 // in the same changelist. The delete shows up first in the changelist,
941 // requiring the children to be moved to a temporary location.
942 TEST_F(ProfileSyncServiceBookmarkTest
, ServerChangeRequiringFosterParent
) {
943 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
946 syncer::WriteTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
948 // Stress the immediate children of other_node because that's where
949 // ApplyModelChanges puts a temporary foster parent node.
950 std::string
url("http://dev.chromium.org/");
951 FakeServerChange
adds(&trans
);
952 int64 f0
= other_bookmarks_id(); // + other_node
953 int64 f1
= adds
.AddFolder(L
"f1", f0
, 0); // + f1
954 int64 f2
= adds
.AddFolder(L
"f2", f1
, 0); // + f2
955 int64 u3
= adds
.AddURL( L
"u3", url
, f2
, 0); // + u3 NOLINT
956 int64 u4
= adds
.AddURL( L
"u4", url
, f2
, u3
); // + u4 NOLINT
957 int64 u5
= adds
.AddURL( L
"u5", url
, f1
, f2
); // + u5 NOLINT
958 int64 f6
= adds
.AddFolder(L
"f6", f1
, u5
); // + f6
959 int64 u7
= adds
.AddURL( L
"u7", url
, f0
, f1
); // + u7 NOLINT
961 syncer::ChangeRecordList::const_iterator it
;
962 // The bookmark model shouldn't yet have seen any of the nodes of |adds|.
963 for (it
= adds
.changes().begin(); it
!= adds
.changes().end(); ++it
)
964 ExpectBrowserNodeUnknown(it
->id
);
966 adds
.ApplyPendingChanges(change_processor_
.get());
968 // Make sure the bookmark model received all of the nodes in |adds|.
969 for (it
= adds
.changes().begin(); it
!= adds
.changes().end(); ++it
)
970 ExpectBrowserNodeMatching(&trans
, it
->id
);
971 ExpectModelMatch(&trans
);
973 // We have to do the moves before the deletions, but FakeServerChange will
974 // put the deletion at the front of the changelist.
975 FakeServerChange
ops(&trans
);
976 ops
.ModifyPosition(f6
, other_bookmarks_id(), 0);
977 ops
.ModifyPosition(u3
, other_bookmarks_id(), f1
); // Prev == f1 is OK here.
978 ops
.ModifyPosition(f2
, other_bookmarks_id(), u7
);
979 ops
.ModifyPosition(u7
, f2
, 0);
980 ops
.ModifyPosition(u4
, other_bookmarks_id(), f2
);
981 ops
.ModifyPosition(u5
, f6
, 0);
984 ops
.ApplyPendingChanges(change_processor_
.get());
986 ExpectModelMatch(&trans
);
989 // Simulate a server change record containing a valid but non-canonical URL.
990 TEST_F(ProfileSyncServiceBookmarkTest
, ServerChangeWithNonCanonicalURL
) {
991 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, SAVE_TO_STORAGE
);
995 syncer::WriteTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
997 FakeServerChange
adds(&trans
);
998 std::string
url("http://dev.chromium.org");
999 EXPECT_NE(GURL(url
).spec(), url
);
1000 adds
.AddURL(L
"u1", url
, other_bookmarks_id(), 0);
1002 adds
.ApplyPendingChanges(change_processor_
.get());
1004 EXPECT_EQ(1, model_
->other_node()->child_count());
1005 ExpectModelMatch(&trans
);
1008 // Now reboot the sync service, forcing a merge step.
1010 LoadBookmarkModel(LOAD_FROM_STORAGE
, SAVE_TO_STORAGE
);
1013 // There should still be just the one bookmark.
1014 EXPECT_EQ(1, model_
->other_node()->child_count());
1018 // Simulate a server change record containing an invalid URL (per GURL).
1019 // TODO(ncarter): Disabled due to crashes. Fix bug 1677563.
1020 TEST_F(ProfileSyncServiceBookmarkTest
, DISABLED_ServerChangeWithInvalidURL
) {
1021 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, SAVE_TO_STORAGE
);
1024 int child_count
= 0;
1026 syncer::WriteTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
1028 FakeServerChange
adds(&trans
);
1029 std::string
url("x");
1030 EXPECT_FALSE(GURL(url
).is_valid());
1031 adds
.AddURL(L
"u1", url
, other_bookmarks_id(), 0);
1033 adds
.ApplyPendingChanges(change_processor_
.get());
1035 // We're lenient about what should happen -- the model could wind up with
1036 // the node or without it; but things should be consistent, and we
1038 child_count
= model_
->other_node()->child_count();
1039 EXPECT_TRUE(child_count
== 0 || child_count
== 1);
1040 ExpectModelMatch(&trans
);
1043 // Now reboot the sync service, forcing a merge step.
1045 LoadBookmarkModel(LOAD_FROM_STORAGE
, SAVE_TO_STORAGE
);
1048 // Things ought not to have changed.
1049 EXPECT_EQ(model_
->other_node()->child_count(), child_count
);
1054 // Test strings that might pose a problem if the titles ever became used as
1055 // file names in the sync backend.
1056 TEST_F(ProfileSyncServiceBookmarkTest
, CornerCaseNames
) {
1057 // TODO(ncarter): Bug 1570238 explains the failure of this test.
1058 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1061 const char* names
[] = {
1062 // The empty string.
1064 // Illegal Windows filenames.
1065 "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4",
1066 "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3",
1067 "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
1068 // Current/parent directory markers.
1070 // Files created automatically by the Windows shell.
1071 "Thumbs.db", ".DS_Store",
1072 // Names including Win32-illegal characters, and path separators.
1073 "foo/bar", "foo\\bar", "foo?bar", "foo:bar", "foo|bar", "foo\"bar",
1074 "foo'bar", "foo<bar", "foo>bar", "foo%bar", "foo*bar", "foo]bar",
1077 // Create both folders and bookmarks using each name.
1078 GURL
url("http://www.doublemint.com");
1079 for (size_t i
= 0; i
< arraysize(names
); ++i
) {
1080 model_
->AddFolder(model_
->other_node(), 0, base::ASCIIToUTF16(names
[i
]));
1081 model_
->AddURL(model_
->other_node(), 0, base::ASCIIToUTF16(names
[i
]), url
);
1084 // Verify that the browser model matches the sync model.
1085 EXPECT_TRUE(model_
->other_node()->child_count() == 2*arraysize(names
));
1089 // Stress the internal representation of position by sparse numbers. We want
1090 // to repeatedly bisect the range of available positions, to force the
1091 // syncer code to renumber its ranges. Pick a number big enough so that it
1092 // would exhaust 32bits of room between items a couple of times.
1093 TEST_F(ProfileSyncServiceBookmarkTest
, RepeatedMiddleInsertion
) {
1094 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1097 static const int kTimesToInsert
= 256;
1099 // Create two book-end nodes to insert between.
1100 model_
->AddFolder(model_
->other_node(), 0, base::ASCIIToUTF16("Alpha"));
1101 model_
->AddFolder(model_
->other_node(), 1, base::ASCIIToUTF16("Omega"));
1104 // Test insertion in first half of range by repeatedly inserting in second
1106 for (int i
= 0; i
< kTimesToInsert
; ++i
) {
1107 base::string16 title
=
1108 base::ASCIIToUTF16("Pre-insertion ") + base::IntToString16(i
);
1109 model_
->AddFolder(model_
->other_node(), 1, title
);
1113 // Test insertion in second half of range by repeatedly inserting in
1114 // second-to-last position.
1115 for (int i
= 0; i
< kTimesToInsert
; ++i
) {
1116 base::string16 title
=
1117 base::ASCIIToUTF16("Post-insertion ") + base::IntToString16(i
);
1118 model_
->AddFolder(model_
->other_node(), count
- 1, title
);
1122 // Verify that the browser model matches the sync model.
1123 EXPECT_EQ(model_
->other_node()->child_count(), count
);
1127 // Introduce a consistency violation into the model, and see that it
1128 // puts itself into a lame, error state.
1129 TEST_F(ProfileSyncServiceBookmarkTest
, UnrecoverableErrorSuspendsService
) {
1130 EXPECT_CALL(mock_error_handler_
,
1131 OnSingleDatatypeUnrecoverableError(_
, _
));
1133 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1136 // Add a node which will be the target of the consistency violation.
1137 const BookmarkNode
* node
=
1138 model_
->AddFolder(model_
->other_node(), 0, base::ASCIIToUTF16("node"));
1139 ExpectSyncerNodeMatching(node
);
1141 // Now destroy the syncer node as if we were the ProfileSyncService without
1142 // updating the ProfileSyncService state. This should introduce
1143 // inconsistency between the two models.
1145 syncer::WriteTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
1146 syncer::WriteNode
sync_node(&trans
);
1147 ASSERT_TRUE(InitSyncNodeFromChromeNode(node
, &sync_node
));
1148 sync_node
.Tombstone();
1150 // The models don't match at this point, but the ProfileSyncService
1151 // doesn't know it yet.
1152 ExpectSyncerNodeKnown(node
);
1154 // Add a child to the inconsistent node. This should cause detection of the
1155 // problem and the syncer should stop processing changes.
1156 model_
->AddFolder(node
, 0, base::ASCIIToUTF16("nested"));
1159 // See what happens if we run model association when there are two exact URL
1160 // duplicate bookmarks. The BookmarkModelAssociator should not fall over when
1162 TEST_F(ProfileSyncServiceBookmarkTest
, MergeDuplicates
) {
1163 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, SAVE_TO_STORAGE
);
1166 model_
->AddURL(model_
->other_node(), 0, base::ASCIIToUTF16("Dup"),
1167 GURL("http://dup.com/"));
1168 model_
->AddURL(model_
->other_node(), 0, base::ASCIIToUTF16("Dup"),
1169 GURL("http://dup.com/"));
1171 EXPECT_EQ(2, model_
->other_node()->child_count());
1173 // Restart the sync service to trigger model association.
1177 EXPECT_EQ(2, model_
->other_node()->child_count());
1181 TEST_F(ProfileSyncServiceBookmarkTest
, ApplySyncDeletesFromJournal
) {
1182 // Initialize sync model and bookmark model as:
1188 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, SAVE_TO_STORAGE
);
1195 int fixed_sync_bk_count
= GetSyncBookmarkCount();
1197 syncer::WriteTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
1198 FakeServerChange
adds(&trans
);
1199 u0
= adds
.AddURL(L
"URL 0", "http://plus.google.com/", bookmark_bar_id(), 0);
1200 f1
= adds
.AddFolder(L
"Folder 1", bookmark_bar_id(), u0
);
1201 u1
= adds
.AddURL(L
"URL 1", "http://www.google.com/", f1
, 0);
1202 f2
= adds
.AddFolder(L
"Folder 2", f1
, u1
);
1203 u2
= adds
.AddURL(L
"URL 2", "http://mail.google.com/", f2
, 0);
1204 adds
.ApplyPendingChanges(change_processor_
.get());
1208 // Reload bookmark model and disable model saving to make sync changes not
1210 LoadBookmarkModel(LOAD_FROM_STORAGE
, DONT_SAVE_TO_STORAGE
);
1211 EXPECT_EQ(6, model_
->bookmark_bar_node()->GetTotalNodeCount());
1212 EXPECT_EQ(fixed_sync_bk_count
+ 5, GetSyncBookmarkCount());
1215 // Remove all folders/bookmarks except u3 added above.
1216 syncer::WriteTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
1217 FakeServerChange
dels(&trans
);
1222 dels
.ApplyPendingChanges(change_processor_
.get());
1225 // Bookmark bar itself and u0 remain.
1226 EXPECT_EQ(2, model_
->bookmark_bar_node()->GetTotalNodeCount());
1228 // Reload bookmarks including ones deleted in sync model from storage.
1229 LoadBookmarkModel(LOAD_FROM_STORAGE
, DONT_SAVE_TO_STORAGE
);
1230 EXPECT_EQ(6, model_
->bookmark_bar_node()->GetTotalNodeCount());
1231 // Add a bookmark under f1 when sync is off so that f1 will not be
1232 // deleted even when f1 matches delete journal because it's not empty.
1233 model_
->AddURL(model_
->bookmark_bar_node()->GetChild(1),
1234 0, base::UTF8ToUTF16("local"), GURL("http://www.youtube.com"));
1235 // Sync model has fixed bookmarks nodes and u3.
1236 EXPECT_EQ(fixed_sync_bk_count
+ 1, GetSyncBookmarkCount());
1238 // Expect 4 bookmarks after model association because u2, f2, u1 are removed
1239 // by delete journal, f1 is not removed by delete journal because it's
1240 // not empty due to www.youtube.com added above.
1241 EXPECT_EQ(4, model_
->bookmark_bar_node()->GetTotalNodeCount());
1242 EXPECT_EQ(base::UTF8ToUTF16("URL 0"),
1243 model_
->bookmark_bar_node()->GetChild(0)->GetTitle());
1244 EXPECT_EQ(base::UTF8ToUTF16("Folder 1"),
1245 model_
->bookmark_bar_node()->GetChild(1)->GetTitle());
1246 EXPECT_EQ(base::UTF8ToUTF16("local"),
1247 model_
->bookmark_bar_node()->GetChild(1)->GetChild(0)->GetTitle());
1250 // Verify purging of delete journals.
1251 // Delete journals for u2, f2, u1 remains because they are used in last
1253 EXPECT_EQ(3u, test_user_share_
.GetDeleteJournalSize());
1256 // Reload again and all delete journals should be gone because none is used
1257 // in last association.
1258 ASSERT_TRUE(test_user_share_
.Reload());
1259 EXPECT_EQ(0u, test_user_share_
.GetDeleteJournalSize());
1263 const wchar_t* title
;
1267 // Map from bookmark node ID to its version.
1268 typedef std::map
<int64
, int64
> BookmarkNodeVersionMap
;
1270 // TODO(ncarter): Integrate the existing TestNode/PopulateNodeFromString code
1271 // in the bookmark model unittest, to make it simpler to set up test data
1272 // here (and reduce the amount of duplication among tests), and to reduce the
1274 class ProfileSyncServiceBookmarkTestWithData
1275 : public ProfileSyncServiceBookmarkTest
{
1277 ProfileSyncServiceBookmarkTestWithData();
1280 // Populates or compares children of the given bookmark node from/with the
1281 // given test data array with the given size. |running_count| is updated as
1282 // urls are added. It is used to set the creation date (or test the creation
1283 // date for CompareWithTestData()).
1284 void PopulateFromTestData(const BookmarkNode
* node
,
1285 const TestData
* data
,
1287 int* running_count
);
1288 void CompareWithTestData(const BookmarkNode
* node
,
1289 const TestData
* data
,
1291 int* running_count
);
1293 void ExpectBookmarkModelMatchesTestData();
1294 void WriteTestDataToBookmarkModel();
1296 // Verify transaction versions of bookmark nodes and sync nodes are equal
1297 // recursively. If node is in |version_expected|, versions should match
1299 void ExpectTransactionVersionMatch(
1300 const BookmarkNode
* node
,
1301 const BookmarkNodeVersionMap
& version_expected
);
1304 const base::Time start_time_
;
1306 DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceBookmarkTestWithData
);
1311 // Constants for bookmark model that looks like:
1313 // | |-- u2, http://www.u2.com/
1315 // | | |-- f1u4, http://www.f1u4.com/
1316 // | | |-- f1u2, http://www.f1u2.com/
1317 // | | |-- f1u3, http://www.f1u3.com/
1318 // | | +-- f1u1, http://www.f1u1.com/
1319 // | |-- u1, http://www.u1.com/
1321 // | |-- f2u2, http://www.f2u2.com/
1322 // | |-- f2u4, http://www.f2u4.com/
1323 // | |-- f2u3, http://www.f2u3.com/
1324 // | +-- f2u1, http://www.f2u1.com/
1325 // +-- Other bookmarks
1327 // | | |-- f3u4, http://www.f3u4.com/
1328 // | | |-- f3u2, http://www.f3u2.com/
1329 // | | |-- f3u3, http://www.f3u3.com/
1330 // | | +-- f3u1, http://www.f3u1.com/
1331 // | |-- u4, http://www.u4.com/
1332 // | |-- u3, http://www.u3.com/
1334 // | | |-- f4u1, http://www.f4u1.com/
1335 // | | |-- f4u2, http://www.f4u2.com/
1336 // | | |-- f4u3, http://www.f4u3.com/
1337 // | | +-- f4u4, http://www.f4u4.com/
1339 // | | +-- dupu1, http://www.dupu1.com/
1341 // | | +-- dupu2, http://www.dupu1.com/
1342 // | +-- ls , http://www.ls.com/
1344 // +-- Mobile bookmarks
1346 // | |-- f5u1, http://www.f5u1.com/
1348 // | |-- f6u1, http://www.f6u1.com/
1349 // | |-- f6u2, http://www.f6u2.com/
1350 // +-- u5, http://www.u5.com/
1352 static TestData kBookmarkBarChildren
[] = {
1353 { L
"u2", "http://www.u2.com/" },
1355 { L
"u1", "http://www.u1.com/" },
1358 static TestData kF1Children
[] = {
1359 { L
"f1u4", "http://www.f1u4.com/" },
1360 { L
"f1u2", "http://www.f1u2.com/" },
1361 { L
"f1u3", "http://www.f1u3.com/" },
1362 { L
"f1u1", "http://www.f1u1.com/" },
1364 static TestData kF2Children
[] = {
1365 { L
"f2u2", "http://www.f2u2.com/" },
1366 { L
"f2u4", "http://www.f2u4.com/" },
1367 { L
"f2u3", "http://www.f2u3.com/" },
1368 { L
"f2u1", "http://www.f2u1.com/" },
1371 static TestData kOtherBookmarkChildren
[] = {
1373 { L
"u4", "http://www.u4.com/" },
1374 { L
"u3", "http://www.u3.com/" },
1378 { L
" ls ", "http://www.ls.com/" }
1380 static TestData kF3Children
[] = {
1381 { L
"f3u4", "http://www.f3u4.com/" },
1382 { L
"f3u2", "http://www.f3u2.com/" },
1383 { L
"f3u3", "http://www.f3u3.com/" },
1384 { L
"f3u1", "http://www.f3u1.com/" },
1386 static TestData kF4Children
[] = {
1387 { L
"f4u1", "http://www.f4u1.com/" },
1388 { L
"f4u2", "http://www.f4u2.com/" },
1389 { L
"f4u3", "http://www.f4u3.com/" },
1390 { L
"f4u4", "http://www.f4u4.com/" },
1392 static TestData kDup1Children
[] = {
1393 { L
"dupu1", "http://www.dupu1.com/" },
1395 static TestData kDup2Children
[] = {
1396 { L
"dupu2", "http://www.dupu2.com/" },
1399 static TestData kMobileBookmarkChildren
[] = {
1402 { L
"u5", "http://www.u5.com/" },
1404 static TestData kF5Children
[] = {
1405 { L
"f5u1", "http://www.f5u1.com/" },
1406 { L
"f5u2", "http://www.f5u2.com/" },
1408 static TestData kF6Children
[] = {
1409 { L
"f6u1", "http://www.f6u1.com/" },
1410 { L
"f6u2", "http://www.f6u2.com/" },
1413 } // anonymous namespace.
1415 ProfileSyncServiceBookmarkTestWithData::
1416 ProfileSyncServiceBookmarkTestWithData()
1417 : start_time_(base::Time::Now()) {
1420 void ProfileSyncServiceBookmarkTestWithData::PopulateFromTestData(
1421 const BookmarkNode
* node
,
1422 const TestData
* data
,
1424 int* running_count
) {
1427 DCHECK(node
->is_folder());
1428 for (int i
= 0; i
< size
; ++i
) {
1429 const TestData
& item
= data
[i
];
1431 const base::Time add_time
=
1432 start_time_
+ base::TimeDelta::FromMinutes(*running_count
);
1433 model_
->AddURLWithCreationTime(node
, i
, base::WideToUTF16Hack(item
.title
),
1434 GURL(item
.url
), add_time
);
1436 model_
->AddFolder(node
, i
, base::WideToUTF16Hack(item
.title
));
1442 void ProfileSyncServiceBookmarkTestWithData::CompareWithTestData(
1443 const BookmarkNode
* node
,
1444 const TestData
* data
,
1446 int* running_count
) {
1449 DCHECK(node
->is_folder());
1450 ASSERT_EQ(size
, node
->child_count());
1451 for (int i
= 0; i
< size
; ++i
) {
1452 const BookmarkNode
* child_node
= node
->GetChild(i
);
1453 const TestData
& item
= data
[i
];
1454 GURL url
= GURL(item
.url
== NULL
? "" : item
.url
);
1455 BookmarkNode
test_node(url
);
1456 test_node
.SetTitle(base::WideToUTF16Hack(item
.title
));
1457 EXPECT_EQ(child_node
->GetTitle(), test_node
.GetTitle());
1459 EXPECT_FALSE(child_node
->is_folder());
1460 EXPECT_TRUE(child_node
->is_url());
1461 EXPECT_EQ(child_node
->url(), test_node
.url());
1462 const base::Time expected_time
=
1463 start_time_
+ base::TimeDelta::FromMinutes(*running_count
);
1464 EXPECT_EQ(expected_time
.ToInternalValue(),
1465 child_node
->date_added().ToInternalValue());
1467 EXPECT_TRUE(child_node
->is_folder());
1468 EXPECT_FALSE(child_node
->is_url());
1474 // TODO(munjal): We should implement some way of generating random data and can
1475 // use the same seed to generate the same sequence.
1476 void ProfileSyncServiceBookmarkTestWithData::WriteTestDataToBookmarkModel() {
1477 const BookmarkNode
* bookmarks_bar_node
= model_
->bookmark_bar_node();
1479 PopulateFromTestData(bookmarks_bar_node
,
1480 kBookmarkBarChildren
,
1481 arraysize(kBookmarkBarChildren
),
1484 ASSERT_GE(bookmarks_bar_node
->child_count(), 4);
1485 const BookmarkNode
* f1_node
= bookmarks_bar_node
->GetChild(1);
1486 PopulateFromTestData(f1_node
, kF1Children
, arraysize(kF1Children
), &count
);
1487 const BookmarkNode
* f2_node
= bookmarks_bar_node
->GetChild(3);
1488 PopulateFromTestData(f2_node
, kF2Children
, arraysize(kF2Children
), &count
);
1490 const BookmarkNode
* other_bookmarks_node
= model_
->other_node();
1491 PopulateFromTestData(other_bookmarks_node
,
1492 kOtherBookmarkChildren
,
1493 arraysize(kOtherBookmarkChildren
),
1496 ASSERT_GE(other_bookmarks_node
->child_count(), 6);
1497 const BookmarkNode
* f3_node
= other_bookmarks_node
->GetChild(0);
1498 PopulateFromTestData(f3_node
, kF3Children
, arraysize(kF3Children
), &count
);
1499 const BookmarkNode
* f4_node
= other_bookmarks_node
->GetChild(3);
1500 PopulateFromTestData(f4_node
, kF4Children
, arraysize(kF4Children
), &count
);
1501 const BookmarkNode
* dup_node
= other_bookmarks_node
->GetChild(4);
1502 PopulateFromTestData(dup_node
, kDup1Children
, arraysize(kDup1Children
),
1504 dup_node
= other_bookmarks_node
->GetChild(5);
1505 PopulateFromTestData(dup_node
, kDup2Children
, arraysize(kDup2Children
),
1508 const BookmarkNode
* mobile_bookmarks_node
= model_
->mobile_node();
1509 PopulateFromTestData(mobile_bookmarks_node
,
1510 kMobileBookmarkChildren
,
1511 arraysize(kMobileBookmarkChildren
),
1514 ASSERT_GE(mobile_bookmarks_node
->child_count(), 3);
1515 const BookmarkNode
* f5_node
= mobile_bookmarks_node
->GetChild(0);
1516 PopulateFromTestData(f5_node
, kF5Children
, arraysize(kF5Children
), &count
);
1517 const BookmarkNode
* f6_node
= mobile_bookmarks_node
->GetChild(1);
1518 PopulateFromTestData(f6_node
, kF6Children
, arraysize(kF6Children
), &count
);
1520 ExpectBookmarkModelMatchesTestData();
1523 void ProfileSyncServiceBookmarkTestWithData::
1524 ExpectBookmarkModelMatchesTestData() {
1525 const BookmarkNode
* bookmark_bar_node
= model_
->bookmark_bar_node();
1527 CompareWithTestData(bookmark_bar_node
,
1528 kBookmarkBarChildren
,
1529 arraysize(kBookmarkBarChildren
),
1532 ASSERT_GE(bookmark_bar_node
->child_count(), 4);
1533 const BookmarkNode
* f1_node
= bookmark_bar_node
->GetChild(1);
1534 CompareWithTestData(f1_node
, kF1Children
, arraysize(kF1Children
), &count
);
1535 const BookmarkNode
* f2_node
= bookmark_bar_node
->GetChild(3);
1536 CompareWithTestData(f2_node
, kF2Children
, arraysize(kF2Children
), &count
);
1538 const BookmarkNode
* other_bookmarks_node
= model_
->other_node();
1539 CompareWithTestData(other_bookmarks_node
,
1540 kOtherBookmarkChildren
,
1541 arraysize(kOtherBookmarkChildren
),
1544 ASSERT_GE(other_bookmarks_node
->child_count(), 6);
1545 const BookmarkNode
* f3_node
= other_bookmarks_node
->GetChild(0);
1546 CompareWithTestData(f3_node
, kF3Children
, arraysize(kF3Children
), &count
);
1547 const BookmarkNode
* f4_node
= other_bookmarks_node
->GetChild(3);
1548 CompareWithTestData(f4_node
, kF4Children
, arraysize(kF4Children
), &count
);
1549 const BookmarkNode
* dup_node
= other_bookmarks_node
->GetChild(4);
1550 CompareWithTestData(dup_node
, kDup1Children
, arraysize(kDup1Children
),
1552 dup_node
= other_bookmarks_node
->GetChild(5);
1553 CompareWithTestData(dup_node
, kDup2Children
, arraysize(kDup2Children
),
1556 const BookmarkNode
* mobile_bookmarks_node
= model_
->mobile_node();
1557 CompareWithTestData(mobile_bookmarks_node
,
1558 kMobileBookmarkChildren
,
1559 arraysize(kMobileBookmarkChildren
),
1562 ASSERT_GE(mobile_bookmarks_node
->child_count(), 3);
1563 const BookmarkNode
* f5_node
= mobile_bookmarks_node
->GetChild(0);
1564 CompareWithTestData(f5_node
, kF5Children
, arraysize(kF5Children
), &count
);
1565 const BookmarkNode
* f6_node
= mobile_bookmarks_node
->GetChild(1);
1566 CompareWithTestData(f6_node
, kF6Children
, arraysize(kF6Children
), &count
);
1569 // Tests persistence of the profile sync service by unloading the
1570 // database and then reloading it from disk.
1571 TEST_F(ProfileSyncServiceBookmarkTestWithData
, Persistence
) {
1572 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, SAVE_TO_STORAGE
);
1575 WriteTestDataToBookmarkModel();
1579 // Force both models to discard their data and reload from disk. This
1580 // simulates what would happen if the browser were to shutdown normally,
1581 // and then relaunch.
1583 UnloadBookmarkModel();
1584 LoadBookmarkModel(LOAD_FROM_STORAGE
, SAVE_TO_STORAGE
);
1587 ExpectBookmarkModelMatchesTestData();
1589 // With the BookmarkModel contents verified, ExpectModelMatch will
1590 // verify the contents of the sync model.
1594 // Tests the merge case when the BookmarkModel is non-empty but the
1595 // sync model is empty. This corresponds to uploading browser
1596 // bookmarks to an initially empty, new account.
1597 TEST_F(ProfileSyncServiceBookmarkTestWithData
, MergeWithEmptySyncModel
) {
1598 // Don't start the sync service until we've populated the bookmark model.
1599 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, SAVE_TO_STORAGE
);
1601 WriteTestDataToBookmarkModel();
1603 // Restart sync. This should trigger a merge step during
1604 // initialization -- we expect the browser bookmarks to be written
1605 // to the sync service during this call.
1608 // Verify that the bookmark model hasn't changed, and that the sync model
1609 // matches it exactly.
1610 ExpectBookmarkModelMatchesTestData();
1614 // Tests the merge case when the BookmarkModel is empty but the sync model is
1615 // non-empty. This corresponds (somewhat) to a clean install of the browser,
1616 // with no bookmarks, connecting to a sync account that has some bookmarks.
1617 TEST_F(ProfileSyncServiceBookmarkTestWithData
, MergeWithEmptyBookmarkModel
) {
1618 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1621 WriteTestDataToBookmarkModel();
1625 // Force the databse to unload and write itself to disk.
1628 // Blow away the bookmark model -- it should be empty afterwards.
1629 UnloadBookmarkModel();
1630 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1631 EXPECT_EQ(model_
->bookmark_bar_node()->child_count(), 0);
1632 EXPECT_EQ(model_
->other_node()->child_count(), 0);
1633 EXPECT_EQ(model_
->mobile_node()->child_count(), 0);
1635 // Now restart the sync service. Starting it should populate the bookmark
1636 // model -- test for consistency.
1638 ExpectBookmarkModelMatchesTestData();
1642 // Tests the merge cases when both the models are expected to be identical
1644 TEST_F(ProfileSyncServiceBookmarkTestWithData
, MergeExpectedIdenticalModels
) {
1645 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, SAVE_TO_STORAGE
);
1647 WriteTestDataToBookmarkModel();
1650 UnloadBookmarkModel();
1652 // At this point both the bookmark model and the server should have the
1653 // exact same data and it should match the test data.
1654 LoadBookmarkModel(LOAD_FROM_STORAGE
, DONT_SAVE_TO_STORAGE
);
1656 ExpectBookmarkModelMatchesTestData();
1659 UnloadBookmarkModel();
1661 // Now reorder some bookmarks in the bookmark model and then merge. Make
1662 // sure we get the order of the server after merge.
1663 LoadBookmarkModel(LOAD_FROM_STORAGE
, DONT_SAVE_TO_STORAGE
);
1664 ExpectBookmarkModelMatchesTestData();
1665 const BookmarkNode
* bookmark_bar
= model_
->bookmark_bar_node();
1666 ASSERT_TRUE(bookmark_bar
);
1667 ASSERT_GT(bookmark_bar
->child_count(), 1);
1668 model_
->Move(bookmark_bar
->GetChild(0), bookmark_bar
, 1);
1671 ExpectBookmarkModelMatchesTestData();
1674 // Tests the merge cases when both the models are expected to be identical
1676 TEST_F(ProfileSyncServiceBookmarkTestWithData
, MergeModelsWithSomeExtras
) {
1677 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1678 WriteTestDataToBookmarkModel();
1679 ExpectBookmarkModelMatchesTestData();
1681 // Remove some nodes and reorder some nodes.
1682 const BookmarkNode
* bookmark_bar_node
= model_
->bookmark_bar_node();
1683 int remove_index
= 2;
1684 ASSERT_GT(bookmark_bar_node
->child_count(), remove_index
);
1685 const BookmarkNode
* child_node
= bookmark_bar_node
->GetChild(remove_index
);
1686 ASSERT_TRUE(child_node
);
1687 ASSERT_TRUE(child_node
->is_url());
1688 model_
->Remove(bookmark_bar_node
, remove_index
);
1689 ASSERT_GT(bookmark_bar_node
->child_count(), remove_index
);
1690 child_node
= bookmark_bar_node
->GetChild(remove_index
);
1691 ASSERT_TRUE(child_node
);
1692 ASSERT_TRUE(child_node
->is_folder());
1693 model_
->Remove(bookmark_bar_node
, remove_index
);
1695 const BookmarkNode
* other_node
= model_
->other_node();
1696 ASSERT_GE(other_node
->child_count(), 1);
1697 const BookmarkNode
* f3_node
= other_node
->GetChild(0);
1698 ASSERT_TRUE(f3_node
);
1699 ASSERT_TRUE(f3_node
->is_folder());
1701 ASSERT_GT(f3_node
->child_count(), remove_index
);
1702 model_
->Remove(f3_node
, remove_index
);
1703 ASSERT_GT(f3_node
->child_count(), remove_index
);
1704 model_
->Remove(f3_node
, remove_index
);
1710 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1711 WriteTestDataToBookmarkModel();
1712 ExpectBookmarkModelMatchesTestData();
1714 // Remove some nodes and reorder some nodes.
1715 bookmark_bar_node
= model_
->bookmark_bar_node();
1717 ASSERT_GT(bookmark_bar_node
->child_count(), remove_index
);
1718 child_node
= bookmark_bar_node
->GetChild(remove_index
);
1719 ASSERT_TRUE(child_node
);
1720 ASSERT_TRUE(child_node
->is_url());
1721 model_
->Remove(bookmark_bar_node
, remove_index
);
1722 ASSERT_GT(bookmark_bar_node
->child_count(), remove_index
);
1723 child_node
= bookmark_bar_node
->GetChild(remove_index
);
1724 ASSERT_TRUE(child_node
);
1725 ASSERT_TRUE(child_node
->is_folder());
1726 model_
->Remove(bookmark_bar_node
, remove_index
);
1728 ASSERT_GE(bookmark_bar_node
->child_count(), 2);
1729 model_
->Move(bookmark_bar_node
->GetChild(0), bookmark_bar_node
, 1);
1731 other_node
= model_
->other_node();
1732 ASSERT_GE(other_node
->child_count(), 1);
1733 f3_node
= other_node
->GetChild(0);
1734 ASSERT_TRUE(f3_node
);
1735 ASSERT_TRUE(f3_node
->is_folder());
1737 ASSERT_GT(f3_node
->child_count(), remove_index
);
1738 model_
->Remove(f3_node
, remove_index
);
1739 ASSERT_GT(f3_node
->child_count(), remove_index
);
1740 model_
->Remove(f3_node
, remove_index
);
1742 ASSERT_GE(other_node
->child_count(), 4);
1743 model_
->Move(other_node
->GetChild(0), other_node
, 1);
1744 model_
->Move(other_node
->GetChild(2), other_node
, 3);
1749 // After the merge, the model should match the test data.
1750 ExpectBookmarkModelMatchesTestData();
1753 // Tests that when persisted model associations are used, things work fine.
1754 TEST_F(ProfileSyncServiceBookmarkTestWithData
, ModelAssociationPersistence
) {
1755 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1756 WriteTestDataToBookmarkModel();
1759 // Force sync to shut down and write itself to disk.
1761 // Now restart sync. This time it should use the persistent
1767 // Tests that when persisted model associations are used, things work fine.
1768 TEST_F(ProfileSyncServiceBookmarkTestWithData
,
1769 ModelAssociationInvalidPersistence
) {
1770 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1771 WriteTestDataToBookmarkModel();
1774 // Force sync to shut down and write itself to disk.
1776 // Change the bookmark model before restarting sync service to simulate
1777 // the situation where bookmark model is different from sync model and
1778 // make sure model associator correctly rebuilds associations.
1779 const BookmarkNode
* bookmark_bar_node
= model_
->bookmark_bar_node();
1780 model_
->AddURL(bookmark_bar_node
, 0, base::ASCIIToUTF16("xtra"),
1781 GURL("http://www.xtra.com"));
1782 // Now restart sync. This time it will try to use the persistent
1783 // associations and realize that they are invalid and hence will rebuild
1789 TEST_F(ProfileSyncServiceBookmarkTestWithData
, SortChildren
) {
1790 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1793 // Write test data to bookmark model and verify that the models match.
1794 WriteTestDataToBookmarkModel();
1795 const BookmarkNode
* folder_added
= model_
->other_node()->GetChild(0);
1796 ASSERT_TRUE(folder_added
);
1797 ASSERT_TRUE(folder_added
->is_folder());
1801 // Sort the other-bookmarks children and expect that the models match.
1802 model_
->SortChildren(folder_added
);
1806 // See what happens if we enable sync but then delete the "Sync Data"
1808 TEST_F(ProfileSyncServiceBookmarkTestWithData
,
1809 RecoverAfterDeletingSyncDataDirectory
) {
1810 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, SAVE_TO_STORAGE
);
1813 WriteTestDataToBookmarkModel();
1817 // Nuke the sync DB and reload.
1821 // First attempt fails due to a persistence error.
1822 EXPECT_TRUE(CreatePermanentBookmarkNodes());
1823 EXPECT_FALSE(AssociateModels());
1825 // Second attempt succeeds due to the previous error resetting the native
1826 // transaction version.
1827 model_associator_
.reset();
1828 EXPECT_TRUE(CreatePermanentBookmarkNodes());
1829 EXPECT_TRUE(AssociateModels());
1831 // Make sure we're back in sync. In real life, the user would need
1832 // to reauthenticate before this happens, but in the test, authentication
1834 ExpectBookmarkModelMatchesTestData();
1838 // Verify that the bookmark model is updated about whether the
1839 // associator is currently running.
1840 TEST_F(ProfileSyncServiceBookmarkTest
, AssociationState
) {
1841 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1843 ExtensiveChangesBookmarkModelObserver observer
;
1844 model_
->AddObserver(&observer
);
1848 EXPECT_EQ(1, observer
.get_started());
1849 EXPECT_EQ(0, observer
.get_completed_count_at_started());
1850 EXPECT_EQ(1, observer
.get_completed());
1852 model_
->RemoveObserver(&observer
);
1855 // Verify that the creation_time_us changes are applied in the local model at
1856 // association time and update time.
1857 TEST_F(ProfileSyncServiceBookmarkTestWithData
, UpdateDateAdded
) {
1858 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1859 WriteTestDataToBookmarkModel();
1861 // Start and stop sync in order to create bookmark nodes in the sync db.
1865 // Modify the date_added field of a bookmark so it doesn't match with
1867 const BookmarkNode
* bookmark_bar_node
= model_
->bookmark_bar_node();
1868 int remove_index
= 2;
1869 ASSERT_GT(bookmark_bar_node
->child_count(), remove_index
);
1870 const BookmarkNode
* child_node
= bookmark_bar_node
->GetChild(remove_index
);
1871 ASSERT_TRUE(child_node
);
1872 EXPECT_TRUE(child_node
->is_url());
1873 model_
->SetDateAdded(child_node
, base::Time::FromInternalValue(10));
1877 // Everything should be back in sync after model association.
1878 ExpectBookmarkModelMatchesTestData();
1881 // Now trigger a change while syncing. We add a new bookmark, sync it, then
1882 // updates it's creation time.
1883 syncer::WriteTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
1884 FakeServerChange
adds(&trans
);
1885 const std::wstring kTitle
= L
"Some site";
1886 const std::string kUrl
= "http://www.whatwhat.yeah/";
1887 const int kCreationTime
= 30;
1888 int64 id
= adds
.AddURL(kTitle
, kUrl
,
1889 bookmark_bar_id(), 0);
1890 adds
.ApplyPendingChanges(change_processor_
.get());
1891 FakeServerChange
updates(&trans
);
1892 updates
.ModifyCreationTime(id
, kCreationTime
);
1893 updates
.ApplyPendingChanges(change_processor_
.get());
1895 const BookmarkNode
* node
= model_
->bookmark_bar_node()->GetChild(0);
1897 EXPECT_TRUE(node
->is_url());
1898 EXPECT_EQ(base::WideToUTF16Hack(kTitle
), node
->GetTitle());
1899 EXPECT_EQ(kUrl
, node
->url().possibly_invalid_spec());
1900 EXPECT_EQ(node
->date_added(), base::Time::FromInternalValue(30));
1903 // Tests that changes to the sync nodes meta info gets reflected in the local
1905 TEST_F(ProfileSyncServiceBookmarkTestWithData
, UpdateMetaInfoFromSync
) {
1906 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1907 WriteTestDataToBookmarkModel();
1910 // Create bookmark nodes containing meta info.
1911 syncer::WriteTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
1912 FakeServerChange
adds(&trans
);
1913 BookmarkNode::MetaInfoMap folder_meta_info
;
1914 folder_meta_info
["folder"] = "foldervalue";
1915 int64 folder_id
= adds
.AddFolderWithMetaInfo(
1916 L
"folder title", &folder_meta_info
, bookmark_bar_id(), 0);
1917 BookmarkNode::MetaInfoMap node_meta_info
;
1918 node_meta_info
["node"] = "nodevalue";
1919 node_meta_info
["other"] = "othervalue";
1920 int64 id
= adds
.AddURLWithMetaInfo(L
"node title", "http://www.foo.com",
1921 &node_meta_info
, folder_id
, 0);
1922 adds
.ApplyPendingChanges(change_processor_
.get());
1924 // Verify that the nodes are created with the correct meta info.
1925 ASSERT_LT(0, model_
->bookmark_bar_node()->child_count());
1926 const BookmarkNode
* folder_node
= model_
->bookmark_bar_node()->GetChild(0);
1927 ASSERT_TRUE(folder_node
->GetMetaInfoMap());
1928 EXPECT_EQ(folder_meta_info
, *folder_node
->GetMetaInfoMap());
1929 ASSERT_LT(0, folder_node
->child_count());
1930 const BookmarkNode
* node
= folder_node
->GetChild(0);
1931 ASSERT_TRUE(node
->GetMetaInfoMap());
1932 EXPECT_EQ(node_meta_info
, *node
->GetMetaInfoMap());
1934 // Update meta info on nodes on server
1935 FakeServerChange
updates(&trans
);
1936 folder_meta_info
.erase("folder");
1937 updates
.ModifyMetaInfo(folder_id
, folder_meta_info
);
1938 node_meta_info
["node"] = "changednodevalue";
1939 node_meta_info
.erase("other");
1940 node_meta_info
["newkey"] = "newkeyvalue";
1941 updates
.ModifyMetaInfo(id
, node_meta_info
);
1942 updates
.ApplyPendingChanges(change_processor_
.get());
1944 // Confirm that the updated values are reflected in the bookmark nodes.
1945 EXPECT_FALSE(folder_node
->GetMetaInfoMap());
1946 ASSERT_TRUE(node
->GetMetaInfoMap());
1947 EXPECT_EQ(node_meta_info
, *node
->GetMetaInfoMap());
1950 // Tests that changes to the local bookmark nodes meta info gets reflected in
1952 TEST_F(ProfileSyncServiceBookmarkTestWithData
, UpdateMetaInfoFromModel
) {
1953 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
1954 WriteTestDataToBookmarkModel();
1956 ExpectBookmarkModelMatchesTestData();
1958 const BookmarkNode
* folder_node
=
1959 model_
->AddFolder(model_
->bookmark_bar_node(), 0,
1960 base::ASCIIToUTF16("folder title"));
1961 const BookmarkNode
* node
= model_
->AddURL(folder_node
, 0,
1962 base::ASCIIToUTF16("node title"),
1963 GURL("http://www.foo.com"));
1966 // Add some meta info and verify sync model matches the changes.
1967 model_
->SetNodeMetaInfo(folder_node
, "folder", "foldervalue");
1968 model_
->SetNodeMetaInfo(node
, "node", "nodevalue");
1969 model_
->SetNodeMetaInfo(node
, "other", "othervalue");
1972 // Change/delete existing meta info and verify.
1973 model_
->DeleteNodeMetaInfo(folder_node
, "folder");
1974 model_
->SetNodeMetaInfo(node
, "node", "changednodevalue");
1975 model_
->DeleteNodeMetaInfo(node
, "other");
1976 model_
->SetNodeMetaInfo(node
, "newkey", "newkeyvalue");
1980 // Output transaction versions of |node| and nodes under it to |node_versions|.
1981 void GetTransactionVersions(
1982 const BookmarkNode
* root
,
1983 BookmarkNodeVersionMap
* node_versions
) {
1984 node_versions
->clear();
1985 std::queue
<const BookmarkNode
*> nodes
;
1987 while (!nodes
.empty()) {
1988 const BookmarkNode
* n
= nodes
.front();
1991 int64 version
= n
->sync_transaction_version();
1992 EXPECT_NE(BookmarkNode::kInvalidSyncTransactionVersion
, version
);
1994 (*node_versions
)[n
->id()] = version
;
1995 for (int i
= 0; i
< n
->child_count(); ++i
)
1996 nodes
.push(n
->GetChild(i
));
2000 void ProfileSyncServiceBookmarkTestWithData::ExpectTransactionVersionMatch(
2001 const BookmarkNode
* node
,
2002 const BookmarkNodeVersionMap
& version_expected
) {
2003 syncer::ReadTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
2005 BookmarkNodeVersionMap bnodes_versions
;
2006 GetTransactionVersions(node
, &bnodes_versions
);
2007 for (BookmarkNodeVersionMap::const_iterator it
= bnodes_versions
.begin();
2008 it
!= bnodes_versions
.end(); ++it
) {
2009 syncer::ReadNode
sync_node(&trans
);
2010 ASSERT_TRUE(model_associator_
->InitSyncNodeFromChromeId(it
->first
,
2012 EXPECT_EQ(sync_node
.GetEntry()->GetTransactionVersion(), it
->second
);
2013 BookmarkNodeVersionMap::const_iterator expected_ver_it
=
2014 version_expected
.find(it
->first
);
2015 if (expected_ver_it
!= version_expected
.end())
2016 EXPECT_EQ(expected_ver_it
->second
, it
->second
);
2020 // Test transaction versions of model and nodes are incremented after changes
2022 TEST_F(ProfileSyncServiceBookmarkTestWithData
, UpdateTransactionVersion
) {
2023 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
2025 WriteTestDataToBookmarkModel();
2026 base::MessageLoop::current()->RunUntilIdle();
2028 BookmarkNodeVersionMap initial_versions
;
2030 // Verify transaction versions in sync model and bookmark model (saved as
2031 // transaction version of root node) are equal after
2032 // WriteTestDataToBookmarkModel() created bookmarks.
2034 syncer::ReadTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
2035 EXPECT_GT(trans
.GetModelVersion(syncer::BOOKMARKS
), 0);
2036 GetTransactionVersions(model_
->root_node(), &initial_versions
);
2037 EXPECT_EQ(trans
.GetModelVersion(syncer::BOOKMARKS
),
2038 initial_versions
[model_
->root_node()->id()]);
2040 ExpectTransactionVersionMatch(model_
->bookmark_bar_node(),
2041 BookmarkNodeVersionMap());
2042 ExpectTransactionVersionMatch(model_
->other_node(),
2043 BookmarkNodeVersionMap());
2044 ExpectTransactionVersionMatch(model_
->mobile_node(),
2045 BookmarkNodeVersionMap());
2047 // Verify model version is incremented and bookmark node versions remain
2049 const BookmarkNode
* bookmark_bar
= model_
->bookmark_bar_node();
2050 model_
->Remove(bookmark_bar
, 0);
2051 base::MessageLoop::current()->RunUntilIdle();
2052 BookmarkNodeVersionMap new_versions
;
2053 GetTransactionVersions(model_
->root_node(), &new_versions
);
2054 EXPECT_EQ(initial_versions
[model_
->root_node()->id()] + 1,
2055 new_versions
[model_
->root_node()->id()]);
2056 ExpectTransactionVersionMatch(model_
->bookmark_bar_node(), initial_versions
);
2057 ExpectTransactionVersionMatch(model_
->other_node(), initial_versions
);
2058 ExpectTransactionVersionMatch(model_
->mobile_node(), initial_versions
);
2060 // Verify model version and version of changed bookmark are incremented and
2061 // versions of others remain same.
2062 const BookmarkNode
* changed_bookmark
=
2063 model_
->bookmark_bar_node()->GetChild(0);
2064 model_
->SetTitle(changed_bookmark
, base::ASCIIToUTF16("test"));
2065 base::MessageLoop::current()->RunUntilIdle();
2066 GetTransactionVersions(model_
->root_node(), &new_versions
);
2067 EXPECT_EQ(initial_versions
[model_
->root_node()->id()] + 2,
2068 new_versions
[model_
->root_node()->id()]);
2069 EXPECT_LT(initial_versions
[changed_bookmark
->id()],
2070 new_versions
[changed_bookmark
->id()]);
2071 initial_versions
.erase(changed_bookmark
->id());
2072 ExpectTransactionVersionMatch(model_
->bookmark_bar_node(), initial_versions
);
2073 ExpectTransactionVersionMatch(model_
->other_node(), initial_versions
);
2074 ExpectTransactionVersionMatch(model_
->mobile_node(), initial_versions
);
2077 // Test that sync persistence errors are detected and trigger a failed
2079 TEST_F(ProfileSyncServiceBookmarkTestWithData
, PersistenceError
) {
2080 LoadBookmarkModel(DELETE_EXISTING_STORAGE
, DONT_SAVE_TO_STORAGE
);
2082 WriteTestDataToBookmarkModel();
2083 base::MessageLoop::current()->RunUntilIdle();
2085 BookmarkNodeVersionMap initial_versions
;
2087 // Verify transaction versions in sync model and bookmark model (saved as
2088 // transaction version of root node) are equal after
2089 // WriteTestDataToBookmarkModel() created bookmarks.
2091 syncer::ReadTransaction
trans(FROM_HERE
, test_user_share_
.user_share());
2092 EXPECT_GT(trans
.GetModelVersion(syncer::BOOKMARKS
), 0);
2093 GetTransactionVersions(model_
->root_node(), &initial_versions
);
2094 EXPECT_EQ(trans
.GetModelVersion(syncer::BOOKMARKS
),
2095 initial_versions
[model_
->root_node()->id()]);
2097 ExpectTransactionVersionMatch(model_
->bookmark_bar_node(),
2098 BookmarkNodeVersionMap());
2099 ExpectTransactionVersionMatch(model_
->other_node(),
2100 BookmarkNodeVersionMap());
2101 ExpectTransactionVersionMatch(model_
->mobile_node(),
2102 BookmarkNodeVersionMap());
2104 // Now shut down sync and artificially increment the native model's version.
2106 int64 root_version
= initial_versions
[model_
->root_node()->id()];
2107 model_
->SetNodeSyncTransactionVersion(model_
->root_node(), root_version
+ 1);
2109 // Upon association, bookmarks should fail to associate.
2110 EXPECT_FALSE(AssociateModels());
2115 } // namespace browser_sync