summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/tools/testserver/chromiumsync.py56
-rwxr-xr-xnet/tools/testserver/chromiumsync_test.py14
-rw-r--r--sync/engine/apply_control_data_updates.cc124
-rw-r--r--sync/engine/apply_control_data_updates.h13
-rw-r--r--sync/engine/apply_control_data_updates_unittest.cc96
-rw-r--r--sync/engine/syncer.cc2
-rw-r--r--sync/engine/syncer_util.cc5
-rw-r--r--sync/internal_api/public/test/test_entry_factory.h2
-rw-r--r--sync/internal_api/test/test_entry_factory.cc14
-rw-r--r--sync/syncable/model_type.cc6
10 files changed, 241 insertions, 91 deletions
diff --git a/net/tools/testserver/chromiumsync.py b/net/tools/testserver/chromiumsync.py
index 62156b2..92542e3 100644
--- a/net/tools/testserver/chromiumsync.py
+++ b/net/tools/testserver/chromiumsync.py
@@ -50,6 +50,8 @@ ALL_TYPES = (
AUTOFILL,
AUTOFILL_PROFILE,
BOOKMARK,
+ DEVICE_INFO,
+ EXPERIMENTS,
EXTENSIONS,
HISTORY_DELETE_DIRECTIVE,
NIGORI,
@@ -59,7 +61,7 @@ ALL_TYPES = (
SESSION,
THEME,
TYPED_URL,
- EXTENSION_SETTINGS) = range(17)
+ EXTENSION_SETTINGS) = range(19)
# An eumeration on the frequency at which the server should send errors
# to the client. This would be specified by the url that triggers the error.
@@ -82,6 +84,8 @@ SYNC_TYPE_TO_DESCRIPTOR = {
AUTOFILL: SYNC_TYPE_FIELDS['autofill'],
AUTOFILL_PROFILE: SYNC_TYPE_FIELDS['autofill_profile'],
BOOKMARK: SYNC_TYPE_FIELDS['bookmark'],
+ DEVICE_INFO: SYNC_TYPE_FIELDS['device_info'],
+ EXPERIMENTS: SYNC_TYPE_FIELDS['experiments'],
EXTENSION_SETTINGS: SYNC_TYPE_FIELDS['extension_setting'],
EXTENSIONS: SYNC_TYPE_FIELDS['extension'],
HISTORY_DELETE_DIRECTIVE: SYNC_TYPE_FIELDS['history_delete_directive'],
@@ -415,10 +419,15 @@ class SyncDataModel(object):
# Specify all the permanent items that a model might need.
_PERMANENT_ITEM_SPECS = [
- PermanentItem('google_chrome', name='Google Chrome',
- parent_tag=ROOT_ID, sync_type=TOP_LEVEL),
+ PermanentItem('google_chrome_apps', name='Apps',
+ parent_tag=ROOT_ID, sync_type=APPS),
+ PermanentItem('google_chrome_app_notifications', name='App Notifications',
+ parent_tag=ROOT_ID, sync_type=APP_NOTIFICATION),
+ PermanentItem('google_chrome_app_settings',
+ name='App Settings',
+ parent_tag=ROOT_ID, sync_type=APP_SETTINGS),
PermanentItem('google_chrome_bookmarks', name='Bookmarks',
- parent_tag='google_chrome', sync_type=BOOKMARK),
+ parent_tag=ROOT_ID, sync_type=BOOKMARK),
PermanentItem('bookmark_bar', name='Bookmark Bar',
parent_tag='google_chrome_bookmarks', sync_type=BOOKMARK),
PermanentItem('synced_bookmarks', name='Mobile Bookmarks',
@@ -428,40 +437,37 @@ class SyncDataModel(object):
PermanentItem('synced_bookmarks', name='Synced Bookmarks',
parent_tag='google_chrome_bookmarks', sync_type=BOOKMARK,
create_by_default=False),
- PermanentItem('google_chrome_preferences', name='Preferences',
- parent_tag='google_chrome', sync_type=PREFERENCE),
PermanentItem('google_chrome_autofill', name='Autofill',
- parent_tag='google_chrome', sync_type=AUTOFILL),
+ parent_tag=ROOT_ID, sync_type=AUTOFILL),
PermanentItem('google_chrome_autofill_profiles', name='Autofill Profiles',
- parent_tag='google_chrome', sync_type=AUTOFILL_PROFILE),
- PermanentItem('google_chrome_app_settings',
- name='App Settings',
- parent_tag='google_chrome', sync_type=APP_SETTINGS),
+ parent_tag=ROOT_ID, sync_type=AUTOFILL_PROFILE),
+ PermanentItem('google_chrome_device_info', name='Device Info',
+ parent_tag=ROOT_ID, sync_type=DEVICE_INFO),
+ PermanentItem('google_chrome_experiments', name='Experiments',
+ parent_tag=ROOT_ID, sync_type=EXPERIMENTS),
PermanentItem('google_chrome_extension_settings',
name='Extension Settings',
- parent_tag='google_chrome', sync_type=EXTENSION_SETTINGS),
+ parent_tag=ROOT_ID, sync_type=EXTENSION_SETTINGS),
PermanentItem('google_chrome_extensions', name='Extensions',
- parent_tag='google_chrome', sync_type=EXTENSIONS),
+ parent_tag=ROOT_ID, sync_type=EXTENSIONS),
PermanentItem('google_chrome_history_delete_directives',
name='History Delete Directives',
- parent_tag='google_chrome',
+ parent_tag=ROOT_ID,
sync_type=HISTORY_DELETE_DIRECTIVE),
+ PermanentItem('google_chrome_nigori', name='Nigori',
+ parent_tag=ROOT_ID, sync_type=NIGORI),
PermanentItem('google_chrome_passwords', name='Passwords',
- parent_tag='google_chrome', sync_type=PASSWORD),
+ parent_tag=ROOT_ID, sync_type=PASSWORD),
+ PermanentItem('google_chrome_preferences', name='Preferences',
+ parent_tag=ROOT_ID, sync_type=PREFERENCE),
PermanentItem('google_chrome_search_engines', name='Search Engines',
- parent_tag='google_chrome', sync_type=SEARCH_ENGINE),
+ parent_tag=ROOT_ID, sync_type=SEARCH_ENGINE),
PermanentItem('google_chrome_sessions', name='Sessions',
- parent_tag='google_chrome', sync_type=SESSION),
+ parent_tag=ROOT_ID, sync_type=SESSION),
PermanentItem('google_chrome_themes', name='Themes',
- parent_tag='google_chrome', sync_type=THEME),
+ parent_tag=ROOT_ID, sync_type=THEME),
PermanentItem('google_chrome_typed_urls', name='Typed URLs',
- parent_tag='google_chrome', sync_type=TYPED_URL),
- PermanentItem('google_chrome_nigori', name='Nigori',
- parent_tag='google_chrome', sync_type=NIGORI),
- PermanentItem('google_chrome_apps', name='Apps',
- parent_tag='google_chrome', sync_type=APPS),
- PermanentItem('google_chrome_app_notifications', name='App Notifications',
- parent_tag='google_chrome', sync_type=APP_NOTIFICATION),
+ parent_tag=ROOT_ID, sync_type=TYPED_URL),
]
def __init__(self):
diff --git a/net/tools/testserver/chromiumsync_test.py b/net/tools/testserver/chromiumsync_test.py
index e680d41..7a68b40 100755
--- a/net/tools/testserver/chromiumsync_test.py
+++ b/net/tools/testserver/chromiumsync_test.py
@@ -50,7 +50,8 @@ class SyncDataModelTest(unittest.TestCase):
declared_specs.add(spec.tag)
unique_datatypes = set([x.sync_type for x in specs])
- self.assertEqual(unique_datatypes, set(chromiumsync.ALL_TYPES),
+ self.assertEqual(unique_datatypes,
+ set(chromiumsync.ALL_TYPES[1:]),
'Every sync datatype should have a permanent folder '
'associated with it')
@@ -70,19 +71,17 @@ class SyncDataModelTest(unittest.TestCase):
def testCreatePermanentItems(self):
self.model._CreateDefaultPermanentItems(chromiumsync.ALL_TYPES)
- self.assertEqual(len(chromiumsync.ALL_TYPES) + 2,
+ self.assertEqual(len(chromiumsync.ALL_TYPES) + 1,
len(self.model._entries))
def ExpectedPermanentItemCount(self, sync_type):
if sync_type == chromiumsync.BOOKMARK:
if self._expect_synced_bookmarks_folder:
- return 5
- else:
return 4
- elif sync_type == chromiumsync.TOP_LEVEL:
- return 1
+ else:
+ return 3
else:
- return 2
+ return 1
def testGetChangesFromTimestampZeroForEachType(self):
all_types = chromiumsync.ALL_TYPES[1:]
@@ -96,7 +95,6 @@ class SyncDataModelTest(unittest.TestCase):
expected_count = self.ExpectedPermanentItemCount(sync_type)
self.assertEqual(expected_count, version)
self.assertEqual(expected_count, len(changes))
- self.assertEqual('google_chrome', changes[0].server_defined_unique_tag)
for change in changes:
self.assertTrue(change.HasField('server_defined_unique_tag'))
self.assertEqual(change.version, change.sync_timestamp)
diff --git a/sync/engine/apply_control_data_updates.cc b/sync/engine/apply_control_data_updates.cc
index d0f182d..2354c48 100644
--- a/sync/engine/apply_control_data_updates.cc
+++ b/sync/engine/apply_control_data_updates.cc
@@ -24,15 +24,74 @@ using syncable::SERVER_SPECIFICS;
using syncable::SPECIFICS;
using syncable::SYNCER;
-void ApplyControlDataUpdates(syncable::Directory* dir) {
+void ApplyControlDataUpdates(sessions::SyncSession* session) {
+ syncable::Directory* dir = session->context()->directory();
syncable::WriteTransaction trans(FROM_HERE, SYNCER, dir);
- if (ApplyNigoriUpdates(&trans, dir->GetCryptographer(&trans))) {
- dir->set_initial_sync_ended_for_type(NIGORI, true);
+ std::vector<int64> handles;
+ dir->GetUnappliedUpdateMetaHandles(
+ &trans, ToFullModelTypeSet(ControlTypes()), &handles);
+
+ // First, go through and manually apply any new top level datatype nodes (so
+ // that we don't have to worry about hitting a CONFLICT_HIERARCHY with an
+ // entry because we haven't applied its parent yet).
+ // TODO(sync): if at some point we support control datatypes with actual
+ // hierarchies we'll need to revisit this logic.
+ for (ModelTypeSet::Iterator iter = ControlTypes().First(); iter.Good();
+ iter.Inc()) {
+ syncable::MutableEntry entry(&trans,
+ syncable::GET_BY_SERVER_TAG,
+ ModelTypeToRootTag(iter.Get()));
+ if (!entry.good())
+ continue;
+ if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE))
+ continue;
+
+ ModelType type = entry.GetServerModelType();
+ if (type == NIGORI) {
+ // Nigori node applications never fail.
+ ApplyNigoriUpdate(&trans,
+ &entry,
+ dir->GetCryptographer(&trans));
+ } else {
+ ApplyControlUpdate(&trans,
+ &entry,
+ dir->GetCryptographer(&trans));
+ }
+ }
+
+ // Go through the rest of the unapplied control updates, skipping over any
+ // top level folders.
+ for (std::vector<int64>::const_iterator iter = handles.begin();
+ iter != handles.end(); ++iter) {
+ syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, *iter);
+ CHECK(entry.good());
+ ModelType type = entry.GetServerModelType();
+ CHECK(ControlTypes().Has(type));
+ if (!entry.Get(syncable::UNIQUE_SERVER_TAG).empty()) {
+ // We should have already applied all top level control nodes.
+ DCHECK(!entry.Get(syncable::IS_UNAPPLIED_UPDATE));
+ continue;
+ }
+
+ ApplyControlUpdate(&trans,
+ &entry,
+ dir->GetCryptographer(&trans));
+ }
+
+ // Set initial sync ended bits for all control types requested.
+ for (ModelTypeSet::Iterator it =
+ session->status_controller().updates_request_types().First();
+ it.Good(); it.Inc()) {
+ if (!IsControlType(it.Get()))
+ continue;
+
+ // This gets persisted to the directory's backing store.
+ dir->set_initial_sync_ended_for_type(it.Get(), true);
}
}
-// Update the sync encryption handler with the server's nigori node.
+// Update the nigori handler with the server's nigori node.
//
// If we have a locally modified nigori node, we merge them manually. This
// handles the case where two clients both set a different passphrase. The
@@ -43,24 +102,18 @@ void ApplyControlDataUpdates(syncable::Directory* dir) {
// passphrase, the cryptographer will preserve the encryption keys based on the
// local passphrase, while the nigori node will preserve the server encryption
// keys.
-bool ApplyNigoriUpdates(syncable::WriteTransaction* trans,
- Cryptographer* cryptographer) {
- syncable::MutableEntry nigori_node(trans, GET_BY_SERVER_TAG,
- ModelTypeToRootTag(NIGORI));
-
- // Mainly for unit tests. We should have a Nigori node by this point.
- if (!nigori_node.good()) {
- return false;
- }
-
- if (!nigori_node.Get(IS_UNAPPLIED_UPDATE)) {
- return true;
- }
+void ApplyNigoriUpdate(syncable::WriteTransaction* const trans,
+ syncable::MutableEntry* const entry,
+ Cryptographer* cryptographer) {
+ DCHECK(entry->Get(IS_UNAPPLIED_UPDATE));
// We apply the nigori update regardless of whether there's a conflict or
// not in order to preserve any new encrypted types or encryption keys.
+ // TODO(zea): consider having this return a bool reflecting whether it was a
+ // valid update or not, and in the case of invalid updates not overwrite the
+ // local data.
const sync_pb::NigoriSpecifics& nigori =
- nigori_node.Get(SERVER_SPECIFICS).nigori();
+ entry->Get(SERVER_SPECIFICS).nigori();
trans->directory()->GetNigoriHandler()->ApplyNigoriUpdate(nigori, trans);
// Make sure any unsynced changes are properly encrypted as necessary.
@@ -82,19 +135,19 @@ bool ApplyNigoriUpdates(syncable::WriteTransaction* trans,
syncable::ProcessUnsyncedChangesForEncryption(trans);
}
- if (!nigori_node.Get(IS_UNSYNCED)) { // Update only.
- UpdateLocalDataFromServerData(trans, &nigori_node);
+ if (!entry->Get(IS_UNSYNCED)) { // Update only.
+ UpdateLocalDataFromServerData(trans, entry);
} else { // Conflict.
const sync_pb::EntitySpecifics& server_specifics =
- nigori_node.Get(SERVER_SPECIFICS);
+ entry->Get(SERVER_SPECIFICS);
const sync_pb::NigoriSpecifics& server_nigori = server_specifics.nigori();
const sync_pb::EntitySpecifics& local_specifics =
- nigori_node.Get(SPECIFICS);
+ entry->Get(SPECIFICS);
const sync_pb::NigoriSpecifics& local_nigori = local_specifics.nigori();
// We initialize the new nigori with the server state, and will override
// it as necessary below.
- sync_pb::EntitySpecifics new_specifics = nigori_node.Get(SERVER_SPECIFICS);
+ sync_pb::EntitySpecifics new_specifics = entry->Get(SERVER_SPECIFICS);
sync_pb::NigoriSpecifics* new_nigori = new_specifics.mutable_nigori();
// If the cryptographer is not ready, another client set a new encryption
@@ -145,18 +198,35 @@ bool ApplyNigoriUpdates(syncable::WriteTransaction* trans,
new_nigori,
trans);
- nigori_node.Put(SPECIFICS, new_specifics);
+ entry->Put(SPECIFICS, new_specifics);
DVLOG(1) << "Resolving simple conflict, merging nigori nodes: "
- << nigori_node;
+ << entry;
- conflict_util::OverwriteServerChanges(&nigori_node);
+ conflict_util::OverwriteServerChanges(entry);
UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
ConflictResolver::NIGORI_MERGE,
ConflictResolver::CONFLICT_RESOLUTION_SIZE);
}
+}
+
+void ApplyControlUpdate(syncable::WriteTransaction* const trans,
+ syncable::MutableEntry* const entry,
+ Cryptographer* cryptographer) {
+ DCHECK_NE(entry->GetServerModelType(), NIGORI);
+ DCHECK(entry->Get(IS_UNAPPLIED_UPDATE));
+ if (entry->Get(IS_UNSYNCED)) {
+ // We just let the server win all conflicts with control types.
+ DVLOG(1) << "Ignoring local changes for control update.";
+ conflict_util::IgnoreLocalChanges(entry);
+ UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
+ ConflictResolver::OVERWRITE_LOCAL,
+ ConflictResolver::CONFLICT_RESOLUTION_SIZE);
+ }
- return true;
+ UpdateAttemptResponse response = AttemptToUpdateEntry(
+ trans, entry, cryptographer);
+ DCHECK_EQ(SUCCESS, response);
}
} // namespace syncer
diff --git a/sync/engine/apply_control_data_updates.h b/sync/engine/apply_control_data_updates.h
index efe2cc8..8d19f47 100644
--- a/sync/engine/apply_control_data_updates.h
+++ b/sync/engine/apply_control_data_updates.h
@@ -9,13 +9,22 @@ namespace syncer {
class Cryptographer;
+namespace sessions {
+class SyncSession;
+}
+
namespace syncable {
class Directory;
+class MutableEntry;
class WriteTransaction;
}
-void ApplyControlDataUpdates(syncable::Directory* dir);
-bool ApplyNigoriUpdates(syncable::WriteTransaction* trans,
+void ApplyControlDataUpdates(sessions::SyncSession* session);
+void ApplyNigoriUpdate(syncable::WriteTransaction* trans,
+ syncable::MutableEntry* const entry,
+ Cryptographer* cryptographer);
+void ApplyControlUpdate(syncable::WriteTransaction* const trans,
+ syncable::MutableEntry* const entry,
Cryptographer* cryptographer);
} // namespace syncer
diff --git a/sync/engine/apply_control_data_updates_unittest.cc b/sync/engine/apply_control_data_updates_unittest.cc
index aa00865..09c5e11 100644
--- a/sync/engine/apply_control_data_updates_unittest.cc
+++ b/sync/engine/apply_control_data_updates_unittest.cc
@@ -41,12 +41,14 @@ class ApplyControlDataUpdatesTest : public SyncerCommandTest {
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;
+ (*mutable_routing_info())[EXPERIMENTS] = GROUP_PASSIVE;
SyncerCommandTest::SetUp();
entry_factory_.reset(new TestEntryFactory(directory()));
+ session()->mutable_status_controller()->set_updates_request_types(
+ ControlTypes());
+
syncable::ReadTransaction trans(FROM_HERE, directory());
}
@@ -90,7 +92,7 @@ TEST_F(ApplyControlDataUpdatesTest, NigoriUpdate) {
ModelTypeToRootTag(NIGORI), specifics, true);
EXPECT_FALSE(cryptographer->has_pending_keys());
- ApplyControlDataUpdates(directory());
+ ApplyControlDataUpdates(session());
EXPECT_FALSE(cryptographer->is_ready());
EXPECT_TRUE(cryptographer->has_pending_keys());
@@ -170,7 +172,7 @@ TEST_F(ApplyControlDataUpdatesTest, EncryptUnsyncedChanges) {
EXPECT_EQ(2*batch_s+1, handles.size());
}
- ApplyControlDataUpdates(directory());
+ ApplyControlDataUpdates(session());
EXPECT_FALSE(cryptographer->has_pending_keys());
EXPECT_TRUE(cryptographer->is_ready());
@@ -198,7 +200,7 @@ TEST_F(ApplyControlDataUpdatesTest, EncryptUnsyncedChanges) {
entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
}
- ApplyControlDataUpdates(directory());
+ ApplyControlDataUpdates(session());
EXPECT_FALSE(cryptographer->has_pending_keys());
EXPECT_TRUE(cryptographer->is_ready());
@@ -285,7 +287,7 @@ TEST_F(ApplyControlDataUpdatesTest, CannotEncryptUnsyncedChanges) {
EXPECT_EQ(2*batch_s+1, handles.size());
}
- ApplyControlDataUpdates(directory());
+ ApplyControlDataUpdates(session());
EXPECT_FALSE(cryptographer->is_ready());
EXPECT_TRUE(cryptographer->has_pending_keys());
@@ -363,7 +365,7 @@ TEST_F(ApplyControlDataUpdatesTest,
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
- ApplyControlDataUpdates(directory());
+ ApplyControlDataUpdates(session());
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
@@ -442,7 +444,7 @@ TEST_F(ApplyControlDataUpdatesTest,
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
- ApplyControlDataUpdates(directory());
+ ApplyControlDataUpdates(session());
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
@@ -516,7 +518,7 @@ TEST_F(ApplyControlDataUpdatesTest,
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
- ApplyControlDataUpdates(directory());
+ ApplyControlDataUpdates(session());
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
@@ -596,7 +598,7 @@ TEST_F(ApplyControlDataUpdatesTest,
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
- ApplyControlDataUpdates(directory());
+ ApplyControlDataUpdates(session());
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
@@ -679,7 +681,7 @@ TEST_F(ApplyControlDataUpdatesTest,
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
- ApplyControlDataUpdates(directory());
+ ApplyControlDataUpdates(session());
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
@@ -760,7 +762,7 @@ TEST_F(ApplyControlDataUpdatesTest,
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
- ApplyControlDataUpdates(directory());
+ ApplyControlDataUpdates(session());
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
@@ -841,7 +843,7 @@ TEST_F(ApplyControlDataUpdatesTest,
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
- ApplyControlDataUpdates(directory());
+ ApplyControlDataUpdates(session());
EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
@@ -868,4 +870,72 @@ TEST_F(ApplyControlDataUpdatesTest,
}
}
+// Check that we can apply a simple control datatype node successfully.
+TEST_F(ApplyControlDataUpdatesTest, ControlApply) {
+ EXPECT_FALSE(directory()->initial_sync_ended_types().Has(EXPERIMENTS));
+
+ std::string experiment_id = "experiment";
+ sync_pb::EntitySpecifics specifics;
+ specifics.mutable_experiments()->mutable_keystore_encryption()->
+ set_enabled(true);
+ int64 experiment_handle = entry_factory_->CreateUnappliedNewItem(
+ experiment_id, specifics, false);
+ ApplyControlDataUpdates(session());
+
+ EXPECT_TRUE(directory()->initial_sync_ended_types().Has(EXPERIMENTS));
+ EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(experiment_handle));
+ EXPECT_TRUE(
+ entry_factory_->GetLocalSpecificsForItem(experiment_handle).
+ experiments().keystore_encryption().enabled());
+}
+
+// Verify that we apply top level folders before their children.
+TEST_F(ApplyControlDataUpdatesTest, ControlApplyParentBeforeChild) {
+ EXPECT_FALSE(directory()->initial_sync_ended_types().Has(EXPERIMENTS));
+
+ std::string parent_id = "parent";
+ std::string experiment_id = "experiment";
+ sync_pb::EntitySpecifics specifics;
+ specifics.mutable_experiments()->mutable_keystore_encryption()->
+ set_enabled(true);
+ int64 experiment_handle = entry_factory_->CreateUnappliedNewItemWithParent(
+ experiment_id, specifics, parent_id);
+ int64 parent_handle = entry_factory_->CreateUnappliedNewItem(
+ parent_id, specifics, true);
+ ApplyControlDataUpdates(session());
+
+ EXPECT_TRUE(directory()->initial_sync_ended_types().Has(EXPERIMENTS));
+ EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(parent_handle));
+ EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(experiment_handle));
+ EXPECT_TRUE(
+ entry_factory_->GetLocalSpecificsForItem(experiment_handle).
+ experiments().keystore_encryption().enabled());
+}
+
+// Verify that we handle control datatype conflicts by preserving the server
+// data.
+TEST_F(ApplyControlDataUpdatesTest, ControlConflict) {
+ EXPECT_FALSE(directory()->initial_sync_ended_types().Has(EXPERIMENTS));
+
+ std::string experiment_id = "experiment";
+ sync_pb::EntitySpecifics local_specifics, server_specifics;
+ server_specifics.mutable_experiments()->mutable_keystore_encryption()->
+ set_enabled(true);
+ local_specifics.mutable_experiments()->mutable_keystore_encryption()->
+ set_enabled(false);
+ int64 experiment_handle = entry_factory_->CreateSyncedItem(
+ experiment_id, EXPERIMENTS, false);
+ entry_factory_->SetServerSpecificsForItem(experiment_handle,
+ server_specifics);
+ entry_factory_->SetLocalSpecificsForItem(experiment_handle,
+ local_specifics);
+ ApplyControlDataUpdates(session());
+
+ EXPECT_TRUE(directory()->initial_sync_ended_types().Has(EXPERIMENTS));
+ EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(experiment_handle));
+ EXPECT_TRUE(
+ entry_factory_->GetLocalSpecificsForItem(experiment_handle).
+ experiments().keystore_encryption().enabled());
+}
+
} // namespace syncer
diff --git a/sync/engine/syncer.cc b/sync/engine/syncer.cc
index 22010ee..90d193a 100644
--- a/sync/engine/syncer.cc
+++ b/sync/engine/syncer.cc
@@ -144,7 +144,7 @@ bool Syncer::SyncShare(sessions::SyncSession* session,
}
case APPLY_UPDATES: {
// These include encryption updates that should be applied early.
- ApplyControlDataUpdates(session->context()->directory());
+ ApplyControlDataUpdates(session);
ApplyUpdatesAndResolveConflictsCommand apply_updates;
apply_updates.Execute(session);
diff --git a/sync/engine/syncer_util.cc b/sync/engine/syncer_util.cc
index cdc1b7a..b88bc7c 100644
--- a/sync/engine/syncer_util.cc
+++ b/sync/engine/syncer_util.cc
@@ -209,7 +209,7 @@ UpdateAttemptResponse AttemptToUpdateEntry(
// We can't decrypt this node yet.
DVLOG(1) << "Received an undecryptable "
<< ModelTypeToString(entry->GetServerModelType())
- << " update, returning encryption_conflict.";
+ << " update, returning conflict_encryption.";
return CONFLICT_ENCRYPTION;
} else if (specifics.has_password() &&
entry->Get(UNIQUE_SERVER_TAG).empty()) {
@@ -217,7 +217,7 @@ UpdateAttemptResponse AttemptToUpdateEntry(
const sync_pb::PasswordSpecifics& password = specifics.password();
if (!cryptographer->CanDecrypt(password.encrypted())) {
DVLOG(1) << "Received an undecryptable password update, returning "
- << "encryption_conflict.";
+ << "conflict_encryption.";
return CONFLICT_ENCRYPTION;
}
}
@@ -232,6 +232,7 @@ UpdateAttemptResponse AttemptToUpdateEntry(
// different ways we deal with it once here to reduce the amount of code and
// potential errors.
if (!parent.good() || parent.Get(IS_DEL) || !parent.Get(IS_DIR)) {
+ DVLOG(1) << "Entry has bad parent, returning conflict_hierarchy.";
return CONFLICT_HIERARCHY;
}
if (entry->Get(PARENT_ID) != new_parent) {
diff --git a/sync/internal_api/public/test/test_entry_factory.h b/sync/internal_api/public/test/test_entry_factory.h
index 1661a33..d5bc911 100644
--- a/sync/internal_api/public/test/test_entry_factory.h
+++ b/sync/internal_api/public/test/test_entry_factory.h
@@ -24,7 +24,7 @@ class TestEntryFactory {
~TestEntryFactory();
// Create a new unapplied folder node with a parent.
- void CreateUnappliedNewItemWithParent(
+ int64 CreateUnappliedNewItemWithParent(
const std::string& item_id,
const sync_pb::EntitySpecifics& specifics,
const std::string& parent_id);
diff --git a/sync/internal_api/test/test_entry_factory.cc b/sync/internal_api/test/test_entry_factory.cc
index 3fa0d67..3cf0dcf 100644
--- a/sync/internal_api/test/test_entry_factory.cc
+++ b/sync/internal_api/test/test_entry_factory.cc
@@ -27,7 +27,7 @@ TestEntryFactory::TestEntryFactory(syncable::Directory *dir)
TestEntryFactory::~TestEntryFactory() { }
-void TestEntryFactory::CreateUnappliedNewItemWithParent(
+int64 TestEntryFactory::CreateUnappliedNewItemWithParent(
const string& item_id,
const sync_pb::EntitySpecifics& specifics,
const string& parent_id) {
@@ -42,6 +42,7 @@ void TestEntryFactory::CreateUnappliedNewItemWithParent(
entry.Put(syncable::SERVER_PARENT_ID, Id::CreateFromServerId(parent_id));
entry.Put(syncable::SERVER_IS_DIR, true);
entry.Put(syncable::SERVER_SPECIFICS, specifics);
+ return entry.Get(syncable::META_HANDLE);
}
int64 TestEntryFactory::CreateUnappliedNewItem(
@@ -56,10 +57,12 @@ int64 TestEntryFactory::CreateUnappliedNewItem(
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_IS_DIR, is_unique);
entry.Put(syncable::SERVER_SPECIFICS, specifics);
- if (is_unique) // For top-level nodes.
- entry.Put(syncable::UNIQUE_SERVER_TAG, item_id);
+ if (is_unique) { // For top-level nodes.
+ entry.Put(syncable::UNIQUE_SERVER_TAG,
+ ModelTypeToRootTag(GetModelTypeFromSpecifics(specifics)));
+ }
return entry.Get(syncable::META_HANDLE);
}
@@ -119,8 +122,7 @@ int64 TestEntryFactory::CreateUnappliedAndUnsyncedItem(
}
int64 TestEntryFactory::CreateSyncedItem(
- const std::string& name, ModelType
- model_type, bool is_folder) {
+ const std::string& name, ModelType model_type, bool is_folder) {
WriteTransaction trans(FROM_HERE, UNITTEST, directory_);
syncable::Id parent_id(TestIdFactory::root());
diff --git a/sync/syncable/model_type.cc b/sync/syncable/model_type.cc
index 08094d0..546ac0c8 100644
--- a/sync/syncable/model_type.cc
+++ b/sync/syncable/model_type.cc
@@ -281,12 +281,6 @@ ModelTypeSet ControlTypes() {
set.Put(ModelTypeFromInt(i));
}
- // TODO(rlarocque): Re-enable this when the server supports it.
- set.Remove(DEVICE_INFO);
-
- // TODO(rlarocque): Re-enable this when the server supports it.
- set.Remove(EXPERIMENTS);
-
return set;
}