Index: sync/engine/apply_updates_command_unittest.cc |
diff --git a/sync/engine/apply_updates_command_unittest.cc b/sync/engine/apply_updates_command_unittest.cc |
deleted file mode 100644 |
index b261c1dcd32127c0e26511ff5f9b964692aa78bb..0000000000000000000000000000000000000000 |
--- a/sync/engine/apply_updates_command_unittest.cc |
+++ /dev/null |
@@ -1,438 +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/location.h" |
-#include "base/memory/scoped_ptr.h" |
-#include "base/stringprintf.h" |
-#include "sync/engine/apply_updates_command.h" |
-#include "sync/engine/syncer.h" |
-#include "sync/internal_api/public/test/test_entry_factory.h" |
-#include "sync/protocol/bookmark_specifics.pb.h" |
-#include "sync/protocol/password_specifics.pb.h" |
-#include "sync/syncable/mutable_entry.h" |
-#include "sync/syncable/read_transaction.h" |
-#include "sync/syncable/syncable_id.h" |
-#include "sync/syncable/syncable_util.h" |
-#include "sync/syncable/write_transaction.h" |
-#include "sync/test/engine/fake_model_worker.h" |
-#include "sync/test/engine/syncer_command_test.h" |
-#include "sync/test/engine/test_id_factory.h" |
-#include "sync/test/fake_sync_encryption_handler.h" |
-#include "sync/util/cryptographer.h" |
-#include "testing/gtest/include/gtest/gtest.h" |
- |
-namespace syncer { |
- |
-using std::string; |
-using syncable::Id; |
-using syncable::MutableEntry; |
-using syncable::UNITTEST; |
-using syncable::WriteTransaction; |
- |
-namespace { |
-sync_pb::EntitySpecifics DefaultBookmarkSpecifics() { |
- sync_pb::EntitySpecifics result; |
- AddDefaultFieldValue(BOOKMARKS, &result); |
- return result; |
-} |
-} // namespace |
- |
-// A test fixture for tests exercising ApplyUpdatesCommand. |
-class ApplyUpdatesCommandTest : public SyncerCommandTest { |
- public: |
- protected: |
- ApplyUpdatesCommandTest() {} |
- 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())[BOOKMARKS] = GROUP_UI; |
- (*mutable_routing_info())[PASSWORDS] = GROUP_PASSWORD; |
- (*mutable_routing_info())[NIGORI] = GROUP_PASSIVE; |
- SyncerCommandTest::SetUp(); |
- entry_factory_.reset(new TestEntryFactory(directory())); |
- ExpectNoGroupsToChange(apply_updates_command_); |
- } |
- |
- protected: |
- DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesCommandTest); |
- |
- ApplyUpdatesCommand apply_updates_command_; |
- scoped_ptr<TestEntryFactory> entry_factory_; |
-}; |
- |
-TEST_F(ApplyUpdatesCommandTest, Simple) { |
- string root_server_id = syncable::GetNullId().GetServerId(); |
- entry_factory_->CreateUnappliedNewItemWithParent("parent", |
- DefaultBookmarkSpecifics(), |
- root_server_id); |
- entry_factory_->CreateUnappliedNewItemWithParent("child", |
- DefaultBookmarkSpecifics(), |
- "parent"); |
- |
- ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
- apply_updates_command_.ExecuteImpl(session()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- EXPECT_EQ(0, status.num_simple_conflicts()) |
- << "Simple update shouldn't result in conflicts"; |
- EXPECT_EQ(0, status.num_encryption_conflicts()) |
- << "Simple update shouldn't result in conflicts"; |
- EXPECT_EQ(0, status.num_hierarchy_conflicts()) |
- << "Simple update shouldn't result in conflicts"; |
- EXPECT_EQ(2, status.num_updates_applied()) |
- << "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(); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "a_child_created_first", DefaultBookmarkSpecifics(), "parent"); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "x_child_created_first", DefaultBookmarkSpecifics(), "parent"); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "parent", DefaultBookmarkSpecifics(), root_server_id); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "a_child_created_second", DefaultBookmarkSpecifics(), "parent"); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "x_child_created_second", DefaultBookmarkSpecifics(), "parent"); |
- |
- ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
- apply_updates_command_.ExecuteImpl(session()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- EXPECT_EQ(0, status.num_simple_conflicts()) |
- << "Simple update shouldn't result in conflicts, even if out-of-order"; |
- EXPECT_EQ(5, status.num_updates_applied()) |
- << "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) { |
- entry_factory_->CreateUnappliedAndUnsyncedItem("item", BOOKMARKS); |
- |
- ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
- apply_updates_command_.ExecuteImpl(session()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- EXPECT_EQ(1, status.num_simple_conflicts()) |
- << "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 = entry_factory_->CreateUnappliedAndUnsyncedItem( |
- "orphaned_by_server", 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, |
- TestIdFactory::MakeServer("bogus_parent")); |
- } |
- |
- ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
- apply_updates_command_.ExecuteImpl(session()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- |
- // An update that is both a simple conflict and a hierarchy conflict should be |
- // treated as a hierarchy conflict. |
- EXPECT_EQ(1, status.num_hierarchy_conflicts()); |
- EXPECT_EQ(0, status.num_simple_conflicts()); |
-} |
- |
- |
-// 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 = entry_factory_->CreateSyncedItem("X", 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, entry_factory_->GetNextRevision()); |
- entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); |
- entry.Put(syncable::SERVER_PARENT_ID, TestIdFactory::MakeServer("Y")); |
- } |
- |
- // Item 'Y' is child of 'X'. |
- entry_factory_->CreateUnsyncedItem( |
- TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true, |
- 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()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- |
- // This should count as a hierarchy conflict. |
- EXPECT_EQ(1, status.num_hierarchy_conflicts()); |
- EXPECT_EQ(0, status.num_simple_conflicts()); |
-} |
- |
-// 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; |
- entry_factory_->CreateUnsyncedItem( |
- Id::CreateFromServerId("parent"), TestIdFactory::root(), |
- "parent", true, 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. |
- entry_factory_->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()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- EXPECT_EQ(1, status.num_hierarchy_conflicts()); |
- EXPECT_EQ(0, status.num_simple_conflicts()); |
-} |
- |
-// 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 = |
- entry_factory_->CreateSyncedItem("parent", 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, entry_factory_->GetNextRevision()); |
- entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); |
- entry.Put(syncable::SERVER_PARENT_ID, TestIdFactory::root()); |
- entry.Put(syncable::SERVER_IS_DEL, true); |
- } |
- |
- // Create a local child of the server-deleted directory. |
- entry_factory_->CreateUnsyncedItem( |
- TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"), |
- "child", false, 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()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- // This should count as a hierarchy conflict. |
- EXPECT_EQ(1, status.num_hierarchy_conflicts()); |
- EXPECT_EQ(0, status.num_simple_conflicts()); |
-} |
- |
-// 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. |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "some_item", DefaultBookmarkSpecifics(), "unknown_parent"); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "some_other_item", DefaultBookmarkSpecifics(), "some_item"); |
- |
- ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
- apply_updates_command_.ExecuteImpl(session()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- EXPECT_EQ(0, status.num_simple_conflicts()) |
- << "Updates with unknown parent should not be treated as 'simple'" |
- << " conflicts"; |
- EXPECT_EQ(2, status.num_hierarchy_conflicts()) |
- << "All updates with an unknown ancestors should be in conflict"; |
- EXPECT_EQ(0, status.num_updates_applied()) |
- << "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(); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "first_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent"); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "first_known_item", DefaultBookmarkSpecifics(), root_server_id); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "second_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent"); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "second_known_item", DefaultBookmarkSpecifics(), "first_known_item"); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "third_known_item", DefaultBookmarkSpecifics(), "fourth_known_item"); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "fourth_known_item", DefaultBookmarkSpecifics(), root_server_id); |
- |
- ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
- apply_updates_command_.ExecuteImpl(session()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- EXPECT_EQ(2, status.num_hierarchy_conflicts()) |
- << "The updates with unknown ancestors should be in conflict"; |
- EXPECT_EQ(4, status.num_updates_applied()) |
- << "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. |
- syncable::ReadTransaction trans(FROM_HERE, directory()); |
- cryptographer = directory()->GetCryptographer(&trans); |
- } |
- |
- 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()); |
- entry_factory_->CreateUnappliedNewItem("item", specifics, false); |
- |
- ExpectGroupToChange(apply_updates_command_, GROUP_PASSWORD); |
- apply_updates_command_.ExecuteImpl(session()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- EXPECT_EQ(0, status.num_simple_conflicts()) |
- << "No update should be in conflict because they're all decryptable"; |
- EXPECT_EQ(1, status.num_updates_applied()) |
- << "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(BOOKMARKS, &encrypted_bookmark); |
- string root_server_id = syncable::GetNullId().GetServerId(); |
- entry_factory_->CreateUnappliedNewItemWithParent( |
- "folder", encrypted_bookmark, root_server_id); |
- entry_factory_->CreateUnappliedNewItem("item2", encrypted_bookmark, false); |
- sync_pb::EntitySpecifics encrypted_password; |
- encrypted_password.mutable_password(); |
- entry_factory_->CreateUnappliedNewItem("item3", encrypted_password, false); |
- |
- ExpectGroupsToChange(apply_updates_command_, GROUP_UI, GROUP_PASSWORD); |
- apply_updates_command_.ExecuteImpl(session()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- EXPECT_TRUE(status.HasConflictingUpdates()) |
- << "Updates that can't be decrypted should trigger the syncer to have " |
- << "conflicting updates."; |
- EXPECT_EQ(0, status.num_simple_conflicts()) |
- << "Updates that can't be decrypted should not be in regular conflict"; |
- EXPECT_EQ(3, status.num_encryption_conflicts()) |
- << "Updates that can't be decrypted should be in encryption conflict"; |
- EXPECT_EQ(0, status.num_updates_applied()) |
- << "No update that can't be decrypted should be applied"; |
-} |
- |
-TEST_F(ApplyUpdatesCommandTest, SomeUndecryptablePassword) { |
- Cryptographer* cryptographer; |
- // Only decryptable password updates should be applied. |
- { |
- sync_pb::EntitySpecifics specifics; |
- sync_pb::PasswordSpecificsData data; |
- data.set_origin("http://example.com/1"); |
- { |
- syncable::ReadTransaction trans(FROM_HERE, directory()); |
- cryptographer = directory()->GetCryptographer(&trans); |
- |
- KeyParams params = {"localhost", "dummy", "foobar"}; |
- cryptographer->AddKey(params); |
- |
- cryptographer->Encrypt(data, |
- specifics.mutable_password()->mutable_encrypted()); |
- } |
- entry_factory_->CreateUnappliedNewItem("item1", specifics, false); |
- } |
- { |
- // Create a new cryptographer, independent of the one in the session. |
- Cryptographer other_cryptographer(cryptographer->encryptor()); |
- KeyParams params = {"localhost", "dummy", "bazqux"}; |
- other_cryptographer.AddKey(params); |
- |
- sync_pb::EntitySpecifics specifics; |
- sync_pb::PasswordSpecificsData data; |
- data.set_origin("http://example.com/2"); |
- |
- other_cryptographer.Encrypt(data, |
- specifics.mutable_password()->mutable_encrypted()); |
- entry_factory_->CreateUnappliedNewItem("item2", specifics, false); |
- } |
- |
- ExpectGroupToChange(apply_updates_command_, GROUP_PASSWORD); |
- apply_updates_command_.ExecuteImpl(session()); |
- |
- const sessions::StatusController& status = session()->status_controller(); |
- EXPECT_TRUE(status.HasConflictingUpdates()) |
- << "Updates that can't be decrypted should trigger the syncer to have " |
- << "conflicting updates."; |
- EXPECT_EQ(0, status.num_simple_conflicts()) |
- << "The updates that can't be decrypted should not be in regular " |
- << "conflict"; |
- EXPECT_EQ(1, status.num_encryption_conflicts()) |
- << "The updates that can't be decrypted should be in encryption " |
- << "conflict"; |
- EXPECT_EQ(1, status.num_updates_applied()) |
- << "The undecryptable password update shouldn't be applied"; |
-} |
- |
-} // namespace syncer |