| 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
|
| index 5e14c59e4761ef0b2251a47f3c29299c940de5b4..eee3edb7714de6b73d9044e463cd076028acf720 100644
|
| --- a/sync/engine/apply_updates_command_unittest.cc
|
| +++ b/sync/engine/apply_updates_command_unittest.cc
|
| @@ -4,7 +4,6 @@
|
|
|
| #include <string>
|
|
|
| -#include "base/format_macros.h"
|
| #include "base/location.h"
|
| #include "base/memory/scoped_ptr.h"
|
| #include "base/stringprintf.h"
|
| @@ -13,9 +12,7 @@
|
| #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/sessions/sync_session.h"
|
| #include "sync/syncable/mutable_entry.h"
|
| -#include "sync/syncable/nigori_util.h"
|
| #include "sync/syncable/read_transaction.h"
|
| #include "sync/syncable/syncable_id.h"
|
| #include "sync/syncable/syncable_util.h"
|
| @@ -23,14 +20,12 @@
|
| #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_encryptor.h"
|
| #include "sync/test/fake_sync_encryption_handler.h"
|
| #include "sync/util/cryptographer.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| namespace syncer {
|
|
|
| -using sessions::SyncSession;
|
| using std::string;
|
| using syncable::Id;
|
| using syncable::MutableEntry;
|
| @@ -71,7 +66,6 @@ class ApplyUpdatesCommandTest : public SyncerCommandTest {
|
| DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesCommandTest);
|
|
|
| ApplyUpdatesCommand apply_updates_command_;
|
| - TestIdFactory id_factory_;
|
| scoped_ptr<TestEntryFactory> entry_factory_;
|
| };
|
|
|
| @@ -168,7 +162,7 @@ TEST_F(ApplyUpdatesCommandTest, HierarchyAndSimpleConflict) {
|
| ASSERT_TRUE(entry.good());
|
|
|
| entry.Put(syncable::SERVER_PARENT_ID,
|
| - id_factory_.MakeServer("bogus_parent"));
|
| + TestIdFactory::MakeServer("bogus_parent"));
|
| }
|
|
|
| ExpectGroupToChange(apply_updates_command_, GROUP_UI);
|
| @@ -205,12 +199,12 @@ TEST_F(ApplyUpdatesCommandTest, HierarchyConflictDirectoryLoop) {
|
| // 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, id_factory_.MakeServer("Y"));
|
| + entry.Put(syncable::SERVER_PARENT_ID, TestIdFactory::MakeServer("Y"));
|
| }
|
|
|
| // Item 'Y' is child of 'X'.
|
| entry_factory_->CreateUnsyncedItem(
|
| - id_factory_.MakeServer("Y"), id_factory_.MakeServer("X"), "Y", true,
|
| + 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
|
| @@ -240,7 +234,7 @@ TEST_F(ApplyUpdatesCommandTest, HierarchyConflictDeletedParent) {
|
| // Create a locally deleted parent item.
|
| int64 parent_handle;
|
| entry_factory_->CreateUnsyncedItem(
|
| - Id::CreateFromServerId("parent"), id_factory_.root(),
|
| + Id::CreateFromServerId("parent"), TestIdFactory::root(),
|
| "parent", true, BOOKMARKS, &parent_handle);
|
| {
|
| WriteTransaction trans(FROM_HERE, UNITTEST, directory());
|
| @@ -286,13 +280,13 @@ TEST_F(ApplyUpdatesCommandTest, HierarchyConflictDeleteNonEmptyDirectory) {
|
| // 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, id_factory_.root());
|
| + 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(
|
| - id_factory_.MakeServer("child"), id_factory_.MakeServer("parent"),
|
| + TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
|
| "child", false, BOOKMARKS, NULL);
|
|
|
| // The server's request to delete the directory must be ignored, otherwise our
|
| @@ -515,349 +509,4 @@ TEST_F(ApplyUpdatesCommandTest, SomeUndecryptablePassword) {
|
| }
|
| }
|
|
|
| -TEST_F(ApplyUpdatesCommandTest, NigoriUpdate) {
|
| - // Storing the cryptographer separately is bad, but for this test we
|
| - // know it's safe.
|
| - Cryptographer* cryptographer;
|
| - ModelTypeSet encrypted_types;
|
| - encrypted_types.Put(PASSWORDS);
|
| - encrypted_types.Put(NIGORI);
|
| - {
|
| - syncable::ReadTransaction trans(FROM_HERE, directory());
|
| - cryptographer = directory()->GetCryptographer(&trans);
|
| - EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
|
| - .Equals(encrypted_types));
|
| - }
|
| -
|
| - // Nigori node updates should update the Cryptographer.
|
| - Cryptographer other_cryptographer(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_everything(true);
|
| - entry_factory_->CreateUnappliedNewItem(
|
| - ModelTypeToRootTag(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());
|
| - {
|
| - syncable::ReadTransaction trans(FROM_HERE, directory());
|
| - EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
|
| - .Equals(ModelTypeSet::All()));
|
| - }
|
| -}
|
| -
|
| -TEST_F(ApplyUpdatesCommandTest, NigoriUpdateForDisabledTypes) {
|
| - // Storing the cryptographer separately is bad, but for this test we
|
| - // know it's safe.
|
| - Cryptographer* cryptographer;
|
| - ModelTypeSet encrypted_types;
|
| - encrypted_types.Put(PASSWORDS);
|
| - encrypted_types.Put(NIGORI);
|
| - {
|
| - syncable::ReadTransaction trans(FROM_HERE, directory());
|
| - cryptographer = directory()->GetCryptographer(&trans);
|
| - EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
|
| - .Equals(encrypted_types));
|
| - }
|
| -
|
| - // Nigori node updates should update the Cryptographer.
|
| - Cryptographer other_cryptographer(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_everything(true);
|
| - entry_factory_->CreateUnappliedNewItem(
|
| - ModelTypeToRootTag(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());
|
| - {
|
| - syncable::ReadTransaction trans(FROM_HERE, directory());
|
| - EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
|
| - .Equals(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;
|
| - ModelTypeSet encrypted_types;
|
| - encrypted_types.Put(PASSWORDS);
|
| - encrypted_types.Put(NIGORI);
|
| - {
|
| - syncable::ReadTransaction trans(FROM_HERE, directory());
|
| - cryptographer = directory()->GetCryptographer(&trans);
|
| - EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
|
| - .Equals(encrypted_types));
|
| -
|
| - // With default encrypted_types, this should be true.
|
| - EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
|
| -
|
| - Syncer::UnsyncedMetaHandles handles;
|
| - GetUnsyncedEntries(&trans, &handles);
|
| - EXPECT_TRUE(handles.empty());
|
| - }
|
| -
|
| - // Create unsynced bookmarks without encryption.
|
| - // First item is a folder
|
| - Id folder_id = id_factory_.NewLocalId();
|
| - entry_factory_->CreateUnsyncedItem(folder_id, id_factory_.root(), "folder",
|
| - true, 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) {
|
| - entry_factory_->CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
|
| - base::StringPrintf("Item %"PRIuS"", i),
|
| - false, BOOKMARKS, NULL);
|
| - }
|
| - // Next five items are children of the root.
|
| - for (; i < 2*batch_s; ++i) {
|
| - entry_factory_->CreateUnsyncedItem(
|
| - id_factory_.NewLocalId(), id_factory_.root(),
|
| - base::StringPrintf("Item %"PRIuS"", i), false,
|
| - 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_everything(true);
|
| - encrypted_types.Put(BOOKMARKS);
|
| - entry_factory_->CreateUnappliedNewItem(
|
| - ModelTypeToRootTag(NIGORI), specifics, true);
|
| - EXPECT_FALSE(cryptographer->has_pending_keys());
|
| - EXPECT_TRUE(cryptographer->is_ready());
|
| -
|
| - {
|
| - // Ensure we have unsynced nodes that aren't properly encrypted.
|
| - syncable::ReadTransaction trans(FROM_HERE, directory());
|
| - EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
|
| -
|
| - Syncer::UnsyncedMetaHandles handles;
|
| - 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());
|
| - {
|
| - syncable::ReadTransaction trans(FROM_HERE, directory());
|
| -
|
| - // If ProcessUnsyncedChangesForEncryption worked, all our unsynced changes
|
| - // should be encrypted now.
|
| - EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
|
| - .Equals(ModelTypeSet::All()));
|
| - EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
|
| -
|
| - Syncer::UnsyncedMetaHandles handles;
|
| - 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,
|
| - ModelTypeToRootTag(NIGORI));
|
| - ASSERT_TRUE(entry.good());
|
| - entry.Put(syncable::SERVER_VERSION, entry_factory_->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());
|
| - {
|
| - syncable::ReadTransaction trans(FROM_HERE, directory());
|
| -
|
| - // All our changes should still be encrypted.
|
| - EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
|
| - .Equals(ModelTypeSet::All()));
|
| - EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
|
| -
|
| - Syncer::UnsyncedMetaHandles handles;
|
| - 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;
|
| - ModelTypeSet encrypted_types;
|
| - encrypted_types.Put(PASSWORDS);
|
| - encrypted_types.Put(NIGORI);
|
| - {
|
| - syncable::ReadTransaction trans(FROM_HERE, directory());
|
| - cryptographer = directory()->GetCryptographer(&trans);
|
| - EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
|
| - .Equals(encrypted_types));
|
| -
|
| - // With default encrypted_types, this should be true.
|
| - EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
|
| -
|
| - Syncer::UnsyncedMetaHandles handles;
|
| - GetUnsyncedEntries(&trans, &handles);
|
| - EXPECT_TRUE(handles.empty());
|
| - }
|
| -
|
| - // Create unsynced bookmarks without encryption.
|
| - // First item is a folder
|
| - Id folder_id = id_factory_.NewLocalId();
|
| - entry_factory_->CreateUnsyncedItem(
|
| - folder_id, id_factory_.root(), "folder", true,
|
| - 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) {
|
| - entry_factory_->CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
|
| - base::StringPrintf("Item %"PRIuS"", i),
|
| - false, BOOKMARKS, NULL);
|
| - }
|
| - // Next five items are children of the root.
|
| - for (; i < 2*batch_s; ++i) {
|
| - entry_factory_->CreateUnsyncedItem(
|
| - id_factory_.NewLocalId(), id_factory_.root(),
|
| - base::StringPrintf("Item %"PRIuS"", i), false,
|
| - BOOKMARKS, NULL);
|
| - }
|
| -
|
| - // We encrypt with new keys, triggering the local cryptographer to be unready
|
| - // and unable to decrypt data (once updated).
|
| - Cryptographer other_cryptographer(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_everything(true);
|
| - encrypted_types.Put(BOOKMARKS);
|
| - entry_factory_->CreateUnappliedNewItem(
|
| - ModelTypeToRootTag(NIGORI), specifics, true);
|
| - EXPECT_FALSE(cryptographer->has_pending_keys());
|
| -
|
| - {
|
| - // Ensure we have unsynced nodes that aren't properly encrypted.
|
| - syncable::ReadTransaction trans(FROM_HERE, directory());
|
| - EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
|
| - Syncer::UnsyncedMetaHandles handles;
|
| - 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());
|
| - {
|
| - syncable::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(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
|
| - .Equals(ModelTypeSet::All()));
|
| - EXPECT_FALSE(cryptographer->is_ready());
|
| - EXPECT_TRUE(cryptographer->has_pending_keys());
|
| -
|
| - Syncer::UnsyncedMetaHandles handles;
|
| - GetUnsyncedEntries(&trans, &handles);
|
| - EXPECT_EQ(2*batch_s+1, handles.size());
|
| - }
|
| -}
|
| -
|
| } // namespace syncer
|
|
|