#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/bookmarks/chrome_bookmark_client.h"
+#include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h"
#include "chrome/browser/sync/glue/bookmark_change_processor.h"
#include "chrome/browser/sync/glue/bookmark_model_associator.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/testing_profile.h"
-#include "components/bookmarks/core/browser/base_bookmark_model_observer.h"
-#include "components/bookmarks/core/browser/bookmark_model.h"
-#include "components/bookmarks/core/test/bookmark_test_helpers.h"
+#include "components/bookmarks/browser/base_bookmark_model_observer.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/sync_driver/data_type_error_handler.h"
#include "components/sync_driver/data_type_error_handler_mock.h"
-#include "content/public/test/test_browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
#include "sync/api/sync_error.h"
#include "sync/internal_api/public/change_record.h"
#include "sync/internal_api/public/read_node.h"
namespace browser_sync {
-using content::BrowserThread;
using syncer::BaseNode;
using testing::_;
using testing::InvokeWithoutArgs;
using testing::Mock;
using testing::StrictMock;
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_IOS)
static const bool kExpectMobileBookmarks = true;
#else
static const bool kExpectMobileBookmarks = false;
-#endif // defined(OS_ANDROID)
+#endif // defined(OS_ANDROID) || defined(OS_IOS)
namespace {
}
// Pass the fake change list to |service|.
- void ApplyPendingChanges(ChangeProcessor* processor) {
+ void ApplyPendingChanges(sync_driver::ChangeProcessor* processor) {
processor->ApplyChangesFromSyncModel(
trans_, 0, syncer::ImmutableChangeRecordList(&changes_));
}
class ExtensiveChangesBookmarkModelObserver : public BaseBookmarkModelObserver {
public:
- explicit ExtensiveChangesBookmarkModelObserver()
+ ExtensiveChangesBookmarkModelObserver()
: started_count_(0),
completed_count_at_started_(0),
completed_count_(0) {}
- virtual void ExtensiveBookmarkChangesBeginning(
- BookmarkModel* model) OVERRIDE {
+ void ExtensiveBookmarkChangesBeginning(BookmarkModel* model) override {
++started_count_;
completed_count_at_started_ = completed_count_;
}
- virtual void ExtensiveBookmarkChangesEnded(BookmarkModel* model) OVERRIDE {
+ void ExtensiveBookmarkChangesEnded(BookmarkModel* model) override {
++completed_count_;
}
- virtual void BookmarkModelChanged() OVERRIDE {}
+ void BookmarkModelChanged() override {}
int get_started() const {
return started_count_;
ProfileSyncServiceBookmarkTest()
: model_(NULL),
- ui_thread_(BrowserThread::UI, &message_loop_),
- file_thread_(BrowserThread::FILE, &message_loop_),
+ thread_bundle_(content::TestBrowserThreadBundle::DEFAULT),
local_merge_result_(syncer::BOOKMARKS),
- syncer_merge_result_(syncer::BOOKMARKS) {
- }
+ syncer_merge_result_(syncer::BOOKMARKS) {}
virtual ~ProfileSyncServiceBookmarkTest() {
StopSync();
test_user_share_.TearDown();
}
+ bool CanSyncNode(const BookmarkNode* node) {
+ return model_->client()->CanSyncNode(node);
+ }
+
// Inserts a folder directly to the share.
// Do not use this after model association is complete.
//
// Be sure to call CreatePermanentBookmarkNodes(), otherwise this will fail.
syncer::ReadNode bookmark_bar(trans);
- EXPECT_EQ(BaseNode::INIT_OK, bookmark_bar.InitByTagLookup("bookmark_bar"));
+ EXPECT_EQ(BaseNode::INIT_OK,
+ bookmark_bar.InitByTagLookupForBookmarks("bookmark_bar"));
syncer::WriteNode node(trans);
EXPECT_TRUE(node.InitBookmarkByCreation(bookmark_bar, NULL));
bool delete_bookmarks = load == DELETE_EXISTING_STORAGE;
profile_.CreateBookmarkModel(delete_bookmarks);
model_ = BookmarkModelFactory::GetForProfile(&profile_);
- test::WaitForBookmarkModelToLoad(model_);
+ bookmarks::test::WaitForBookmarkModelToLoad(model_);
// This noticeably speeds up the unit tests that request it.
if (save == DONT_SAVE_TO_STORAGE)
model_->ClearStore();
- message_loop_.RunUntilIdle();
+ base::MessageLoop::current()->RunUntilIdle();
}
int GetSyncBookmarkCount() {
syncer::ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
syncer::ReadNode node(&trans);
- if (node.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::BOOKMARKS)) !=
- syncer::BaseNode::INIT_OK)
+ if (node.InitTypeRoot(syncer::BOOKMARKS) != syncer::BaseNode::INIT_OK)
return 0;
return node.GetTotalNodeCount();
}
uber_root.InitByRootLookup();
syncer::ReadNode root(&trans);
- root_exists = (root.InitByTagLookup(syncer::ModelTypeToRootTag(type)) ==
- BaseNode::INIT_OK);
+ root_exists = (root.InitTypeRoot(type) == BaseNode::INIT_OK);
}
if (!root_exists) {
const int kNumPermanentNodes = 3;
const std::string permanent_tags[kNumPermanentNodes] = {
- "bookmark_bar", "other_bookmarks", "synced_bookmarks"
+#if defined(OS_IOS)
+ "synced_bookmarks",
+#endif
+ "bookmark_bar",
+ "other_bookmarks",
+#if !defined(OS_IOS)
+ "synced_bookmarks",
+#endif
};
syncer::WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
syncer::ReadNode root(&trans);
- EXPECT_EQ(BaseNode::INIT_OK, root.InitByTagLookup(
- syncer::ModelTypeToRootTag(type)));
+ EXPECT_EQ(BaseNode::INIT_OK, root.InitTypeRoot(type));
// Loop through creating permanent nodes as necessary.
int64 last_child_id = syncer::kInvalidId;
// First check if the node already exists. This is for tests that involve
// persistence and set up sync more than once.
syncer::ReadNode lookup(&trans);
- if (lookup.InitByTagLookup(permanent_tags[i]) ==
+ if (lookup.InitByTagLookupForBookmarks(permanent_tags[i]) ==
syncer::ReadNode::INIT_OK) {
last_child_id = lookup.GetId();
continue;
}
model_associator_.reset();
- message_loop_.RunUntilIdle();
+ base::MessageLoop::current()->RunUntilIdle();
// TODO(akalin): Actually close the database and flush it to disk
// (and make StartSync reload from disk). This would require
void UnloadBookmarkModel() {
profile_.CreateBookmarkModel(false /* delete_bookmarks */);
model_ = NULL;
- message_loop_.RunUntilIdle();
+ base::MessageLoop::current()->RunUntilIdle();
}
bool InitSyncNodeFromChromeNode(const BookmarkNode* bnode,
EXPECT_EQ(gnode.GetPredecessorId(), gprev.GetId());
EXPECT_EQ(gnode.GetParentId(), gprev.GetParentId());
}
- if (browser_index == bnode->parent()->child_count() - 1) {
+ // Note: the managed node is the last child of the root_node but isn't
+ // synced; if CanSyncNode() is false then there is no next node to sync.
+ const BookmarkNode* bnext = NULL;
+ if (browser_index + 1 < bnode->parent()->child_count())
+ bnext = bnode->parent()->GetChild(browser_index + 1);
+ if (!bnext || !CanSyncNode(bnext)) {
EXPECT_EQ(gnode.GetSuccessorId(), 0);
} else {
- const BookmarkNode* bnext =
- bnode->parent()->GetChild(browser_index + 1);
syncer::ReadNode gnext(trans);
ASSERT_TRUE(InitSyncNodeFromChromeNode(bnext, &gnext));
EXPECT_EQ(gnode.GetSuccessorId(), gnext.GetId());
const BookmarkNode* bnode =
model_associator_->GetChromeNodeFromSyncId(sync_id);
ASSERT_TRUE(bnode);
+ ASSERT_TRUE(CanSyncNode(bnode));
+
int64 id = model_associator_->GetSyncIdFromChromeId(bnode->id());
EXPECT_EQ(id, sync_id);
ExpectSyncerNodeMatching(trans, bnode);
void ExpectModelMatch(syncer::BaseTransaction* trans) {
const BookmarkNode* root = model_->root_node();
+#if defined(OS_IOS)
+ EXPECT_EQ(root->GetIndexOf(model_->mobile_node()), 0);
+ EXPECT_EQ(root->GetIndexOf(model_->bookmark_bar_node()), 1);
+ EXPECT_EQ(root->GetIndexOf(model_->other_node()), 2);
+#else
EXPECT_EQ(root->GetIndexOf(model_->bookmark_bar_node()), 0);
EXPECT_EQ(root->GetIndexOf(model_->other_node()), 1);
EXPECT_EQ(root->GetIndexOf(model_->mobile_node()), 2);
+#endif
std::stack<int64> stack;
stack.push(bookmark_bar_id());
}
protected:
+ TestingProfile profile_;
BookmarkModel* model_;
syncer::TestUserShare test_user_share_;
scoped_ptr<BookmarkChangeProcessor> change_processor_;
- StrictMock<DataTypeErrorHandlerMock> mock_error_handler_;
+ StrictMock<sync_driver::DataTypeErrorHandlerMock> mock_error_handler_;
scoped_ptr<BookmarkModelAssociator> model_associator_;
private:
- // Used by both |ui_thread_| and |file_thread_|.
- base::MessageLoop message_loop_;
- content::TestBrowserThread ui_thread_;
- // Needed by |model_|.
- content::TestBrowserThread file_thread_;
-
+ content::TestBrowserThreadBundle thread_bundle_;
syncer::SyncMergeResult local_merge_result_;
syncer::SyncMergeResult syncer_merge_result_;
-
- TestingProfile profile_;
};
TEST_F(ProfileSyncServiceBookmarkTest, InitialState) {
// puts itself into a lame, error state.
TEST_F(ProfileSyncServiceBookmarkTest, UnrecoverableErrorSuspendsService) {
EXPECT_CALL(mock_error_handler_,
- OnSingleDatatypeUnrecoverableError(_, _));
+ OnSingleDataTypeUnrecoverableError(_));
LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
StartSync();
void ExpectBookmarkModelMatchesTestData();
void WriteTestDataToBookmarkModel();
+ // Output transaction versions of |node| and nodes under it to
+ // |node_versions|.
+ void GetTransactionVersions(const BookmarkNode* root,
+ BookmarkNodeVersionMap* node_versions);
+
// Verify transaction versions of bookmark nodes and sync nodes are equal
// recursively. If node is in |version_expected|, versions should match
// there, too.
ExpectModelMatch();
}
-// Output transaction versions of |node| and nodes under it to |node_versions|.
-void GetTransactionVersions(
+void ProfileSyncServiceBookmarkTestWithData::GetTransactionVersions(
const BookmarkNode* root,
BookmarkNodeVersionMap* node_versions) {
node_versions->clear();
EXPECT_NE(BookmarkNode::kInvalidSyncTransactionVersion, version);
(*node_versions)[n->id()] = version;
- for (int i = 0; i < n->child_count(); ++i)
+ for (int i = 0; i < n->child_count(); ++i) {
+ if (!CanSyncNode(n->GetChild(i)))
+ continue;
nodes.push(n->GetChild(i));
+ }
}
}
EXPECT_FALSE(AssociateModels());
}
+// It's possible for update/add calls from the bookmark model to be out of
+// order, or asynchronous. Handle that without triggering an error.
+TEST_F(ProfileSyncServiceBookmarkTest, UpdateThenAdd) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ StartSync();
+
+ EXPECT_TRUE(other_bookmarks_id());
+ EXPECT_TRUE(bookmark_bar_id());
+ EXPECT_TRUE(mobile_bookmarks_id());
+
+ ExpectModelMatch();
+
+ // Now destroy the change processor then add a bookmark, to simulate
+ // missing the Update call.
+ change_processor_.reset();
+ const BookmarkNode* node = model_->AddURL(model_->bookmark_bar_node(),
+ 0,
+ base::ASCIIToUTF16("title"),
+ GURL("http://www.url.com"));
+
+ // Recreate the change processor then update that bookmark. Sync should
+ // receive the update call and gracefully treat that as if it were an add.
+ change_processor_.reset(new BookmarkChangeProcessor(
+ &profile_, model_associator_.get(), &mock_error_handler_));
+ change_processor_->Start(test_user_share_.user_share());
+ model_->SetTitle(node, base::ASCIIToUTF16("title2"));
+ ExpectModelMatch();
+
+ // Then simulate the add call arriving late.
+ change_processor_->BookmarkNodeAdded(model_, model_->bookmark_bar_node(), 0);
+ ExpectModelMatch();
+}
+
} // namespace
} // namespace browser_sync