OLD | NEW |
1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <string> | |
6 | |
7 #include "base/format_macros.h" | |
8 #include "base/string_util.h" | |
9 #include "chrome/browser/sync/engine/apply_updates_command.h" | 5 #include "chrome/browser/sync/engine/apply_updates_command.h" |
10 #include "chrome/browser/sync/engine/syncer.h" | |
11 #include "chrome/browser/sync/engine/syncer_util.h" | |
12 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" | 6 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" |
13 #include "chrome/browser/sync/sessions/sync_session.h" | 7 #include "chrome/browser/sync/sessions/sync_session.h" |
14 #include "chrome/browser/sync/syncable/directory_manager.h" | 8 #include "chrome/browser/sync/syncable/directory_manager.h" |
15 #include "chrome/browser/sync/syncable/nigori_util.h" | |
16 #include "chrome/browser/sync/syncable/syncable.h" | 9 #include "chrome/browser/sync/syncable/syncable.h" |
17 #include "chrome/browser/sync/syncable/syncable_id.h" | 10 #include "chrome/browser/sync/syncable/syncable_id.h" |
18 #include "chrome/test/sync/engine/syncer_command_test.h" | 11 #include "chrome/test/sync/engine/syncer_command_test.h" |
19 #include "chrome/test/sync/engine/test_id_factory.h" | |
20 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
21 | 13 |
22 namespace browser_sync { | 14 namespace browser_sync { |
23 | 15 |
24 using sessions::SyncSession; | 16 using sessions::SyncSession; |
25 using std::string; | 17 using std::string; |
26 using syncable::Entry; | 18 using syncable::Entry; |
27 using syncable::GetEncryptedDataTypes; | |
28 using syncable::Id; | 19 using syncable::Id; |
29 using syncable::MutableEntry; | 20 using syncable::MutableEntry; |
30 using syncable::ReadTransaction; | 21 using syncable::ReadTransaction; |
31 using syncable::ScopedDirLookup; | 22 using syncable::ScopedDirLookup; |
32 using syncable::UNITTEST; | 23 using syncable::UNITTEST; |
33 using syncable::WriteTransaction; | 24 using syncable::WriteTransaction; |
34 | 25 |
35 // A test fixture for tests exercising ApplyUpdatesCommand. | 26 // A test fixture for tests exercising ApplyUpdatesCommand. |
36 class ApplyUpdatesCommandTest : public SyncerCommandTest { | 27 class ApplyUpdatesCommandTest : public SyncerCommandTest { |
37 public: | 28 public: |
38 protected: | 29 protected: |
39 ApplyUpdatesCommandTest() : next_revision_(1) {} | 30 ApplyUpdatesCommandTest() : next_revision_(1) {} |
40 virtual ~ApplyUpdatesCommandTest() {} | 31 virtual ~ApplyUpdatesCommandTest() {} |
41 | 32 |
42 virtual void SetUp() { | 33 virtual void SetUp() { |
43 workers()->clear(); | 34 workers()->clear(); |
44 mutable_routing_info()->clear(); | 35 mutable_routing_info()->clear(); |
45 // GROUP_PASSIVE worker. | 36 // GROUP_PASSIVE worker. |
46 workers()->push_back(make_scoped_refptr(new ModelSafeWorker())); | 37 workers()->push_back(make_scoped_refptr(new ModelSafeWorker())); |
47 (*mutable_routing_info())[syncable::BOOKMARKS] = GROUP_PASSIVE; | 38 (*mutable_routing_info())[syncable::BOOKMARKS] = GROUP_PASSIVE; |
48 (*mutable_routing_info())[syncable::PASSWORDS] = GROUP_PASSIVE; | 39 (*mutable_routing_info())[syncable::PASSWORDS] = GROUP_PASSIVE; |
49 (*mutable_routing_info())[syncable::NIGORI] = GROUP_PASSIVE; | 40 (*mutable_routing_info())[syncable::NIGORI] = GROUP_PASSIVE; |
50 SyncerCommandTest::SetUp(); | 41 SyncerCommandTest::SetUp(); |
51 } | 42 } |
52 | 43 |
53 // Create a new unapplied bookmark node with a parent. | 44 // Create a new unapplied update. |
54 void CreateUnappliedNewItemWithParent(const string& item_id, | 45 void CreateUnappliedNewItemWithParent(const string& item_id, |
55 const string& parent_id) { | 46 const string& parent_id) { |
56 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); | 47 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); |
57 ASSERT_TRUE(dir.good()); | 48 ASSERT_TRUE(dir.good()); |
58 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); | 49 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); |
59 MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM, | 50 MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM, |
60 Id::CreateFromServerId(item_id)); | 51 Id::CreateFromServerId(item_id)); |
61 ASSERT_TRUE(entry.good()); | 52 ASSERT_TRUE(entry.good()); |
62 entry.Put(syncable::SERVER_VERSION, next_revision_++); | 53 entry.Put(syncable::SERVER_VERSION, next_revision_++); |
63 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); | 54 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); |
64 | 55 |
65 entry.Put(syncable::SERVER_NON_UNIQUE_NAME, item_id); | 56 entry.Put(syncable::SERVER_NON_UNIQUE_NAME, item_id); |
66 entry.Put(syncable::SERVER_PARENT_ID, Id::CreateFromServerId(parent_id)); | 57 entry.Put(syncable::SERVER_PARENT_ID, Id::CreateFromServerId(parent_id)); |
67 entry.Put(syncable::SERVER_IS_DIR, true); | 58 entry.Put(syncable::SERVER_IS_DIR, true); |
68 sync_pb::EntitySpecifics default_bookmark_specifics; | 59 sync_pb::EntitySpecifics default_bookmark_specifics; |
69 default_bookmark_specifics.MutableExtension(sync_pb::bookmark); | 60 default_bookmark_specifics.MutableExtension(sync_pb::bookmark); |
70 entry.Put(syncable::SERVER_SPECIFICS, default_bookmark_specifics); | 61 entry.Put(syncable::SERVER_SPECIFICS, default_bookmark_specifics); |
71 } | 62 } |
72 | 63 |
73 // Create a new unapplied update without a parent. | |
74 void CreateUnappliedNewItem(const string& item_id, | 64 void CreateUnappliedNewItem(const string& item_id, |
75 const sync_pb::EntitySpecifics& specifics, | 65 const sync_pb::EntitySpecifics& specifics) { |
76 bool is_unique) { | |
77 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); | 66 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); |
78 ASSERT_TRUE(dir.good()); | 67 ASSERT_TRUE(dir.good()); |
79 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); | 68 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); |
80 MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM, | 69 MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM, |
81 Id::CreateFromServerId(item_id)); | 70 Id::CreateFromServerId(item_id)); |
82 ASSERT_TRUE(entry.good()); | 71 ASSERT_TRUE(entry.good()); |
83 entry.Put(syncable::SERVER_VERSION, next_revision_++); | 72 entry.Put(syncable::SERVER_VERSION, next_revision_++); |
84 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); | 73 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); |
| 74 |
85 entry.Put(syncable::SERVER_NON_UNIQUE_NAME, item_id); | 75 entry.Put(syncable::SERVER_NON_UNIQUE_NAME, item_id); |
86 entry.Put(syncable::SERVER_PARENT_ID, syncable::kNullId); | 76 entry.Put(syncable::SERVER_PARENT_ID, syncable::kNullId); |
87 entry.Put(syncable::SERVER_IS_DIR, false); | 77 entry.Put(syncable::SERVER_IS_DIR, false); |
88 entry.Put(syncable::SERVER_SPECIFICS, specifics); | 78 entry.Put(syncable::SERVER_SPECIFICS, specifics); |
89 if (is_unique) // For top-level nodes. | |
90 entry.Put(syncable::UNIQUE_SERVER_TAG, item_id); | |
91 } | |
92 | |
93 // Create an unsynced item in the database. If item_id is a local ID, it | |
94 // will be treated as a create-new. Otherwise, if it's a server ID, we'll | |
95 // fake the server data so that it looks like it exists on the server. | |
96 // Returns the methandle of the created item in |metahandle_out| if not NULL. | |
97 void CreateUnsyncedItem(const Id& item_id, | |
98 const Id& parent_id, | |
99 const string& name, | |
100 bool is_folder, | |
101 syncable::ModelType model_type, | |
102 int64* metahandle_out) { | |
103 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); | |
104 ASSERT_TRUE(dir.good()); | |
105 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); | |
106 Id predecessor_id = dir->GetLastChildId(&trans, parent_id); | |
107 MutableEntry entry(&trans, syncable::CREATE, parent_id, name); | |
108 ASSERT_TRUE(entry.good()); | |
109 entry.Put(syncable::ID, item_id); | |
110 entry.Put(syncable::BASE_VERSION, | |
111 item_id.ServerKnows() ? next_revision_++ : 0); | |
112 entry.Put(syncable::IS_UNSYNCED, true); | |
113 entry.Put(syncable::IS_DIR, is_folder); | |
114 entry.Put(syncable::IS_DEL, false); | |
115 entry.Put(syncable::PARENT_ID, parent_id); | |
116 entry.PutPredecessor(predecessor_id); | |
117 sync_pb::EntitySpecifics default_specifics; | |
118 syncable::AddDefaultExtensionValue(model_type, &default_specifics); | |
119 entry.Put(syncable::SPECIFICS, default_specifics); | |
120 if (item_id.ServerKnows()) { | |
121 entry.Put(syncable::SERVER_SPECIFICS, default_specifics); | |
122 entry.Put(syncable::SERVER_IS_DIR, is_folder); | |
123 entry.Put(syncable::SERVER_PARENT_ID, parent_id); | |
124 entry.Put(syncable::SERVER_IS_DEL, false); | |
125 } | |
126 if (metahandle_out) | |
127 *metahandle_out = entry.Get(syncable::META_HANDLE); | |
128 } | 79 } |
129 | 80 |
130 ApplyUpdatesCommand apply_updates_command_; | 81 ApplyUpdatesCommand apply_updates_command_; |
131 TestIdFactory id_factory_; | 82 |
132 private: | 83 private: |
133 int64 next_revision_; | 84 int64 next_revision_; |
134 DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesCommandTest); | 85 DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesCommandTest); |
135 }; | 86 }; |
136 | 87 |
137 TEST_F(ApplyUpdatesCommandTest, Simple) { | 88 TEST_F(ApplyUpdatesCommandTest, Simple) { |
138 string root_server_id = syncable::kNullId.GetServerId(); | 89 string root_server_id = syncable::kNullId.GetServerId(); |
139 CreateUnappliedNewItemWithParent("parent", root_server_id); | 90 CreateUnappliedNewItemWithParent("parent", root_server_id); |
140 CreateUnappliedNewItemWithParent("child", "parent"); | 91 CreateUnappliedNewItemWithParent("child", "parent"); |
141 | 92 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
220 | 171 |
221 browser_sync::KeyParams params = {"localhost", "dummy", "foobar"}; | 172 browser_sync::KeyParams params = {"localhost", "dummy", "foobar"}; |
222 cryptographer->AddKey(params); | 173 cryptographer->AddKey(params); |
223 | 174 |
224 sync_pb::EntitySpecifics specifics; | 175 sync_pb::EntitySpecifics specifics; |
225 sync_pb::PasswordSpecificsData data; | 176 sync_pb::PasswordSpecificsData data; |
226 data.set_origin("http://example.com"); | 177 data.set_origin("http://example.com"); |
227 | 178 |
228 cryptographer->Encrypt(data, | 179 cryptographer->Encrypt(data, |
229 specifics.MutableExtension(sync_pb::password)->mutable_encrypted()); | 180 specifics.MutableExtension(sync_pb::password)->mutable_encrypted()); |
230 CreateUnappliedNewItem("item", specifics, false); | 181 CreateUnappliedNewItem("item", specifics); |
231 | 182 |
232 apply_updates_command_.ExecuteImpl(session()); | 183 apply_updates_command_.ExecuteImpl(session()); |
233 | 184 |
234 sessions::StatusController* status = session()->status_controller(); | 185 sessions::StatusController* status = session()->status_controller(); |
235 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); | 186 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); |
236 EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize()) | 187 EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize()) |
237 << "All updates should have been attempted"; | 188 << "All updates should have been attempted"; |
238 EXPECT_EQ(0, status->conflict_progress().ConflictingItemsSize()) | 189 EXPECT_EQ(0, status->conflict_progress().ConflictingItemsSize()) |
239 << "No update should be in conflict because they're all decryptable"; | 190 << "No update should be in conflict because they're all decryptable"; |
240 EXPECT_EQ(1, status->update_progress().SuccessfullyAppliedUpdateCount()) | 191 EXPECT_EQ(1, status->update_progress().SuccessfullyAppliedUpdateCount()) |
241 << "The updates that can be decrypted should be applied"; | 192 << "The updates that can be decrypted should be applied"; |
242 } | 193 } |
243 | 194 |
244 TEST_F(ApplyUpdatesCommandTest, UndecryptablePassword) { | 195 TEST_F(ApplyUpdatesCommandTest, UndecryptablePassword) { |
245 // Undecryptable password updates should not be applied. | 196 // Undecryptable password updates should not be applied. |
246 sync_pb::EntitySpecifics specifics; | 197 sync_pb::EntitySpecifics specifics; |
247 specifics.MutableExtension(sync_pb::password); | 198 specifics.MutableExtension(sync_pb::password); |
248 CreateUnappliedNewItem("item", specifics, false); | 199 CreateUnappliedNewItem("item", specifics); |
249 | 200 |
250 apply_updates_command_.ExecuteImpl(session()); | 201 apply_updates_command_.ExecuteImpl(session()); |
251 | 202 |
252 sessions::StatusController* status = session()->status_controller(); | 203 sessions::StatusController* status = session()->status_controller(); |
253 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); | 204 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); |
254 EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize()) | 205 EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize()) |
255 << "All updates should have been attempted"; | 206 << "All updates should have been attempted"; |
256 EXPECT_EQ(1, status->conflict_progress().ConflictingItemsSize()) | 207 EXPECT_EQ(1, status->conflict_progress().ConflictingItemsSize()) |
257 << "The updates that can't be decrypted should be in conflict"; | 208 << "The updates that can't be decrypted should be in conflict"; |
258 EXPECT_EQ(0, status->update_progress().SuccessfullyAppliedUpdateCount()) | 209 EXPECT_EQ(0, status->update_progress().SuccessfullyAppliedUpdateCount()) |
259 << "No update that can't be decrypted should be applied"; | 210 << "No update that can't be decrypted should be applied"; |
260 } | 211 } |
261 | 212 |
262 TEST_F(ApplyUpdatesCommandTest, SomeUndecryptablePassword) { | 213 TEST_F(ApplyUpdatesCommandTest, SomeUndecryptablePassword) { |
263 // Only decryptable password updates should be applied. | 214 // Only decryptable password updates should be applied. |
264 { | 215 { |
265 Cryptographer* cryptographer = | 216 Cryptographer* cryptographer = |
266 session()->context()->directory_manager()->cryptographer(); | 217 session()->context()->directory_manager()->cryptographer(); |
267 | 218 |
268 KeyParams params = {"localhost", "dummy", "foobar"}; | 219 KeyParams params = {"localhost", "dummy", "foobar"}; |
269 cryptographer->AddKey(params); | 220 cryptographer->AddKey(params); |
270 | 221 |
271 sync_pb::EntitySpecifics specifics; | 222 sync_pb::EntitySpecifics specifics; |
272 sync_pb::PasswordSpecificsData data; | 223 sync_pb::PasswordSpecificsData data; |
273 data.set_origin("http://example.com/1"); | 224 data.set_origin("http://example.com/1"); |
274 | 225 |
275 cryptographer->Encrypt(data, | 226 cryptographer->Encrypt(data, |
276 specifics.MutableExtension(sync_pb::password)->mutable_encrypted()); | 227 specifics.MutableExtension(sync_pb::password)->mutable_encrypted()); |
277 CreateUnappliedNewItem("item1", specifics, false); | 228 CreateUnappliedNewItem("item1", specifics); |
278 } | 229 } |
279 { | 230 { |
280 // Create a new cryptographer, independent of the one in the session. | 231 // Create a new cryptographer, independent of the one in the session. |
281 Cryptographer cryptographer; | 232 Cryptographer cryptographer; |
282 KeyParams params = {"localhost", "dummy", "bazqux"}; | 233 KeyParams params = {"localhost", "dummy", "bazqux"}; |
283 cryptographer.AddKey(params); | 234 cryptographer.AddKey(params); |
284 | 235 |
285 sync_pb::EntitySpecifics specifics; | 236 sync_pb::EntitySpecifics specifics; |
286 sync_pb::PasswordSpecificsData data; | 237 sync_pb::PasswordSpecificsData data; |
287 data.set_origin("http://example.com/2"); | 238 data.set_origin("http://example.com/2"); |
288 | 239 |
289 cryptographer.Encrypt(data, | 240 cryptographer.Encrypt(data, |
290 specifics.MutableExtension(sync_pb::password)->mutable_encrypted()); | 241 specifics.MutableExtension(sync_pb::password)->mutable_encrypted()); |
291 CreateUnappliedNewItem("item2", specifics, false); | 242 CreateUnappliedNewItem("item2", specifics); |
292 } | 243 } |
293 | 244 |
294 apply_updates_command_.ExecuteImpl(session()); | 245 apply_updates_command_.ExecuteImpl(session()); |
295 | 246 |
296 sessions::StatusController* status = session()->status_controller(); | 247 sessions::StatusController* status = session()->status_controller(); |
297 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); | 248 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); |
298 EXPECT_EQ(2, status->update_progress().AppliedUpdatesSize()) | 249 EXPECT_EQ(2, status->update_progress().AppliedUpdatesSize()) |
299 << "All updates should have been attempted"; | 250 << "All updates should have been attempted"; |
300 EXPECT_EQ(1, status->conflict_progress().ConflictingItemsSize()) | 251 EXPECT_EQ(1, status->conflict_progress().ConflictingItemsSize()) |
301 << "The decryptable password update should be applied"; | 252 << "The decryptable password update should be applied"; |
302 EXPECT_EQ(1, status->update_progress().SuccessfullyAppliedUpdateCount()) | 253 EXPECT_EQ(1, status->update_progress().SuccessfullyAppliedUpdateCount()) |
303 << "The undecryptable password update shouldn't be applied"; | 254 << "The undecryptable password update shouldn't be applied"; |
304 } | 255 } |
305 | 256 |
306 TEST_F(ApplyUpdatesCommandTest, NigoriUpdate) { | 257 TEST_F(ApplyUpdatesCommandTest, NigoriUpdate) { |
307 syncable::ModelTypeSet encrypted_types; | |
308 { | |
309 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); | |
310 ASSERT_TRUE(dir.good()); | |
311 ReadTransaction trans(dir, __FILE__, __LINE__); | |
312 EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans)); | |
313 } | |
314 | |
315 // Nigori node updates should update the Cryptographer. | 258 // Nigori node updates should update the Cryptographer. |
316 Cryptographer other_cryptographer; | 259 Cryptographer other_cryptographer; |
317 KeyParams params = {"localhost", "dummy", "foobar"}; | 260 KeyParams params = {"localhost", "dummy", "foobar"}; |
318 other_cryptographer.AddKey(params); | 261 other_cryptographer.AddKey(params); |
319 | 262 |
320 sync_pb::EntitySpecifics specifics; | 263 sync_pb::EntitySpecifics specifics; |
321 sync_pb::NigoriSpecifics* nigori = | 264 other_cryptographer.GetKeys( |
322 specifics.MutableExtension(sync_pb::nigori); | 265 specifics.MutableExtension(sync_pb::nigori)->mutable_encrypted()); |
323 other_cryptographer.GetKeys(nigori->mutable_encrypted()); | 266 |
324 nigori->set_encrypt_bookmarks(true); | 267 CreateUnappliedNewItem("item", specifics); |
325 encrypted_types.insert(syncable::BOOKMARKS); | |
326 CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI), | |
327 specifics, true); | |
328 | 268 |
329 Cryptographer* cryptographer = | 269 Cryptographer* cryptographer = |
330 session()->context()->directory_manager()->cryptographer(); | 270 session()->context()->directory_manager()->cryptographer(); |
331 EXPECT_FALSE(cryptographer->has_pending_keys()); | 271 EXPECT_FALSE(cryptographer->has_pending_keys()); |
332 | 272 |
333 apply_updates_command_.ExecuteImpl(session()); | 273 apply_updates_command_.ExecuteImpl(session()); |
334 | 274 |
335 sessions::StatusController* status = session()->status_controller(); | 275 sessions::StatusController* status = session()->status_controller(); |
336 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); | |
337 EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize()) | |
338 << "All updates should have been attempted"; | |
339 EXPECT_EQ(0, status->conflict_progress().ConflictingItemsSize()) | |
340 << "The nigori update shouldn't be in conflict"; | |
341 EXPECT_EQ(1, status->update_progress().SuccessfullyAppliedUpdateCount()) | |
342 << "The nigori update should be applied"; | |
343 | |
344 EXPECT_FALSE(cryptographer->is_ready()); | |
345 EXPECT_TRUE(cryptographer->has_pending_keys()); | |
346 } | |
347 | |
348 TEST_F(ApplyUpdatesCommandTest, EncryptUnsyncedChanges) { | |
349 syncable::ModelTypeSet encrypted_types; | |
350 { | |
351 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); | |
352 ASSERT_TRUE(dir.good()); | |
353 ReadTransaction trans(dir, __FILE__, __LINE__); | |
354 EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans)); | |
355 | |
356 // With empty encrypted_types, this should be true. | |
357 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types)); | |
358 | |
359 Syncer::UnsyncedMetaHandles handles; | |
360 SyncerUtil::GetUnsyncedEntries(&trans, &handles); | |
361 EXPECT_TRUE(handles.empty()); | |
362 } | |
363 | |
364 // Create unsynced bookmarks without encryption. | |
365 // First item is a folder | |
366 Id folder_id = id_factory_.NewLocalId(); | |
367 CreateUnsyncedItem(folder_id, id_factory_.root(), "folder", | |
368 true, syncable::BOOKMARKS, NULL); | |
369 // Next five items are children of the folder | |
370 size_t i; | |
371 size_t batch_s = 5; | |
372 for (i = 0; i < batch_s; ++i) { | |
373 CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id, | |
374 StringPrintf("Item %"PRIuS"", i), false, | |
375 syncable::BOOKMARKS, NULL); | |
376 } | |
377 // Next five items are children of the root. | |
378 for (; i < 2*batch_s; ++i) { | |
379 CreateUnsyncedItem(id_factory_.NewLocalId(), id_factory_.root(), | |
380 StringPrintf("Item %"PRIuS"", i), false, | |
381 syncable::BOOKMARKS, NULL); | |
382 } | |
383 | |
384 Cryptographer* cryptographer = | |
385 session()->context()->directory_manager()->cryptographer(); | |
386 KeyParams params = {"localhost", "dummy", "foobar"}; | |
387 cryptographer->AddKey(params); | |
388 sync_pb::EntitySpecifics specifics; | |
389 sync_pb::NigoriSpecifics* nigori = | |
390 specifics.MutableExtension(sync_pb::nigori); | |
391 cryptographer->GetKeys(nigori->mutable_encrypted()); | |
392 nigori->set_encrypt_bookmarks(true); | |
393 encrypted_types.insert(syncable::BOOKMARKS); | |
394 CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI), | |
395 specifics, true); | |
396 EXPECT_FALSE(cryptographer->has_pending_keys()); | |
397 EXPECT_TRUE(cryptographer->is_ready()); | |
398 | |
399 { | |
400 // Ensure we have unsynced nodes that aren't properly encrypted. | |
401 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); | |
402 ASSERT_TRUE(dir.good()); | |
403 ReadTransaction trans(dir, __FILE__, __LINE__); | |
404 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types)); | |
405 | |
406 Syncer::UnsyncedMetaHandles handles; | |
407 SyncerUtil::GetUnsyncedEntries(&trans, &handles); | |
408 EXPECT_EQ(2*batch_s+1, handles.size()); | |
409 } | |
410 | |
411 apply_updates_command_.ExecuteImpl(session()); | |
412 | |
413 sessions::StatusController* status = session()->status_controller(); | |
414 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); | 276 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); |
415 EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize()) | 277 EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize()) |
416 << "All updates should have been attempted"; | 278 << "All updates should have been attempted"; |
417 EXPECT_EQ(0, status->conflict_progress().ConflictingItemsSize()) | 279 EXPECT_EQ(0, status->conflict_progress().ConflictingItemsSize()) |
418 << "The nigori update shouldn't be in conflict"; | 280 << "The nigori update shouldn't be in conflict"; |
419 EXPECT_EQ(1, status->update_progress().SuccessfullyAppliedUpdateCount()) | 281 EXPECT_EQ(1, status->update_progress().SuccessfullyAppliedUpdateCount()) |
420 << "The nigori update should be applied"; | 282 << "The nigori update should be applied"; |
421 EXPECT_FALSE(cryptographer->has_pending_keys()); | |
422 EXPECT_TRUE(cryptographer->is_ready()); | |
423 { | |
424 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); | |
425 ASSERT_TRUE(dir.good()); | |
426 ReadTransaction trans(dir, __FILE__, __LINE__); | |
427 | 283 |
428 // If ProcessUnsyncedChangesForEncryption worked, all our unsynced changes | |
429 // should be encrypted now. | |
430 EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans)); | |
431 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types)); | |
432 | |
433 Syncer::UnsyncedMetaHandles handles; | |
434 SyncerUtil::GetUnsyncedEntries(&trans, &handles); | |
435 EXPECT_EQ(2*batch_s+1, handles.size()); | |
436 } | |
437 } | |
438 | |
439 TEST_F(ApplyUpdatesCommandTest, CannotEncryptUnsyncedChanges) { | |
440 syncable::ModelTypeSet encrypted_types; | |
441 { | |
442 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); | |
443 ASSERT_TRUE(dir.good()); | |
444 ReadTransaction trans(dir, __FILE__, __LINE__); | |
445 EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans)); | |
446 | |
447 // With empty encrypted_types, this should be true. | |
448 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types)); | |
449 | |
450 Syncer::UnsyncedMetaHandles handles; | |
451 SyncerUtil::GetUnsyncedEntries(&trans, &handles); | |
452 EXPECT_TRUE(handles.empty()); | |
453 } | |
454 | |
455 // Create unsynced bookmarks without encryption. | |
456 // First item is a folder | |
457 Id folder_id = id_factory_.NewLocalId(); | |
458 CreateUnsyncedItem(folder_id, id_factory_.root(), "folder", true, | |
459 syncable::BOOKMARKS, NULL); | |
460 // Next five items are children of the folder | |
461 size_t i; | |
462 size_t batch_s = 5; | |
463 for (i = 0; i < batch_s; ++i) { | |
464 CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id, | |
465 StringPrintf("Item %"PRIuS"", i), false, | |
466 syncable::BOOKMARKS, NULL); | |
467 } | |
468 // Next five items are children of the root. | |
469 for (; i < 2*batch_s; ++i) { | |
470 CreateUnsyncedItem(id_factory_.NewLocalId(), id_factory_.root(), | |
471 StringPrintf("Item %"PRIuS"", i), false, | |
472 syncable::BOOKMARKS, NULL); | |
473 } | |
474 | |
475 // We encrypt with new keys, triggering the local cryptographer to be unready | |
476 // and unable to decrypt data (once updated). | |
477 Cryptographer other_cryptographer; | |
478 KeyParams params = {"localhost", "dummy", "foobar"}; | |
479 other_cryptographer.AddKey(params); | |
480 sync_pb::EntitySpecifics specifics; | |
481 sync_pb::NigoriSpecifics* nigori = | |
482 specifics.MutableExtension(sync_pb::nigori); | |
483 other_cryptographer.GetKeys(nigori->mutable_encrypted()); | |
484 nigori->set_encrypt_bookmarks(true); | |
485 encrypted_types.insert(syncable::BOOKMARKS); | |
486 CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI), | |
487 specifics, true); | |
488 Cryptographer* cryptographer = | |
489 session()->context()->directory_manager()->cryptographer(); | |
490 EXPECT_FALSE(cryptographer->has_pending_keys()); | |
491 | |
492 { | |
493 // Ensure we have unsynced nodes that aren't properly encrypted. | |
494 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); | |
495 ASSERT_TRUE(dir.good()); | |
496 ReadTransaction trans(dir, __FILE__, __LINE__); | |
497 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types)); | |
498 Syncer::UnsyncedMetaHandles handles; | |
499 SyncerUtil::GetUnsyncedEntries(&trans, &handles); | |
500 EXPECT_EQ(2*batch_s+1, handles.size()); | |
501 } | |
502 | |
503 apply_updates_command_.ExecuteImpl(session()); | |
504 | |
505 sessions::StatusController* status = session()->status_controller(); | |
506 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); | |
507 EXPECT_EQ(1, status->update_progress().AppliedUpdatesSize()) | |
508 << "All updates should have been attempted"; | |
509 EXPECT_EQ(1, status->conflict_progress().ConflictingItemsSize()) | |
510 << "The unsynced chnages trigger a conflict with the nigori update."; | |
511 EXPECT_EQ(0, status->update_progress().SuccessfullyAppliedUpdateCount()) | |
512 << "The nigori update should not be applied"; | |
513 EXPECT_FALSE(cryptographer->is_ready()); | 284 EXPECT_FALSE(cryptographer->is_ready()); |
514 EXPECT_TRUE(cryptographer->has_pending_keys()); | 285 EXPECT_TRUE(cryptographer->has_pending_keys()); |
515 { | |
516 // Ensure the unsynced nodes are still not encrypted. | |
517 ScopedDirLookup dir(syncdb()->manager(), syncdb()->name()); | |
518 ASSERT_TRUE(dir.good()); | |
519 ReadTransaction trans(dir, __FILE__, __LINE__); | |
520 | |
521 // Since we're in conflict, the specifics don't reflect the unapplied | |
522 // changes. | |
523 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types)); | |
524 encrypted_types.clear(); | |
525 EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans)); | |
526 | |
527 Syncer::UnsyncedMetaHandles handles; | |
528 SyncerUtil::GetUnsyncedEntries(&trans, &handles); | |
529 EXPECT_EQ(2*batch_s+1, handles.size()); | |
530 } | |
531 } | 286 } |
532 | 287 |
533 } // namespace browser_sync | 288 } // namespace browser_sync |
OLD | NEW |