Home | History | Annotate | Download | only in sync
      1 // Copyright (c) 2011 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 // TODO(akalin): This file is basically just a unit test for
      6 // BookmarkChangeProcessor.  Write unit tests for
      7 // BookmarkModelAssociator separately.
      8 
      9 #include <stack>
     10 #include <vector>
     11 
     12 #include "base/file_path.h"
     13 #include "base/file_util.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/message_loop.h"
     16 #include "base/string16.h"
     17 #include "base/string_number_conversions.h"
     18 #include "base/string_util.h"
     19 #include "base/utf_string_conversions.h"
     20 #include "chrome/browser/bookmarks/bookmark_model.h"
     21 #include "chrome/browser/sync/abstract_profile_sync_service_test.h"
     22 #include "chrome/browser/sync/engine/syncapi.h"
     23 #include "chrome/browser/sync/glue/bookmark_change_processor.h"
     24 #include "chrome/browser/sync/glue/bookmark_model_associator.h"
     25 #include "chrome/browser/sync/syncable/directory_manager.h"
     26 #include "chrome/test/sync/engine/test_id_factory.h"
     27 #include "chrome/test/sync/engine/test_user_share.h"
     28 #include "chrome/test/testing_profile.h"
     29 #include "content/browser/browser_thread.h"
     30 #include "testing/gmock/include/gmock/gmock.h"
     31 #include "testing/gtest/include/gtest/gtest.h"
     32 
     33 namespace browser_sync {
     34 
     35 namespace {
     36 
     37 using testing::_;
     38 using testing::InvokeWithoutArgs;
     39 using testing::Mock;
     40 using testing::StrictMock;
     41 
     42 class TestBookmarkModelAssociator : public BookmarkModelAssociator {
     43  public:
     44   TestBookmarkModelAssociator(
     45       BookmarkModel* bookmark_model,
     46       sync_api::UserShare* user_share,
     47       UnrecoverableErrorHandler* unrecoverable_error_handler)
     48       : BookmarkModelAssociator(bookmark_model, user_share,
     49                                 unrecoverable_error_handler),
     50         user_share_(user_share) {}
     51 
     52   // TODO(akalin): This logic lazily creates any tagged node that is
     53   // requested.  A better way would be to have utility functions to
     54   // create sync nodes from some bookmark structure and to use that.
     55   virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id) {
     56     std::wstring tag_wide;
     57     if (!UTF8ToWide(tag.c_str(), tag.length(), &tag_wide)) {
     58       NOTREACHED() << "Unable to convert UTF8 to wide for string: " << tag;
     59       return false;
     60     }
     61 
     62     bool root_exists = false;
     63     syncable::ModelType type = model_type();
     64     {
     65       sync_api::WriteTransaction trans(user_share_);
     66       sync_api::ReadNode uber_root(&trans);
     67       uber_root.InitByRootLookup();
     68 
     69       sync_api::ReadNode root(&trans);
     70       root_exists = root.InitByTagLookup(
     71           ProfileSyncServiceTestHelper::GetTagForType(type));
     72     }
     73 
     74     if (!root_exists) {
     75       bool created = ProfileSyncServiceTestHelper::CreateRoot(
     76           type,
     77           user_share_,
     78           &id_factory_);
     79       if (!created)
     80         return false;
     81     }
     82 
     83     sync_api::WriteTransaction trans(user_share_);
     84     sync_api::ReadNode root(&trans);
     85     EXPECT_TRUE(root.InitByTagLookup(
     86         ProfileSyncServiceTestHelper::GetTagForType(type)));
     87 
     88     // First, try to find a node with the title among the root's children.
     89     // This will be the case if we are testing model persistence, and
     90     // are reloading a sync repository created earlier in the test.
     91     int64 last_child_id = sync_api::kInvalidId;
     92     for (int64 id = root.GetFirstChildId(); id != sync_api::kInvalidId; /***/) {
     93       sync_api::ReadNode child(&trans);
     94       child.InitByIdLookup(id);
     95       last_child_id = id;
     96       if (tag_wide == child.GetTitle()) {
     97         *sync_id = id;
     98         return true;
     99       }
    100       id = child.GetSuccessorId();
    101     }
    102 
    103     sync_api::ReadNode predecessor_node(&trans);
    104     sync_api::ReadNode* predecessor = NULL;
    105     if (last_child_id != sync_api::kInvalidId) {
    106       predecessor_node.InitByIdLookup(last_child_id);
    107       predecessor = &predecessor_node;
    108     }
    109     sync_api::WriteNode node(&trans);
    110     // Create new fake tagged nodes at the end of the ordering.
    111     node.InitByCreation(type, root, predecessor);
    112     node.SetIsFolder(true);
    113     node.SetTitle(tag_wide);
    114     node.SetExternalId(0);
    115     *sync_id = node.GetId();
    116     return true;
    117   }
    118 
    119  private:
    120   sync_api::UserShare* user_share_;
    121   browser_sync::TestIdFactory id_factory_;
    122 };
    123 
    124 // FakeServerChange constructs a list of sync_api::ChangeRecords while modifying
    125 // the sync model, and can pass the ChangeRecord list to a
    126 // sync_api::SyncObserver (i.e., the ProfileSyncService) to test the client
    127 // change-application behavior.
    128 // Tests using FakeServerChange should be careful to avoid back-references,
    129 // since FakeServerChange will send the edits in the order specified.
    130 class FakeServerChange {
    131  public:
    132   explicit FakeServerChange(sync_api::WriteTransaction* trans) : trans_(trans) {
    133   }
    134 
    135   // Pretend that the server told the syncer to add a bookmark object.
    136   int64 Add(const std::wstring& title,
    137             const std::string& url,
    138             bool is_folder,
    139             int64 parent_id,
    140             int64 predecessor_id) {
    141     sync_api::ReadNode parent(trans_);
    142     EXPECT_TRUE(parent.InitByIdLookup(parent_id));
    143     sync_api::WriteNode node(trans_);
    144     if (predecessor_id == 0) {
    145       EXPECT_TRUE(node.InitByCreation(syncable::BOOKMARKS, parent, NULL));
    146     } else {
    147       sync_api::ReadNode predecessor(trans_);
    148       EXPECT_TRUE(predecessor.InitByIdLookup(predecessor_id));
    149       EXPECT_EQ(predecessor.GetParentId(), parent.GetId());
    150       EXPECT_TRUE(node.InitByCreation(syncable::BOOKMARKS, parent,
    151                                       &predecessor));
    152     }
    153     EXPECT_EQ(node.GetPredecessorId(), predecessor_id);
    154     EXPECT_EQ(node.GetParentId(), parent_id);
    155     node.SetIsFolder(is_folder);
    156     node.SetTitle(title);
    157     if (!is_folder)
    158       node.SetURL(GURL(url));
    159     sync_api::SyncManager::ChangeRecord record;
    160     record.action = sync_api::SyncManager::ChangeRecord::ACTION_ADD;
    161     record.id = node.GetId();
    162     changes_.push_back(record);
    163     return node.GetId();
    164   }
    165 
    166   // Add a bookmark folder.
    167   int64 AddFolder(const std::wstring& title,
    168                   int64 parent_id,
    169                   int64 predecessor_id) {
    170     return Add(title, std::string(), true, parent_id, predecessor_id);
    171   }
    172 
    173   // Add a bookmark.
    174   int64 AddURL(const std::wstring& title,
    175                const std::string& url,
    176                int64 parent_id,
    177                int64 predecessor_id) {
    178     return Add(title, url, false, parent_id, predecessor_id);
    179   }
    180 
    181   // Pretend that the server told the syncer to delete an object.
    182   void Delete(int64 id) {
    183     {
    184       // Delete the sync node.
    185       sync_api::WriteNode node(trans_);
    186       EXPECT_TRUE(node.InitByIdLookup(id));
    187       EXPECT_FALSE(node.GetFirstChildId());
    188       node.Remove();
    189     }
    190     {
    191       // Verify the deletion.
    192       sync_api::ReadNode node(trans_);
    193       EXPECT_FALSE(node.InitByIdLookup(id));
    194     }
    195 
    196     sync_api::SyncManager::ChangeRecord record;
    197     record.action = sync_api::SyncManager::ChangeRecord::ACTION_DELETE;
    198     record.id = id;
    199     // Deletions are always first in the changelist, but we can't actually do
    200     // WriteNode::Remove() on the node until its children are moved. So, as
    201     // a practical matter, users of FakeServerChange must move or delete
    202     // children before parents.  Thus, we must insert the deletion record
    203     // at the front of the vector.
    204     changes_.insert(changes_.begin(), record);
    205   }
    206 
    207   // Set a new title value, and return the old value.
    208   std::wstring ModifyTitle(int64 id, const std::wstring& new_title) {
    209     sync_api::WriteNode node(trans_);
    210     EXPECT_TRUE(node.InitByIdLookup(id));
    211     std::wstring old_title = node.GetTitle();
    212     node.SetTitle(new_title);
    213     SetModified(id);
    214     return old_title;
    215   }
    216 
    217   // Set a new parent and predecessor value.  Return the old parent id.
    218   // We could return the old predecessor id, but it turns out not to be
    219   // very useful for assertions.
    220   int64 ModifyPosition(int64 id, int64 parent_id, int64 predecessor_id) {
    221     sync_api::ReadNode parent(trans_);
    222     EXPECT_TRUE(parent.InitByIdLookup(parent_id));
    223     sync_api::WriteNode node(trans_);
    224     EXPECT_TRUE(node.InitByIdLookup(id));
    225     int64 old_parent_id = node.GetParentId();
    226     if (predecessor_id == 0) {
    227       EXPECT_TRUE(node.SetPosition(parent, NULL));
    228     } else {
    229       sync_api::ReadNode predecessor(trans_);
    230       EXPECT_TRUE(predecessor.InitByIdLookup(predecessor_id));
    231       EXPECT_EQ(predecessor.GetParentId(), parent.GetId());
    232       EXPECT_TRUE(node.SetPosition(parent, &predecessor));
    233     }
    234     SetModified(id);
    235     return old_parent_id;
    236   }
    237 
    238   // Pass the fake change list to |service|.
    239   void ApplyPendingChanges(ChangeProcessor* processor) {
    240     processor->ApplyChangesFromSyncModel(trans_,
    241         changes_.size() ? &changes_[0] : NULL, changes_.size());
    242   }
    243 
    244   const std::vector<sync_api::SyncManager::ChangeRecord>& changes() {
    245     return changes_;
    246   }
    247 
    248  private:
    249   // Helper function to push an ACTION_UPDATE record onto the back
    250   // of the changelist.
    251   void SetModified(int64 id) {
    252     // Coalesce multi-property edits.
    253     if (!changes_.empty() && changes_.back().id == id &&
    254         changes_.back().action ==
    255         sync_api::SyncManager::ChangeRecord::ACTION_UPDATE)
    256       return;
    257     sync_api::SyncManager::ChangeRecord record;
    258     record.action = sync_api::SyncManager::ChangeRecord::ACTION_UPDATE;
    259     record.id = id;
    260     changes_.push_back(record);
    261   }
    262 
    263   // The transaction on which everything happens.
    264   sync_api::WriteTransaction *trans_;
    265 
    266   // The change list we construct.
    267   std::vector<sync_api::SyncManager::ChangeRecord> changes_;
    268 };
    269 
    270 class MockUnrecoverableErrorHandler : public UnrecoverableErrorHandler {
    271  public:
    272   MOCK_METHOD2(OnUnrecoverableError,
    273                void(const tracked_objects::Location&, const std::string&));
    274 };
    275 
    276 class ProfileSyncServiceBookmarkTest : public testing::Test {
    277  protected:
    278   enum LoadOption { LOAD_FROM_STORAGE, DELETE_EXISTING_STORAGE };
    279   enum SaveOption { SAVE_TO_STORAGE, DONT_SAVE_TO_STORAGE };
    280 
    281   ProfileSyncServiceBookmarkTest()
    282       : ui_thread_(BrowserThread::UI, &message_loop_),
    283         file_thread_(BrowserThread::FILE, &message_loop_),
    284         model_(NULL) {
    285   }
    286 
    287   virtual ~ProfileSyncServiceBookmarkTest() {
    288     StopSync();
    289     UnloadBookmarkModel();
    290   }
    291 
    292   virtual void SetUp() {
    293     test_user_share_.SetUp();
    294   }
    295 
    296   virtual void TearDown() {
    297     test_user_share_.TearDown();
    298   }
    299 
    300   // Load (or re-load) the bookmark model.  |load| controls use of the
    301   // bookmarks file on disk.  |save| controls whether the newly loaded
    302   // bookmark model will write out a bookmark file as it goes.
    303   void LoadBookmarkModel(LoadOption load, SaveOption save) {
    304     bool delete_bookmarks = load == DELETE_EXISTING_STORAGE;
    305     profile_.CreateBookmarkModel(delete_bookmarks);
    306     model_ = profile_.GetBookmarkModel();
    307     // Wait for the bookmarks model to load.
    308     profile_.BlockUntilBookmarkModelLoaded();
    309     // This noticeably speeds up the unit tests that request it.
    310     if (save == DONT_SAVE_TO_STORAGE)
    311       model_->ClearStore();
    312     message_loop_.RunAllPending();
    313   }
    314 
    315   void StartSync() {
    316     // Set up model associator.
    317     model_associator_.reset(new TestBookmarkModelAssociator(
    318         profile_.GetBookmarkModel(),
    319         test_user_share_.user_share(),
    320         &mock_unrecoverable_error_handler_));
    321     EXPECT_TRUE(model_associator_->AssociateModels());
    322     MessageLoop::current()->RunAllPending();
    323 
    324     // Set up change processor.
    325     change_processor_.reset(
    326         new BookmarkChangeProcessor(model_associator_.get(),
    327                                     &mock_unrecoverable_error_handler_));
    328     change_processor_->Start(&profile_, test_user_share_.user_share());
    329   }
    330 
    331   void StopSync() {
    332     change_processor_->Stop();
    333     change_processor_.reset();
    334 
    335     EXPECT_TRUE(model_associator_->DisassociateModels());
    336     model_associator_.reset();
    337 
    338     message_loop_.RunAllPending();
    339 
    340     // TODO(akalin): Actually close the database and flush it to disk
    341     // (and make StartSync reload from disk).  This would require
    342     // refactoring TestUserShare.
    343   }
    344 
    345   void UnloadBookmarkModel() {
    346     profile_.CreateBookmarkModel(false /* delete_bookmarks */);
    347     model_ = NULL;
    348     message_loop_.RunAllPending();
    349   }
    350 
    351   bool InitSyncNodeFromChromeNode(const BookmarkNode* bnode,
    352                                   sync_api::BaseNode* sync_node) {
    353     return model_associator_->InitSyncNodeFromChromeId(bnode->id(),
    354                                                        sync_node);
    355   }
    356 
    357   void ExpectSyncerNodeMatching(sync_api::BaseTransaction* trans,
    358                                 const BookmarkNode* bnode) {
    359     sync_api::ReadNode gnode(trans);
    360     ASSERT_TRUE(InitSyncNodeFromChromeNode(bnode, &gnode));
    361     // Non-root node titles and parents must match.
    362     if (bnode != model_->GetBookmarkBarNode() &&
    363         bnode != model_->other_node()) {
    364       EXPECT_EQ(bnode->GetTitle(), WideToUTF16Hack(gnode.GetTitle()));
    365       EXPECT_EQ(
    366           model_associator_->GetChromeNodeFromSyncId(gnode.GetParentId()),
    367           bnode->parent());
    368     }
    369     EXPECT_EQ(bnode->is_folder(), gnode.GetIsFolder());
    370     if (bnode->is_url())
    371       EXPECT_EQ(bnode->GetURL(), gnode.GetURL());
    372 
    373     // Check for position matches.
    374     int browser_index = bnode->parent()->GetIndexOf(bnode);
    375     if (browser_index == 0) {
    376       EXPECT_EQ(gnode.GetPredecessorId(), 0);
    377     } else {
    378       const BookmarkNode* bprev =
    379           bnode->parent()->GetChild(browser_index - 1);
    380       sync_api::ReadNode gprev(trans);
    381       ASSERT_TRUE(InitSyncNodeFromChromeNode(bprev, &gprev));
    382       EXPECT_EQ(gnode.GetPredecessorId(), gprev.GetId());
    383       EXPECT_EQ(gnode.GetParentId(), gprev.GetParentId());
    384     }
    385     if (browser_index == bnode->parent()->child_count() - 1) {
    386       EXPECT_EQ(gnode.GetSuccessorId(), 0);
    387     } else {
    388       const BookmarkNode* bnext =
    389           bnode->parent()->GetChild(browser_index + 1);
    390       sync_api::ReadNode gnext(trans);
    391       ASSERT_TRUE(InitSyncNodeFromChromeNode(bnext, &gnext));
    392       EXPECT_EQ(gnode.GetSuccessorId(), gnext.GetId());
    393       EXPECT_EQ(gnode.GetParentId(), gnext.GetParentId());
    394     }
    395     if (bnode->child_count()) {
    396       EXPECT_TRUE(gnode.GetFirstChildId());
    397     }
    398   }
    399 
    400   void ExpectSyncerNodeMatching(const BookmarkNode* bnode) {
    401     sync_api::ReadTransaction trans(test_user_share_.user_share());
    402     ExpectSyncerNodeMatching(&trans, bnode);
    403   }
    404 
    405   void ExpectBrowserNodeMatching(sync_api::BaseTransaction* trans,
    406                                  int64 sync_id) {
    407     EXPECT_TRUE(sync_id);
    408     const BookmarkNode* bnode =
    409         model_associator_->GetChromeNodeFromSyncId(sync_id);
    410     ASSERT_TRUE(bnode);
    411     int64 id = model_associator_->GetSyncIdFromChromeId(bnode->id());
    412     EXPECT_EQ(id, sync_id);
    413     ExpectSyncerNodeMatching(trans, bnode);
    414   }
    415 
    416   void ExpectBrowserNodeUnknown(int64 sync_id) {
    417     EXPECT_FALSE(model_associator_->GetChromeNodeFromSyncId(sync_id));
    418   }
    419 
    420   void ExpectBrowserNodeKnown(int64 sync_id) {
    421     EXPECT_TRUE(model_associator_->GetChromeNodeFromSyncId(sync_id));
    422   }
    423 
    424   void ExpectSyncerNodeKnown(const BookmarkNode* node) {
    425     int64 sync_id = model_associator_->GetSyncIdFromChromeId(node->id());
    426     EXPECT_NE(sync_id, sync_api::kInvalidId);
    427   }
    428 
    429   void ExpectSyncerNodeUnknown(const BookmarkNode* node) {
    430     int64 sync_id = model_associator_->GetSyncIdFromChromeId(node->id());
    431     EXPECT_EQ(sync_id, sync_api::kInvalidId);
    432   }
    433 
    434   void ExpectBrowserNodeTitle(int64 sync_id, const std::wstring& title) {
    435     const BookmarkNode* bnode =
    436         model_associator_->GetChromeNodeFromSyncId(sync_id);
    437     ASSERT_TRUE(bnode);
    438     EXPECT_EQ(bnode->GetTitle(), WideToUTF16Hack(title));
    439   }
    440 
    441   void ExpectBrowserNodeURL(int64 sync_id, const std::string& url) {
    442     const BookmarkNode* bnode =
    443         model_associator_->GetChromeNodeFromSyncId(sync_id);
    444     ASSERT_TRUE(bnode);
    445     EXPECT_EQ(GURL(url), bnode->GetURL());
    446   }
    447 
    448   void ExpectBrowserNodeParent(int64 sync_id, int64 parent_sync_id) {
    449     const BookmarkNode* node =
    450         model_associator_->GetChromeNodeFromSyncId(sync_id);
    451     ASSERT_TRUE(node);
    452     const BookmarkNode* parent =
    453         model_associator_->GetChromeNodeFromSyncId(parent_sync_id);
    454     EXPECT_TRUE(parent);
    455     EXPECT_EQ(node->parent(), parent);
    456   }
    457 
    458   void ExpectModelMatch(sync_api::BaseTransaction* trans) {
    459     const BookmarkNode* root = model_->root_node();
    460     EXPECT_EQ(root->GetIndexOf(model_->GetBookmarkBarNode()), 0);
    461     EXPECT_EQ(root->GetIndexOf(model_->other_node()), 1);
    462 
    463     std::stack<int64> stack;
    464     stack.push(bookmark_bar_id());
    465     while (!stack.empty()) {
    466       int64 id = stack.top();
    467       stack.pop();
    468       if (!id) continue;
    469 
    470       ExpectBrowserNodeMatching(trans, id);
    471 
    472       sync_api::ReadNode gnode(trans);
    473       ASSERT_TRUE(gnode.InitByIdLookup(id));
    474       stack.push(gnode.GetFirstChildId());
    475       stack.push(gnode.GetSuccessorId());
    476     }
    477   }
    478 
    479   void ExpectModelMatch() {
    480     sync_api::ReadTransaction trans(test_user_share_.user_share());
    481     ExpectModelMatch(&trans);
    482   }
    483 
    484   int64 other_bookmarks_id() {
    485     return
    486         model_associator_->GetSyncIdFromChromeId(model_->other_node()->id());
    487   }
    488 
    489   int64 bookmark_bar_id() {
    490     return model_associator_->GetSyncIdFromChromeId(
    491         model_->GetBookmarkBarNode()->id());
    492   }
    493 
    494  private:
    495   // Used by both |ui_thread_| and |file_thread_|.
    496   MessageLoop message_loop_;
    497   BrowserThread ui_thread_;
    498   // Needed by |model_|.
    499   BrowserThread file_thread_;
    500 
    501   TestingProfile profile_;
    502   scoped_ptr<TestBookmarkModelAssociator> model_associator_;
    503 
    504  protected:
    505   BookmarkModel* model_;
    506   TestUserShare test_user_share_;
    507   scoped_ptr<BookmarkChangeProcessor> change_processor_;
    508   StrictMock<MockUnrecoverableErrorHandler> mock_unrecoverable_error_handler_;
    509 };
    510 
    511 TEST_F(ProfileSyncServiceBookmarkTest, InitialState) {
    512   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
    513   StartSync();
    514 
    515   EXPECT_TRUE(other_bookmarks_id());
    516   EXPECT_TRUE(bookmark_bar_id());
    517 
    518   ExpectModelMatch();
    519 }
    520 
    521 TEST_F(ProfileSyncServiceBookmarkTest, BookmarkModelOperations) {
    522   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
    523   StartSync();
    524 
    525   // Test addition.
    526   const BookmarkNode* folder =
    527       model_->AddFolder(model_->other_node(), 0, ASCIIToUTF16("foobar"));
    528   ExpectSyncerNodeMatching(folder);
    529   ExpectModelMatch();
    530   const BookmarkNode* folder2 =
    531       model_->AddFolder(folder, 0, ASCIIToUTF16("nested"));
    532   ExpectSyncerNodeMatching(folder2);
    533   ExpectModelMatch();
    534   const BookmarkNode* url1 = model_->AddURL(
    535       folder, 0, ASCIIToUTF16("Internets #1 Pies Site"),
    536       GURL("http://www.easypie.com/"));
    537   ExpectSyncerNodeMatching(url1);
    538   ExpectModelMatch();
    539   const BookmarkNode* url2 = model_->AddURL(
    540       folder, 1, ASCIIToUTF16("Airplanes"), GURL("http://www.easyjet.com/"));
    541   ExpectSyncerNodeMatching(url2);
    542   ExpectModelMatch();
    543 
    544   // Test modification.
    545   model_->SetTitle(url2, ASCIIToUTF16("EasyJet"));
    546   ExpectModelMatch();
    547   model_->Move(url1, folder2, 0);
    548   ExpectModelMatch();
    549   model_->Move(folder2, model_->GetBookmarkBarNode(), 0);
    550   ExpectModelMatch();
    551   model_->SetTitle(folder2, ASCIIToUTF16("Not Nested"));
    552   ExpectModelMatch();
    553   model_->Move(folder, folder2, 0);
    554   ExpectModelMatch();
    555   model_->SetTitle(folder, ASCIIToUTF16("who's nested now?"));
    556   ExpectModelMatch();
    557   model_->Copy(url2, model_->GetBookmarkBarNode(), 0);
    558   ExpectModelMatch();
    559 
    560   // Test deletion.
    561   // Delete a single item.
    562   model_->Remove(url2->parent(), url2->parent()->GetIndexOf(url2));
    563   ExpectModelMatch();
    564   // Delete an item with several children.
    565   model_->Remove(folder2->parent(),
    566                  folder2->parent()->GetIndexOf(folder2));
    567   ExpectModelMatch();
    568 }
    569 
    570 TEST_F(ProfileSyncServiceBookmarkTest, ServerChangeProcessing) {
    571   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
    572   StartSync();
    573 
    574   sync_api::WriteTransaction trans(test_user_share_.user_share());
    575 
    576   FakeServerChange adds(&trans);
    577   int64 f1 = adds.AddFolder(L"Server Folder B", bookmark_bar_id(), 0);
    578   int64 f2 = adds.AddFolder(L"Server Folder A", bookmark_bar_id(), f1);
    579   int64 u1 = adds.AddURL(L"Some old site", "ftp://nifty.andrew.cmu.edu/",
    580                          bookmark_bar_id(), f2);
    581   int64 u2 = adds.AddURL(L"Nifty", "ftp://nifty.andrew.cmu.edu/", f1, 0);
    582   // u3 is a duplicate URL
    583   int64 u3 = adds.AddURL(L"Nifty2", "ftp://nifty.andrew.cmu.edu/", f1, u2);
    584   // u4 is a duplicate title, different URL.
    585   adds.AddURL(L"Some old site", "http://slog.thestranger.com/",
    586               bookmark_bar_id(), u1);
    587   // u5 tests an empty-string title.
    588   std::string javascript_url(
    589       "javascript:(function(){var w=window.open(" \
    590       "'about:blank','gnotesWin','location=0,menubar=0," \
    591       "scrollbars=0,status=0,toolbar=0,width=300," \
    592       "height=300,resizable');});");
    593   adds.AddURL(L"", javascript_url, other_bookmarks_id(), 0);
    594 
    595   std::vector<sync_api::SyncManager::ChangeRecord>::const_iterator it;
    596   // The bookmark model shouldn't yet have seen any of the nodes of |adds|.
    597   for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
    598     ExpectBrowserNodeUnknown(it->id);
    599 
    600   adds.ApplyPendingChanges(change_processor_.get());
    601 
    602   // Make sure the bookmark model received all of the nodes in |adds|.
    603   for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
    604     ExpectBrowserNodeMatching(&trans, it->id);
    605   ExpectModelMatch(&trans);
    606 
    607   // Part two: test modifications.
    608   FakeServerChange mods(&trans);
    609   // Mess with u2, and move it into empty folder f2
    610   // TODO(ncarter): Determine if we allow ModifyURL ops or not.
    611   /* std::wstring u2_old_url = mods.ModifyURL(u2, L"http://www.google.com"); */
    612   std::wstring u2_old_title = mods.ModifyTitle(u2, L"The Google");
    613   int64 u2_old_parent = mods.ModifyPosition(u2, f2, 0);
    614 
    615   // Now move f1 after u2.
    616   std::wstring f1_old_title = mods.ModifyTitle(f1, L"Server Folder C");
    617   int64 f1_old_parent = mods.ModifyPosition(f1, f2, u2);
    618 
    619   // Then add u3 after f1.
    620   int64 u3_old_parent = mods.ModifyPosition(u3, f2, f1);
    621 
    622   // Test that the property changes have not yet taken effect.
    623   ExpectBrowserNodeTitle(u2, u2_old_title);
    624   /* ExpectBrowserNodeURL(u2, u2_old_url); */
    625   ExpectBrowserNodeParent(u2, u2_old_parent);
    626 
    627   ExpectBrowserNodeTitle(f1, f1_old_title);
    628   ExpectBrowserNodeParent(f1, f1_old_parent);
    629 
    630   ExpectBrowserNodeParent(u3, u3_old_parent);
    631 
    632   // Apply the changes.
    633   mods.ApplyPendingChanges(change_processor_.get());
    634 
    635   // Check for successful application.
    636   for (it = mods.changes().begin(); it != mods.changes().end(); ++it)
    637     ExpectBrowserNodeMatching(&trans, it->id);
    638   ExpectModelMatch(&trans);
    639 
    640   // Part 3: Test URL deletion.
    641   FakeServerChange dels(&trans);
    642   dels.Delete(u2);
    643   dels.Delete(u3);
    644 
    645   ExpectBrowserNodeKnown(u2);
    646   ExpectBrowserNodeKnown(u3);
    647 
    648   dels.ApplyPendingChanges(change_processor_.get());
    649 
    650   ExpectBrowserNodeUnknown(u2);
    651   ExpectBrowserNodeUnknown(u3);
    652   ExpectModelMatch(&trans);
    653 }
    654 
    655 // Tests a specific case in ApplyModelChanges where we move the
    656 // children out from under a parent, and then delete the parent
    657 // in the same changelist.  The delete shows up first in the changelist,
    658 // requiring the children to be moved to a temporary location.
    659 TEST_F(ProfileSyncServiceBookmarkTest, ServerChangeRequiringFosterParent) {
    660   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
    661   StartSync();
    662 
    663   sync_api::WriteTransaction trans(test_user_share_.user_share());
    664 
    665   // Stress the immediate children of other_node because that's where
    666   // ApplyModelChanges puts a temporary foster parent node.
    667   std::string url("http://dev.chromium.org/");
    668   FakeServerChange adds(&trans);
    669   int64 f0 = other_bookmarks_id();                 // + other_node
    670   int64 f1 = adds.AddFolder(L"f1",      f0, 0);    //   + f1
    671   int64 f2 = adds.AddFolder(L"f2",      f1, 0);    //     + f2
    672   int64 u3 = adds.AddURL(   L"u3", url, f2, 0);    //       + u3    NOLINT
    673   int64 u4 = adds.AddURL(   L"u4", url, f2, u3);   //       + u4    NOLINT
    674   int64 u5 = adds.AddURL(   L"u5", url, f1, f2);   //     + u5      NOLINT
    675   int64 f6 = adds.AddFolder(L"f6",      f1, u5);   //     + f6
    676   int64 u7 = adds.AddURL(   L"u7", url, f0, f1);   //   + u7        NOLINT
    677 
    678   std::vector<sync_api::SyncManager::ChangeRecord>::const_iterator it;
    679   // The bookmark model shouldn't yet have seen any of the nodes of |adds|.
    680   for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
    681     ExpectBrowserNodeUnknown(it->id);
    682 
    683   adds.ApplyPendingChanges(change_processor_.get());
    684 
    685   // Make sure the bookmark model received all of the nodes in |adds|.
    686   for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
    687     ExpectBrowserNodeMatching(&trans, it->id);
    688   ExpectModelMatch(&trans);
    689 
    690   // We have to do the moves before the deletions, but FakeServerChange will
    691   // put the deletion at the front of the changelist.
    692   FakeServerChange ops(&trans);
    693   ops.ModifyPosition(f6, other_bookmarks_id(), 0);
    694   ops.ModifyPosition(u3, other_bookmarks_id(), f1);  // Prev == f1 is OK here.
    695   ops.ModifyPosition(f2, other_bookmarks_id(), u7);
    696   ops.ModifyPosition(u7, f2, 0);
    697   ops.ModifyPosition(u4, other_bookmarks_id(), f2);
    698   ops.ModifyPosition(u5, f6, 0);
    699   ops.Delete(f1);
    700 
    701   ops.ApplyPendingChanges(change_processor_.get());
    702 
    703   ExpectModelMatch(&trans);
    704 }
    705 
    706 // Simulate a server change record containing a valid but non-canonical URL.
    707 TEST_F(ProfileSyncServiceBookmarkTest, ServerChangeWithNonCanonicalURL) {
    708   LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
    709   StartSync();
    710 
    711   {
    712     sync_api::WriteTransaction trans(test_user_share_.user_share());
    713 
    714     FakeServerChange adds(&trans);
    715     std::string url("http://dev.chromium.org");
    716     EXPECT_NE(GURL(url).spec(), url);
    717     adds.AddURL(L"u1", url, other_bookmarks_id(), 0);
    718 
    719     adds.ApplyPendingChanges(change_processor_.get());
    720 
    721     EXPECT_TRUE(model_->other_node()->child_count() == 1);
    722     ExpectModelMatch(&trans);
    723   }
    724 
    725   // Now reboot the sync service, forcing a merge step.
    726   StopSync();
    727   LoadBookmarkModel(LOAD_FROM_STORAGE, SAVE_TO_STORAGE);
    728   StartSync();
    729 
    730   // There should still be just the one bookmark.
    731   EXPECT_TRUE(model_->other_node()->child_count() == 1);
    732   ExpectModelMatch();
    733 }
    734 
    735 // Simulate a server change record containing an invalid URL (per GURL).
    736 // TODO(ncarter): Disabled due to crashes.  Fix bug 1677563.
    737 TEST_F(ProfileSyncServiceBookmarkTest, DISABLED_ServerChangeWithInvalidURL) {
    738   LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
    739   StartSync();
    740 
    741   int child_count = 0;
    742   {
    743     sync_api::WriteTransaction trans(test_user_share_.user_share());
    744 
    745     FakeServerChange adds(&trans);
    746     std::string url("x");
    747     EXPECT_FALSE(GURL(url).is_valid());
    748     adds.AddURL(L"u1", url, other_bookmarks_id(), 0);
    749 
    750     adds.ApplyPendingChanges(change_processor_.get());
    751 
    752     // We're lenient about what should happen -- the model could wind up with
    753     // the node or without it; but things should be consistent, and we
    754     // shouldn't crash.
    755     child_count = model_->other_node()->child_count();
    756     EXPECT_TRUE(child_count == 0 || child_count == 1);
    757     ExpectModelMatch(&trans);
    758   }
    759 
    760   // Now reboot the sync service, forcing a merge step.
    761   StopSync();
    762   LoadBookmarkModel(LOAD_FROM_STORAGE, SAVE_TO_STORAGE);
    763   StartSync();
    764 
    765   // Things ought not to have changed.
    766   EXPECT_EQ(model_->other_node()->child_count(), child_count);
    767   ExpectModelMatch();
    768 }
    769 
    770 
    771 // Test strings that might pose a problem if the titles ever became used as
    772 // file names in the sync backend.
    773 TEST_F(ProfileSyncServiceBookmarkTest, CornerCaseNames) {
    774   // TODO(ncarter): Bug 1570238 explains the failure of this test.
    775   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
    776   StartSync();
    777 
    778   const char* names[] = {
    779       // The empty string.
    780       "",
    781       // Illegal Windows filenames.
    782       "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4",
    783       "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3",
    784       "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
    785       // Current/parent directory markers.
    786       ".", "..", "...",
    787       // Files created automatically by the Windows shell.
    788       "Thumbs.db", ".DS_Store",
    789       // Names including Win32-illegal characters, and path separators.
    790       "foo/bar", "foo\\bar", "foo?bar", "foo:bar", "foo|bar", "foo\"bar",
    791       "foo'bar", "foo<bar", "foo>bar", "foo%bar", "foo*bar", "foo]bar",
    792       "foo[bar",
    793   };
    794   // Create both folders and bookmarks using each name.
    795   GURL url("http://www.doublemint.com");
    796   for (size_t i = 0; i < arraysize(names); ++i) {
    797     model_->AddFolder(model_->other_node(), 0, ASCIIToUTF16(names[i]));
    798     model_->AddURL(model_->other_node(), 0, ASCIIToUTF16(names[i]), url);
    799   }
    800 
    801   // Verify that the browser model matches the sync model.
    802   EXPECT_TRUE(model_->other_node()->child_count() == 2*arraysize(names));
    803   ExpectModelMatch();
    804 }
    805 
    806 // Stress the internal representation of position by sparse numbers. We want
    807 // to repeatedly bisect the range of available positions, to force the
    808 // syncer code to renumber its ranges.  Pick a number big enough so that it
    809 // would exhaust 32bits of room between items a couple of times.
    810 TEST_F(ProfileSyncServiceBookmarkTest, RepeatedMiddleInsertion) {
    811   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
    812   StartSync();
    813 
    814   static const int kTimesToInsert = 256;
    815 
    816   // Create two book-end nodes to insert between.
    817   model_->AddFolder(model_->other_node(), 0, ASCIIToUTF16("Alpha"));
    818   model_->AddFolder(model_->other_node(), 1, ASCIIToUTF16("Omega"));
    819   int count = 2;
    820 
    821   // Test insertion in first half of range by repeatedly inserting in second
    822   // position.
    823   for (int i = 0; i < kTimesToInsert; ++i) {
    824     string16 title = ASCIIToUTF16("Pre-insertion ") + base::IntToString16(i);
    825     model_->AddFolder(model_->other_node(), 1, title);
    826     count++;
    827   }
    828 
    829   // Test insertion in second half of range by repeatedly inserting in
    830   // second-to-last position.
    831   for (int i = 0; i < kTimesToInsert; ++i) {
    832     string16 title = ASCIIToUTF16("Post-insertion ") + base::IntToString16(i);
    833     model_->AddFolder(model_->other_node(), count - 1, title);
    834     count++;
    835   }
    836 
    837   // Verify that the browser model matches the sync model.
    838   EXPECT_EQ(model_->other_node()->child_count(), count);
    839   ExpectModelMatch();
    840 }
    841 
    842 // Introduce a consistency violation into the model, and see that it
    843 // puts itself into a lame, error state.
    844 TEST_F(ProfileSyncServiceBookmarkTest, UnrecoverableErrorSuspendsService) {
    845   EXPECT_CALL(mock_unrecoverable_error_handler_,
    846               OnUnrecoverableError(_, _));
    847 
    848   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
    849   StartSync();
    850 
    851   // Add a node which will be the target of the consistency violation.
    852   const BookmarkNode* node =
    853       model_->AddFolder(model_->other_node(), 0, ASCIIToUTF16("node"));
    854   ExpectSyncerNodeMatching(node);
    855 
    856   // Now destroy the syncer node as if we were the ProfileSyncService without
    857   // updating the ProfileSyncService state.  This should introduce
    858   // inconsistency between the two models.
    859   {
    860     sync_api::WriteTransaction trans(test_user_share_.user_share());
    861     sync_api::WriteNode sync_node(&trans);
    862     ASSERT_TRUE(InitSyncNodeFromChromeNode(node, &sync_node));
    863     sync_node.Remove();
    864   }
    865   // The models don't match at this point, but the ProfileSyncService
    866   // doesn't know it yet.
    867   ExpectSyncerNodeKnown(node);
    868 
    869   // Add a child to the inconsistent node.  This should cause detection of the
    870   // problem and the syncer should stop processing changes.
    871   model_->AddFolder(node, 0, ASCIIToUTF16("nested"));
    872 }
    873 
    874 // See what happens if we run model association when there are two exact URL
    875 // duplicate bookmarks.  The BookmarkModelAssociator should not fall over when
    876 // this happens.
    877 TEST_F(ProfileSyncServiceBookmarkTest, MergeDuplicates) {
    878   LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
    879   StartSync();
    880 
    881   model_->AddURL(model_->other_node(), 0, ASCIIToUTF16("Dup"),
    882                  GURL("http://dup.com/"));
    883   model_->AddURL(model_->other_node(), 0, ASCIIToUTF16("Dup"),
    884                  GURL("http://dup.com/"));
    885 
    886   EXPECT_EQ(2, model_->other_node()->child_count());
    887 
    888   // Restart the sync service to trigger model association.
    889   StopSync();
    890   StartSync();
    891 
    892   EXPECT_EQ(2, model_->other_node()->child_count());
    893   ExpectModelMatch();
    894 }
    895 
    896 struct TestData {
    897   const wchar_t* title;
    898   const char* url;
    899 };
    900 
    901 // TODO(ncarter): Integrate the existing TestNode/PopulateNodeFromString code
    902 // in the bookmark model unittest, to make it simpler to set up test data
    903 // here (and reduce the amount of duplication among tests), and to reduce the
    904 // duplication.
    905 class ProfileSyncServiceBookmarkTestWithData
    906     : public ProfileSyncServiceBookmarkTest {
    907  protected:
    908   // Populates or compares children of the given bookmark node from/with the
    909   // given test data array with the given size.
    910   void PopulateFromTestData(const BookmarkNode* node,
    911                             const TestData* data,
    912                             int size);
    913   void CompareWithTestData(const BookmarkNode* node,
    914                            const TestData* data,
    915                            int size);
    916 
    917   void ExpectBookmarkModelMatchesTestData();
    918   void WriteTestDataToBookmarkModel();
    919 };
    920 
    921 namespace {
    922 
    923 // Constants for bookmark model that looks like:
    924 // |-- Bookmark bar
    925 // |   |-- u2, http://www.u2.com/
    926 // |   |-- f1
    927 // |   |   |-- f1u4, http://www.f1u4.com/
    928 // |   |   |-- f1u2, http://www.f1u2.com/
    929 // |   |   |-- f1u3, http://www.f1u3.com/
    930 // |   |   +-- f1u1, http://www.f1u1.com/
    931 // |   |-- u1, http://www.u1.com/
    932 // |   +-- f2
    933 // |       |-- f2u2, http://www.f2u2.com/
    934 // |       |-- f2u4, http://www.f2u4.com/
    935 // |       |-- f2u3, http://www.f2u3.com/
    936 // |       +-- f2u1, http://www.f2u1.com/
    937 // +-- Other bookmarks
    938 //     |-- f3
    939 //     |   |-- f3u4, http://www.f3u4.com/
    940 //     |   |-- f3u2, http://www.f3u2.com/
    941 //     |   |-- f3u3, http://www.f3u3.com/
    942 //     |   +-- f3u1, http://www.f3u1.com/
    943 //     |-- u4, http://www.u4.com/
    944 //     |-- u3, http://www.u3.com/
    945 //     --- f4
    946 //     |   |-- f4u1, http://www.f4u1.com/
    947 //     |   |-- f4u2, http://www.f4u2.com/
    948 //     |   |-- f4u3, http://www.f4u3.com/
    949 //     |   +-- f4u4, http://www.f4u4.com/
    950 //     |-- dup
    951 //     |   +-- dupu1, http://www.dupu1.com/
    952 //     +-- dup
    953 //         +-- dupu2, http://www.dupu1.com/
    954 //
    955 static TestData kBookmarkBarChildren[] = {
    956   { L"u2", "http://www.u2.com/" },
    957   { L"f1", NULL },
    958   { L"u1", "http://www.u1.com/" },
    959   { L"f2", NULL },
    960 };
    961 static TestData kF1Children[] = {
    962   { L"f1u4", "http://www.f1u4.com/" },
    963   { L"f1u2", "http://www.f1u2.com/" },
    964   { L"f1u3", "http://www.f1u3.com/" },
    965   { L"f1u1", "http://www.f1u1.com/" },
    966 };
    967 static TestData kF2Children[] = {
    968   { L"f2u2", "http://www.f2u2.com/" },
    969   { L"f2u4", "http://www.f2u4.com/" },
    970   { L"f2u3", "http://www.f2u3.com/" },
    971   { L"f2u1", "http://www.f2u1.com/" },
    972 };
    973 
    974 static TestData kOtherBookmarkChildren[] = {
    975   { L"f3", NULL },
    976   { L"u4", "http://www.u4.com/" },
    977   { L"u3", "http://www.u3.com/" },
    978   { L"f4", NULL },
    979   { L"dup", NULL },
    980   { L"dup", NULL },
    981 };
    982 static TestData kF3Children[] = {
    983   { L"f3u4", "http://www.f3u4.com/" },
    984   { L"f3u2", "http://www.f3u2.com/" },
    985   { L"f3u3", "http://www.f3u3.com/" },
    986   { L"f3u1", "http://www.f3u1.com/" },
    987 };
    988 static TestData kF4Children[] = {
    989   { L"f4u1", "http://www.f4u1.com/" },
    990   { L"f4u2", "http://www.f4u2.com/" },
    991   { L"f4u3", "http://www.f4u3.com/" },
    992   { L"f4u4", "http://www.f4u4.com/" },
    993 };
    994 static TestData kDup1Children[] = {
    995   { L"dupu1", "http://www.dupu1.com/" },
    996 };
    997 static TestData kDup2Children[] = {
    998   { L"dupu2", "http://www.dupu2.com/" },
    999 };
   1000 
   1001 }  // anonymous namespace.
   1002 
   1003 void ProfileSyncServiceBookmarkTestWithData::PopulateFromTestData(
   1004     const BookmarkNode* node, const TestData* data, int size) {
   1005   DCHECK(node);
   1006   DCHECK(data);
   1007   DCHECK(node->is_folder());
   1008   for (int i = 0; i < size; ++i) {
   1009     const TestData& item = data[i];
   1010     if (item.url) {
   1011       model_->AddURL(node, i, WideToUTF16Hack(item.title), GURL(item.url));
   1012     } else {
   1013       model_->AddFolder(node, i, WideToUTF16Hack(item.title));
   1014     }
   1015   }
   1016 }
   1017 
   1018 void ProfileSyncServiceBookmarkTestWithData::CompareWithTestData(
   1019     const BookmarkNode* node, const TestData* data, int size) {
   1020   DCHECK(node);
   1021   DCHECK(data);
   1022   DCHECK(node->is_folder());
   1023   ASSERT_EQ(size, node->child_count());
   1024   for (int i = 0; i < size; ++i) {
   1025     const BookmarkNode* child_node = node->GetChild(i);
   1026     const TestData& item = data[i];
   1027     EXPECT_EQ(child_node->GetTitle(), WideToUTF16Hack(item.title));
   1028     if (item.url) {
   1029       EXPECT_FALSE(child_node->is_folder());
   1030       EXPECT_TRUE(child_node->is_url());
   1031       EXPECT_EQ(child_node->GetURL(), GURL(item.url));
   1032     } else {
   1033       EXPECT_TRUE(child_node->is_folder());
   1034       EXPECT_FALSE(child_node->is_url());
   1035     }
   1036   }
   1037 }
   1038 
   1039 // TODO(munjal): We should implement some way of generating random data and can
   1040 // use the same seed to generate the same sequence.
   1041 void ProfileSyncServiceBookmarkTestWithData::WriteTestDataToBookmarkModel() {
   1042   const BookmarkNode* bookmarks_bar_node = model_->GetBookmarkBarNode();
   1043   PopulateFromTestData(bookmarks_bar_node,
   1044                        kBookmarkBarChildren,
   1045                        arraysize(kBookmarkBarChildren));
   1046 
   1047   ASSERT_GE(bookmarks_bar_node->child_count(), 4);
   1048   const BookmarkNode* f1_node = bookmarks_bar_node->GetChild(1);
   1049   PopulateFromTestData(f1_node, kF1Children, arraysize(kF1Children));
   1050   const BookmarkNode* f2_node = bookmarks_bar_node->GetChild(3);
   1051   PopulateFromTestData(f2_node, kF2Children, arraysize(kF2Children));
   1052 
   1053   const BookmarkNode* other_bookmarks_node = model_->other_node();
   1054   PopulateFromTestData(other_bookmarks_node,
   1055                        kOtherBookmarkChildren,
   1056                        arraysize(kOtherBookmarkChildren));
   1057 
   1058   ASSERT_GE(other_bookmarks_node->child_count(), 6);
   1059   const BookmarkNode* f3_node = other_bookmarks_node->GetChild(0);
   1060   PopulateFromTestData(f3_node, kF3Children, arraysize(kF3Children));
   1061   const BookmarkNode* f4_node = other_bookmarks_node->GetChild(3);
   1062   PopulateFromTestData(f4_node, kF4Children, arraysize(kF4Children));
   1063   const BookmarkNode* dup_node = other_bookmarks_node->GetChild(4);
   1064   PopulateFromTestData(dup_node, kDup1Children, arraysize(kDup1Children));
   1065   dup_node = other_bookmarks_node->GetChild(5);
   1066   PopulateFromTestData(dup_node, kDup2Children, arraysize(kDup2Children));
   1067 
   1068   ExpectBookmarkModelMatchesTestData();
   1069 }
   1070 
   1071 void ProfileSyncServiceBookmarkTestWithData::
   1072     ExpectBookmarkModelMatchesTestData() {
   1073   const BookmarkNode* bookmark_bar_node = model_->GetBookmarkBarNode();
   1074   CompareWithTestData(bookmark_bar_node,
   1075                       kBookmarkBarChildren,
   1076                       arraysize(kBookmarkBarChildren));
   1077 
   1078   ASSERT_GE(bookmark_bar_node->child_count(), 4);
   1079   const BookmarkNode* f1_node = bookmark_bar_node->GetChild(1);
   1080   CompareWithTestData(f1_node, kF1Children, arraysize(kF1Children));
   1081   const BookmarkNode* f2_node = bookmark_bar_node->GetChild(3);
   1082   CompareWithTestData(f2_node, kF2Children, arraysize(kF2Children));
   1083 
   1084   const BookmarkNode* other_bookmarks_node = model_->other_node();
   1085   CompareWithTestData(other_bookmarks_node,
   1086                       kOtherBookmarkChildren,
   1087                       arraysize(kOtherBookmarkChildren));
   1088 
   1089   ASSERT_GE(other_bookmarks_node->child_count(), 6);
   1090   const BookmarkNode* f3_node = other_bookmarks_node->GetChild(0);
   1091   CompareWithTestData(f3_node, kF3Children, arraysize(kF3Children));
   1092   const BookmarkNode* f4_node = other_bookmarks_node->GetChild(3);
   1093   CompareWithTestData(f4_node, kF4Children, arraysize(kF4Children));
   1094   const BookmarkNode* dup_node = other_bookmarks_node->GetChild(4);
   1095   CompareWithTestData(dup_node, kDup1Children, arraysize(kDup1Children));
   1096   dup_node = other_bookmarks_node->GetChild(5);
   1097   CompareWithTestData(dup_node, kDup2Children, arraysize(kDup2Children));
   1098 }
   1099 
   1100 // Tests persistence of the profile sync service by unloading the
   1101 // database and then reloading it from disk.
   1102 TEST_F(ProfileSyncServiceBookmarkTestWithData, Persistence) {
   1103   LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
   1104   StartSync();
   1105 
   1106   WriteTestDataToBookmarkModel();
   1107 
   1108   ExpectModelMatch();
   1109 
   1110   // Force both models to discard their data and reload from disk.  This
   1111   // simulates what would happen if the browser were to shutdown normally,
   1112   // and then relaunch.
   1113   StopSync();
   1114   UnloadBookmarkModel();
   1115   LoadBookmarkModel(LOAD_FROM_STORAGE, SAVE_TO_STORAGE);
   1116   StartSync();
   1117 
   1118   ExpectBookmarkModelMatchesTestData();
   1119 
   1120   // With the BookmarkModel contents verified, ExpectModelMatch will
   1121   // verify the contents of the sync model.
   1122   ExpectModelMatch();
   1123 }
   1124 
   1125 // Tests the merge case when the BookmarkModel is non-empty but the
   1126 // sync model is empty.  This corresponds to uploading browser
   1127 // bookmarks to an initially empty, new account.
   1128 TEST_F(ProfileSyncServiceBookmarkTestWithData, MergeWithEmptySyncModel) {
   1129   // Don't start the sync service until we've populated the bookmark model.
   1130   LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
   1131 
   1132   WriteTestDataToBookmarkModel();
   1133 
   1134   // Restart sync.  This should trigger a merge step during
   1135   // initialization -- we expect the browser bookmarks to be written
   1136   // to the sync service during this call.
   1137   StartSync();
   1138 
   1139   // Verify that the bookmark model hasn't changed, and that the sync model
   1140   // matches it exactly.
   1141   ExpectBookmarkModelMatchesTestData();
   1142   ExpectModelMatch();
   1143 }
   1144 
   1145 // Tests the merge case when the BookmarkModel is empty but the sync model is
   1146 // non-empty.  This corresponds (somewhat) to a clean install of the browser,
   1147 // with no bookmarks, connecting to a sync account that has some bookmarks.
   1148 TEST_F(ProfileSyncServiceBookmarkTestWithData, MergeWithEmptyBookmarkModel) {
   1149   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
   1150   StartSync();
   1151 
   1152   WriteTestDataToBookmarkModel();
   1153 
   1154   ExpectModelMatch();
   1155 
   1156   // Force the databse to unload and write itself to disk.
   1157   StopSync();
   1158 
   1159   // Blow away the bookmark model -- it should be empty afterwards.
   1160   UnloadBookmarkModel();
   1161   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
   1162   EXPECT_EQ(model_->GetBookmarkBarNode()->child_count(), 0);
   1163   EXPECT_EQ(model_->other_node()->child_count(), 0);
   1164 
   1165   // Now restart the sync service.  Starting it should populate the bookmark
   1166   // model -- test for consistency.
   1167   StartSync();
   1168   ExpectBookmarkModelMatchesTestData();
   1169   ExpectModelMatch();
   1170 }
   1171 
   1172 // Tests the merge cases when both the models are expected to be identical
   1173 // after the merge.
   1174 TEST_F(ProfileSyncServiceBookmarkTestWithData, MergeExpectedIdenticalModels) {
   1175   LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
   1176   StartSync();
   1177   WriteTestDataToBookmarkModel();
   1178   ExpectModelMatch();
   1179   StopSync();
   1180   UnloadBookmarkModel();
   1181 
   1182   // At this point both the bookmark model and the server should have the
   1183   // exact same data and it should match the test data.
   1184   LoadBookmarkModel(LOAD_FROM_STORAGE, DONT_SAVE_TO_STORAGE);
   1185   StartSync();
   1186   ExpectBookmarkModelMatchesTestData();
   1187   ExpectModelMatch();
   1188   StopSync();
   1189   UnloadBookmarkModel();
   1190 
   1191   // Now reorder some bookmarks in the bookmark model and then merge. Make
   1192   // sure we get the order of the server after merge.
   1193   LoadBookmarkModel(LOAD_FROM_STORAGE, DONT_SAVE_TO_STORAGE);
   1194   ExpectBookmarkModelMatchesTestData();
   1195   const BookmarkNode* bookmark_bar = model_->GetBookmarkBarNode();
   1196   ASSERT_TRUE(bookmark_bar);
   1197   ASSERT_GT(bookmark_bar->child_count(), 1);
   1198   model_->Move(bookmark_bar->GetChild(0), bookmark_bar, 1);
   1199   StartSync();
   1200   ExpectModelMatch();
   1201   ExpectBookmarkModelMatchesTestData();
   1202 }
   1203 
   1204 // Tests the merge cases when both the models are expected to be identical
   1205 // after the merge.
   1206 TEST_F(ProfileSyncServiceBookmarkTestWithData, MergeModelsWithSomeExtras) {
   1207   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
   1208   WriteTestDataToBookmarkModel();
   1209   ExpectBookmarkModelMatchesTestData();
   1210 
   1211   // Remove some nodes and reorder some nodes.
   1212   const BookmarkNode* bookmark_bar_node = model_->GetBookmarkBarNode();
   1213   int remove_index = 2;
   1214   ASSERT_GT(bookmark_bar_node->child_count(), remove_index);
   1215   const BookmarkNode* child_node = bookmark_bar_node->GetChild(remove_index);
   1216   ASSERT_TRUE(child_node);
   1217   ASSERT_TRUE(child_node->is_url());
   1218   model_->Remove(bookmark_bar_node, remove_index);
   1219   ASSERT_GT(bookmark_bar_node->child_count(), remove_index);
   1220   child_node = bookmark_bar_node->GetChild(remove_index);
   1221   ASSERT_TRUE(child_node);
   1222   ASSERT_TRUE(child_node->is_folder());
   1223   model_->Remove(bookmark_bar_node, remove_index);
   1224 
   1225   const BookmarkNode* other_node = model_->other_node();
   1226   ASSERT_GE(other_node->child_count(), 1);
   1227   const BookmarkNode* f3_node = other_node->GetChild(0);
   1228   ASSERT_TRUE(f3_node);
   1229   ASSERT_TRUE(f3_node->is_folder());
   1230   remove_index = 2;
   1231   ASSERT_GT(f3_node->child_count(), remove_index);
   1232   model_->Remove(f3_node, remove_index);
   1233   ASSERT_GT(f3_node->child_count(), remove_index);
   1234   model_->Remove(f3_node, remove_index);
   1235 
   1236   StartSync();
   1237   ExpectModelMatch();
   1238   StopSync();
   1239 
   1240   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
   1241   WriteTestDataToBookmarkModel();
   1242   ExpectBookmarkModelMatchesTestData();
   1243 
   1244   // Remove some nodes and reorder some nodes.
   1245   bookmark_bar_node = model_->GetBookmarkBarNode();
   1246   remove_index = 0;
   1247   ASSERT_GT(bookmark_bar_node->child_count(), remove_index);
   1248   child_node = bookmark_bar_node->GetChild(remove_index);
   1249   ASSERT_TRUE(child_node);
   1250   ASSERT_TRUE(child_node->is_url());
   1251   model_->Remove(bookmark_bar_node, remove_index);
   1252   ASSERT_GT(bookmark_bar_node->child_count(), remove_index);
   1253   child_node = bookmark_bar_node->GetChild(remove_index);
   1254   ASSERT_TRUE(child_node);
   1255   ASSERT_TRUE(child_node->is_folder());
   1256   model_->Remove(bookmark_bar_node, remove_index);
   1257 
   1258   ASSERT_GE(bookmark_bar_node->child_count(), 2);
   1259   model_->Move(bookmark_bar_node->GetChild(0), bookmark_bar_node, 1);
   1260 
   1261   other_node = model_->other_node();
   1262   ASSERT_GE(other_node->child_count(), 1);
   1263   f3_node = other_node->GetChild(0);
   1264   ASSERT_TRUE(f3_node);
   1265   ASSERT_TRUE(f3_node->is_folder());
   1266   remove_index = 0;
   1267   ASSERT_GT(f3_node->child_count(), remove_index);
   1268   model_->Remove(f3_node, remove_index);
   1269   ASSERT_GT(f3_node->child_count(), remove_index);
   1270   model_->Remove(f3_node, remove_index);
   1271 
   1272   ASSERT_GE(other_node->child_count(), 4);
   1273   model_->Move(other_node->GetChild(0), other_node, 1);
   1274   model_->Move(other_node->GetChild(2), other_node, 3);
   1275 
   1276   StartSync();
   1277   ExpectModelMatch();
   1278 
   1279   // After the merge, the model should match the test data.
   1280   ExpectBookmarkModelMatchesTestData();
   1281 }
   1282 
   1283 // Tests that when persisted model associations are used, things work fine.
   1284 TEST_F(ProfileSyncServiceBookmarkTestWithData, ModelAssociationPersistence) {
   1285   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
   1286   WriteTestDataToBookmarkModel();
   1287   StartSync();
   1288   ExpectModelMatch();
   1289   // Force sync to shut down and write itself to disk.
   1290   StopSync();
   1291   // Now restart sync. This time it should use the persistent
   1292   // associations.
   1293   StartSync();
   1294   ExpectModelMatch();
   1295 }
   1296 
   1297 // Tests that when persisted model associations are used, things work fine.
   1298 TEST_F(ProfileSyncServiceBookmarkTestWithData,
   1299        ModelAssociationInvalidPersistence) {
   1300   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
   1301   WriteTestDataToBookmarkModel();
   1302   StartSync();
   1303   ExpectModelMatch();
   1304   // Force sync to shut down and write itself to disk.
   1305   StopSync();
   1306   // Change the bookmark model before restarting sync service to simulate
   1307   // the situation where bookmark model is different from sync model and
   1308   // make sure model associator correctly rebuilds associations.
   1309   const BookmarkNode* bookmark_bar_node = model_->GetBookmarkBarNode();
   1310   model_->AddURL(bookmark_bar_node, 0, ASCIIToUTF16("xtra"),
   1311                  GURL("http://www.xtra.com"));
   1312   // Now restart sync. This time it will try to use the persistent
   1313   // associations and realize that they are invalid and hence will rebuild
   1314   // associations.
   1315   StartSync();
   1316   ExpectModelMatch();
   1317 }
   1318 
   1319 TEST_F(ProfileSyncServiceBookmarkTestWithData, SortChildren) {
   1320   LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
   1321   StartSync();
   1322 
   1323   // Write test data to bookmark model and verify that the models match.
   1324   WriteTestDataToBookmarkModel();
   1325   const BookmarkNode* folder_added = model_->other_node()->GetChild(0);
   1326   ASSERT_TRUE(folder_added);
   1327   ASSERT_TRUE(folder_added->is_folder());
   1328 
   1329   ExpectModelMatch();
   1330 
   1331   // Sort the other-bookmarks children and expect that hte models match.
   1332   model_->SortChildren(folder_added);
   1333   ExpectModelMatch();
   1334 }
   1335 
   1336 // See what happens if we enable sync but then delete the "Sync Data"
   1337 // folder.
   1338 TEST_F(ProfileSyncServiceBookmarkTestWithData,
   1339        RecoverAfterDeletingSyncDataDirectory) {
   1340   LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
   1341   StartSync();
   1342 
   1343   WriteTestDataToBookmarkModel();
   1344 
   1345   StopSync();
   1346 
   1347   // Nuke the sync DB and reload.
   1348   test_user_share_.TearDown();
   1349   test_user_share_.SetUp();
   1350 
   1351   StartSync();
   1352 
   1353   // Make sure we're back in sync.  In real life, the user would need
   1354   // to reauthenticate before this happens, but in the test, authentication
   1355   // is sidestepped.
   1356   ExpectBookmarkModelMatchesTestData();
   1357   ExpectModelMatch();
   1358 }
   1359 
   1360 }  // namespace
   1361 
   1362 }  // namespace browser_sync
   1363