Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4621)

Unified Diff: chrome/browser/sync/engine/apply_updates_command_unittest.cc

Issue 9699057: [Sync] Move 'sync' target to sync/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address Tim's comments Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/sync/engine/apply_updates_command_unittest.cc
diff --git a/chrome/browser/sync/engine/apply_updates_command_unittest.cc b/chrome/browser/sync/engine/apply_updates_command_unittest.cc
deleted file mode 100644
index 1672d6e534859aee90dbb3d8c7350f3a518077a1..0000000000000000000000000000000000000000
--- a/chrome/browser/sync/engine/apply_updates_command_unittest.cc
+++ /dev/null
@@ -1,1007 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include "base/format_macros.h"
-#include "base/location.h"
-#include "base/stringprintf.h"
-#include "chrome/browser/sync/engine/apply_updates_command.h"
-#include "chrome/browser/sync/engine/nigori_util.h"
-#include "chrome/browser/sync/engine/syncer.h"
-#include "chrome/browser/sync/engine/syncer_util.h"
-#include "chrome/browser/sync/sessions/sync_session.h"
-#include "chrome/browser/sync/syncable/syncable.h"
-#include "chrome/browser/sync/syncable/syncable_id.h"
-#include "chrome/browser/sync/test/engine/fake_model_worker.h"
-#include "chrome/browser/sync/test/engine/syncer_command_test.h"
-#include "chrome/browser/sync/test/engine/test_id_factory.h"
-#include "chrome/browser/sync/test/fake_encryptor.h"
-#include "chrome/browser/sync/util/cryptographer.h"
-#include "sync/protocol/bookmark_specifics.pb.h"
-#include "sync/protocol/password_specifics.pb.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace browser_sync {
-
-using sessions::SyncSession;
-using std::string;
-using syncable::Entry;
-using syncable::Id;
-using syncable::MutableEntry;
-using syncable::ReadTransaction;
-using syncable::UNITTEST;
-using syncable::WriteTransaction;
-
-namespace {
-sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
- sync_pb::EntitySpecifics result;
- AddDefaultFieldValue(syncable::BOOKMARKS, &result);
- return result;
-}
-} // namespace
-
-// A test fixture for tests exercising ApplyUpdatesCommand.
-class ApplyUpdatesCommandTest : public SyncerCommandTest {
- public:
- protected:
- ApplyUpdatesCommandTest() : next_revision_(1) {}
- virtual ~ApplyUpdatesCommandTest() {}
-
- virtual void SetUp() {
- workers()->clear();
- mutable_routing_info()->clear();
- workers()->push_back(
- make_scoped_refptr(new FakeModelWorker(GROUP_UI)));
- workers()->push_back(
- make_scoped_refptr(new FakeModelWorker(GROUP_PASSWORD)));
- (*mutable_routing_info())[syncable::BOOKMARKS] = GROUP_UI;
- (*mutable_routing_info())[syncable::PASSWORDS] = GROUP_PASSWORD;
- (*mutable_routing_info())[syncable::NIGORI] = GROUP_PASSIVE;
- SyncerCommandTest::SetUp();
- ExpectNoGroupsToChange(apply_updates_command_);
- }
-
- // Create a new unapplied folder node with a parent.
- void CreateUnappliedNewItemWithParent(
- const string& item_id,
- const sync_pb::EntitySpecifics& specifics,
- const string& parent_id) {
- WriteTransaction trans(FROM_HERE, UNITTEST, directory());
- MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
- Id::CreateFromServerId(item_id));
- ASSERT_TRUE(entry.good());
- entry.Put(syncable::SERVER_VERSION, next_revision_++);
- entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
-
- entry.Put(syncable::SERVER_NON_UNIQUE_NAME, item_id);
- entry.Put(syncable::SERVER_PARENT_ID, Id::CreateFromServerId(parent_id));
- entry.Put(syncable::SERVER_IS_DIR, true);
- entry.Put(syncable::SERVER_SPECIFICS, specifics);
- }
-
- // Create a new unapplied update without a parent.
- void CreateUnappliedNewItem(const string& item_id,
- const sync_pb::EntitySpecifics& specifics,
- bool is_unique) {
- WriteTransaction trans(FROM_HERE, UNITTEST, directory());
- MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
- Id::CreateFromServerId(item_id));
- ASSERT_TRUE(entry.good());
- entry.Put(syncable::SERVER_VERSION, next_revision_++);
- entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
- entry.Put(syncable::SERVER_NON_UNIQUE_NAME, item_id);
- entry.Put(syncable::SERVER_PARENT_ID, syncable::GetNullId());
- entry.Put(syncable::SERVER_IS_DIR, false);
- entry.Put(syncable::SERVER_SPECIFICS, specifics);
- if (is_unique) // For top-level nodes.
- entry.Put(syncable::UNIQUE_SERVER_TAG, item_id);
- }
-
- // Create an unsynced item in the database. If item_id is a local ID, it
- // will be treated as a create-new. Otherwise, if it's a server ID, we'll
- // fake the server data so that it looks like it exists on the server.
- // Returns the methandle of the created item in |metahandle_out| if not NULL.
- void CreateUnsyncedItem(const Id& item_id,
- const Id& parent_id,
- const string& name,
- bool is_folder,
- syncable::ModelType model_type,
- int64* metahandle_out) {
- WriteTransaction trans(FROM_HERE, UNITTEST, directory());
- Id predecessor_id;
- ASSERT_TRUE(
- directory()->GetLastChildIdForTest(&trans, parent_id, &predecessor_id));
- MutableEntry entry(&trans, syncable::CREATE, parent_id, name);
- ASSERT_TRUE(entry.good());
- entry.Put(syncable::ID, item_id);
- entry.Put(syncable::BASE_VERSION,
- item_id.ServerKnows() ? next_revision_++ : 0);
- entry.Put(syncable::IS_UNSYNCED, true);
- entry.Put(syncable::IS_DIR, is_folder);
- entry.Put(syncable::IS_DEL, false);
- entry.Put(syncable::PARENT_ID, parent_id);
- CHECK(entry.PutPredecessor(predecessor_id));
- sync_pb::EntitySpecifics default_specifics;
- syncable::AddDefaultFieldValue(model_type, &default_specifics);
- entry.Put(syncable::SPECIFICS, default_specifics);
- if (item_id.ServerKnows()) {
- entry.Put(syncable::SERVER_SPECIFICS, default_specifics);
- entry.Put(syncable::SERVER_IS_DIR, is_folder);
- entry.Put(syncable::SERVER_PARENT_ID, parent_id);
- entry.Put(syncable::SERVER_IS_DEL, false);
- }
- if (metahandle_out)
- *metahandle_out = entry.Get(syncable::META_HANDLE);
- }
-
- // Creates an item that is both unsynced an an unapplied update. Returns the
- // metahandle of the created item.
- int64 CreateUnappliedAndUnsyncedItem(const string& name,
- syncable::ModelType model_type) {
- int64 metahandle = 0;
- CreateUnsyncedItem(id_factory_.MakeServer(name), id_factory_.root(), name,
- false, model_type, &metahandle);
-
- WriteTransaction trans(FROM_HERE, UNITTEST, directory());
- MutableEntry entry(&trans, syncable::GET_BY_HANDLE, metahandle);
- if (!entry.good()) {
- ADD_FAILURE();
- return syncable::kInvalidMetaHandle;
- }
-
- entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
- entry.Put(syncable::SERVER_VERSION, GetNextRevision());
-
- return metahandle;
- }
-
-
- // Creates an item that has neither IS_UNSYNED or IS_UNAPPLIED_UPDATE. The
- // item is known to both the server and client. Returns the metahandle of
- // the created item.
- int64 CreateSyncedItem(const std::string& name, syncable::ModelType
- model_type, bool is_folder) {
- WriteTransaction trans(FROM_HERE, UNITTEST, directory());
-
- syncable::Id parent_id(id_factory_.root());
- syncable::Id item_id(id_factory_.MakeServer(name));
- int64 version = GetNextRevision();
-
- sync_pb::EntitySpecifics default_specifics;
- syncable::AddDefaultFieldValue(model_type, &default_specifics);
-
- MutableEntry entry(&trans, syncable::CREATE, parent_id, name);
- if (!entry.good()) {
- ADD_FAILURE();
- return syncable::kInvalidMetaHandle;
- }
-
- entry.Put(syncable::ID, item_id);
- entry.Put(syncable::BASE_VERSION, version);
- entry.Put(syncable::IS_UNSYNCED, false);
- entry.Put(syncable::NON_UNIQUE_NAME, name);
- entry.Put(syncable::IS_DIR, is_folder);
- entry.Put(syncable::IS_DEL, false);
- entry.Put(syncable::PARENT_ID, parent_id);
-
- if (!entry.PutPredecessor(id_factory_.root())) {
- ADD_FAILURE();
- return syncable::kInvalidMetaHandle;
- }
- entry.Put(syncable::SPECIFICS, default_specifics);
-
- entry.Put(syncable::SERVER_VERSION, GetNextRevision());
- entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
- entry.Put(syncable::SERVER_NON_UNIQUE_NAME, "X");
- entry.Put(syncable::SERVER_PARENT_ID, id_factory_.MakeServer("Y"));
- entry.Put(syncable::SERVER_IS_DIR, is_folder);
- entry.Put(syncable::SERVER_IS_DEL, false);
- entry.Put(syncable::SERVER_SPECIFICS, default_specifics);
- entry.Put(syncable::SERVER_PARENT_ID, parent_id);
-
- return entry.Get(syncable::META_HANDLE);
- }
-
- int64 GetNextRevision() {
- return next_revision_++;
- }
-
- ApplyUpdatesCommand apply_updates_command_;
- FakeEncryptor encryptor_;
- TestIdFactory id_factory_;
- private:
- int64 next_revision_;
- DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesCommandTest);
-};
-
-TEST_F(ApplyUpdatesCommandTest, Simple) {
- string root_server_id = syncable::GetNullId().GetServerId();
- CreateUnappliedNewItemWithParent("parent",
- DefaultBookmarkSpecifics(),
- root_server_id);
- CreateUnappliedNewItemWithParent("child",
- DefaultBookmarkSpecifics(),
- "parent");
-
- ExpectGroupToChange(apply_updates_command_, GROUP_UI);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
-
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(2, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "Simple update shouldn't result in conflicts";
- EXPECT_EQ(0, status->conflict_progress()->EncryptionConflictingItemsSize())
- << "Simple update shouldn't result in conflicts";
- EXPECT_EQ(0, status->conflict_progress()->HierarchyConflictingItemsSize())
- << "Simple update shouldn't result in conflicts";
- EXPECT_EQ(2, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "All items should have been successfully applied";
-}
-
-TEST_F(ApplyUpdatesCommandTest, UpdateWithChildrenBeforeParents) {
- // Set a bunch of updates which are difficult to apply in the order
- // they're received due to dependencies on other unseen items.
- string root_server_id = syncable::GetNullId().GetServerId();
- CreateUnappliedNewItemWithParent("a_child_created_first",
- DefaultBookmarkSpecifics(),
- "parent");
- CreateUnappliedNewItemWithParent("x_child_created_first",
- DefaultBookmarkSpecifics(),
- "parent");
- CreateUnappliedNewItemWithParent("parent",
- DefaultBookmarkSpecifics(),
- root_server_id);
- CreateUnappliedNewItemWithParent("a_child_created_second",
- DefaultBookmarkSpecifics(),
- "parent");
- CreateUnappliedNewItemWithParent("x_child_created_second",
- DefaultBookmarkSpecifics(),
- "parent");
-
- ExpectGroupToChange(apply_updates_command_, GROUP_UI);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(5, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "Simple update shouldn't result in conflicts, even if out-of-order";
- EXPECT_EQ(5, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "All updates should have been successfully applied";
-}
-
-// Runs the ApplyUpdatesCommand on an item that has both local and remote
-// modifications (IS_UNSYNCED and IS_UNAPPLIED_UPDATE). We expect the command
-// to detect that this update can't be applied because it is in a CONFLICT
-// state.
-TEST_F(ApplyUpdatesCommandTest, SimpleConflict) {
- CreateUnappliedAndUnsyncedItem("item", syncable::BOOKMARKS);
-
- ExpectGroupToChange(apply_updates_command_, GROUP_UI);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(1, status->conflict_progress()->SimpleConflictingItemsSize())
- << "Unsynced and unapplied item should be a simple conflict";
-}
-
-// Runs the ApplyUpdatesCommand on an item that has both local and remote
-// modifications *and* the remote modification cannot be applied without
-// violating the tree constraints. We expect the command to detect that this
-// update can't be applied and that this situation can't be resolved with the
-// simple conflict processing logic; it is in a CONFLICT_HIERARCHY state.
-TEST_F(ApplyUpdatesCommandTest, HierarchyAndSimpleConflict) {
- // Create a simply-conflicting item. It will start with valid parent ids.
- int64 handle = CreateUnappliedAndUnsyncedItem("orphaned_by_server",
- syncable::BOOKMARKS);
- {
- // Manually set the SERVER_PARENT_ID to bad value.
- // A bad parent indicates a hierarchy conflict.
- WriteTransaction trans(FROM_HERE, UNITTEST, directory());
- MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
- ASSERT_TRUE(entry.good());
-
- entry.Put(syncable::SERVER_PARENT_ID,
- id_factory_.MakeServer("bogus_parent"));
- }
-
- ExpectGroupToChange(apply_updates_command_, GROUP_UI);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
-
- EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize());
-
- // An update that is both a simple conflict and a hierarchy conflict should be
- // treated as a hierarchy conflict.
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize());
-}
-
-
-// Runs the ApplyUpdatesCommand on an item with remote modifications that would
-// create a directory loop if the update were applied. We expect the command to
-// detect that this update can't be applied because it is in a
-// CONFLICT_HIERARCHY state.
-TEST_F(ApplyUpdatesCommandTest, HierarchyConflictDirectoryLoop) {
- // Item 'X' locally has parent of 'root'. Server is updating it to have
- // parent of 'Y'.
- {
- // Create it as a child of root node.
- int64 handle = CreateSyncedItem("X", syncable::BOOKMARKS, true);
-
- WriteTransaction trans(FROM_HERE, UNITTEST, directory());
- MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
- ASSERT_TRUE(entry.good());
-
- // Re-parent from root to "Y"
- entry.Put(syncable::SERVER_VERSION, GetNextRevision());
- entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
- entry.Put(syncable::SERVER_PARENT_ID, id_factory_.MakeServer("Y"));
- }
-
- // Item 'Y' is child of 'X'.
- CreateUnsyncedItem(id_factory_.MakeServer("Y"), id_factory_.MakeServer("X"),
- "Y", true, syncable::BOOKMARKS, NULL);
-
- // If the server's update were applied, we would have X be a child of Y, and Y
- // as a child of X. That's a directory loop. The UpdateApplicator should
- // prevent the update from being applied and note that this is a hierarchy
- // conflict.
-
- ExpectGroupToChange(apply_updates_command_, GROUP_UI);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
-
- EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize());
-
- // This should count as a hierarchy conflict.
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize());
-}
-
-// Runs the ApplyUpdatesCommand on a directory where the server sent us an
-// update to add a child to a locally deleted (and unsynced) parent. We expect
-// the command to not apply the update and to indicate the update is in a
-// CONFLICT_HIERARCHY state.
-TEST_F(ApplyUpdatesCommandTest, HierarchyConflictDeletedParent) {
- // Create a locally deleted parent item.
- int64 parent_handle;
- CreateUnsyncedItem(Id::CreateFromServerId("parent"), id_factory_.root(),
- "parent", true, syncable::BOOKMARKS, &parent_handle);
- {
- WriteTransaction trans(FROM_HERE, UNITTEST, directory());
- MutableEntry entry(&trans, syncable::GET_BY_HANDLE, parent_handle);
- entry.Put(syncable::IS_DEL, true);
- }
-
- // Create an incoming child from the server.
- CreateUnappliedNewItemWithParent("child", DefaultBookmarkSpecifics(),
- "parent");
-
- // The server's update may seem valid to some other client, but on this client
- // that new item's parent no longer exists. The update should not be applied
- // and the update applicator should indicate this is a hierarchy conflict.
-
- ExpectGroupToChange(apply_updates_command_, GROUP_UI);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
-
- // This should count as a hierarchy conflict.
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize());
-}
-
-// Runs the ApplyUpdatesCommand on a directory where the server is trying to
-// delete a folder that has a recently added (and unsynced) child. We expect
-// the command to not apply the update because it is in a CONFLICT_HIERARCHY
-// state.
-TEST_F(ApplyUpdatesCommandTest, HierarchyConflictDeleteNonEmptyDirectory) {
- // Create a server-deleted directory.
- {
- // Create it as a child of root node.
- int64 handle = CreateSyncedItem("parent", syncable::BOOKMARKS, true);
-
- WriteTransaction trans(FROM_HERE, UNITTEST, directory());
- MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
- ASSERT_TRUE(entry.good());
-
- // Delete it on the server.
- entry.Put(syncable::SERVER_VERSION, GetNextRevision());
- entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
- entry.Put(syncable::SERVER_PARENT_ID, id_factory_.root());
- entry.Put(syncable::SERVER_IS_DEL, true);
- }
-
- // Create a local child of the server-deleted directory.
- CreateUnsyncedItem(id_factory_.MakeServer("child"),
- id_factory_.MakeServer("parent"), "child", false,
- syncable::BOOKMARKS, NULL);
-
- // The server's request to delete the directory must be ignored, otherwise our
- // unsynced new child would be orphaned. This is a hierarchy conflict.
-
- ExpectGroupToChange(apply_updates_command_, GROUP_UI);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
-
- // This should count as a hierarchy conflict.
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize());
-}
-
-// Runs the ApplyUpdatesCommand on a server-created item that has a locally
-// unknown parent. We expect the command to not apply the update because the
-// item is in a CONFLICT_HIERARCHY state.
-TEST_F(ApplyUpdatesCommandTest, HierarchyConflictUnknownParent) {
- // We shouldn't be able to do anything with either of these items.
- CreateUnappliedNewItemWithParent("some_item",
- DefaultBookmarkSpecifics(),
- "unknown_parent");
- CreateUnappliedNewItemWithParent("some_other_item",
- DefaultBookmarkSpecifics(),
- "some_item");
-
- ExpectGroupToChange(apply_updates_command_, GROUP_UI);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(2, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "Updates with unknown parent should not be treated as 'simple'"
- << " conflicts";
- EXPECT_EQ(2, status->conflict_progress()->HierarchyConflictingItemsSize())
- << "All updates with an unknown ancestors should be in conflict";
- EXPECT_EQ(0, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "No item with an unknown ancestor should be applied";
-}
-
-TEST_F(ApplyUpdatesCommandTest, ItemsBothKnownAndUnknown) {
- // See what happens when there's a mixture of good and bad updates.
- string root_server_id = syncable::GetNullId().GetServerId();
- CreateUnappliedNewItemWithParent("first_unknown_item",
- DefaultBookmarkSpecifics(),
- "unknown_parent");
- CreateUnappliedNewItemWithParent("first_known_item",
- DefaultBookmarkSpecifics(),
- root_server_id);
- CreateUnappliedNewItemWithParent("second_unknown_item",
- DefaultBookmarkSpecifics(),
- "unknown_parent");
- CreateUnappliedNewItemWithParent("second_known_item",
- DefaultBookmarkSpecifics(),
- "first_known_item");
- CreateUnappliedNewItemWithParent("third_known_item",
- DefaultBookmarkSpecifics(),
- "fourth_known_item");
- CreateUnappliedNewItemWithParent("fourth_known_item",
- DefaultBookmarkSpecifics(),
- root_server_id);
-
- ExpectGroupToChange(apply_updates_command_, GROUP_UI);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(6, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(2, status->conflict_progress()->HierarchyConflictingItemsSize())
- << "The updates with unknown ancestors should be in conflict";
- EXPECT_EQ(4, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "The updates with known ancestors should be successfully applied";
-}
-
-TEST_F(ApplyUpdatesCommandTest, DecryptablePassword) {
- // Decryptable password updates should be applied.
- Cryptographer* cryptographer;
- {
- // Storing the cryptographer separately is bad, but for this test we
- // know it's safe.
- ReadTransaction trans(FROM_HERE, directory());
- cryptographer = directory()->GetCryptographer(&trans);
- }
-
- browser_sync::KeyParams params = {"localhost", "dummy", "foobar"};
- cryptographer->AddKey(params);
-
- sync_pb::EntitySpecifics specifics;
- sync_pb::PasswordSpecificsData data;
- data.set_origin("http://example.com");
-
- cryptographer->Encrypt(data,
- specifics.mutable_password()->mutable_encrypted());
- CreateUnappliedNewItem("item", specifics, false);
-
- ExpectGroupToChange(apply_updates_command_, GROUP_PASSWORD);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSWORD);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "No update should be in conflict because they're all decryptable";
- EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "The updates that can be decrypted should be applied";
-}
-
-TEST_F(ApplyUpdatesCommandTest, UndecryptableData) {
- // Undecryptable updates should not be applied.
- sync_pb::EntitySpecifics encrypted_bookmark;
- encrypted_bookmark.mutable_encrypted();
- AddDefaultFieldValue(syncable::BOOKMARKS, &encrypted_bookmark);
- string root_server_id = syncable::GetNullId().GetServerId();
- CreateUnappliedNewItemWithParent("folder",
- encrypted_bookmark,
- root_server_id);
- CreateUnappliedNewItem("item2", encrypted_bookmark, false);
- sync_pb::EntitySpecifics encrypted_password;
- encrypted_password.mutable_password();
- CreateUnappliedNewItem("item3", encrypted_password, false);
-
- ExpectGroupsToChange(apply_updates_command_, GROUP_UI, GROUP_PASSWORD);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- EXPECT_TRUE(status->HasConflictingUpdates())
- << "Updates that can't be decrypted should trigger the syncer to have "
- << "conflicting updates.";
- {
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(2, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "The updates that can't be decrypted should not be in regular "
- << "conflict";
- EXPECT_EQ(2, status->conflict_progress()->EncryptionConflictingItemsSize())
- << "The updates that can't be decrypted should be in encryption "
- << "conflict";
- EXPECT_EQ(0, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "No update that can't be decrypted should be applied";
- }
- {
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSWORD);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "The updates that can't be decrypted should not be in regular "
- << "conflict";
- EXPECT_EQ(1, status->conflict_progress()->EncryptionConflictingItemsSize())
- << "The updates that can't be decrypted should be in encryption "
- << "conflict";
- EXPECT_EQ(0, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "No update that can't be decrypted should be applied";
- }
-}
-
-TEST_F(ApplyUpdatesCommandTest, SomeUndecryptablePassword) {
- // Only decryptable password updates should be applied.
- {
- sync_pb::EntitySpecifics specifics;
- sync_pb::PasswordSpecificsData data;
- data.set_origin("http://example.com/1");
- {
- ReadTransaction trans(FROM_HERE, directory());
- Cryptographer* cryptographer = directory()->GetCryptographer(&trans);
-
- KeyParams params = {"localhost", "dummy", "foobar"};
- cryptographer->AddKey(params);
-
- cryptographer->Encrypt(data,
- specifics.mutable_password()->mutable_encrypted());
- }
- CreateUnappliedNewItem("item1", specifics, false);
- }
- {
- // Create a new cryptographer, independent of the one in the session.
- Cryptographer cryptographer(&encryptor_);
- KeyParams params = {"localhost", "dummy", "bazqux"};
- cryptographer.AddKey(params);
-
- sync_pb::EntitySpecifics specifics;
- sync_pb::PasswordSpecificsData data;
- data.set_origin("http://example.com/2");
-
- cryptographer.Encrypt(data,
- specifics.mutable_password()->mutable_encrypted());
- CreateUnappliedNewItem("item2", specifics, false);
- }
-
- ExpectGroupToChange(apply_updates_command_, GROUP_PASSWORD);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- EXPECT_TRUE(status->HasConflictingUpdates())
- << "Updates that can't be decrypted should trigger the syncer to have "
- << "conflicting updates.";
- {
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSWORD);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(2, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "The updates that can't be decrypted should not be in regular "
- << "conflict";
- EXPECT_EQ(1, status->conflict_progress()->EncryptionConflictingItemsSize())
- << "The updates that can't be decrypted should be in encryption "
- << "conflict";
- EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "The undecryptable password update shouldn't be applied";
- }
-}
-
-TEST_F(ApplyUpdatesCommandTest, NigoriUpdate) {
- // Storing the cryptographer separately is bad, but for this test we
- // know it's safe.
- Cryptographer* cryptographer;
- syncable::ModelTypeSet encrypted_types;
- encrypted_types.Put(syncable::PASSWORDS);
- encrypted_types.Put(syncable::NIGORI);
- {
- ReadTransaction trans(FROM_HERE, directory());
- cryptographer = directory()->GetCryptographer(&trans);
- EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(encrypted_types));
- }
-
- // Nigori node updates should update the Cryptographer.
- Cryptographer other_cryptographer(&encryptor_);
- KeyParams params = {"localhost", "dummy", "foobar"};
- other_cryptographer.AddKey(params);
-
- sync_pb::EntitySpecifics specifics;
- sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
- other_cryptographer.GetKeys(nigori->mutable_encrypted());
- nigori->set_encrypt_bookmarks(true);
- encrypted_types.Put(syncable::BOOKMARKS);
- CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
- specifics, true);
- EXPECT_FALSE(cryptographer->has_pending_keys());
-
- ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "The nigori update shouldn't be in conflict";
- EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "The nigori update should be applied";
-
- EXPECT_FALSE(cryptographer->is_ready());
- EXPECT_TRUE(cryptographer->has_pending_keys());
- EXPECT_TRUE(
- cryptographer->GetEncryptedTypes()
- .Equals(syncable::ModelTypeSet::All()));
-}
-
-TEST_F(ApplyUpdatesCommandTest, NigoriUpdateForDisabledTypes) {
- // Storing the cryptographer separately is bad, but for this test we
- // know it's safe.
- Cryptographer* cryptographer;
- syncable::ModelTypeSet encrypted_types;
- encrypted_types.Put(syncable::PASSWORDS);
- encrypted_types.Put(syncable::NIGORI);
- {
- ReadTransaction trans(FROM_HERE, directory());
- cryptographer = directory()->GetCryptographer(&trans);
- EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(encrypted_types));
- }
-
- // Nigori node updates should update the Cryptographer.
- Cryptographer other_cryptographer(&encryptor_);
- KeyParams params = {"localhost", "dummy", "foobar"};
- other_cryptographer.AddKey(params);
-
- sync_pb::EntitySpecifics specifics;
- sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
- other_cryptographer.GetKeys(nigori->mutable_encrypted());
- nigori->set_encrypt_sessions(true);
- nigori->set_encrypt_themes(true);
- encrypted_types.Put(syncable::SESSIONS);
- encrypted_types.Put(syncable::THEMES);
- CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
- specifics, true);
- EXPECT_FALSE(cryptographer->has_pending_keys());
-
- ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "The nigori update shouldn't be in conflict";
- EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "The nigori update should be applied";
-
- EXPECT_FALSE(cryptographer->is_ready());
- EXPECT_TRUE(cryptographer->has_pending_keys());
- EXPECT_TRUE(
- cryptographer->GetEncryptedTypes()
- .Equals(syncable::ModelTypeSet::All()));
-}
-
-// Create some local unsynced and unencrypted data. Apply a nigori update that
-// turns on encryption for the unsynced data. Ensure we properly encrypt the
-// data as part of the nigori update. Apply another nigori update with no
-// changes. Ensure we ignore already-encrypted unsynced data and that nothing
-// breaks.
-TEST_F(ApplyUpdatesCommandTest, EncryptUnsyncedChanges) {
- // Storing the cryptographer separately is bad, but for this test we
- // know it's safe.
- Cryptographer* cryptographer;
- syncable::ModelTypeSet encrypted_types;
- encrypted_types.Put(syncable::PASSWORDS);
- encrypted_types.Put(syncable::NIGORI);
- {
- ReadTransaction trans(FROM_HERE, directory());
- cryptographer = directory()->GetCryptographer(&trans);
- EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(encrypted_types));
-
- // With default encrypted_types, this should be true.
- EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
-
- Syncer::UnsyncedMetaHandles handles;
- SyncerUtil::GetUnsyncedEntries(&trans, &handles);
- EXPECT_TRUE(handles.empty());
- }
-
- // Create unsynced bookmarks without encryption.
- // First item is a folder
- Id folder_id = id_factory_.NewLocalId();
- CreateUnsyncedItem(folder_id, id_factory_.root(), "folder",
- true, syncable::BOOKMARKS, NULL);
- // Next five items are children of the folder
- size_t i;
- size_t batch_s = 5;
- for (i = 0; i < batch_s; ++i) {
- CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
- base::StringPrintf("Item %"PRIuS"", i), false,
- syncable::BOOKMARKS, NULL);
- }
- // Next five items are children of the root.
- for (; i < 2*batch_s; ++i) {
- CreateUnsyncedItem(id_factory_.NewLocalId(), id_factory_.root(),
- base::StringPrintf("Item %"PRIuS"", i), false,
- syncable::BOOKMARKS, NULL);
- }
-
- KeyParams params = {"localhost", "dummy", "foobar"};
- cryptographer->AddKey(params);
- sync_pb::EntitySpecifics specifics;
- sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
- cryptographer->GetKeys(nigori->mutable_encrypted());
- nigori->set_encrypt_bookmarks(true);
- encrypted_types.Put(syncable::BOOKMARKS);
- CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
- specifics, true);
- EXPECT_FALSE(cryptographer->has_pending_keys());
- EXPECT_TRUE(cryptographer->is_ready());
-
- {
- // Ensure we have unsynced nodes that aren't properly encrypted.
- ReadTransaction trans(FROM_HERE, directory());
- EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
-
- Syncer::UnsyncedMetaHandles handles;
- SyncerUtil::GetUnsyncedEntries(&trans, &handles);
- EXPECT_EQ(2*batch_s+1, handles.size());
- }
-
- ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
- apply_updates_command_.ExecuteImpl(session());
-
- {
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "No updates should be in conflict";
- EXPECT_EQ(0, status->conflict_progress()->EncryptionConflictingItemsSize())
- << "No updates should be in conflict";
- EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "The nigori update should be applied";
- }
- EXPECT_FALSE(cryptographer->has_pending_keys());
- EXPECT_TRUE(cryptographer->is_ready());
- {
- ReadTransaction trans(FROM_HERE, directory());
-
- // If ProcessUnsyncedChangesForEncryption worked, all our unsynced changes
- // should be encrypted now.
- EXPECT_TRUE(syncable::ModelTypeSet::All().Equals(
- cryptographer->GetEncryptedTypes()));
- EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
-
- Syncer::UnsyncedMetaHandles handles;
- SyncerUtil::GetUnsyncedEntries(&trans, &handles);
- EXPECT_EQ(2*batch_s+1, handles.size());
- }
-
- // Simulate another nigori update that doesn't change anything.
- {
- WriteTransaction trans(FROM_HERE, UNITTEST, directory());
- MutableEntry entry(&trans, syncable::GET_BY_SERVER_TAG,
- syncable::ModelTypeToRootTag(syncable::NIGORI));
- ASSERT_TRUE(entry.good());
- entry.Put(syncable::SERVER_VERSION, GetNextRevision());
- entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
- }
- ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
- apply_updates_command_.ExecuteImpl(session());
- {
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(2, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "No updates should be in conflict";
- EXPECT_EQ(0, status->conflict_progress()->EncryptionConflictingItemsSize())
- << "No updates should be in conflict";
- EXPECT_EQ(2, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "The nigori update should be applied";
- }
- EXPECT_FALSE(cryptographer->has_pending_keys());
- EXPECT_TRUE(cryptographer->is_ready());
- {
- ReadTransaction trans(FROM_HERE, directory());
-
- // All our changes should still be encrypted.
- EXPECT_TRUE(syncable::ModelTypeSet::All().Equals(
- cryptographer->GetEncryptedTypes()));
- EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
-
- Syncer::UnsyncedMetaHandles handles;
- SyncerUtil::GetUnsyncedEntries(&trans, &handles);
- EXPECT_EQ(2*batch_s+1, handles.size());
- }
-}
-
-TEST_F(ApplyUpdatesCommandTest, CannotEncryptUnsyncedChanges) {
- // Storing the cryptographer separately is bad, but for this test we
- // know it's safe.
- Cryptographer* cryptographer;
- syncable::ModelTypeSet encrypted_types;
- encrypted_types.Put(syncable::PASSWORDS);
- encrypted_types.Put(syncable::NIGORI);
- {
- ReadTransaction trans(FROM_HERE, directory());
- cryptographer = directory()->GetCryptographer(&trans);
- EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(encrypted_types));
-
- // With default encrypted_types, this should be true.
- EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
-
- Syncer::UnsyncedMetaHandles handles;
- SyncerUtil::GetUnsyncedEntries(&trans, &handles);
- EXPECT_TRUE(handles.empty());
- }
-
- // Create unsynced bookmarks without encryption.
- // First item is a folder
- Id folder_id = id_factory_.NewLocalId();
- CreateUnsyncedItem(folder_id, id_factory_.root(), "folder", true,
- syncable::BOOKMARKS, NULL);
- // Next five items are children of the folder
- size_t i;
- size_t batch_s = 5;
- for (i = 0; i < batch_s; ++i) {
- CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
- base::StringPrintf("Item %"PRIuS"", i), false,
- syncable::BOOKMARKS, NULL);
- }
- // Next five items are children of the root.
- for (; i < 2*batch_s; ++i) {
- CreateUnsyncedItem(id_factory_.NewLocalId(), id_factory_.root(),
- base::StringPrintf("Item %"PRIuS"", i), false,
- syncable::BOOKMARKS, NULL);
- }
-
- // We encrypt with new keys, triggering the local cryptographer to be unready
- // and unable to decrypt data (once updated).
- Cryptographer other_cryptographer(&encryptor_);
- KeyParams params = {"localhost", "dummy", "foobar"};
- other_cryptographer.AddKey(params);
- sync_pb::EntitySpecifics specifics;
- sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
- other_cryptographer.GetKeys(nigori->mutable_encrypted());
- nigori->set_encrypt_bookmarks(true);
- encrypted_types.Put(syncable::BOOKMARKS);
- CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
- specifics, true);
- EXPECT_FALSE(cryptographer->has_pending_keys());
-
- {
- // Ensure we have unsynced nodes that aren't properly encrypted.
- ReadTransaction trans(FROM_HERE, directory());
- EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
- Syncer::UnsyncedMetaHandles handles;
- SyncerUtil::GetUnsyncedEntries(&trans, &handles);
- EXPECT_EQ(2*batch_s+1, handles.size());
- }
-
- ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
- apply_updates_command_.ExecuteImpl(session());
-
- sessions::StatusController* status = session()->mutable_status_controller();
- sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
- ASSERT_TRUE(status->update_progress());
- EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
- << "All updates should have been attempted";
- ASSERT_TRUE(status->conflict_progress());
- EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
- << "The unsynced changes don't trigger a blocking conflict with the "
- << "nigori update.";
- EXPECT_EQ(0, status->conflict_progress()->EncryptionConflictingItemsSize())
- << "The unsynced changes don't trigger an encryption conflict with the "
- << "nigori update.";
- EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
- << "The nigori update should be applied";
- EXPECT_FALSE(cryptographer->is_ready());
- EXPECT_TRUE(cryptographer->has_pending_keys());
- {
- ReadTransaction trans(FROM_HERE, directory());
-
- // Since we have pending keys, we would have failed to encrypt, but the
- // cryptographer should be updated.
- EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
- EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(
- syncable::ModelTypeSet().All()));
- EXPECT_FALSE(cryptographer->is_ready());
- EXPECT_TRUE(cryptographer->has_pending_keys());
-
- Syncer::UnsyncedMetaHandles handles;
- SyncerUtil::GetUnsyncedEntries(&trans, &handles);
- EXPECT_EQ(2*batch_s+1, handles.size());
- }
-}
-
-} // namespace browser_sync
« no previous file with comments | « chrome/browser/sync/engine/apply_updates_command.cc ('k') | chrome/browser/sync/engine/build_commit_command.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698