1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Unit tests for the SyncApi. Note that a lot of the underlying
6 // functionality is provided by the Syncable layer, which has its own
7 // unit tests. We'll test SyncApi specific things in this harness.
8
9 #include <cstddef>
10 #include <map>
11
12 #include "base/basictypes.h"
13 #include "base/callback.h"
14 #include "base/compiler_specific.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/format_macros.h"
17 #include "base/location.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/message_loop/message_loop.h"
20 #include "base/message_loop/message_loop_proxy.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/test/values_test_util.h"
25 #include "base/values.h"
26 #include "sync/engine/sync_scheduler.h"
27 #include "sync/internal_api/public/base/cancelation_signal.h"
28 #include "sync/internal_api/public/base/model_type_test_util.h"
29 #include "sync/internal_api/public/change_record.h"
30 #include "sync/internal_api/public/engine/model_safe_worker.h"
31 #include "sync/internal_api/public/engine/polling_constants.h"
32 #include "sync/internal_api/public/http_post_provider_factory.h"
33 #include "sync/internal_api/public/http_post_provider_interface.h"
34 #include "sync/internal_api/public/read_node.h"
35 #include "sync/internal_api/public/read_transaction.h"
36 #include "sync/internal_api/public/test/test_entry_factory.h"
37 #include "sync/internal_api/public/test/test_internal_components_factory.h"
38 #include "sync/internal_api/public/test/test_user_share.h"
39 #include "sync/internal_api/public/write_node.h"
40 #include "sync/internal_api/public/write_transaction.h"
41 #include "sync/internal_api/sync_encryption_handler_impl.h"
42 #include "sync/internal_api/sync_manager_impl.h"
43 #include "sync/internal_api/syncapi_internal.h"
44 #include "sync/js/js_arg_list.h"
45 #include "sync/js/js_backend.h"
46 #include "sync/js/js_event_handler.h"
47 #include "sync/js/js_reply_handler.h"
48 #include "sync/js/js_test_util.h"
49 #include "sync/notifier/fake_invalidation_handler.h"
50 #include "sync/notifier/invalidation_handler.h"
51 #include "sync/notifier/invalidator.h"
52 #include "sync/protocol/bookmark_specifics.pb.h"
53 #include "sync/protocol/encryption.pb.h"
54 #include "sync/protocol/extension_specifics.pb.h"
55 #include "sync/protocol/password_specifics.pb.h"
56 #include "sync/protocol/preference_specifics.pb.h"
57 #include "sync/protocol/proto_value_conversions.h"
58 #include "sync/protocol/sync.pb.h"
59 #include "sync/sessions/sync_session.h"
60 #include "sync/syncable/directory.h"
61 #include "sync/syncable/entry.h"
62 #include "sync/syncable/mutable_entry.h"
63 #include "sync/syncable/nigori_util.h"
64 #include "sync/syncable/syncable_id.h"
65 #include "sync/syncable/syncable_read_transaction.h"
66 #include "sync/syncable/syncable_util.h"
67 #include "sync/syncable/syncable_write_transaction.h"
68 #include "sync/test/callback_counter.h"
69 #include "sync/test/engine/fake_model_worker.h"
70 #include "sync/test/engine/fake_sync_scheduler.h"
71 #include "sync/test/engine/test_id_factory.h"
72 #include "sync/test/fake_encryptor.h"
73 #include "sync/util/cryptographer.h"
74 #include "sync/util/extensions_activity.h"
75 #include "sync/util/test_unrecoverable_error_handler.h"
76 #include "sync/util/time.h"
77 #include "testing/gmock/include/gmock/gmock.h"
78 #include "testing/gtest/include/gtest/gtest.h"
79
80 using base::ExpectDictStringValue;
81 using testing::_;
82 using testing::DoAll;
83 using testing::InSequence;
84 using testing::Return;
85 using testing::SaveArg;
86 using testing::StrictMock;
87
88 namespace syncer {
89
90 using sessions::SyncSessionSnapshot;
91 using syncable::GET_BY_HANDLE;
92 using syncable::IS_DEL;
93 using syncable::IS_UNSYNCED;
94 using syncable::NON_UNIQUE_NAME;
95 using syncable::SPECIFICS;
96 using syncable::kEncryptedString;
97
98 namespace {
99
ExpectInt64Value(int64 expected_value,const base::DictionaryValue & value,const std::string & key)100 void ExpectInt64Value(int64 expected_value,
101 const base::DictionaryValue& value,
102 const std::string& key) {
103 std::string int64_str;
104 EXPECT_TRUE(value.GetString(key, &int64_str));
105 int64 val = 0;
106 EXPECT_TRUE(base::StringToInt64(int64_str, &val));
107 EXPECT_EQ(expected_value, val);
108 }
109
ExpectTimeValue(const base::Time & expected_value,const base::DictionaryValue & value,const std::string & key)110 void ExpectTimeValue(const base::Time& expected_value,
111 const base::DictionaryValue& value,
112 const std::string& key) {
113 std::string time_str;
114 EXPECT_TRUE(value.GetString(key, &time_str));
115 EXPECT_EQ(GetTimeDebugString(expected_value), time_str);
116 }
117
118 // Makes a non-folder child of the root node. Returns the id of the
119 // newly-created node.
MakeNode(UserShare * share,ModelType model_type,const std::string & client_tag)120 int64 MakeNode(UserShare* share,
121 ModelType model_type,
122 const std::string& client_tag) {
123 WriteTransaction trans(FROM_HERE, share);
124 ReadNode root_node(&trans);
125 root_node.InitByRootLookup();
126 WriteNode node(&trans);
127 WriteNode::InitUniqueByCreationResult result =
128 node.InitUniqueByCreation(model_type, root_node, client_tag);
129 EXPECT_EQ(WriteNode::INIT_SUCCESS, result);
130 node.SetIsFolder(false);
131 return node.GetId();
132 }
133
134 // Makes a folder child of a non-root node. Returns the id of the
135 // newly-created node.
MakeFolderWithParent(UserShare * share,ModelType model_type,int64 parent_id,BaseNode * predecessor)136 int64 MakeFolderWithParent(UserShare* share,
137 ModelType model_type,
138 int64 parent_id,
139 BaseNode* predecessor) {
140 WriteTransaction trans(FROM_HERE, share);
141 ReadNode parent_node(&trans);
142 EXPECT_EQ(BaseNode::INIT_OK, parent_node.InitByIdLookup(parent_id));
143 WriteNode node(&trans);
144 EXPECT_TRUE(node.InitBookmarkByCreation(parent_node, predecessor));
145 node.SetIsFolder(true);
146 return node.GetId();
147 }
148
MakeBookmarkWithParent(UserShare * share,int64 parent_id,BaseNode * predecessor)149 int64 MakeBookmarkWithParent(UserShare* share,
150 int64 parent_id,
151 BaseNode* predecessor) {
152 WriteTransaction trans(FROM_HERE, share);
153 ReadNode parent_node(&trans);
154 EXPECT_EQ(BaseNode::INIT_OK, parent_node.InitByIdLookup(parent_id));
155 WriteNode node(&trans);
156 EXPECT_TRUE(node.InitBookmarkByCreation(parent_node, predecessor));
157 return node.GetId();
158 }
159
160 // Creates the "synced" root node for a particular datatype. We use the syncable
161 // methods here so that the syncer treats these nodes as if they were already
162 // received from the server.
MakeServerNodeForType(UserShare * share,ModelType model_type)163 int64 MakeServerNodeForType(UserShare* share,
164 ModelType model_type) {
165 sync_pb::EntitySpecifics specifics;
166 AddDefaultFieldValue(model_type, &specifics);
167 syncable::WriteTransaction trans(
168 FROM_HERE, syncable::UNITTEST, share->directory.get());
169 // Attempt to lookup by nigori tag.
170 std::string type_tag = ModelTypeToRootTag(model_type);
171 syncable::Id node_id = syncable::Id::CreateFromServerId(type_tag);
172 syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
173 node_id);
174 EXPECT_TRUE(entry.good());
175 entry.PutBaseVersion(1);
176 entry.PutServerVersion(1);
177 entry.PutIsUnappliedUpdate(false);
178 entry.PutServerParentId(syncable::GetNullId());
179 entry.PutServerIsDir(true);
180 entry.PutIsDir(true);
181 entry.PutServerSpecifics(specifics);
182 entry.PutUniqueServerTag(type_tag);
183 entry.PutNonUniqueName(type_tag);
184 entry.PutIsDel(false);
185 entry.PutSpecifics(specifics);
186 return entry.GetMetahandle();
187 }
188
189 // Simulates creating a "synced" node as a child of the root datatype node.
MakeServerNode(UserShare * share,ModelType model_type,const std::string & client_tag,const std::string & hashed_tag,const sync_pb::EntitySpecifics & specifics)190 int64 MakeServerNode(UserShare* share, ModelType model_type,
191 const std::string& client_tag,
192 const std::string& hashed_tag,
193 const sync_pb::EntitySpecifics& specifics) {
194 syncable::WriteTransaction trans(
195 FROM_HERE, syncable::UNITTEST, share->directory.get());
196 syncable::Entry root_entry(&trans, syncable::GET_BY_SERVER_TAG,
197 ModelTypeToRootTag(model_type));
198 EXPECT_TRUE(root_entry.good());
199 syncable::Id root_id = root_entry.GetId();
200 syncable::Id node_id = syncable::Id::CreateFromServerId(client_tag);
201 syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
202 node_id);
203 EXPECT_TRUE(entry.good());
204 entry.PutBaseVersion(1);
205 entry.PutServerVersion(1);
206 entry.PutIsUnappliedUpdate(false);
207 entry.PutServerParentId(root_id);
208 entry.PutParentId(root_id);
209 entry.PutServerIsDir(false);
210 entry.PutIsDir(false);
211 entry.PutServerSpecifics(specifics);
212 entry.PutNonUniqueName(client_tag);
213 entry.PutUniqueClientTag(hashed_tag);
214 entry.PutIsDel(false);
215 entry.PutSpecifics(specifics);
216 return entry.GetMetahandle();
217 }
218
219 } // namespace
220
221 class SyncApiTest : public testing::Test {
222 public:
SetUp()223 virtual void SetUp() {
224 test_user_share_.SetUp();
225 }
226
TearDown()227 virtual void TearDown() {
228 test_user_share_.TearDown();
229 }
230
231 protected:
232 base::MessageLoop message_loop_;
233 TestUserShare test_user_share_;
234 };
235
TEST_F(SyncApiTest,SanityCheckTest)236 TEST_F(SyncApiTest, SanityCheckTest) {
237 {
238 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
239 EXPECT_TRUE(trans.GetWrappedTrans());
240 }
241 {
242 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
243 EXPECT_TRUE(trans.GetWrappedTrans());
244 }
245 {
246 // No entries but root should exist
247 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
248 ReadNode node(&trans);
249 // Metahandle 1 can be root, sanity check 2
250 EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_NOT_GOOD, node.InitByIdLookup(2));
251 }
252 }
253
TEST_F(SyncApiTest,BasicTagWrite)254 TEST_F(SyncApiTest, BasicTagWrite) {
255 {
256 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
257 ReadNode root_node(&trans);
258 root_node.InitByRootLookup();
259 EXPECT_EQ(root_node.GetFirstChildId(), 0);
260 }
261
262 ignore_result(MakeNode(test_user_share_.user_share(),
263 BOOKMARKS, "testtag"));
264
265 {
266 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
267 ReadNode node(&trans);
268 EXPECT_EQ(BaseNode::INIT_OK,
269 node.InitByClientTagLookup(BOOKMARKS, "testtag"));
270
271 ReadNode root_node(&trans);
272 root_node.InitByRootLookup();
273 EXPECT_NE(node.GetId(), 0);
274 EXPECT_EQ(node.GetId(), root_node.GetFirstChildId());
275 }
276 }
277
TEST_F(SyncApiTest,ModelTypesSiloed)278 TEST_F(SyncApiTest, ModelTypesSiloed) {
279 {
280 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
281 ReadNode root_node(&trans);
282 root_node.InitByRootLookup();
283 EXPECT_EQ(root_node.GetFirstChildId(), 0);
284 }
285
286 ignore_result(MakeNode(test_user_share_.user_share(),
287 BOOKMARKS, "collideme"));
288 ignore_result(MakeNode(test_user_share_.user_share(),
289 PREFERENCES, "collideme"));
290 ignore_result(MakeNode(test_user_share_.user_share(),
291 AUTOFILL, "collideme"));
292
293 {
294 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
295
296 ReadNode bookmarknode(&trans);
297 EXPECT_EQ(BaseNode::INIT_OK,
298 bookmarknode.InitByClientTagLookup(BOOKMARKS,
299 "collideme"));
300
301 ReadNode prefnode(&trans);
302 EXPECT_EQ(BaseNode::INIT_OK,
303 prefnode.InitByClientTagLookup(PREFERENCES,
304 "collideme"));
305
306 ReadNode autofillnode(&trans);
307 EXPECT_EQ(BaseNode::INIT_OK,
308 autofillnode.InitByClientTagLookup(AUTOFILL,
309 "collideme"));
310
311 EXPECT_NE(bookmarknode.GetId(), prefnode.GetId());
312 EXPECT_NE(autofillnode.GetId(), prefnode.GetId());
313 EXPECT_NE(bookmarknode.GetId(), autofillnode.GetId());
314 }
315 }
316
TEST_F(SyncApiTest,ReadMissingTagsFails)317 TEST_F(SyncApiTest, ReadMissingTagsFails) {
318 {
319 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
320 ReadNode node(&trans);
321 EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_NOT_GOOD,
322 node.InitByClientTagLookup(BOOKMARKS,
323 "testtag"));
324 }
325 {
326 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
327 WriteNode node(&trans);
328 EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_NOT_GOOD,
329 node.InitByClientTagLookup(BOOKMARKS,
330 "testtag"));
331 }
332 }
333
334 // TODO(chron): Hook this all up to the server and write full integration tests
335 // for update->undelete behavior.
TEST_F(SyncApiTest,TestDeleteBehavior)336 TEST_F(SyncApiTest, TestDeleteBehavior) {
337 int64 node_id;
338 int64 folder_id;
339 std::string test_title("test1");
340
341 {
342 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
343 ReadNode root_node(&trans);
344 root_node.InitByRootLookup();
345
346 // we'll use this spare folder later
347 WriteNode folder_node(&trans);
348 EXPECT_TRUE(folder_node.InitBookmarkByCreation(root_node, NULL));
349 folder_id = folder_node.GetId();
350
351 WriteNode wnode(&trans);
352 WriteNode::InitUniqueByCreationResult result =
353 wnode.InitUniqueByCreation(BOOKMARKS, root_node, "testtag");
354 EXPECT_EQ(WriteNode::INIT_SUCCESS, result);
355 wnode.SetIsFolder(false);
356 wnode.SetTitle(UTF8ToWide(test_title));
357
358 node_id = wnode.GetId();
359 }
360
361 // Ensure we can delete something with a tag.
362 {
363 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
364 WriteNode wnode(&trans);
365 EXPECT_EQ(BaseNode::INIT_OK,
366 wnode.InitByClientTagLookup(BOOKMARKS,
367 "testtag"));
368 EXPECT_FALSE(wnode.GetIsFolder());
369 EXPECT_EQ(wnode.GetTitle(), test_title);
370
371 wnode.Tombstone();
372 }
373
374 // Lookup of a node which was deleted should return failure,
375 // but have found some data about the node.
376 {
377 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
378 ReadNode node(&trans);
379 EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_IS_DEL,
380 node.InitByClientTagLookup(BOOKMARKS,
381 "testtag"));
382 // Note that for proper function of this API this doesn't need to be
383 // filled, we're checking just to make sure the DB worked in this test.
384 EXPECT_EQ(node.GetTitle(), test_title);
385 }
386
387 {
388 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
389 ReadNode folder_node(&trans);
390 EXPECT_EQ(BaseNode::INIT_OK, folder_node.InitByIdLookup(folder_id));
391
392 WriteNode wnode(&trans);
393 // This will undelete the tag.
394 WriteNode::InitUniqueByCreationResult result =
395 wnode.InitUniqueByCreation(BOOKMARKS, folder_node, "testtag");
396 EXPECT_EQ(WriteNode::INIT_SUCCESS, result);
397 EXPECT_EQ(wnode.GetIsFolder(), false);
398 EXPECT_EQ(wnode.GetParentId(), folder_node.GetId());
399 EXPECT_EQ(wnode.GetId(), node_id);
400 EXPECT_NE(wnode.GetTitle(), test_title); // Title should be cleared
401 wnode.SetTitle(UTF8ToWide(test_title));
402 }
403
404 // Now look up should work.
405 {
406 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
407 ReadNode node(&trans);
408 EXPECT_EQ(BaseNode::INIT_OK,
409 node.InitByClientTagLookup(BOOKMARKS,
410 "testtag"));
411 EXPECT_EQ(node.GetTitle(), test_title);
412 EXPECT_EQ(node.GetModelType(), BOOKMARKS);
413 }
414 }
415
TEST_F(SyncApiTest,WriteAndReadPassword)416 TEST_F(SyncApiTest, WriteAndReadPassword) {
417 KeyParams params = {"localhost", "username", "passphrase"};
418 {
419 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
420 trans.GetCryptographer()->AddKey(params);
421 }
422 {
423 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
424 ReadNode root_node(&trans);
425 root_node.InitByRootLookup();
426
427 WriteNode password_node(&trans);
428 WriteNode::InitUniqueByCreationResult result =
429 password_node.InitUniqueByCreation(PASSWORDS,
430 root_node, "foo");
431 EXPECT_EQ(WriteNode::INIT_SUCCESS, result);
432 sync_pb::PasswordSpecificsData data;
433 data.set_password_value("secret");
434 password_node.SetPasswordSpecifics(data);
435 }
436 {
437 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
438 ReadNode root_node(&trans);
439 root_node.InitByRootLookup();
440
441 ReadNode password_node(&trans);
442 EXPECT_EQ(BaseNode::INIT_OK,
443 password_node.InitByClientTagLookup(PASSWORDS, "foo"));
444 const sync_pb::PasswordSpecificsData& data =
445 password_node.GetPasswordSpecifics();
446 EXPECT_EQ("secret", data.password_value());
447 }
448 }
449
TEST_F(SyncApiTest,WriteEncryptedTitle)450 TEST_F(SyncApiTest, WriteEncryptedTitle) {
451 KeyParams params = {"localhost", "username", "passphrase"};
452 {
453 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
454 trans.GetCryptographer()->AddKey(params);
455 }
456 test_user_share_.encryption_handler()->EnableEncryptEverything();
457 int bookmark_id;
458 {
459 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
460 ReadNode root_node(&trans);
461 root_node.InitByRootLookup();
462
463 WriteNode bookmark_node(&trans);
464 ASSERT_TRUE(bookmark_node.InitBookmarkByCreation(root_node, NULL));
465 bookmark_id = bookmark_node.GetId();
466 bookmark_node.SetTitle(UTF8ToWide("foo"));
467
468 WriteNode pref_node(&trans);
469 WriteNode::InitUniqueByCreationResult result =
470 pref_node.InitUniqueByCreation(PREFERENCES, root_node, "bar");
471 ASSERT_EQ(WriteNode::INIT_SUCCESS, result);
472 pref_node.SetTitle(UTF8ToWide("bar"));
473 }
474 {
475 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
476 ReadNode root_node(&trans);
477 root_node.InitByRootLookup();
478
479 ReadNode bookmark_node(&trans);
480 ASSERT_EQ(BaseNode::INIT_OK, bookmark_node.InitByIdLookup(bookmark_id));
481 EXPECT_EQ("foo", bookmark_node.GetTitle());
482 EXPECT_EQ(kEncryptedString,
483 bookmark_node.GetEntry()->GetNonUniqueName());
484
485 ReadNode pref_node(&trans);
486 ASSERT_EQ(BaseNode::INIT_OK,
487 pref_node.InitByClientTagLookup(PREFERENCES,
488 "bar"));
489 EXPECT_EQ(kEncryptedString, pref_node.GetTitle());
490 }
491 }
492
TEST_F(SyncApiTest,BaseNodeSetSpecifics)493 TEST_F(SyncApiTest, BaseNodeSetSpecifics) {
494 int64 child_id = MakeNode(test_user_share_.user_share(),
495 BOOKMARKS, "testtag");
496 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
497 WriteNode node(&trans);
498 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(child_id));
499
500 sync_pb::EntitySpecifics entity_specifics;
501 entity_specifics.mutable_bookmark()->set_url("http://www.google.com");
502
503 EXPECT_NE(entity_specifics.SerializeAsString(),
504 node.GetEntitySpecifics().SerializeAsString());
505 node.SetEntitySpecifics(entity_specifics);
506 EXPECT_EQ(entity_specifics.SerializeAsString(),
507 node.GetEntitySpecifics().SerializeAsString());
508 }
509
TEST_F(SyncApiTest,BaseNodeSetSpecificsPreservesUnknownFields)510 TEST_F(SyncApiTest, BaseNodeSetSpecificsPreservesUnknownFields) {
511 int64 child_id = MakeNode(test_user_share_.user_share(),
512 BOOKMARKS, "testtag");
513 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
514 WriteNode node(&trans);
515 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(child_id));
516 EXPECT_TRUE(node.GetEntitySpecifics().unknown_fields().empty());
517
518 sync_pb::EntitySpecifics entity_specifics;
519 entity_specifics.mutable_bookmark()->set_url("http://www.google.com");
520 entity_specifics.mutable_unknown_fields()->AddFixed32(5, 100);
521 node.SetEntitySpecifics(entity_specifics);
522 EXPECT_FALSE(node.GetEntitySpecifics().unknown_fields().empty());
523
524 entity_specifics.mutable_unknown_fields()->Clear();
525 node.SetEntitySpecifics(entity_specifics);
526 EXPECT_FALSE(node.GetEntitySpecifics().unknown_fields().empty());
527 }
528
529 namespace {
530
CheckNodeValue(const BaseNode & node,const base::DictionaryValue & value,bool is_detailed)531 void CheckNodeValue(const BaseNode& node, const base::DictionaryValue& value,
532 bool is_detailed) {
533 size_t expected_field_count = 4;
534
535 ExpectInt64Value(node.GetId(), value, "id");
536 {
537 bool is_folder = false;
538 EXPECT_TRUE(value.GetBoolean("isFolder", &is_folder));
539 EXPECT_EQ(node.GetIsFolder(), is_folder);
540 }
541 ExpectDictStringValue(node.GetTitle(), value, "title");
542
543 ModelType expected_model_type = node.GetModelType();
544 std::string type_str;
545 EXPECT_TRUE(value.GetString("type", &type_str));
546 if (expected_model_type >= FIRST_REAL_MODEL_TYPE) {
547 ModelType model_type = ModelTypeFromString(type_str);
548 EXPECT_EQ(expected_model_type, model_type);
549 } else if (expected_model_type == TOP_LEVEL_FOLDER) {
550 EXPECT_EQ("Top-level folder", type_str);
551 } else if (expected_model_type == UNSPECIFIED) {
552 EXPECT_EQ("Unspecified", type_str);
553 } else {
554 ADD_FAILURE();
555 }
556
557 if (is_detailed) {
558 {
559 scoped_ptr<base::DictionaryValue> expected_entry(
560 node.GetEntry()->ToValue(NULL));
561 const base::Value* entry = NULL;
562 EXPECT_TRUE(value.Get("entry", &entry));
563 EXPECT_TRUE(base::Value::Equals(entry, expected_entry.get()));
564 }
565
566 ExpectInt64Value(node.GetParentId(), value, "parentId");
567 ExpectTimeValue(node.GetModificationTime(), value, "modificationTime");
568 ExpectInt64Value(node.GetExternalId(), value, "externalId");
569 expected_field_count += 4;
570
571 if (value.HasKey("predecessorId")) {
572 ExpectInt64Value(node.GetPredecessorId(), value, "predecessorId");
573 expected_field_count++;
574 }
575 if (value.HasKey("successorId")) {
576 ExpectInt64Value(node.GetSuccessorId(), value, "successorId");
577 expected_field_count++;
578 }
579 if (value.HasKey("firstChildId")) {
580 ExpectInt64Value(node.GetFirstChildId(), value, "firstChildId");
581 expected_field_count++;
582 }
583 }
584
585 EXPECT_EQ(expected_field_count, value.size());
586 }
587
588 } // namespace
589
TEST_F(SyncApiTest,BaseNodeGetSummaryAsValue)590 TEST_F(SyncApiTest, BaseNodeGetSummaryAsValue) {
591 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
592 ReadNode node(&trans);
593 node.InitByRootLookup();
594 scoped_ptr<base::DictionaryValue> details(node.GetSummaryAsValue());
595 if (details) {
596 CheckNodeValue(node, *details, false);
597 } else {
598 ADD_FAILURE();
599 }
600 }
601
TEST_F(SyncApiTest,BaseNodeGetDetailsAsValue)602 TEST_F(SyncApiTest, BaseNodeGetDetailsAsValue) {
603 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
604 ReadNode node(&trans);
605 node.InitByRootLookup();
606 scoped_ptr<base::DictionaryValue> details(node.GetDetailsAsValue());
607 if (details) {
608 CheckNodeValue(node, *details, true);
609 } else {
610 ADD_FAILURE();
611 }
612 }
613
TEST_F(SyncApiTest,EmptyTags)614 TEST_F(SyncApiTest, EmptyTags) {
615 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
616 ReadNode root_node(&trans);
617 root_node.InitByRootLookup();
618 WriteNode node(&trans);
619 std::string empty_tag;
620 WriteNode::InitUniqueByCreationResult result =
621 node.InitUniqueByCreation(TYPED_URLS, root_node, empty_tag);
622 EXPECT_NE(WriteNode::INIT_SUCCESS, result);
623 EXPECT_EQ(BaseNode::INIT_FAILED_PRECONDITION,
624 node.InitByTagLookup(empty_tag));
625 }
626
627 // Test counting nodes when the type's root node has no children.
TEST_F(SyncApiTest,GetTotalNodeCountEmpty)628 TEST_F(SyncApiTest, GetTotalNodeCountEmpty) {
629 int64 type_root = MakeServerNodeForType(test_user_share_.user_share(),
630 BOOKMARKS);
631 {
632 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
633 ReadNode type_root_node(&trans);
634 EXPECT_EQ(BaseNode::INIT_OK,
635 type_root_node.InitByIdLookup(type_root));
636 EXPECT_EQ(1, type_root_node.GetTotalNodeCount());
637 }
638 }
639
640 // Test counting nodes when there is one child beneath the type's root.
TEST_F(SyncApiTest,GetTotalNodeCountOneChild)641 TEST_F(SyncApiTest, GetTotalNodeCountOneChild) {
642 int64 type_root = MakeServerNodeForType(test_user_share_.user_share(),
643 BOOKMARKS);
644 int64 parent = MakeFolderWithParent(test_user_share_.user_share(),
645 BOOKMARKS,
646 type_root,
647 NULL);
648 {
649 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
650 ReadNode type_root_node(&trans);
651 EXPECT_EQ(BaseNode::INIT_OK,
652 type_root_node.InitByIdLookup(type_root));
653 EXPECT_EQ(2, type_root_node.GetTotalNodeCount());
654 ReadNode parent_node(&trans);
655 EXPECT_EQ(BaseNode::INIT_OK,
656 parent_node.InitByIdLookup(parent));
657 EXPECT_EQ(1, parent_node.GetTotalNodeCount());
658 }
659 }
660
661 // Test counting nodes when there are multiple children beneath the type root,
662 // and one of those children has children of its own.
TEST_F(SyncApiTest,GetTotalNodeCountMultipleChildren)663 TEST_F(SyncApiTest, GetTotalNodeCountMultipleChildren) {
664 int64 type_root = MakeServerNodeForType(test_user_share_.user_share(),
665 BOOKMARKS);
666 int64 parent = MakeFolderWithParent(test_user_share_.user_share(),
667 BOOKMARKS,
668 type_root,
669 NULL);
670 ignore_result(MakeFolderWithParent(test_user_share_.user_share(),
671 BOOKMARKS,
672 type_root,
673 NULL));
674 int64 child1 = MakeFolderWithParent(
675 test_user_share_.user_share(),
676 BOOKMARKS,
677 parent,
678 NULL);
679 ignore_result(MakeBookmarkWithParent(
680 test_user_share_.user_share(),
681 parent,
682 NULL));
683 ignore_result(MakeBookmarkWithParent(
684 test_user_share_.user_share(),
685 child1,
686 NULL));
687
688 {
689 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
690 ReadNode type_root_node(&trans);
691 EXPECT_EQ(BaseNode::INIT_OK,
692 type_root_node.InitByIdLookup(type_root));
693 EXPECT_EQ(6, type_root_node.GetTotalNodeCount());
694 ReadNode node(&trans);
695 EXPECT_EQ(BaseNode::INIT_OK,
696 node.InitByIdLookup(parent));
697 EXPECT_EQ(4, node.GetTotalNodeCount());
698 }
699 }
700
701 namespace {
702
703 class TestHttpPostProviderInterface : public HttpPostProviderInterface {
704 public:
~TestHttpPostProviderInterface()705 virtual ~TestHttpPostProviderInterface() {}
706
SetExtraRequestHeaders(const char * headers)707 virtual void SetExtraRequestHeaders(const char* headers) OVERRIDE {}
SetURL(const char * url,int port)708 virtual void SetURL(const char* url, int port) OVERRIDE {}
SetPostPayload(const char * content_type,int content_length,const char * content)709 virtual void SetPostPayload(const char* content_type,
710 int content_length,
711 const char* content) OVERRIDE {}
MakeSynchronousPost(int * error_code,int * response_code)712 virtual bool MakeSynchronousPost(int* error_code, int* response_code)
713 OVERRIDE {
714 return false;
715 }
GetResponseContentLength() const716 virtual int GetResponseContentLength() const OVERRIDE {
717 return 0;
718 }
GetResponseContent() const719 virtual const char* GetResponseContent() const OVERRIDE {
720 return "";
721 }
GetResponseHeaderValue(const std::string & name) const722 virtual const std::string GetResponseHeaderValue(
723 const std::string& name) const OVERRIDE {
724 return std::string();
725 }
Abort()726 virtual void Abort() OVERRIDE {}
727 };
728
729 class TestHttpPostProviderFactory : public HttpPostProviderFactory {
730 public:
~TestHttpPostProviderFactory()731 virtual ~TestHttpPostProviderFactory() {}
Init(const std::string & user_agent)732 virtual void Init(const std::string& user_agent) OVERRIDE { }
Create()733 virtual HttpPostProviderInterface* Create() OVERRIDE {
734 return new TestHttpPostProviderInterface();
735 }
Destroy(HttpPostProviderInterface * http)736 virtual void Destroy(HttpPostProviderInterface* http) OVERRIDE {
737 delete static_cast<TestHttpPostProviderInterface*>(http);
738 }
739 };
740
741 class SyncManagerObserverMock : public SyncManager::Observer {
742 public:
743 MOCK_METHOD1(OnSyncCycleCompleted,
744 void(const SyncSessionSnapshot&)); // NOLINT
745 MOCK_METHOD4(OnInitializationComplete,
746 void(const WeakHandle<JsBackend>&,
747 const WeakHandle<DataTypeDebugInfoListener>&,
748 bool,
749 syncer::ModelTypeSet)); // NOLINT
750 MOCK_METHOD1(OnConnectionStatusChange, void(ConnectionStatus)); // NOLINT
751 MOCK_METHOD0(OnStopSyncingPermanently, void()); // NOLINT
752 MOCK_METHOD1(OnUpdatedToken, void(const std::string&)); // NOLINT
753 MOCK_METHOD1(OnActionableError,
754 void(const SyncProtocolError&)); // NOLINT
755 };
756
757 class SyncEncryptionHandlerObserverMock
758 : public SyncEncryptionHandler::Observer {
759 public:
760 MOCK_METHOD2(OnPassphraseRequired,
761 void(PassphraseRequiredReason,
762 const sync_pb::EncryptedData&)); // NOLINT
763 MOCK_METHOD0(OnPassphraseAccepted, void()); // NOLINT
764 MOCK_METHOD2(OnBootstrapTokenUpdated,
765 void(const std::string&, BootstrapTokenType type)); // NOLINT
766 MOCK_METHOD2(OnEncryptedTypesChanged,
767 void(ModelTypeSet, bool)); // NOLINT
768 MOCK_METHOD0(OnEncryptionComplete, void()); // NOLINT
769 MOCK_METHOD1(OnCryptographerStateChanged, void(Cryptographer*)); // NOLINT
770 MOCK_METHOD2(OnPassphraseTypeChanged, void(PassphraseType,
771 base::Time)); // NOLINT
772 };
773
774 } // namespace
775
776 class SyncManagerTest : public testing::Test,
777 public SyncManager::ChangeDelegate {
778 protected:
779 enum NigoriStatus {
780 DONT_WRITE_NIGORI,
781 WRITE_TO_NIGORI
782 };
783
784 enum EncryptionStatus {
785 UNINITIALIZED,
786 DEFAULT_ENCRYPTION,
787 FULL_ENCRYPTION
788 };
789
SyncManagerTest()790 SyncManagerTest()
791 : sync_manager_("Test sync manager") {
792 switches_.encryption_method =
793 InternalComponentsFactory::ENCRYPTION_KEYSTORE;
794 }
795
~SyncManagerTest()796 virtual ~SyncManagerTest() {
797 }
798
799 // Test implementation.
SetUp()800 void SetUp() {
801 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
802
803 extensions_activity_ = new ExtensionsActivity();
804
805 SyncCredentials credentials;
806 credentials.email = "foo@bar.com";
807 credentials.sync_token = "sometoken";
808
809 sync_manager_.AddObserver(&manager_observer_);
810 EXPECT_CALL(manager_observer_, OnInitializationComplete(_, _, _, _)).
811 WillOnce(DoAll(SaveArg<0>(&js_backend_),
812 SaveArg<2>(&initialization_succeeded_)));
813
814 EXPECT_FALSE(js_backend_.IsInitialized());
815
816 std::vector<ModelSafeWorker*> workers;
817 ModelSafeRoutingInfo routing_info;
818 GetModelSafeRoutingInfo(&routing_info);
819
820 // This works only because all routing info types are GROUP_PASSIVE.
821 // If we had types in other groups, we would need additional workers
822 // to support them.
823 scoped_refptr<ModelSafeWorker> worker = new FakeModelWorker(GROUP_PASSIVE);
824 workers.push_back(worker.get());
825
826 // Takes ownership of |fake_invalidator_|.
827 sync_manager_.Init(
828 temp_dir_.path(),
829 WeakHandle<JsEventHandler>(),
830 "bogus",
831 0,
832 false,
833 scoped_ptr<HttpPostProviderFactory>(new TestHttpPostProviderFactory()),
834 workers,
835 extensions_activity_.get(),
836 this,
837 credentials,
838 "fake_invalidator_client_id",
839 std::string(),
840 std::string(), // bootstrap tokens
841 scoped_ptr<InternalComponentsFactory>(GetFactory()).get(),
842 &encryptor_,
843 scoped_ptr<UnrecoverableErrorHandler>(
844 new TestUnrecoverableErrorHandler).Pass(),
845 NULL,
846 &cancelation_signal_);
847
848 sync_manager_.GetEncryptionHandler()->AddObserver(&encryption_observer_);
849
850 EXPECT_TRUE(js_backend_.IsInitialized());
851
852 if (initialization_succeeded_) {
853 for (ModelSafeRoutingInfo::iterator i = routing_info.begin();
854 i != routing_info.end(); ++i) {
855 type_roots_[i->first] = MakeServerNodeForType(
856 sync_manager_.GetUserShare(), i->first);
857 }
858 }
859 PumpLoop();
860 }
861
TearDown()862 void TearDown() {
863 sync_manager_.RemoveObserver(&manager_observer_);
864 sync_manager_.ShutdownOnSyncThread();
865 PumpLoop();
866 }
867
GetModelSafeRoutingInfo(ModelSafeRoutingInfo * out)868 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
869 (*out)[NIGORI] = GROUP_PASSIVE;
870 (*out)[DEVICE_INFO] = GROUP_PASSIVE;
871 (*out)[EXPERIMENTS] = GROUP_PASSIVE;
872 (*out)[BOOKMARKS] = GROUP_PASSIVE;
873 (*out)[THEMES] = GROUP_PASSIVE;
874 (*out)[SESSIONS] = GROUP_PASSIVE;
875 (*out)[PASSWORDS] = GROUP_PASSIVE;
876 (*out)[PREFERENCES] = GROUP_PASSIVE;
877 (*out)[PRIORITY_PREFERENCES] = GROUP_PASSIVE;
878 }
879
OnChangesApplied(ModelType model_type,int64 model_version,const BaseTransaction * trans,const ImmutableChangeRecordList & changes)880 virtual void OnChangesApplied(
881 ModelType model_type,
882 int64 model_version,
883 const BaseTransaction* trans,
884 const ImmutableChangeRecordList& changes) OVERRIDE {}
885
OnChangesComplete(ModelType model_type)886 virtual void OnChangesComplete(ModelType model_type) OVERRIDE {}
887
888 // Helper methods.
SetUpEncryption(NigoriStatus nigori_status,EncryptionStatus encryption_status)889 bool SetUpEncryption(NigoriStatus nigori_status,
890 EncryptionStatus encryption_status) {
891 UserShare* share = sync_manager_.GetUserShare();
892
893 // We need to create the nigori node as if it were an applied server update.
894 int64 nigori_id = GetIdForDataType(NIGORI);
895 if (nigori_id == kInvalidId)
896 return false;
897
898 // Set the nigori cryptographer information.
899 if (encryption_status == FULL_ENCRYPTION)
900 sync_manager_.GetEncryptionHandler()->EnableEncryptEverything();
901
902 WriteTransaction trans(FROM_HERE, share);
903 Cryptographer* cryptographer = trans.GetCryptographer();
904 if (!cryptographer)
905 return false;
906 if (encryption_status != UNINITIALIZED) {
907 KeyParams params = {"localhost", "dummy", "foobar"};
908 cryptographer->AddKey(params);
909 } else {
910 DCHECK_NE(nigori_status, WRITE_TO_NIGORI);
911 }
912 if (nigori_status == WRITE_TO_NIGORI) {
913 sync_pb::NigoriSpecifics nigori;
914 cryptographer->GetKeys(nigori.mutable_encryption_keybag());
915 share->directory->GetNigoriHandler()->UpdateNigoriFromEncryptedTypes(
916 &nigori,
917 trans.GetWrappedTrans());
918 WriteNode node(&trans);
919 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(nigori_id));
920 node.SetNigoriSpecifics(nigori);
921 }
922 return cryptographer->is_ready();
923 }
924
GetIdForDataType(ModelType type)925 int64 GetIdForDataType(ModelType type) {
926 if (type_roots_.count(type) == 0)
927 return 0;
928 return type_roots_[type];
929 }
930
PumpLoop()931 void PumpLoop() {
932 message_loop_.RunUntilIdle();
933 }
934
SendJsMessage(const std::string & name,const JsArgList & args,const WeakHandle<JsReplyHandler> & reply_handler)935 void SendJsMessage(const std::string& name, const JsArgList& args,
936 const WeakHandle<JsReplyHandler>& reply_handler) {
937 js_backend_.Call(FROM_HERE, &JsBackend::ProcessJsMessage,
938 name, args, reply_handler);
939 PumpLoop();
940 }
941
SetJsEventHandler(const WeakHandle<JsEventHandler> & event_handler)942 void SetJsEventHandler(const WeakHandle<JsEventHandler>& event_handler) {
943 js_backend_.Call(FROM_HERE, &JsBackend::SetJsEventHandler,
944 event_handler);
945 PumpLoop();
946 }
947
948 // Looks up an entry by client tag and resets IS_UNSYNCED value to false.
949 // Returns true if entry was previously unsynced, false if IS_UNSYNCED was
950 // already false.
ResetUnsyncedEntry(ModelType type,const std::string & client_tag)951 bool ResetUnsyncedEntry(ModelType type,
952 const std::string& client_tag) {
953 UserShare* share = sync_manager_.GetUserShare();
954 syncable::WriteTransaction trans(
955 FROM_HERE, syncable::UNITTEST, share->directory.get());
956 const std::string hash = syncable::GenerateSyncableHash(type, client_tag);
957 syncable::MutableEntry entry(&trans, syncable::GET_BY_CLIENT_TAG,
958 hash);
959 EXPECT_TRUE(entry.good());
960 if (!entry.GetIsUnsynced())
961 return false;
962 entry.PutIsUnsynced(false);
963 return true;
964 }
965
GetFactory()966 virtual InternalComponentsFactory* GetFactory() {
967 return new TestInternalComponentsFactory(GetSwitches(), STORAGE_IN_MEMORY);
968 }
969
970 // Returns true if we are currently encrypting all sync data. May
971 // be called on any thread.
EncryptEverythingEnabledForTest()972 bool EncryptEverythingEnabledForTest() {
973 return sync_manager_.GetEncryptionHandler()->EncryptEverythingEnabled();
974 }
975
976 // Gets the set of encrypted types from the cryptographer
977 // Note: opens a transaction. May be called from any thread.
GetEncryptedTypes()978 ModelTypeSet GetEncryptedTypes() {
979 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
980 return GetEncryptedTypesWithTrans(&trans);
981 }
982
GetEncryptedTypesWithTrans(BaseTransaction * trans)983 ModelTypeSet GetEncryptedTypesWithTrans(BaseTransaction* trans) {
984 return trans->GetDirectory()->GetNigoriHandler()->
985 GetEncryptedTypes(trans->GetWrappedTrans());
986 }
987
SimulateInvalidatorStateChangeForTest(InvalidatorState state)988 void SimulateInvalidatorStateChangeForTest(InvalidatorState state) {
989 DCHECK(sync_manager_.thread_checker_.CalledOnValidThread());
990 sync_manager_.OnInvalidatorStateChange(state);
991 }
992
TriggerOnIncomingNotificationForTest(ModelTypeSet model_types)993 void TriggerOnIncomingNotificationForTest(ModelTypeSet model_types) {
994 DCHECK(sync_manager_.thread_checker_.CalledOnValidThread());
995 ObjectIdSet id_set = ModelTypeSetToObjectIdSet(model_types);
996 ObjectIdInvalidationMap invalidation_map =
997 ObjectIdInvalidationMap::InvalidateAll(id_set);
998 sync_manager_.OnIncomingInvalidation(invalidation_map);
999 }
1000
SetProgressMarkerForType(ModelType type,bool set)1001 void SetProgressMarkerForType(ModelType type, bool set) {
1002 if (set) {
1003 sync_pb::DataTypeProgressMarker marker;
1004 marker.set_token("token");
1005 marker.set_data_type_id(GetSpecificsFieldNumberFromModelType(type));
1006 sync_manager_.directory()->SetDownloadProgress(type, marker);
1007 } else {
1008 sync_pb::DataTypeProgressMarker marker;
1009 sync_manager_.directory()->SetDownloadProgress(type, marker);
1010 }
1011 }
1012
GetSwitches() const1013 InternalComponentsFactory::Switches GetSwitches() const {
1014 return switches_;
1015 }
1016
1017 private:
1018 // Needed by |sync_manager_|.
1019 base::MessageLoop message_loop_;
1020 // Needed by |sync_manager_|.
1021 base::ScopedTempDir temp_dir_;
1022 // Sync Id's for the roots of the enabled datatypes.
1023 std::map<ModelType, int64> type_roots_;
1024 scoped_refptr<ExtensionsActivity> extensions_activity_;
1025
1026 protected:
1027 FakeEncryptor encryptor_;
1028 SyncManagerImpl sync_manager_;
1029 CancelationSignal cancelation_signal_;
1030 WeakHandle<JsBackend> js_backend_;
1031 bool initialization_succeeded_;
1032 StrictMock<SyncManagerObserverMock> manager_observer_;
1033 StrictMock<SyncEncryptionHandlerObserverMock> encryption_observer_;
1034 InternalComponentsFactory::Switches switches_;
1035 };
1036
TEST_F(SyncManagerTest,ProcessJsMessage)1037 TEST_F(SyncManagerTest, ProcessJsMessage) {
1038 const JsArgList kNoArgs;
1039
1040 StrictMock<MockJsReplyHandler> reply_handler;
1041
1042 base::ListValue disabled_args;
1043 disabled_args.Append(new base::StringValue("TRANSIENT_INVALIDATION_ERROR"));
1044
1045 EXPECT_CALL(reply_handler,
1046 HandleJsReply("getNotificationState",
1047 HasArgsAsList(disabled_args)));
1048
1049 // This message should be dropped.
1050 SendJsMessage("unknownMessage", kNoArgs, reply_handler.AsWeakHandle());
1051
1052 SendJsMessage("getNotificationState", kNoArgs, reply_handler.AsWeakHandle());
1053 }
1054
TEST_F(SyncManagerTest,ProcessJsMessageGetRootNodeDetails)1055 TEST_F(SyncManagerTest, ProcessJsMessageGetRootNodeDetails) {
1056 const JsArgList kNoArgs;
1057
1058 StrictMock<MockJsReplyHandler> reply_handler;
1059
1060 JsArgList return_args;
1061
1062 EXPECT_CALL(reply_handler,
1063 HandleJsReply("getRootNodeDetails", _))
1064 .WillOnce(SaveArg<1>(&return_args));
1065
1066 SendJsMessage("getRootNodeDetails", kNoArgs, reply_handler.AsWeakHandle());
1067
1068 EXPECT_EQ(1u, return_args.Get().GetSize());
1069 const base::DictionaryValue* node_info = NULL;
1070 EXPECT_TRUE(return_args.Get().GetDictionary(0, &node_info));
1071 if (node_info) {
1072 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1073 ReadNode node(&trans);
1074 node.InitByRootLookup();
1075 CheckNodeValue(node, *node_info, true);
1076 } else {
1077 ADD_FAILURE();
1078 }
1079 }
1080
CheckGetNodesByIdReturnArgs(SyncManager * sync_manager,const JsArgList & return_args,int64 id,bool is_detailed)1081 void CheckGetNodesByIdReturnArgs(SyncManager* sync_manager,
1082 const JsArgList& return_args,
1083 int64 id,
1084 bool is_detailed) {
1085 EXPECT_EQ(1u, return_args.Get().GetSize());
1086 const base::ListValue* nodes = NULL;
1087 ASSERT_TRUE(return_args.Get().GetList(0, &nodes));
1088 ASSERT_TRUE(nodes);
1089 EXPECT_EQ(1u, nodes->GetSize());
1090 const base::DictionaryValue* node_info = NULL;
1091 EXPECT_TRUE(nodes->GetDictionary(0, &node_info));
1092 ASSERT_TRUE(node_info);
1093 ReadTransaction trans(FROM_HERE, sync_manager->GetUserShare());
1094 ReadNode node(&trans);
1095 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(id));
1096 CheckNodeValue(node, *node_info, is_detailed);
1097 }
1098
1099 class SyncManagerGetNodesByIdTest : public SyncManagerTest {
1100 protected:
~SyncManagerGetNodesByIdTest()1101 virtual ~SyncManagerGetNodesByIdTest() {}
1102
RunGetNodesByIdTest(const char * message_name,bool is_detailed)1103 void RunGetNodesByIdTest(const char* message_name, bool is_detailed) {
1104 int64 root_id = kInvalidId;
1105 {
1106 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1107 ReadNode root_node(&trans);
1108 root_node.InitByRootLookup();
1109 root_id = root_node.GetId();
1110 }
1111
1112 int64 child_id =
1113 MakeNode(sync_manager_.GetUserShare(), BOOKMARKS, "testtag");
1114
1115 StrictMock<MockJsReplyHandler> reply_handler;
1116
1117 JsArgList return_args;
1118
1119 const int64 ids[] = { root_id, child_id };
1120
1121 EXPECT_CALL(reply_handler,
1122 HandleJsReply(message_name, _))
1123 .Times(arraysize(ids)).WillRepeatedly(SaveArg<1>(&return_args));
1124
1125 for (size_t i = 0; i < arraysize(ids); ++i) {
1126 base::ListValue args;
1127 base::ListValue* id_values = new base::ListValue();
1128 args.Append(id_values);
1129 id_values->Append(new base::StringValue(base::Int64ToString(ids[i])));
1130 SendJsMessage(message_name,
1131 JsArgList(&args), reply_handler.AsWeakHandle());
1132
1133 CheckGetNodesByIdReturnArgs(&sync_manager_, return_args,
1134 ids[i], is_detailed);
1135 }
1136 }
1137
RunGetNodesByIdFailureTest(const char * message_name)1138 void RunGetNodesByIdFailureTest(const char* message_name) {
1139 StrictMock<MockJsReplyHandler> reply_handler;
1140
1141 base::ListValue empty_list_args;
1142 empty_list_args.Append(new base::ListValue());
1143
1144 EXPECT_CALL(reply_handler,
1145 HandleJsReply(message_name,
1146 HasArgsAsList(empty_list_args)))
1147 .Times(6);
1148
1149 {
1150 base::ListValue args;
1151 SendJsMessage(message_name,
1152 JsArgList(&args), reply_handler.AsWeakHandle());
1153 }
1154
1155 {
1156 base::ListValue args;
1157 args.Append(new base::ListValue());
1158 SendJsMessage(message_name,
1159 JsArgList(&args), reply_handler.AsWeakHandle());
1160 }
1161
1162 {
1163 base::ListValue args;
1164 base::ListValue* ids = new base::ListValue();
1165 args.Append(ids);
1166 ids->Append(new base::StringValue(std::string()));
1167 SendJsMessage(
1168 message_name, JsArgList(&args), reply_handler.AsWeakHandle());
1169 }
1170
1171 {
1172 base::ListValue args;
1173 base::ListValue* ids = new base::ListValue();
1174 args.Append(ids);
1175 ids->Append(new base::StringValue("nonsense"));
1176 SendJsMessage(message_name,
1177 JsArgList(&args), reply_handler.AsWeakHandle());
1178 }
1179
1180 {
1181 base::ListValue args;
1182 base::ListValue* ids = new base::ListValue();
1183 args.Append(ids);
1184 ids->Append(new base::StringValue("0"));
1185 SendJsMessage(message_name,
1186 JsArgList(&args), reply_handler.AsWeakHandle());
1187 }
1188
1189 {
1190 base::ListValue args;
1191 base::ListValue* ids = new base::ListValue();
1192 args.Append(ids);
1193 ids->Append(new base::StringValue("9999"));
1194 SendJsMessage(message_name,
1195 JsArgList(&args), reply_handler.AsWeakHandle());
1196 }
1197 }
1198 };
1199
TEST_F(SyncManagerGetNodesByIdTest,GetNodeSummariesById)1200 TEST_F(SyncManagerGetNodesByIdTest, GetNodeSummariesById) {
1201 RunGetNodesByIdTest("getNodeSummariesById", false);
1202 }
1203
TEST_F(SyncManagerGetNodesByIdTest,GetNodeDetailsById)1204 TEST_F(SyncManagerGetNodesByIdTest, GetNodeDetailsById) {
1205 RunGetNodesByIdTest("getNodeDetailsById", true);
1206 }
1207
TEST_F(SyncManagerGetNodesByIdTest,GetNodeSummariesByIdFailure)1208 TEST_F(SyncManagerGetNodesByIdTest, GetNodeSummariesByIdFailure) {
1209 RunGetNodesByIdFailureTest("getNodeSummariesById");
1210 }
1211
TEST_F(SyncManagerGetNodesByIdTest,GetNodeDetailsByIdFailure)1212 TEST_F(SyncManagerGetNodesByIdTest, GetNodeDetailsByIdFailure) {
1213 RunGetNodesByIdFailureTest("getNodeDetailsById");
1214 }
1215
TEST_F(SyncManagerTest,GetChildNodeIds)1216 TEST_F(SyncManagerTest, GetChildNodeIds) {
1217 StrictMock<MockJsReplyHandler> reply_handler;
1218
1219 JsArgList return_args;
1220
1221 EXPECT_CALL(reply_handler,
1222 HandleJsReply("getChildNodeIds", _))
1223 .Times(1).WillRepeatedly(SaveArg<1>(&return_args));
1224
1225 {
1226 base::ListValue args;
1227 args.Append(new base::StringValue("1"));
1228 SendJsMessage("getChildNodeIds",
1229 JsArgList(&args), reply_handler.AsWeakHandle());
1230 }
1231
1232 EXPECT_EQ(1u, return_args.Get().GetSize());
1233 const base::ListValue* nodes = NULL;
1234 ASSERT_TRUE(return_args.Get().GetList(0, &nodes));
1235 ASSERT_TRUE(nodes);
1236 EXPECT_EQ(9u, nodes->GetSize());
1237 }
1238
TEST_F(SyncManagerTest,GetChildNodeIdsFailure)1239 TEST_F(SyncManagerTest, GetChildNodeIdsFailure) {
1240 StrictMock<MockJsReplyHandler> reply_handler;
1241
1242 base::ListValue empty_list_args;
1243 empty_list_args.Append(new base::ListValue());
1244
1245 EXPECT_CALL(reply_handler,
1246 HandleJsReply("getChildNodeIds",
1247 HasArgsAsList(empty_list_args)))
1248 .Times(5);
1249
1250 {
1251 base::ListValue args;
1252 SendJsMessage("getChildNodeIds",
1253 JsArgList(&args), reply_handler.AsWeakHandle());
1254 }
1255
1256 {
1257 base::ListValue args;
1258 args.Append(new base::StringValue(std::string()));
1259 SendJsMessage(
1260 "getChildNodeIds", JsArgList(&args), reply_handler.AsWeakHandle());
1261 }
1262
1263 {
1264 base::ListValue args;
1265 args.Append(new base::StringValue("nonsense"));
1266 SendJsMessage("getChildNodeIds",
1267 JsArgList(&args), reply_handler.AsWeakHandle());
1268 }
1269
1270 {
1271 base::ListValue args;
1272 args.Append(new base::StringValue("0"));
1273 SendJsMessage("getChildNodeIds",
1274 JsArgList(&args), reply_handler.AsWeakHandle());
1275 }
1276
1277 {
1278 base::ListValue args;
1279 args.Append(new base::StringValue("9999"));
1280 SendJsMessage("getChildNodeIds",
1281 JsArgList(&args), reply_handler.AsWeakHandle());
1282 }
1283 }
1284
TEST_F(SyncManagerTest,GetAllNodesTest)1285 TEST_F(SyncManagerTest, GetAllNodesTest) {
1286 StrictMock<MockJsReplyHandler> reply_handler;
1287 JsArgList return_args;
1288
1289 EXPECT_CALL(reply_handler,
1290 HandleJsReply("getAllNodes", _))
1291 .Times(1).WillRepeatedly(SaveArg<1>(&return_args));
1292
1293 {
1294 base::ListValue args;
1295 SendJsMessage("getAllNodes",
1296 JsArgList(&args), reply_handler.AsWeakHandle());
1297 }
1298
1299 // There's not much value in verifying every attribute on every node here.
1300 // Most of the value of this test has already been achieved: we've verified we
1301 // can call the above function without crashing or leaking memory.
1302 //
1303 // Let's just check the list size and a few of its elements. Anything more
1304 // would make this test brittle without greatly increasing our chances of
1305 // catching real bugs.
1306
1307 const base::ListValue* node_list;
1308 const base::DictionaryValue* first_result;
1309
1310 // The resulting argument list should have one argument, a list of nodes.
1311 ASSERT_EQ(1U, return_args.Get().GetSize());
1312 ASSERT_TRUE(return_args.Get().GetList(0, &node_list));
1313
1314 // The database creation logic depends on the routing info.
1315 // Refer to setup methods for more information.
1316 ModelSafeRoutingInfo routes;
1317 GetModelSafeRoutingInfo(&routes);
1318 size_t directory_size = routes.size() + 1;
1319
1320 ASSERT_EQ(directory_size, node_list->GetSize());
1321 ASSERT_TRUE(node_list->GetDictionary(0, &first_result));
1322 EXPECT_TRUE(first_result->HasKey("ID"));
1323 EXPECT_TRUE(first_result->HasKey("NON_UNIQUE_NAME"));
1324 }
1325
1326 // Simulate various invalidator state changes. Those should propagate
1327 // JS events.
TEST_F(SyncManagerTest,OnInvalidatorStateChangeJsEvents)1328 TEST_F(SyncManagerTest, OnInvalidatorStateChangeJsEvents) {
1329 StrictMock<MockJsEventHandler> event_handler;
1330
1331 base::DictionaryValue enabled_details;
1332 enabled_details.SetString("state", "INVALIDATIONS_ENABLED");
1333 base::DictionaryValue credentials_rejected_details;
1334 credentials_rejected_details.SetString(
1335 "state", "INVALIDATION_CREDENTIALS_REJECTED");
1336 base::DictionaryValue transient_error_details;
1337 transient_error_details.SetString("state", "TRANSIENT_INVALIDATION_ERROR");
1338 base::DictionaryValue auth_error_details;
1339 auth_error_details.SetString("status", "CONNECTION_AUTH_ERROR");
1340
1341 EXPECT_CALL(event_handler,
1342 HandleJsEvent("onNotificationStateChange",
1343 HasDetailsAsDictionary(enabled_details)));
1344
1345 EXPECT_CALL(
1346 event_handler,
1347 HandleJsEvent("onNotificationStateChange",
1348 HasDetailsAsDictionary(credentials_rejected_details)))
1349 .Times(2);
1350
1351 EXPECT_CALL(event_handler,
1352 HandleJsEvent("onNotificationStateChange",
1353 HasDetailsAsDictionary(transient_error_details)));
1354
1355 // Test needs to simulate INVALIDATION_CREDENTIALS_REJECTED with event handler
1356 // attached because this is the only time when CONNECTION_AUTH_ERROR
1357 // notification will be generated, therefore the only chance to verify that
1358 // "onConnectionStatusChange" event is delivered
1359 SetJsEventHandler(event_handler.AsWeakHandle());
1360 SimulateInvalidatorStateChangeForTest(INVALIDATION_CREDENTIALS_REJECTED);
1361 SetJsEventHandler(WeakHandle<JsEventHandler>());
1362
1363 SimulateInvalidatorStateChangeForTest(INVALIDATIONS_ENABLED);
1364 SimulateInvalidatorStateChangeForTest(INVALIDATION_CREDENTIALS_REJECTED);
1365 SimulateInvalidatorStateChangeForTest(TRANSIENT_INVALIDATION_ERROR);
1366
1367 SetJsEventHandler(event_handler.AsWeakHandle());
1368 SimulateInvalidatorStateChangeForTest(INVALIDATIONS_ENABLED);
1369 SimulateInvalidatorStateChangeForTest(INVALIDATION_CREDENTIALS_REJECTED);
1370 SimulateInvalidatorStateChangeForTest(TRANSIENT_INVALIDATION_ERROR);
1371 SetJsEventHandler(WeakHandle<JsEventHandler>());
1372
1373 SimulateInvalidatorStateChangeForTest(INVALIDATIONS_ENABLED);
1374 SimulateInvalidatorStateChangeForTest(INVALIDATION_CREDENTIALS_REJECTED);
1375 SimulateInvalidatorStateChangeForTest(TRANSIENT_INVALIDATION_ERROR);
1376
1377 // Should trigger the replies.
1378 PumpLoop();
1379 }
1380
TEST_F(SyncManagerTest,OnIncomingNotification)1381 TEST_F(SyncManagerTest, OnIncomingNotification) {
1382 StrictMock<MockJsEventHandler> event_handler;
1383
1384 const ModelTypeSet empty_model_types;
1385 const ModelTypeSet model_types(
1386 BOOKMARKS, THEMES);
1387
1388 // Build expected_args to have a single argument with the string
1389 // equivalents of model_types.
1390 base::DictionaryValue expected_details;
1391 {
1392 base::ListValue* model_type_list = new base::ListValue();
1393 expected_details.SetString("source", "REMOTE_INVALIDATION");
1394 expected_details.Set("changedTypes", model_type_list);
1395 for (ModelTypeSet::Iterator it = model_types.First();
1396 it.Good(); it.Inc()) {
1397 model_type_list->Append(
1398 new base::StringValue(ModelTypeToString(it.Get())));
1399 }
1400 }
1401
1402 EXPECT_CALL(event_handler,
1403 HandleJsEvent("onIncomingNotification",
1404 HasDetailsAsDictionary(expected_details)));
1405
1406 TriggerOnIncomingNotificationForTest(empty_model_types);
1407 TriggerOnIncomingNotificationForTest(model_types);
1408
1409 SetJsEventHandler(event_handler.AsWeakHandle());
1410 TriggerOnIncomingNotificationForTest(model_types);
1411 SetJsEventHandler(WeakHandle<JsEventHandler>());
1412
1413 TriggerOnIncomingNotificationForTest(empty_model_types);
1414 TriggerOnIncomingNotificationForTest(model_types);
1415
1416 // Should trigger the replies.
1417 PumpLoop();
1418 }
1419
TEST_F(SyncManagerTest,RefreshEncryptionReady)1420 TEST_F(SyncManagerTest, RefreshEncryptionReady) {
1421 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
1422 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1423 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1424 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, false));
1425
1426 sync_manager_.GetEncryptionHandler()->Init();
1427 PumpLoop();
1428
1429 const ModelTypeSet encrypted_types = GetEncryptedTypes();
1430 EXPECT_TRUE(encrypted_types.Has(PASSWORDS));
1431 EXPECT_FALSE(EncryptEverythingEnabledForTest());
1432
1433 {
1434 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1435 ReadNode node(&trans);
1436 EXPECT_EQ(BaseNode::INIT_OK,
1437 node.InitByIdLookup(GetIdForDataType(NIGORI)));
1438 sync_pb::NigoriSpecifics nigori = node.GetNigoriSpecifics();
1439 EXPECT_TRUE(nigori.has_encryption_keybag());
1440 Cryptographer* cryptographer = trans.GetCryptographer();
1441 EXPECT_TRUE(cryptographer->is_ready());
1442 EXPECT_TRUE(cryptographer->CanDecrypt(nigori.encryption_keybag()));
1443 }
1444 }
1445
1446 // Attempt to refresh encryption when nigori not downloaded.
TEST_F(SyncManagerTest,RefreshEncryptionNotReady)1447 TEST_F(SyncManagerTest, RefreshEncryptionNotReady) {
1448 // Don't set up encryption (no nigori node created).
1449
1450 // Should fail. Triggers an OnPassphraseRequired because the cryptographer
1451 // is not ready.
1452 EXPECT_CALL(encryption_observer_, OnPassphraseRequired(_, _)).Times(1);
1453 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1454 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, false));
1455 sync_manager_.GetEncryptionHandler()->Init();
1456 PumpLoop();
1457
1458 const ModelTypeSet encrypted_types = GetEncryptedTypes();
1459 EXPECT_TRUE(encrypted_types.Has(PASSWORDS)); // Hardcoded.
1460 EXPECT_FALSE(EncryptEverythingEnabledForTest());
1461 }
1462
1463 // Attempt to refresh encryption when nigori is empty.
TEST_F(SyncManagerTest,RefreshEncryptionEmptyNigori)1464 TEST_F(SyncManagerTest, RefreshEncryptionEmptyNigori) {
1465 EXPECT_TRUE(SetUpEncryption(DONT_WRITE_NIGORI, DEFAULT_ENCRYPTION));
1466 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()).Times(1);
1467 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1468 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, false));
1469
1470 // Should write to nigori.
1471 sync_manager_.GetEncryptionHandler()->Init();
1472 PumpLoop();
1473
1474 const ModelTypeSet encrypted_types = GetEncryptedTypes();
1475 EXPECT_TRUE(encrypted_types.Has(PASSWORDS)); // Hardcoded.
1476 EXPECT_FALSE(EncryptEverythingEnabledForTest());
1477
1478 {
1479 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1480 ReadNode node(&trans);
1481 EXPECT_EQ(BaseNode::INIT_OK,
1482 node.InitByIdLookup(GetIdForDataType(NIGORI)));
1483 sync_pb::NigoriSpecifics nigori = node.GetNigoriSpecifics();
1484 EXPECT_TRUE(nigori.has_encryption_keybag());
1485 Cryptographer* cryptographer = trans.GetCryptographer();
1486 EXPECT_TRUE(cryptographer->is_ready());
1487 EXPECT_TRUE(cryptographer->CanDecrypt(nigori.encryption_keybag()));
1488 }
1489 }
1490
TEST_F(SyncManagerTest,EncryptDataTypesWithNoData)1491 TEST_F(SyncManagerTest, EncryptDataTypesWithNoData) {
1492 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
1493 EXPECT_CALL(encryption_observer_,
1494 OnEncryptedTypesChanged(
1495 HasModelTypes(EncryptableUserTypes()), true));
1496 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1497 sync_manager_.GetEncryptionHandler()->EnableEncryptEverything();
1498 EXPECT_TRUE(EncryptEverythingEnabledForTest());
1499 }
1500
TEST_F(SyncManagerTest,EncryptDataTypesWithData)1501 TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
1502 size_t batch_size = 5;
1503 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
1504
1505 // Create some unencrypted unsynced data.
1506 int64 folder = MakeFolderWithParent(sync_manager_.GetUserShare(),
1507 BOOKMARKS,
1508 GetIdForDataType(BOOKMARKS),
1509 NULL);
1510 // First batch_size nodes are children of folder.
1511 size_t i;
1512 for (i = 0; i < batch_size; ++i) {
1513 MakeBookmarkWithParent(sync_manager_.GetUserShare(), folder, NULL);
1514 }
1515 // Next batch_size nodes are a different type and on their own.
1516 for (; i < 2*batch_size; ++i) {
1517 MakeNode(sync_manager_.GetUserShare(), SESSIONS,
1518 base::StringPrintf("%" PRIuS "", i));
1519 }
1520 // Last batch_size nodes are a third type that will not need encryption.
1521 for (; i < 3*batch_size; ++i) {
1522 MakeNode(sync_manager_.GetUserShare(), THEMES,
1523 base::StringPrintf("%" PRIuS "", i));
1524 }
1525
1526 {
1527 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1528 EXPECT_TRUE(GetEncryptedTypesWithTrans(&trans).Equals(
1529 SyncEncryptionHandler::SensitiveTypes()));
1530 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
1531 trans.GetWrappedTrans(),
1532 BOOKMARKS,
1533 false /* not encrypted */));
1534 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
1535 trans.GetWrappedTrans(),
1536 SESSIONS,
1537 false /* not encrypted */));
1538 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
1539 trans.GetWrappedTrans(),
1540 THEMES,
1541 false /* not encrypted */));
1542 }
1543
1544 EXPECT_CALL(encryption_observer_,
1545 OnEncryptedTypesChanged(
1546 HasModelTypes(EncryptableUserTypes()), true));
1547 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1548 sync_manager_.GetEncryptionHandler()->EnableEncryptEverything();
1549 EXPECT_TRUE(EncryptEverythingEnabledForTest());
1550 {
1551 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1552 EXPECT_TRUE(GetEncryptedTypesWithTrans(&trans).Equals(
1553 EncryptableUserTypes()));
1554 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
1555 trans.GetWrappedTrans(),
1556 BOOKMARKS,
1557 true /* is encrypted */));
1558 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
1559 trans.GetWrappedTrans(),
1560 SESSIONS,
1561 true /* is encrypted */));
1562 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
1563 trans.GetWrappedTrans(),
1564 THEMES,
1565 true /* is encrypted */));
1566 }
1567
1568 // Trigger's a ReEncryptEverything with new passphrase.
1569 testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
1570 EXPECT_CALL(encryption_observer_,
1571 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
1572 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
1573 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1574 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1575 EXPECT_CALL(encryption_observer_,
1576 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
1577 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
1578 "new_passphrase", true);
1579 EXPECT_TRUE(EncryptEverythingEnabledForTest());
1580 {
1581 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1582 EXPECT_TRUE(GetEncryptedTypesWithTrans(&trans).Equals(
1583 EncryptableUserTypes()));
1584 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
1585 trans.GetWrappedTrans(),
1586 BOOKMARKS,
1587 true /* is encrypted */));
1588 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
1589 trans.GetWrappedTrans(),
1590 SESSIONS,
1591 true /* is encrypted */));
1592 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
1593 trans.GetWrappedTrans(),
1594 THEMES,
1595 true /* is encrypted */));
1596 }
1597 // Calling EncryptDataTypes with an empty encrypted types should not trigger
1598 // a reencryption and should just notify immediately.
1599 testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
1600 EXPECT_CALL(encryption_observer_,
1601 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)).Times(0);
1602 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()).Times(0);
1603 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()).Times(0);
1604 sync_manager_.GetEncryptionHandler()->EnableEncryptEverything();
1605 }
1606
1607 // Test that when there are no pending keys and the cryptographer is not
1608 // initialized, we add a key based on the current GAIA password.
1609 // (case 1 in SyncManager::SyncInternal::SetEncryptionPassphrase)
TEST_F(SyncManagerTest,SetInitialGaiaPass)1610 TEST_F(SyncManagerTest, SetInitialGaiaPass) {
1611 EXPECT_FALSE(SetUpEncryption(DONT_WRITE_NIGORI, UNINITIALIZED));
1612 EXPECT_CALL(encryption_observer_,
1613 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
1614 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
1615 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1616 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1617 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
1618 "new_passphrase",
1619 false);
1620 EXPECT_EQ(IMPLICIT_PASSPHRASE,
1621 sync_manager_.GetEncryptionHandler()->GetPassphraseType());
1622 EXPECT_FALSE(EncryptEverythingEnabledForTest());
1623 {
1624 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1625 ReadNode node(&trans);
1626 EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag));
1627 sync_pb::NigoriSpecifics nigori = node.GetNigoriSpecifics();
1628 Cryptographer* cryptographer = trans.GetCryptographer();
1629 EXPECT_TRUE(cryptographer->is_ready());
1630 EXPECT_TRUE(cryptographer->CanDecrypt(nigori.encryption_keybag()));
1631 }
1632 }
1633
1634 // Test that when there are no pending keys and we have on the old GAIA
1635 // password, we update and re-encrypt everything with the new GAIA password.
1636 // (case 1 in SyncManager::SyncInternal::SetEncryptionPassphrase)
TEST_F(SyncManagerTest,UpdateGaiaPass)1637 TEST_F(SyncManagerTest, UpdateGaiaPass) {
1638 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
1639 Cryptographer verifier(&encryptor_);
1640 {
1641 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1642 Cryptographer* cryptographer = trans.GetCryptographer();
1643 std::string bootstrap_token;
1644 cryptographer->GetBootstrapToken(&bootstrap_token);
1645 verifier.Bootstrap(bootstrap_token);
1646 }
1647 EXPECT_CALL(encryption_observer_,
1648 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
1649 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
1650 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1651 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1652 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
1653 "new_passphrase",
1654 false);
1655 EXPECT_EQ(IMPLICIT_PASSPHRASE,
1656 sync_manager_.GetEncryptionHandler()->GetPassphraseType());
1657 EXPECT_FALSE(EncryptEverythingEnabledForTest());
1658 {
1659 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1660 Cryptographer* cryptographer = trans.GetCryptographer();
1661 EXPECT_TRUE(cryptographer->is_ready());
1662 // Verify the default key has changed.
1663 sync_pb::EncryptedData encrypted;
1664 cryptographer->GetKeys(&encrypted);
1665 EXPECT_FALSE(verifier.CanDecrypt(encrypted));
1666 }
1667 }
1668
1669 // Sets a new explicit passphrase. This should update the bootstrap token
1670 // and re-encrypt everything.
1671 // (case 2 in SyncManager::SyncInternal::SetEncryptionPassphrase)
TEST_F(SyncManagerTest,SetPassphraseWithPassword)1672 TEST_F(SyncManagerTest, SetPassphraseWithPassword) {
1673 Cryptographer verifier(&encryptor_);
1674 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
1675 {
1676 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1677 // Store the default (soon to be old) key.
1678 Cryptographer* cryptographer = trans.GetCryptographer();
1679 std::string bootstrap_token;
1680 cryptographer->GetBootstrapToken(&bootstrap_token);
1681 verifier.Bootstrap(bootstrap_token);
1682
1683 ReadNode root_node(&trans);
1684 root_node.InitByRootLookup();
1685
1686 WriteNode password_node(&trans);
1687 WriteNode::InitUniqueByCreationResult result =
1688 password_node.InitUniqueByCreation(PASSWORDS,
1689 root_node, "foo");
1690 EXPECT_EQ(WriteNode::INIT_SUCCESS, result);
1691 sync_pb::PasswordSpecificsData data;
1692 data.set_password_value("secret");
1693 password_node.SetPasswordSpecifics(data);
1694 }
1695 EXPECT_CALL(encryption_observer_,
1696 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
1697 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
1698 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1699 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1700 EXPECT_CALL(encryption_observer_,
1701 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
1702 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
1703 "new_passphrase",
1704 true);
1705 EXPECT_EQ(CUSTOM_PASSPHRASE,
1706 sync_manager_.GetEncryptionHandler()->GetPassphraseType());
1707 EXPECT_FALSE(EncryptEverythingEnabledForTest());
1708 {
1709 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1710 Cryptographer* cryptographer = trans.GetCryptographer();
1711 EXPECT_TRUE(cryptographer->is_ready());
1712 // Verify the default key has changed.
1713 sync_pb::EncryptedData encrypted;
1714 cryptographer->GetKeys(&encrypted);
1715 EXPECT_FALSE(verifier.CanDecrypt(encrypted));
1716
1717 ReadNode password_node(&trans);
1718 EXPECT_EQ(BaseNode::INIT_OK,
1719 password_node.InitByClientTagLookup(PASSWORDS,
1720 "foo"));
1721 const sync_pb::PasswordSpecificsData& data =
1722 password_node.GetPasswordSpecifics();
1723 EXPECT_EQ("secret", data.password_value());
1724 }
1725 }
1726
1727 // Manually set the pending keys in the cryptographer/nigori to reflect the data
1728 // being encrypted with a new (unprovided) GAIA password, then supply the
1729 // password.
1730 // (case 7 in SyncManager::SyncInternal::SetDecryptionPassphrase)
TEST_F(SyncManagerTest,SupplyPendingGAIAPass)1731 TEST_F(SyncManagerTest, SupplyPendingGAIAPass) {
1732 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
1733 Cryptographer other_cryptographer(&encryptor_);
1734 {
1735 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1736 Cryptographer* cryptographer = trans.GetCryptographer();
1737 std::string bootstrap_token;
1738 cryptographer->GetBootstrapToken(&bootstrap_token);
1739 other_cryptographer.Bootstrap(bootstrap_token);
1740
1741 // Now update the nigori to reflect the new keys, and update the
1742 // cryptographer to have pending keys.
1743 KeyParams params = {"localhost", "dummy", "passphrase2"};
1744 other_cryptographer.AddKey(params);
1745 WriteNode node(&trans);
1746 EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag));
1747 sync_pb::NigoriSpecifics nigori;
1748 other_cryptographer.GetKeys(nigori.mutable_encryption_keybag());
1749 cryptographer->SetPendingKeys(nigori.encryption_keybag());
1750 EXPECT_TRUE(cryptographer->has_pending_keys());
1751 node.SetNigoriSpecifics(nigori);
1752 }
1753 EXPECT_CALL(encryption_observer_,
1754 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
1755 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
1756 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1757 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1758 sync_manager_.GetEncryptionHandler()->SetDecryptionPassphrase("passphrase2");
1759 EXPECT_EQ(IMPLICIT_PASSPHRASE,
1760 sync_manager_.GetEncryptionHandler()->GetPassphraseType());
1761 EXPECT_FALSE(EncryptEverythingEnabledForTest());
1762 {
1763 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1764 Cryptographer* cryptographer = trans.GetCryptographer();
1765 EXPECT_TRUE(cryptographer->is_ready());
1766 // Verify we're encrypting with the new key.
1767 sync_pb::EncryptedData encrypted;
1768 cryptographer->GetKeys(&encrypted);
1769 EXPECT_TRUE(other_cryptographer.CanDecrypt(encrypted));
1770 }
1771 }
1772
1773 // Manually set the pending keys in the cryptographer/nigori to reflect the data
1774 // being encrypted with an old (unprovided) GAIA password. Attempt to supply
1775 // the current GAIA password and verify the bootstrap token is updated. Then
1776 // supply the old GAIA password, and verify we re-encrypt all data with the
1777 // new GAIA password.
1778 // (cases 4 and 5 in SyncManager::SyncInternal::SetEncryptionPassphrase)
TEST_F(SyncManagerTest,SupplyPendingOldGAIAPass)1779 TEST_F(SyncManagerTest, SupplyPendingOldGAIAPass) {
1780 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
1781 Cryptographer other_cryptographer(&encryptor_);
1782 {
1783 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1784 Cryptographer* cryptographer = trans.GetCryptographer();
1785 std::string bootstrap_token;
1786 cryptographer->GetBootstrapToken(&bootstrap_token);
1787 other_cryptographer.Bootstrap(bootstrap_token);
1788
1789 // Now update the nigori to reflect the new keys, and update the
1790 // cryptographer to have pending keys.
1791 KeyParams params = {"localhost", "dummy", "old_gaia"};
1792 other_cryptographer.AddKey(params);
1793 WriteNode node(&trans);
1794 EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag));
1795 sync_pb::NigoriSpecifics nigori;
1796 other_cryptographer.GetKeys(nigori.mutable_encryption_keybag());
1797 node.SetNigoriSpecifics(nigori);
1798 cryptographer->SetPendingKeys(nigori.encryption_keybag());
1799
1800 // other_cryptographer now contains all encryption keys, and is encrypting
1801 // with the newest gaia.
1802 KeyParams new_params = {"localhost", "dummy", "new_gaia"};
1803 other_cryptographer.AddKey(new_params);
1804 }
1805 // The bootstrap token should have been updated. Save it to ensure it's based
1806 // on the new GAIA password.
1807 std::string bootstrap_token;
1808 EXPECT_CALL(encryption_observer_,
1809 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN))
1810 .WillOnce(SaveArg<0>(&bootstrap_token));
1811 EXPECT_CALL(encryption_observer_, OnPassphraseRequired(_,_));
1812 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1813 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
1814 "new_gaia",
1815 false);
1816 EXPECT_EQ(IMPLICIT_PASSPHRASE,
1817 sync_manager_.GetEncryptionHandler()->GetPassphraseType());
1818 EXPECT_FALSE(EncryptEverythingEnabledForTest());
1819 testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
1820 {
1821 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1822 Cryptographer* cryptographer = trans.GetCryptographer();
1823 EXPECT_TRUE(cryptographer->is_initialized());
1824 EXPECT_FALSE(cryptographer->is_ready());
1825 // Verify we're encrypting with the new key, even though we have pending
1826 // keys.
1827 sync_pb::EncryptedData encrypted;
1828 other_cryptographer.GetKeys(&encrypted);
1829 EXPECT_TRUE(cryptographer->CanDecrypt(encrypted));
1830 }
1831 EXPECT_CALL(encryption_observer_,
1832 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
1833 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
1834 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1835 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1836 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
1837 "old_gaia",
1838 false);
1839 EXPECT_EQ(IMPLICIT_PASSPHRASE,
1840 sync_manager_.GetEncryptionHandler()->GetPassphraseType());
1841 {
1842 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1843 Cryptographer* cryptographer = trans.GetCryptographer();
1844 EXPECT_TRUE(cryptographer->is_ready());
1845
1846 // Verify we're encrypting with the new key.
1847 sync_pb::EncryptedData encrypted;
1848 other_cryptographer.GetKeys(&encrypted);
1849 EXPECT_TRUE(cryptographer->CanDecrypt(encrypted));
1850
1851 // Verify the saved bootstrap token is based on the new gaia password.
1852 Cryptographer temp_cryptographer(&encryptor_);
1853 temp_cryptographer.Bootstrap(bootstrap_token);
1854 EXPECT_TRUE(temp_cryptographer.CanDecrypt(encrypted));
1855 }
1856 }
1857
1858 // Manually set the pending keys in the cryptographer/nigori to reflect the data
1859 // being encrypted with an explicit (unprovided) passphrase, then supply the
1860 // passphrase.
1861 // (case 9 in SyncManager::SyncInternal::SetDecryptionPassphrase)
TEST_F(SyncManagerTest,SupplyPendingExplicitPass)1862 TEST_F(SyncManagerTest, SupplyPendingExplicitPass) {
1863 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
1864 Cryptographer other_cryptographer(&encryptor_);
1865 {
1866 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1867 Cryptographer* cryptographer = trans.GetCryptographer();
1868 std::string bootstrap_token;
1869 cryptographer->GetBootstrapToken(&bootstrap_token);
1870 other_cryptographer.Bootstrap(bootstrap_token);
1871
1872 // Now update the nigori to reflect the new keys, and update the
1873 // cryptographer to have pending keys.
1874 KeyParams params = {"localhost", "dummy", "explicit"};
1875 other_cryptographer.AddKey(params);
1876 WriteNode node(&trans);
1877 EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag));
1878 sync_pb::NigoriSpecifics nigori;
1879 other_cryptographer.GetKeys(nigori.mutable_encryption_keybag());
1880 cryptographer->SetPendingKeys(nigori.encryption_keybag());
1881 EXPECT_TRUE(cryptographer->has_pending_keys());
1882 nigori.set_keybag_is_frozen(true);
1883 node.SetNigoriSpecifics(nigori);
1884 }
1885 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1886 EXPECT_CALL(encryption_observer_,
1887 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
1888 EXPECT_CALL(encryption_observer_, OnPassphraseRequired(_, _));
1889 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, false));
1890 sync_manager_.GetEncryptionHandler()->Init();
1891 EXPECT_CALL(encryption_observer_,
1892 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
1893 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
1894 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1895 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1896 sync_manager_.GetEncryptionHandler()->SetDecryptionPassphrase("explicit");
1897 EXPECT_EQ(CUSTOM_PASSPHRASE,
1898 sync_manager_.GetEncryptionHandler()->GetPassphraseType());
1899 EXPECT_FALSE(EncryptEverythingEnabledForTest());
1900 {
1901 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1902 Cryptographer* cryptographer = trans.GetCryptographer();
1903 EXPECT_TRUE(cryptographer->is_ready());
1904 // Verify we're encrypting with the new key.
1905 sync_pb::EncryptedData encrypted;
1906 cryptographer->GetKeys(&encrypted);
1907 EXPECT_TRUE(other_cryptographer.CanDecrypt(encrypted));
1908 }
1909 }
1910
1911 // Manually set the pending keys in the cryptographer/nigori to reflect the data
1912 // being encrypted with a new (unprovided) GAIA password, then supply the
1913 // password as a user-provided password.
1914 // This is the android case 7/8.
TEST_F(SyncManagerTest,SupplyPendingGAIAPassUserProvided)1915 TEST_F(SyncManagerTest, SupplyPendingGAIAPassUserProvided) {
1916 EXPECT_FALSE(SetUpEncryption(DONT_WRITE_NIGORI, UNINITIALIZED));
1917 Cryptographer other_cryptographer(&encryptor_);
1918 {
1919 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1920 Cryptographer* cryptographer = trans.GetCryptographer();
1921 // Now update the nigori to reflect the new keys, and update the
1922 // cryptographer to have pending keys.
1923 KeyParams params = {"localhost", "dummy", "passphrase"};
1924 other_cryptographer.AddKey(params);
1925 WriteNode node(&trans);
1926 EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag));
1927 sync_pb::NigoriSpecifics nigori;
1928 other_cryptographer.GetKeys(nigori.mutable_encryption_keybag());
1929 node.SetNigoriSpecifics(nigori);
1930 cryptographer->SetPendingKeys(nigori.encryption_keybag());
1931 EXPECT_FALSE(cryptographer->is_ready());
1932 }
1933 EXPECT_CALL(encryption_observer_,
1934 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
1935 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
1936 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1937 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1938 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
1939 "passphrase",
1940 false);
1941 EXPECT_EQ(IMPLICIT_PASSPHRASE,
1942 sync_manager_.GetEncryptionHandler()->GetPassphraseType());
1943 EXPECT_FALSE(EncryptEverythingEnabledForTest());
1944 {
1945 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1946 Cryptographer* cryptographer = trans.GetCryptographer();
1947 EXPECT_TRUE(cryptographer->is_ready());
1948 }
1949 }
1950
TEST_F(SyncManagerTest,SetPassphraseWithEmptyPasswordNode)1951 TEST_F(SyncManagerTest, SetPassphraseWithEmptyPasswordNode) {
1952 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
1953 int64 node_id = 0;
1954 std::string tag = "foo";
1955 {
1956 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1957 ReadNode root_node(&trans);
1958 root_node.InitByRootLookup();
1959
1960 WriteNode password_node(&trans);
1961 WriteNode::InitUniqueByCreationResult result =
1962 password_node.InitUniqueByCreation(PASSWORDS, root_node, tag);
1963 EXPECT_EQ(WriteNode::INIT_SUCCESS, result);
1964 node_id = password_node.GetId();
1965 }
1966 EXPECT_CALL(encryption_observer_,
1967 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
1968 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
1969 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
1970 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
1971 EXPECT_CALL(encryption_observer_,
1972 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
1973 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
1974 "new_passphrase",
1975 true);
1976 EXPECT_EQ(CUSTOM_PASSPHRASE,
1977 sync_manager_.GetEncryptionHandler()->GetPassphraseType());
1978 EXPECT_FALSE(EncryptEverythingEnabledForTest());
1979 {
1980 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1981 ReadNode password_node(&trans);
1982 EXPECT_EQ(BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY,
1983 password_node.InitByClientTagLookup(PASSWORDS,
1984 tag));
1985 }
1986 {
1987 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1988 ReadNode password_node(&trans);
1989 EXPECT_EQ(BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY,
1990 password_node.InitByIdLookup(node_id));
1991 }
1992 }
1993
TEST_F(SyncManagerTest,NudgeDelayTest)1994 TEST_F(SyncManagerTest, NudgeDelayTest) {
1995 EXPECT_EQ(sync_manager_.GetNudgeDelayTimeDelta(BOOKMARKS),
1996 base::TimeDelta::FromMilliseconds(
1997 SyncManagerImpl::GetDefaultNudgeDelay()));
1998
1999 EXPECT_EQ(sync_manager_.GetNudgeDelayTimeDelta(AUTOFILL),
2000 base::TimeDelta::FromSeconds(
2001 kDefaultShortPollIntervalSeconds));
2002
2003 EXPECT_EQ(sync_manager_.GetNudgeDelayTimeDelta(PREFERENCES),
2004 base::TimeDelta::FromMilliseconds(
2005 SyncManagerImpl::GetPreferencesNudgeDelay()));
2006 }
2007
2008 // Friended by WriteNode, so can't be in an anonymouse namespace.
TEST_F(SyncManagerTest,EncryptBookmarksWithLegacyData)2009 TEST_F(SyncManagerTest, EncryptBookmarksWithLegacyData) {
2010 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
2011 std::string title;
2012 SyncAPINameToServerName("Google", &title);
2013 std::string url = "http://www.google.com";
2014 std::string raw_title2 = ".."; // An invalid cosmo title.
2015 std::string title2;
2016 SyncAPINameToServerName(raw_title2, &title2);
2017 std::string url2 = "http://www.bla.com";
2018
2019 // Create a bookmark using the legacy format.
2020 int64 node_id1 = MakeNode(sync_manager_.GetUserShare(),
2021 BOOKMARKS,
2022 "testtag");
2023 int64 node_id2 = MakeNode(sync_manager_.GetUserShare(),
2024 BOOKMARKS,
2025 "testtag2");
2026 {
2027 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2028 WriteNode node(&trans);
2029 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(node_id1));
2030
2031 sync_pb::EntitySpecifics entity_specifics;
2032 entity_specifics.mutable_bookmark()->set_url(url);
2033 node.SetEntitySpecifics(entity_specifics);
2034
2035 // Set the old style title.
2036 syncable::MutableEntry* node_entry = node.entry_;
2037 node_entry->PutNonUniqueName(title);
2038
2039 WriteNode node2(&trans);
2040 EXPECT_EQ(BaseNode::INIT_OK, node2.InitByIdLookup(node_id2));
2041
2042 sync_pb::EntitySpecifics entity_specifics2;
2043 entity_specifics2.mutable_bookmark()->set_url(url2);
2044 node2.SetEntitySpecifics(entity_specifics2);
2045
2046 // Set the old style title.
2047 syncable::MutableEntry* node_entry2 = node2.entry_;
2048 node_entry2->PutNonUniqueName(title2);
2049 }
2050
2051 {
2052 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2053 ReadNode node(&trans);
2054 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(node_id1));
2055 EXPECT_EQ(BOOKMARKS, node.GetModelType());
2056 EXPECT_EQ(title, node.GetTitle());
2057 EXPECT_EQ(title, node.GetBookmarkSpecifics().title());
2058 EXPECT_EQ(url, node.GetBookmarkSpecifics().url());
2059
2060 ReadNode node2(&trans);
2061 EXPECT_EQ(BaseNode::INIT_OK, node2.InitByIdLookup(node_id2));
2062 EXPECT_EQ(BOOKMARKS, node2.GetModelType());
2063 // We should de-canonicalize the title in GetTitle(), but the title in the
2064 // specifics should be stored in the server legal form.
2065 EXPECT_EQ(raw_title2, node2.GetTitle());
2066 EXPECT_EQ(title2, node2.GetBookmarkSpecifics().title());
2067 EXPECT_EQ(url2, node2.GetBookmarkSpecifics().url());
2068 }
2069
2070 {
2071 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2072 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
2073 trans.GetWrappedTrans(),
2074 BOOKMARKS,
2075 false /* not encrypted */));
2076 }
2077
2078 EXPECT_CALL(encryption_observer_,
2079 OnEncryptedTypesChanged(
2080 HasModelTypes(EncryptableUserTypes()), true));
2081 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
2082 sync_manager_.GetEncryptionHandler()->EnableEncryptEverything();
2083 EXPECT_TRUE(EncryptEverythingEnabledForTest());
2084
2085 {
2086 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2087 EXPECT_TRUE(GetEncryptedTypesWithTrans(&trans).Equals(
2088 EncryptableUserTypes()));
2089 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest(
2090 trans.GetWrappedTrans(),
2091 BOOKMARKS,
2092 true /* is encrypted */));
2093
2094 ReadNode node(&trans);
2095 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(node_id1));
2096 EXPECT_EQ(BOOKMARKS, node.GetModelType());
2097 EXPECT_EQ(title, node.GetTitle());
2098 EXPECT_EQ(title, node.GetBookmarkSpecifics().title());
2099 EXPECT_EQ(url, node.GetBookmarkSpecifics().url());
2100
2101 ReadNode node2(&trans);
2102 EXPECT_EQ(BaseNode::INIT_OK, node2.InitByIdLookup(node_id2));
2103 EXPECT_EQ(BOOKMARKS, node2.GetModelType());
2104 // We should de-canonicalize the title in GetTitle(), but the title in the
2105 // specifics should be stored in the server legal form.
2106 EXPECT_EQ(raw_title2, node2.GetTitle());
2107 EXPECT_EQ(title2, node2.GetBookmarkSpecifics().title());
2108 EXPECT_EQ(url2, node2.GetBookmarkSpecifics().url());
2109 }
2110 }
2111
2112 // Create a bookmark and set the title/url, then verify the data was properly
2113 // set. This replicates the unique way bookmarks have of creating sync nodes.
2114 // See BookmarkChangeProcessor::PlaceSyncNode(..).
TEST_F(SyncManagerTest,CreateLocalBookmark)2115 TEST_F(SyncManagerTest, CreateLocalBookmark) {
2116 std::string title = "title";
2117 std::string url = "url";
2118 {
2119 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2120 ReadNode bookmark_root(&trans);
2121 ASSERT_EQ(BaseNode::INIT_OK,
2122 bookmark_root.InitByTagLookup(ModelTypeToRootTag(BOOKMARKS)));
2123 WriteNode node(&trans);
2124 ASSERT_TRUE(node.InitBookmarkByCreation(bookmark_root, NULL));
2125 node.SetIsFolder(false);
2126 node.SetTitle(UTF8ToWide(title));
2127
2128 sync_pb::BookmarkSpecifics bookmark_specifics(node.GetBookmarkSpecifics());
2129 bookmark_specifics.set_url(url);
2130 node.SetBookmarkSpecifics(bookmark_specifics);
2131 }
2132 {
2133 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2134 ReadNode bookmark_root(&trans);
2135 ASSERT_EQ(BaseNode::INIT_OK,
2136 bookmark_root.InitByTagLookup(ModelTypeToRootTag(BOOKMARKS)));
2137 int64 child_id = bookmark_root.GetFirstChildId();
2138
2139 ReadNode node(&trans);
2140 ASSERT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(child_id));
2141 EXPECT_FALSE(node.GetIsFolder());
2142 EXPECT_EQ(title, node.GetTitle());
2143 EXPECT_EQ(url, node.GetBookmarkSpecifics().url());
2144 }
2145 }
2146
2147 // Verifies WriteNode::UpdateEntryWithEncryption does not make unnecessary
2148 // changes.
TEST_F(SyncManagerTest,UpdateEntryWithEncryption)2149 TEST_F(SyncManagerTest, UpdateEntryWithEncryption) {
2150 std::string client_tag = "title";
2151 sync_pb::EntitySpecifics entity_specifics;
2152 entity_specifics.mutable_bookmark()->set_url("url");
2153 entity_specifics.mutable_bookmark()->set_title("title");
2154 MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag,
2155 syncable::GenerateSyncableHash(BOOKMARKS,
2156 client_tag),
2157 entity_specifics);
2158 // New node shouldn't start off unsynced.
2159 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2160 // Manually change to the same data. Should not set is_unsynced.
2161 {
2162 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2163 WriteNode node(&trans);
2164 EXPECT_EQ(BaseNode::INIT_OK,
2165 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2166 node.SetEntitySpecifics(entity_specifics);
2167 }
2168 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2169
2170 // Encrypt the datatatype, should set is_unsynced.
2171 EXPECT_CALL(encryption_observer_,
2172 OnEncryptedTypesChanged(
2173 HasModelTypes(EncryptableUserTypes()), true));
2174 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
2175 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, FULL_ENCRYPTION));
2176
2177 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
2178 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, true));
2179 sync_manager_.GetEncryptionHandler()->Init();
2180 PumpLoop();
2181 {
2182 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2183 ReadNode node(&trans);
2184 EXPECT_EQ(BaseNode::INIT_OK,
2185 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2186 const syncable::Entry* node_entry = node.GetEntry();
2187 const sync_pb::EntitySpecifics& specifics = node_entry->GetSpecifics();
2188 EXPECT_TRUE(specifics.has_encrypted());
2189 EXPECT_EQ(kEncryptedString, node_entry->GetNonUniqueName());
2190 Cryptographer* cryptographer = trans.GetCryptographer();
2191 EXPECT_TRUE(cryptographer->is_ready());
2192 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
2193 specifics.encrypted()));
2194 }
2195 EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2196
2197 // Set a new passphrase. Should set is_unsynced.
2198 testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
2199 EXPECT_CALL(encryption_observer_,
2200 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
2201 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
2202 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
2203 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
2204 EXPECT_CALL(encryption_observer_,
2205 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
2206 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
2207 "new_passphrase",
2208 true);
2209 {
2210 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2211 ReadNode node(&trans);
2212 EXPECT_EQ(BaseNode::INIT_OK,
2213 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2214 const syncable::Entry* node_entry = node.GetEntry();
2215 const sync_pb::EntitySpecifics& specifics = node_entry->GetSpecifics();
2216 EXPECT_TRUE(specifics.has_encrypted());
2217 EXPECT_EQ(kEncryptedString, node_entry->GetNonUniqueName());
2218 Cryptographer* cryptographer = trans.GetCryptographer();
2219 EXPECT_TRUE(cryptographer->is_ready());
2220 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
2221 specifics.encrypted()));
2222 }
2223 EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2224
2225 // Force a re-encrypt everything. Should not set is_unsynced.
2226 testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
2227 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
2228 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
2229 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, true));
2230
2231 sync_manager_.GetEncryptionHandler()->Init();
2232 PumpLoop();
2233
2234 {
2235 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2236 ReadNode node(&trans);
2237 EXPECT_EQ(BaseNode::INIT_OK,
2238 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2239 const syncable::Entry* node_entry = node.GetEntry();
2240 const sync_pb::EntitySpecifics& specifics = node_entry->GetSpecifics();
2241 EXPECT_TRUE(specifics.has_encrypted());
2242 EXPECT_EQ(kEncryptedString, node_entry->GetNonUniqueName());
2243 Cryptographer* cryptographer = trans.GetCryptographer();
2244 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
2245 specifics.encrypted()));
2246 }
2247 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2248
2249 // Manually change to the same data. Should not set is_unsynced.
2250 {
2251 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2252 WriteNode node(&trans);
2253 EXPECT_EQ(BaseNode::INIT_OK,
2254 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2255 node.SetEntitySpecifics(entity_specifics);
2256 const syncable::Entry* node_entry = node.GetEntry();
2257 const sync_pb::EntitySpecifics& specifics = node_entry->GetSpecifics();
2258 EXPECT_TRUE(specifics.has_encrypted());
2259 EXPECT_FALSE(node_entry->GetIsUnsynced());
2260 EXPECT_EQ(kEncryptedString, node_entry->GetNonUniqueName());
2261 Cryptographer* cryptographer = trans.GetCryptographer();
2262 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
2263 specifics.encrypted()));
2264 }
2265 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2266
2267 // Manually change to different data. Should set is_unsynced.
2268 {
2269 entity_specifics.mutable_bookmark()->set_url("url2");
2270 entity_specifics.mutable_bookmark()->set_title("title2");
2271 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2272 WriteNode node(&trans);
2273 EXPECT_EQ(BaseNode::INIT_OK,
2274 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2275 node.SetEntitySpecifics(entity_specifics);
2276 const syncable::Entry* node_entry = node.GetEntry();
2277 const sync_pb::EntitySpecifics& specifics = node_entry->GetSpecifics();
2278 EXPECT_TRUE(specifics.has_encrypted());
2279 EXPECT_TRUE(node_entry->GetIsUnsynced());
2280 EXPECT_EQ(kEncryptedString, node_entry->GetNonUniqueName());
2281 Cryptographer* cryptographer = trans.GetCryptographer();
2282 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
2283 specifics.encrypted()));
2284 }
2285 }
2286
2287 // Passwords have their own handling for encryption. Verify it does not result
2288 // in unnecessary writes via SetEntitySpecifics.
TEST_F(SyncManagerTest,UpdatePasswordSetEntitySpecificsNoChange)2289 TEST_F(SyncManagerTest, UpdatePasswordSetEntitySpecificsNoChange) {
2290 std::string client_tag = "title";
2291 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
2292 sync_pb::EntitySpecifics entity_specifics;
2293 {
2294 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2295 Cryptographer* cryptographer = trans.GetCryptographer();
2296 sync_pb::PasswordSpecificsData data;
2297 data.set_password_value("secret");
2298 cryptographer->Encrypt(
2299 data,
2300 entity_specifics.mutable_password()->
2301 mutable_encrypted());
2302 }
2303 MakeServerNode(sync_manager_.GetUserShare(), PASSWORDS, client_tag,
2304 syncable::GenerateSyncableHash(PASSWORDS,
2305 client_tag),
2306 entity_specifics);
2307 // New node shouldn't start off unsynced.
2308 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag));
2309
2310 // Manually change to the same data via SetEntitySpecifics. Should not set
2311 // is_unsynced.
2312 {
2313 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2314 WriteNode node(&trans);
2315 EXPECT_EQ(BaseNode::INIT_OK,
2316 node.InitByClientTagLookup(PASSWORDS, client_tag));
2317 node.SetEntitySpecifics(entity_specifics);
2318 }
2319 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag));
2320 }
2321
2322 // Passwords have their own handling for encryption. Verify it does not result
2323 // in unnecessary writes via SetPasswordSpecifics.
TEST_F(SyncManagerTest,UpdatePasswordSetPasswordSpecifics)2324 TEST_F(SyncManagerTest, UpdatePasswordSetPasswordSpecifics) {
2325 std::string client_tag = "title";
2326 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
2327 sync_pb::EntitySpecifics entity_specifics;
2328 {
2329 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2330 Cryptographer* cryptographer = trans.GetCryptographer();
2331 sync_pb::PasswordSpecificsData data;
2332 data.set_password_value("secret");
2333 cryptographer->Encrypt(
2334 data,
2335 entity_specifics.mutable_password()->
2336 mutable_encrypted());
2337 }
2338 MakeServerNode(sync_manager_.GetUserShare(), PASSWORDS, client_tag,
2339 syncable::GenerateSyncableHash(PASSWORDS,
2340 client_tag),
2341 entity_specifics);
2342 // New node shouldn't start off unsynced.
2343 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag));
2344
2345 // Manually change to the same data via SetPasswordSpecifics. Should not set
2346 // is_unsynced.
2347 {
2348 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2349 WriteNode node(&trans);
2350 EXPECT_EQ(BaseNode::INIT_OK,
2351 node.InitByClientTagLookup(PASSWORDS, client_tag));
2352 node.SetPasswordSpecifics(node.GetPasswordSpecifics());
2353 }
2354 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag));
2355
2356 // Manually change to different data. Should set is_unsynced.
2357 {
2358 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2359 WriteNode node(&trans);
2360 EXPECT_EQ(BaseNode::INIT_OK,
2361 node.InitByClientTagLookup(PASSWORDS, client_tag));
2362 Cryptographer* cryptographer = trans.GetCryptographer();
2363 sync_pb::PasswordSpecificsData data;
2364 data.set_password_value("secret2");
2365 cryptographer->Encrypt(
2366 data,
2367 entity_specifics.mutable_password()->mutable_encrypted());
2368 node.SetPasswordSpecifics(data);
2369 const syncable::Entry* node_entry = node.GetEntry();
2370 EXPECT_TRUE(node_entry->GetIsUnsynced());
2371 }
2372 }
2373
2374 // Passwords have their own handling for encryption. Verify setting a new
2375 // passphrase updates the data.
TEST_F(SyncManagerTest,UpdatePasswordNewPassphrase)2376 TEST_F(SyncManagerTest, UpdatePasswordNewPassphrase) {
2377 std::string client_tag = "title";
2378 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
2379 sync_pb::EntitySpecifics entity_specifics;
2380 {
2381 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2382 Cryptographer* cryptographer = trans.GetCryptographer();
2383 sync_pb::PasswordSpecificsData data;
2384 data.set_password_value("secret");
2385 cryptographer->Encrypt(
2386 data,
2387 entity_specifics.mutable_password()->mutable_encrypted());
2388 }
2389 MakeServerNode(sync_manager_.GetUserShare(), PASSWORDS, client_tag,
2390 syncable::GenerateSyncableHash(PASSWORDS,
2391 client_tag),
2392 entity_specifics);
2393 // New node shouldn't start off unsynced.
2394 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag));
2395
2396 // Set a new passphrase. Should set is_unsynced.
2397 testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
2398 EXPECT_CALL(encryption_observer_,
2399 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
2400 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
2401 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
2402 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
2403 EXPECT_CALL(encryption_observer_,
2404 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
2405 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
2406 "new_passphrase",
2407 true);
2408 EXPECT_EQ(CUSTOM_PASSPHRASE,
2409 sync_manager_.GetEncryptionHandler()->GetPassphraseType());
2410 EXPECT_TRUE(ResetUnsyncedEntry(PASSWORDS, client_tag));
2411 }
2412
2413 // Passwords have their own handling for encryption. Verify it does not result
2414 // in unnecessary writes via ReencryptEverything.
TEST_F(SyncManagerTest,UpdatePasswordReencryptEverything)2415 TEST_F(SyncManagerTest, UpdatePasswordReencryptEverything) {
2416 std::string client_tag = "title";
2417 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
2418 sync_pb::EntitySpecifics entity_specifics;
2419 {
2420 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2421 Cryptographer* cryptographer = trans.GetCryptographer();
2422 sync_pb::PasswordSpecificsData data;
2423 data.set_password_value("secret");
2424 cryptographer->Encrypt(
2425 data,
2426 entity_specifics.mutable_password()->mutable_encrypted());
2427 }
2428 MakeServerNode(sync_manager_.GetUserShare(), PASSWORDS, client_tag,
2429 syncable::GenerateSyncableHash(PASSWORDS,
2430 client_tag),
2431 entity_specifics);
2432 // New node shouldn't start off unsynced.
2433 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag));
2434
2435 // Force a re-encrypt everything. Should not set is_unsynced.
2436 testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
2437 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
2438 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
2439 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, false));
2440 sync_manager_.GetEncryptionHandler()->Init();
2441 PumpLoop();
2442 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag));
2443 }
2444
2445 // Verify SetTitle(..) doesn't unnecessarily set IS_UNSYNCED for bookmarks
2446 // when we write the same data, but does set it when we write new data.
TEST_F(SyncManagerTest,SetBookmarkTitle)2447 TEST_F(SyncManagerTest, SetBookmarkTitle) {
2448 std::string client_tag = "title";
2449 sync_pb::EntitySpecifics entity_specifics;
2450 entity_specifics.mutable_bookmark()->set_url("url");
2451 entity_specifics.mutable_bookmark()->set_title("title");
2452 MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag,
2453 syncable::GenerateSyncableHash(BOOKMARKS,
2454 client_tag),
2455 entity_specifics);
2456 // New node shouldn't start off unsynced.
2457 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2458
2459 // Manually change to the same title. Should not set is_unsynced.
2460 {
2461 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2462 WriteNode node(&trans);
2463 EXPECT_EQ(BaseNode::INIT_OK,
2464 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2465 node.SetTitle(UTF8ToWide(client_tag));
2466 }
2467 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2468
2469 // Manually change to new title. Should set is_unsynced.
2470 {
2471 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2472 WriteNode node(&trans);
2473 EXPECT_EQ(BaseNode::INIT_OK,
2474 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2475 node.SetTitle(UTF8ToWide("title2"));
2476 }
2477 EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2478 }
2479
2480 // Verify SetTitle(..) doesn't unnecessarily set IS_UNSYNCED for encrypted
2481 // bookmarks when we write the same data, but does set it when we write new
2482 // data.
TEST_F(SyncManagerTest,SetBookmarkTitleWithEncryption)2483 TEST_F(SyncManagerTest, SetBookmarkTitleWithEncryption) {
2484 std::string client_tag = "title";
2485 sync_pb::EntitySpecifics entity_specifics;
2486 entity_specifics.mutable_bookmark()->set_url("url");
2487 entity_specifics.mutable_bookmark()->set_title("title");
2488 MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag,
2489 syncable::GenerateSyncableHash(BOOKMARKS,
2490 client_tag),
2491 entity_specifics);
2492 // New node shouldn't start off unsynced.
2493 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2494
2495 // Encrypt the datatatype, should set is_unsynced.
2496 EXPECT_CALL(encryption_observer_,
2497 OnEncryptedTypesChanged(
2498 HasModelTypes(EncryptableUserTypes()), true));
2499 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
2500 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, FULL_ENCRYPTION));
2501 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
2502 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, true));
2503 sync_manager_.GetEncryptionHandler()->Init();
2504 PumpLoop();
2505 EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2506
2507 // Manually change to the same title. Should not set is_unsynced.
2508 // NON_UNIQUE_NAME should be kEncryptedString.
2509 {
2510 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2511 WriteNode node(&trans);
2512 EXPECT_EQ(BaseNode::INIT_OK,
2513 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2514 node.SetTitle(UTF8ToWide(client_tag));
2515 const syncable::Entry* node_entry = node.GetEntry();
2516 const sync_pb::EntitySpecifics& specifics = node_entry->GetSpecifics();
2517 EXPECT_TRUE(specifics.has_encrypted());
2518 EXPECT_EQ(kEncryptedString, node_entry->GetNonUniqueName());
2519 }
2520 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2521
2522 // Manually change to new title. Should set is_unsynced. NON_UNIQUE_NAME
2523 // should still be kEncryptedString.
2524 {
2525 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2526 WriteNode node(&trans);
2527 EXPECT_EQ(BaseNode::INIT_OK,
2528 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2529 node.SetTitle(UTF8ToWide("title2"));
2530 const syncable::Entry* node_entry = node.GetEntry();
2531 const sync_pb::EntitySpecifics& specifics = node_entry->GetSpecifics();
2532 EXPECT_TRUE(specifics.has_encrypted());
2533 EXPECT_EQ(kEncryptedString, node_entry->GetNonUniqueName());
2534 }
2535 EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag));
2536 }
2537
2538 // Verify SetTitle(..) doesn't unnecessarily set IS_UNSYNCED for non-bookmarks
2539 // when we write the same data, but does set it when we write new data.
TEST_F(SyncManagerTest,SetNonBookmarkTitle)2540 TEST_F(SyncManagerTest, SetNonBookmarkTitle) {
2541 std::string client_tag = "title";
2542 sync_pb::EntitySpecifics entity_specifics;
2543 entity_specifics.mutable_preference()->set_name("name");
2544 entity_specifics.mutable_preference()->set_value("value");
2545 MakeServerNode(sync_manager_.GetUserShare(),
2546 PREFERENCES,
2547 client_tag,
2548 syncable::GenerateSyncableHash(PREFERENCES,
2549 client_tag),
2550 entity_specifics);
2551 // New node shouldn't start off unsynced.
2552 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, client_tag));
2553
2554 // Manually change to the same title. Should not set is_unsynced.
2555 {
2556 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2557 WriteNode node(&trans);
2558 EXPECT_EQ(BaseNode::INIT_OK,
2559 node.InitByClientTagLookup(PREFERENCES, client_tag));
2560 node.SetTitle(UTF8ToWide(client_tag));
2561 }
2562 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, client_tag));
2563
2564 // Manually change to new title. Should set is_unsynced.
2565 {
2566 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2567 WriteNode node(&trans);
2568 EXPECT_EQ(BaseNode::INIT_OK,
2569 node.InitByClientTagLookup(PREFERENCES, client_tag));
2570 node.SetTitle(UTF8ToWide("title2"));
2571 }
2572 EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, client_tag));
2573 }
2574
2575 // Verify SetTitle(..) doesn't unnecessarily set IS_UNSYNCED for encrypted
2576 // non-bookmarks when we write the same data or when we write new data
2577 // data (should remained kEncryptedString).
TEST_F(SyncManagerTest,SetNonBookmarkTitleWithEncryption)2578 TEST_F(SyncManagerTest, SetNonBookmarkTitleWithEncryption) {
2579 std::string client_tag = "title";
2580 sync_pb::EntitySpecifics entity_specifics;
2581 entity_specifics.mutable_preference()->set_name("name");
2582 entity_specifics.mutable_preference()->set_value("value");
2583 MakeServerNode(sync_manager_.GetUserShare(),
2584 PREFERENCES,
2585 client_tag,
2586 syncable::GenerateSyncableHash(PREFERENCES,
2587 client_tag),
2588 entity_specifics);
2589 // New node shouldn't start off unsynced.
2590 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, client_tag));
2591
2592 // Encrypt the datatatype, should set is_unsynced.
2593 EXPECT_CALL(encryption_observer_,
2594 OnEncryptedTypesChanged(
2595 HasModelTypes(EncryptableUserTypes()), true));
2596 EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
2597 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, FULL_ENCRYPTION));
2598 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
2599 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, true));
2600 sync_manager_.GetEncryptionHandler()->Init();
2601 PumpLoop();
2602 EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, client_tag));
2603
2604 // Manually change to the same title. Should not set is_unsynced.
2605 // NON_UNIQUE_NAME should be kEncryptedString.
2606 {
2607 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2608 WriteNode node(&trans);
2609 EXPECT_EQ(BaseNode::INIT_OK,
2610 node.InitByClientTagLookup(PREFERENCES, client_tag));
2611 node.SetTitle(UTF8ToWide(client_tag));
2612 const syncable::Entry* node_entry = node.GetEntry();
2613 const sync_pb::EntitySpecifics& specifics = node_entry->GetSpecifics();
2614 EXPECT_TRUE(specifics.has_encrypted());
2615 EXPECT_EQ(kEncryptedString, node_entry->GetNonUniqueName());
2616 }
2617 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, client_tag));
2618
2619 // Manually change to new title. Should not set is_unsynced because the
2620 // NON_UNIQUE_NAME should still be kEncryptedString.
2621 {
2622 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2623 WriteNode node(&trans);
2624 EXPECT_EQ(BaseNode::INIT_OK,
2625 node.InitByClientTagLookup(PREFERENCES, client_tag));
2626 node.SetTitle(UTF8ToWide("title2"));
2627 const syncable::Entry* node_entry = node.GetEntry();
2628 const sync_pb::EntitySpecifics& specifics = node_entry->GetSpecifics();
2629 EXPECT_TRUE(specifics.has_encrypted());
2630 EXPECT_EQ(kEncryptedString, node_entry->GetNonUniqueName());
2631 EXPECT_FALSE(node_entry->GetIsUnsynced());
2632 }
2633 }
2634
2635 // Ensure that titles are truncated to 255 bytes, and attempting to reset
2636 // them to their longer version does not set IS_UNSYNCED.
TEST_F(SyncManagerTest,SetLongTitle)2637 TEST_F(SyncManagerTest, SetLongTitle) {
2638 const int kNumChars = 512;
2639 const std::string kClientTag = "tag";
2640 std::string title(kNumChars, '0');
2641 sync_pb::EntitySpecifics entity_specifics;
2642 entity_specifics.mutable_preference()->set_name("name");
2643 entity_specifics.mutable_preference()->set_value("value");
2644 MakeServerNode(sync_manager_.GetUserShare(),
2645 PREFERENCES,
2646 "short_title",
2647 syncable::GenerateSyncableHash(PREFERENCES,
2648 kClientTag),
2649 entity_specifics);
2650 // New node shouldn't start off unsynced.
2651 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, kClientTag));
2652
2653 // Manually change to the long title. Should set is_unsynced.
2654 {
2655 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2656 WriteNode node(&trans);
2657 EXPECT_EQ(BaseNode::INIT_OK,
2658 node.InitByClientTagLookup(PREFERENCES, kClientTag));
2659 node.SetTitle(UTF8ToWide(title));
2660 EXPECT_EQ(node.GetTitle(), title.substr(0, 255));
2661 }
2662 EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, kClientTag));
2663
2664 // Manually change to the same title. Should not set is_unsynced.
2665 {
2666 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2667 WriteNode node(&trans);
2668 EXPECT_EQ(BaseNode::INIT_OK,
2669 node.InitByClientTagLookup(PREFERENCES, kClientTag));
2670 node.SetTitle(UTF8ToWide(title));
2671 EXPECT_EQ(node.GetTitle(), title.substr(0, 255));
2672 }
2673 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, kClientTag));
2674
2675 // Manually change to new title. Should set is_unsynced.
2676 {
2677 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2678 WriteNode node(&trans);
2679 EXPECT_EQ(BaseNode::INIT_OK,
2680 node.InitByClientTagLookup(PREFERENCES, kClientTag));
2681 node.SetTitle(UTF8ToWide("title2"));
2682 }
2683 EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, kClientTag));
2684 }
2685
2686 // Create an encrypted entry when the cryptographer doesn't think the type is
2687 // marked for encryption. Ensure reads/writes don't break and don't unencrypt
2688 // the data.
TEST_F(SyncManagerTest,SetPreviouslyEncryptedSpecifics)2689 TEST_F(SyncManagerTest, SetPreviouslyEncryptedSpecifics) {
2690 std::string client_tag = "tag";
2691 std::string url = "url";
2692 std::string url2 = "new_url";
2693 std::string title = "title";
2694 sync_pb::EntitySpecifics entity_specifics;
2695 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION));
2696 {
2697 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2698 Cryptographer* crypto = trans.GetCryptographer();
2699 sync_pb::EntitySpecifics bm_specifics;
2700 bm_specifics.mutable_bookmark()->set_title("title");
2701 bm_specifics.mutable_bookmark()->set_url("url");
2702 sync_pb::EncryptedData encrypted;
2703 crypto->Encrypt(bm_specifics, &encrypted);
2704 entity_specifics.mutable_encrypted()->CopyFrom(encrypted);
2705 AddDefaultFieldValue(BOOKMARKS, &entity_specifics);
2706 }
2707 MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag,
2708 syncable::GenerateSyncableHash(BOOKMARKS,
2709 client_tag),
2710 entity_specifics);
2711
2712 {
2713 // Verify the data.
2714 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2715 ReadNode node(&trans);
2716 EXPECT_EQ(BaseNode::INIT_OK,
2717 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2718 EXPECT_EQ(title, node.GetTitle());
2719 EXPECT_EQ(url, node.GetBookmarkSpecifics().url());
2720 }
2721
2722 {
2723 // Overwrite the url (which overwrites the specifics).
2724 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2725 WriteNode node(&trans);
2726 EXPECT_EQ(BaseNode::INIT_OK,
2727 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2728
2729 sync_pb::BookmarkSpecifics bookmark_specifics(node.GetBookmarkSpecifics());
2730 bookmark_specifics.set_url(url2);
2731 node.SetBookmarkSpecifics(bookmark_specifics);
2732 }
2733
2734 {
2735 // Verify it's still encrypted and it has the most recent url.
2736 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
2737 ReadNode node(&trans);
2738 EXPECT_EQ(BaseNode::INIT_OK,
2739 node.InitByClientTagLookup(BOOKMARKS, client_tag));
2740 EXPECT_EQ(title, node.GetTitle());
2741 EXPECT_EQ(url2, node.GetBookmarkSpecifics().url());
2742 const syncable::Entry* node_entry = node.GetEntry();
2743 EXPECT_EQ(kEncryptedString, node_entry->GetNonUniqueName());
2744 const sync_pb::EntitySpecifics& specifics = node_entry->GetSpecifics();
2745 EXPECT_TRUE(specifics.has_encrypted());
2746 }
2747 }
2748
2749 // Verify transaction version of a model type is incremented when node of
2750 // that type is updated.
TEST_F(SyncManagerTest,IncrementTransactionVersion)2751 TEST_F(SyncManagerTest, IncrementTransactionVersion) {
2752 ModelSafeRoutingInfo routing_info;
2753 GetModelSafeRoutingInfo(&routing_info);
2754
2755 {
2756 ReadTransaction read_trans(FROM_HERE, sync_manager_.GetUserShare());
2757 for (ModelSafeRoutingInfo::iterator i = routing_info.begin();
2758 i != routing_info.end(); ++i) {
2759 // Transaction version is incremented when SyncManagerTest::SetUp()
2760 // creates a node of each type.
2761 EXPECT_EQ(1,
2762 sync_manager_.GetUserShare()->directory->
2763 GetTransactionVersion(i->first));
2764 }
2765 }
2766
2767 // Create bookmark node to increment transaction version of bookmark model.
2768 std::string client_tag = "title";
2769 sync_pb::EntitySpecifics entity_specifics;
2770 entity_specifics.mutable_bookmark()->set_url("url");
2771 entity_specifics.mutable_bookmark()->set_title("title");
2772 MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag,
2773 syncable::GenerateSyncableHash(BOOKMARKS,
2774 client_tag),
2775 entity_specifics);
2776
2777 {
2778 ReadTransaction read_trans(FROM_HERE, sync_manager_.GetUserShare());
2779 for (ModelSafeRoutingInfo::iterator i = routing_info.begin();
2780 i != routing_info.end(); ++i) {
2781 EXPECT_EQ(i->first == BOOKMARKS ? 2 : 1,
2782 sync_manager_.GetUserShare()->directory->
2783 GetTransactionVersion(i->first));
2784 }
2785 }
2786 }
2787
2788 class MockSyncScheduler : public FakeSyncScheduler {
2789 public:
MockSyncScheduler()2790 MockSyncScheduler() : FakeSyncScheduler() {}
~MockSyncScheduler()2791 virtual ~MockSyncScheduler() {}
2792
2793 MOCK_METHOD1(Start, void(SyncScheduler::Mode));
2794 MOCK_METHOD1(ScheduleConfiguration, void(const ConfigurationParams&));
2795 };
2796
2797 class ComponentsFactory : public TestInternalComponentsFactory {
2798 public:
ComponentsFactory(const Switches & switches,SyncScheduler * scheduler_to_use,sessions::SyncSessionContext ** session_context)2799 ComponentsFactory(const Switches& switches,
2800 SyncScheduler* scheduler_to_use,
2801 sessions::SyncSessionContext** session_context)
2802 : TestInternalComponentsFactory(switches, syncer::STORAGE_IN_MEMORY),
2803 scheduler_to_use_(scheduler_to_use),
2804 session_context_(session_context) {}
~ComponentsFactory()2805 virtual ~ComponentsFactory() {}
2806
BuildScheduler(const std::string & name,sessions::SyncSessionContext * context,CancelationSignal * stop_handle)2807 virtual scoped_ptr<SyncScheduler> BuildScheduler(
2808 const std::string& name,
2809 sessions::SyncSessionContext* context,
2810 CancelationSignal* stop_handle) OVERRIDE {
2811 *session_context_ = context;
2812 return scheduler_to_use_.Pass();
2813 }
2814
2815 private:
2816 scoped_ptr<SyncScheduler> scheduler_to_use_;
2817 sessions::SyncSessionContext** session_context_;
2818 };
2819
2820 class SyncManagerTestWithMockScheduler : public SyncManagerTest {
2821 public:
SyncManagerTestWithMockScheduler()2822 SyncManagerTestWithMockScheduler() : scheduler_(NULL) {}
GetFactory()2823 virtual InternalComponentsFactory* GetFactory() OVERRIDE {
2824 scheduler_ = new MockSyncScheduler();
2825 return new ComponentsFactory(GetSwitches(), scheduler_, &session_context_);
2826 }
2827
scheduler()2828 MockSyncScheduler* scheduler() { return scheduler_; }
session_context()2829 sessions::SyncSessionContext* session_context() {
2830 return session_context_;
2831 }
2832
2833 private:
2834 MockSyncScheduler* scheduler_;
2835 sessions::SyncSessionContext* session_context_;
2836 };
2837
2838 // Test that the configuration params are properly created and sent to
2839 // ScheduleConfigure. No callback should be invoked. Any disabled datatypes
2840 // should be purged.
TEST_F(SyncManagerTestWithMockScheduler,BasicConfiguration)2841 TEST_F(SyncManagerTestWithMockScheduler, BasicConfiguration) {
2842 ConfigureReason reason = CONFIGURE_REASON_RECONFIGURATION;
2843 ModelTypeSet types_to_download(BOOKMARKS, PREFERENCES);
2844 ModelSafeRoutingInfo new_routing_info;
2845 GetModelSafeRoutingInfo(&new_routing_info);
2846 ModelTypeSet enabled_types = GetRoutingInfoTypes(new_routing_info);
2847 ModelTypeSet disabled_types = Difference(ModelTypeSet::All(), enabled_types);
2848
2849 ConfigurationParams params;
2850 EXPECT_CALL(*scheduler(), Start(SyncScheduler::CONFIGURATION_MODE));
2851 EXPECT_CALL(*scheduler(), ScheduleConfiguration(_)).
2852 WillOnce(SaveArg<0>(¶ms));
2853
2854 // Set data for all types.
2855 ModelTypeSet protocol_types = ProtocolTypes();
2856 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
2857 iter.Inc()) {
2858 SetProgressMarkerForType(iter.Get(), true);
2859 }
2860
2861 CallbackCounter ready_task_counter, retry_task_counter;
2862 sync_manager_.ConfigureSyncer(
2863 reason,
2864 types_to_download,
2865 disabled_types,
2866 ModelTypeSet(),
2867 ModelTypeSet(),
2868 new_routing_info,
2869 base::Bind(&CallbackCounter::Callback,
2870 base::Unretained(&ready_task_counter)),
2871 base::Bind(&CallbackCounter::Callback,
2872 base::Unretained(&retry_task_counter)));
2873 EXPECT_EQ(0, ready_task_counter.times_called());
2874 EXPECT_EQ(0, retry_task_counter.times_called());
2875 EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::RECONFIGURATION,
2876 params.source);
2877 EXPECT_TRUE(types_to_download.Equals(params.types_to_download));
2878 EXPECT_EQ(new_routing_info, params.routing_info);
2879
2880 // Verify all the disabled types were purged.
2881 EXPECT_TRUE(sync_manager_.InitialSyncEndedTypes().Equals(
2882 enabled_types));
2883 EXPECT_TRUE(sync_manager_.GetTypesWithEmptyProgressMarkerToken(
2884 ModelTypeSet::All()).Equals(disabled_types));
2885 }
2886
2887 // Test that on a reconfiguration (configuration where the session context
2888 // already has routing info), only those recently disabled types are purged.
TEST_F(SyncManagerTestWithMockScheduler,ReConfiguration)2889 TEST_F(SyncManagerTestWithMockScheduler, ReConfiguration) {
2890 ConfigureReason reason = CONFIGURE_REASON_RECONFIGURATION;
2891 ModelTypeSet types_to_download(BOOKMARKS, PREFERENCES);
2892 ModelTypeSet disabled_types = ModelTypeSet(THEMES, SESSIONS);
2893 ModelSafeRoutingInfo old_routing_info;
2894 ModelSafeRoutingInfo new_routing_info;
2895 GetModelSafeRoutingInfo(&old_routing_info);
2896 new_routing_info = old_routing_info;
2897 new_routing_info.erase(THEMES);
2898 new_routing_info.erase(SESSIONS);
2899 ModelTypeSet enabled_types = GetRoutingInfoTypes(new_routing_info);
2900
2901 ConfigurationParams params;
2902 EXPECT_CALL(*scheduler(), Start(SyncScheduler::CONFIGURATION_MODE));
2903 EXPECT_CALL(*scheduler(), ScheduleConfiguration(_)).
2904 WillOnce(SaveArg<0>(¶ms));
2905
2906 // Set data for all types except those recently disabled (so we can verify
2907 // only those recently disabled are purged) .
2908 ModelTypeSet protocol_types = ProtocolTypes();
2909 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
2910 iter.Inc()) {
2911 if (!disabled_types.Has(iter.Get())) {
2912 SetProgressMarkerForType(iter.Get(), true);
2913 } else {
2914 SetProgressMarkerForType(iter.Get(), false);
2915 }
2916 }
2917
2918 // Set the context to have the old routing info.
2919 session_context()->set_routing_info(old_routing_info);
2920
2921 CallbackCounter ready_task_counter, retry_task_counter;
2922 sync_manager_.ConfigureSyncer(
2923 reason,
2924 types_to_download,
2925 ModelTypeSet(),
2926 ModelTypeSet(),
2927 ModelTypeSet(),
2928 new_routing_info,
2929 base::Bind(&CallbackCounter::Callback,
2930 base::Unretained(&ready_task_counter)),
2931 base::Bind(&CallbackCounter::Callback,
2932 base::Unretained(&retry_task_counter)));
2933 EXPECT_EQ(0, ready_task_counter.times_called());
2934 EXPECT_EQ(0, retry_task_counter.times_called());
2935 EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::RECONFIGURATION,
2936 params.source);
2937 EXPECT_TRUE(types_to_download.Equals(params.types_to_download));
2938 EXPECT_EQ(new_routing_info, params.routing_info);
2939
2940 // Verify only the recently disabled types were purged.
2941 EXPECT_TRUE(sync_manager_.GetTypesWithEmptyProgressMarkerToken(
2942 ProtocolTypes()).Equals(disabled_types));
2943 }
2944
2945 // Test that PurgePartiallySyncedTypes purges only those types that have not
2946 // fully completed their initial download and apply.
TEST_F(SyncManagerTest,PurgePartiallySyncedTypes)2947 TEST_F(SyncManagerTest, PurgePartiallySyncedTypes) {
2948 ModelSafeRoutingInfo routing_info;
2949 GetModelSafeRoutingInfo(&routing_info);
2950 ModelTypeSet enabled_types = GetRoutingInfoTypes(routing_info);
2951
2952 UserShare* share = sync_manager_.GetUserShare();
2953
2954 // The test harness automatically initializes all types in the routing info.
2955 // Check that autofill is not among them.
2956 ASSERT_FALSE(enabled_types.Has(AUTOFILL));
2957
2958 // Further ensure that the test harness did not create its root node.
2959 {
2960 syncable::ReadTransaction trans(FROM_HERE, share->directory.get());
2961 syncable::Entry autofill_root_node(&trans, syncable::GET_BY_SERVER_TAG,
2962 ModelTypeToRootTag(AUTOFILL));
2963 ASSERT_FALSE(autofill_root_node.good());
2964 }
2965
2966 // One more redundant check.
2967 ASSERT_FALSE(sync_manager_.InitialSyncEndedTypes().Has(AUTOFILL));
2968
2969 // Give autofill a progress marker.
2970 sync_pb::DataTypeProgressMarker autofill_marker;
2971 autofill_marker.set_data_type_id(
2972 GetSpecificsFieldNumberFromModelType(AUTOFILL));
2973 autofill_marker.set_token("token");
2974 share->directory->SetDownloadProgress(AUTOFILL, autofill_marker);
2975
2976 // Also add a pending autofill root node update from the server.
2977 TestEntryFactory factory_(share->directory.get());
2978 int autofill_meta = factory_.CreateUnappliedRootNode(AUTOFILL);
2979
2980 // Preferences is an enabled type. Check that the harness initialized it.
2981 ASSERT_TRUE(enabled_types.Has(PREFERENCES));
2982 ASSERT_TRUE(sync_manager_.InitialSyncEndedTypes().Has(PREFERENCES));
2983
2984 // Give preferencse a progress marker.
2985 sync_pb::DataTypeProgressMarker prefs_marker;
2986 prefs_marker.set_data_type_id(
2987 GetSpecificsFieldNumberFromModelType(PREFERENCES));
2988 prefs_marker.set_token("token");
2989 share->directory->SetDownloadProgress(PREFERENCES, prefs_marker);
2990
2991 // Add a fully synced preferences node under the root.
2992 std::string pref_client_tag = "prefABC";
2993 std::string pref_hashed_tag = "hashXYZ";
2994 sync_pb::EntitySpecifics pref_specifics;
2995 AddDefaultFieldValue(PREFERENCES, &pref_specifics);
2996 int pref_meta = MakeServerNode(
2997 share, PREFERENCES, pref_client_tag, pref_hashed_tag, pref_specifics);
2998
2999 // And now, the purge.
3000 EXPECT_TRUE(sync_manager_.PurgePartiallySyncedTypes());
3001
3002 // Ensure that autofill lost its progress marker, but preferences did not.
3003 ModelTypeSet empty_tokens =
3004 sync_manager_.GetTypesWithEmptyProgressMarkerToken(ModelTypeSet::All());
3005 EXPECT_TRUE(empty_tokens.Has(AUTOFILL));
3006 EXPECT_FALSE(empty_tokens.Has(PREFERENCES));
3007
3008 // Ensure that autofill lots its node, but preferences did not.
3009 {
3010 syncable::ReadTransaction trans(FROM_HERE, share->directory.get());
3011 syncable::Entry autofill_node(&trans, GET_BY_HANDLE, autofill_meta);
3012 syncable::Entry pref_node(&trans, GET_BY_HANDLE, pref_meta);
3013 EXPECT_FALSE(autofill_node.good());
3014 EXPECT_TRUE(pref_node.good());
3015 }
3016 }
3017
3018 // Test CleanupDisabledTypes properly purges all disabled types as specified
3019 // by the previous and current enabled params.
TEST_F(SyncManagerTest,PurgeDisabledTypes)3020 TEST_F(SyncManagerTest, PurgeDisabledTypes) {
3021 ModelSafeRoutingInfo routing_info;
3022 GetModelSafeRoutingInfo(&routing_info);
3023 ModelTypeSet enabled_types = GetRoutingInfoTypes(routing_info);
3024 ModelTypeSet disabled_types = Difference(ModelTypeSet::All(), enabled_types);
3025
3026 // The harness should have initialized the enabled_types for us.
3027 EXPECT_TRUE(enabled_types.Equals(sync_manager_.InitialSyncEndedTypes()));
3028
3029 // Set progress markers for all types.
3030 ModelTypeSet protocol_types = ProtocolTypes();
3031 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
3032 iter.Inc()) {
3033 SetProgressMarkerForType(iter.Get(), true);
3034 }
3035
3036 // Verify all the enabled types remain after cleanup, and all the disabled
3037 // types were purged.
3038 sync_manager_.PurgeDisabledTypes(disabled_types,
3039 ModelTypeSet(),
3040 ModelTypeSet());
3041 EXPECT_TRUE(enabled_types.Equals(sync_manager_.InitialSyncEndedTypes()));
3042 EXPECT_TRUE(disabled_types.Equals(
3043 sync_manager_.GetTypesWithEmptyProgressMarkerToken(ModelTypeSet::All())));
3044
3045 // Disable some more types.
3046 disabled_types.Put(BOOKMARKS);
3047 disabled_types.Put(PREFERENCES);
3048 ModelTypeSet new_enabled_types =
3049 Difference(ModelTypeSet::All(), disabled_types);
3050
3051 // Verify only the non-disabled types remain after cleanup.
3052 sync_manager_.PurgeDisabledTypes(disabled_types,
3053 ModelTypeSet(),
3054 ModelTypeSet());
3055 EXPECT_TRUE(new_enabled_types.Equals(sync_manager_.InitialSyncEndedTypes()));
3056 EXPECT_TRUE(disabled_types.Equals(
3057 sync_manager_.GetTypesWithEmptyProgressMarkerToken(ModelTypeSet::All())));
3058 }
3059
3060 // Test PurgeDisabledTypes properly unapplies types by deleting their local data
3061 // and preserving their server data and progress marker.
TEST_F(SyncManagerTest,PurgeUnappliedTypes)3062 TEST_F(SyncManagerTest, PurgeUnappliedTypes) {
3063 ModelSafeRoutingInfo routing_info;
3064 GetModelSafeRoutingInfo(&routing_info);
3065 ModelTypeSet unapplied_types = ModelTypeSet(BOOKMARKS, PREFERENCES);
3066 ModelTypeSet enabled_types = GetRoutingInfoTypes(routing_info);
3067 ModelTypeSet disabled_types = Difference(ModelTypeSet::All(), enabled_types);
3068
3069 // The harness should have initialized the enabled_types for us.
3070 EXPECT_TRUE(enabled_types.Equals(sync_manager_.InitialSyncEndedTypes()));
3071
3072 // Set progress markers for all types.
3073 ModelTypeSet protocol_types = ProtocolTypes();
3074 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
3075 iter.Inc()) {
3076 SetProgressMarkerForType(iter.Get(), true);
3077 }
3078
3079 // Add the following kinds of items:
3080 // 1. Fully synced preference.
3081 // 2. Locally created preference, server unknown, unsynced
3082 // 3. Locally deleted preference, server known, unsynced
3083 // 4. Server deleted preference, locally known.
3084 // 5. Server created preference, locally unknown, unapplied.
3085 // 6. A fully synced bookmark (no unique_client_tag).
3086 UserShare* share = sync_manager_.GetUserShare();
3087 sync_pb::EntitySpecifics pref_specifics;
3088 AddDefaultFieldValue(PREFERENCES, &pref_specifics);
3089 sync_pb::EntitySpecifics bm_specifics;
3090 AddDefaultFieldValue(BOOKMARKS, &bm_specifics);
3091 int pref1_meta = MakeServerNode(
3092 share, PREFERENCES, "pref1", "hash1", pref_specifics);
3093 int64 pref2_meta = MakeNode(share, PREFERENCES, "pref2");
3094 int pref3_meta = MakeServerNode(
3095 share, PREFERENCES, "pref3", "hash3", pref_specifics);
3096 int pref4_meta = MakeServerNode(
3097 share, PREFERENCES, "pref4", "hash4", pref_specifics);
3098 int pref5_meta = MakeServerNode(
3099 share, PREFERENCES, "pref5", "hash5", pref_specifics);
3100 int bookmark_meta = MakeServerNode(
3101 share, BOOKMARKS, "bookmark", "", bm_specifics);
3102
3103 {
3104 syncable::WriteTransaction trans(FROM_HERE,
3105 syncable::SYNCER,
3106 share->directory.get());
3107 // Pref's 1 and 2 are already set up properly.
3108 // Locally delete pref 3.
3109 syncable::MutableEntry pref3(&trans, GET_BY_HANDLE, pref3_meta);
3110 pref3.PutIsDel(true);
3111 pref3.PutIsUnsynced(true);
3112 // Delete pref 4 at the server.
3113 syncable::MutableEntry pref4(&trans, GET_BY_HANDLE, pref4_meta);
3114 pref4.PutServerIsDel(true);
3115 pref4.PutIsUnappliedUpdate(true);
3116 pref4.PutServerVersion(2);
3117 // Pref 5 is an new unapplied update.
3118 syncable::MutableEntry pref5(&trans, GET_BY_HANDLE, pref5_meta);
3119 pref5.PutIsUnappliedUpdate(true);
3120 pref5.PutIsDel(true);
3121 pref5.PutBaseVersion(-1);
3122 // Bookmark is already set up properly
3123 }
3124
3125 // Take a snapshot to clear all the dirty bits.
3126 share->directory.get()->SaveChanges();
3127
3128 // Now request a purge for the unapplied types.
3129 disabled_types.PutAll(unapplied_types);
3130 sync_manager_.PurgeDisabledTypes(disabled_types,
3131 ModelTypeSet(),
3132 unapplied_types);
3133
3134 // Verify the unapplied types still have progress markers and initial sync
3135 // ended after cleanup.
3136 EXPECT_TRUE(sync_manager_.InitialSyncEndedTypes().HasAll(unapplied_types));
3137 EXPECT_TRUE(
3138 sync_manager_.GetTypesWithEmptyProgressMarkerToken(unapplied_types).
3139 Empty());
3140
3141 // Ensure the items were unapplied as necessary.
3142 {
3143 syncable::ReadTransaction trans(FROM_HERE, share->directory.get());
3144 syncable::Entry pref_node(&trans, GET_BY_HANDLE, pref1_meta);
3145 ASSERT_TRUE(pref_node.good());
3146 EXPECT_TRUE(pref_node.GetKernelCopy().is_dirty());
3147 EXPECT_FALSE(pref_node.GetIsUnsynced());
3148 EXPECT_TRUE(pref_node.GetIsUnappliedUpdate());
3149 EXPECT_TRUE(pref_node.GetIsDel());
3150 EXPECT_GT(pref_node.GetServerVersion(), 0);
3151 EXPECT_EQ(pref_node.GetBaseVersion(), -1);
3152
3153 // Pref 2 should just be locally deleted.
3154 syncable::Entry pref2_node(&trans, GET_BY_HANDLE, pref2_meta);
3155 ASSERT_TRUE(pref2_node.good());
3156 EXPECT_TRUE(pref2_node.GetKernelCopy().is_dirty());
3157 EXPECT_FALSE(pref2_node.GetIsUnsynced());
3158 EXPECT_TRUE(pref2_node.GetIsDel());
3159 EXPECT_FALSE(pref2_node.GetIsUnappliedUpdate());
3160 EXPECT_TRUE(pref2_node.GetIsDel());
3161 EXPECT_EQ(pref2_node.GetServerVersion(), 0);
3162 EXPECT_EQ(pref2_node.GetBaseVersion(), -1);
3163
3164 syncable::Entry pref3_node(&trans, GET_BY_HANDLE, pref3_meta);
3165 ASSERT_TRUE(pref3_node.good());
3166 EXPECT_TRUE(pref3_node.GetKernelCopy().is_dirty());
3167 EXPECT_FALSE(pref3_node.GetIsUnsynced());
3168 EXPECT_TRUE(pref3_node.GetIsUnappliedUpdate());
3169 EXPECT_TRUE(pref3_node.GetIsDel());
3170 EXPECT_GT(pref3_node.GetServerVersion(), 0);
3171 EXPECT_EQ(pref3_node.GetBaseVersion(), -1);
3172
3173 syncable::Entry pref4_node(&trans, GET_BY_HANDLE, pref4_meta);
3174 ASSERT_TRUE(pref4_node.good());
3175 EXPECT_TRUE(pref4_node.GetKernelCopy().is_dirty());
3176 EXPECT_FALSE(pref4_node.GetIsUnsynced());
3177 EXPECT_TRUE(pref4_node.GetIsUnappliedUpdate());
3178 EXPECT_TRUE(pref4_node.GetIsDel());
3179 EXPECT_GT(pref4_node.GetServerVersion(), 0);
3180 EXPECT_EQ(pref4_node.GetBaseVersion(), -1);
3181
3182 // Pref 5 should remain untouched.
3183 syncable::Entry pref5_node(&trans, GET_BY_HANDLE, pref5_meta);
3184 ASSERT_TRUE(pref5_node.good());
3185 EXPECT_FALSE(pref5_node.GetKernelCopy().is_dirty());
3186 EXPECT_FALSE(pref5_node.GetIsUnsynced());
3187 EXPECT_TRUE(pref5_node.GetIsUnappliedUpdate());
3188 EXPECT_TRUE(pref5_node.GetIsDel());
3189 EXPECT_GT(pref5_node.GetServerVersion(), 0);
3190 EXPECT_EQ(pref5_node.GetBaseVersion(), -1);
3191
3192 syncable::Entry bookmark_node(&trans, GET_BY_HANDLE, bookmark_meta);
3193 ASSERT_TRUE(bookmark_node.good());
3194 EXPECT_TRUE(bookmark_node.GetKernelCopy().is_dirty());
3195 EXPECT_FALSE(bookmark_node.GetIsUnsynced());
3196 EXPECT_TRUE(bookmark_node.GetIsUnappliedUpdate());
3197 EXPECT_TRUE(bookmark_node.GetIsDel());
3198 EXPECT_GT(bookmark_node.GetServerVersion(), 0);
3199 EXPECT_EQ(bookmark_node.GetBaseVersion(), -1);
3200 }
3201 }
3202
3203 // A test harness to exercise the code that processes and passes changes from
3204 // the "SYNCER"-WriteTransaction destructor, through the SyncManager, to the
3205 // ChangeProcessor.
3206 class SyncManagerChangeProcessingTest : public SyncManagerTest {
3207 public:
OnChangesApplied(ModelType model_type,int64 model_version,const BaseTransaction * trans,const ImmutableChangeRecordList & changes)3208 virtual void OnChangesApplied(
3209 ModelType model_type,
3210 int64 model_version,
3211 const BaseTransaction* trans,
3212 const ImmutableChangeRecordList& changes) OVERRIDE {
3213 last_changes_ = changes;
3214 }
3215
OnChangesComplete(ModelType model_type)3216 virtual void OnChangesComplete(ModelType model_type) OVERRIDE {}
3217
GetRecentChangeList()3218 const ImmutableChangeRecordList& GetRecentChangeList() {
3219 return last_changes_;
3220 }
3221
share()3222 UserShare* share() {
3223 return sync_manager_.GetUserShare();
3224 }
3225
3226 // Set some flags so our nodes reasonably approximate the real world scenario
3227 // and can get past CheckTreeInvariants.
3228 //
3229 // It's never going to be truly accurate, since we're squashing update
3230 // receipt, processing and application into a single transaction.
SetNodeProperties(syncable::MutableEntry * entry)3231 void SetNodeProperties(syncable::MutableEntry *entry) {
3232 entry->PutId(id_factory_.NewServerId());
3233 entry->PutBaseVersion(10);
3234 entry->PutServerVersion(10);
3235 }
3236
3237 // Looks for the given change in the list. Returns the index at which it was
3238 // found. Returns -1 on lookup failure.
FindChangeInList(int64 id,ChangeRecord::Action action)3239 size_t FindChangeInList(int64 id, ChangeRecord::Action action) {
3240 SCOPED_TRACE(id);
3241 for (size_t i = 0; i < last_changes_.Get().size(); ++i) {
3242 if (last_changes_.Get()[i].id == id
3243 && last_changes_.Get()[i].action == action) {
3244 return i;
3245 }
3246 }
3247 ADD_FAILURE() << "Failed to find specified change";
3248 return -1;
3249 }
3250
3251 // Returns the current size of the change list.
3252 //
3253 // Note that spurious changes do not necessarily indicate a problem.
3254 // Assertions on change list size can help detect problems, but it may be
3255 // necessary to reduce their strictness if the implementation changes.
GetChangeListSize()3256 size_t GetChangeListSize() {
3257 return last_changes_.Get().size();
3258 }
3259
3260 protected:
3261 ImmutableChangeRecordList last_changes_;
3262 TestIdFactory id_factory_;
3263 };
3264
3265 // Test creation of a folder and a bookmark.
TEST_F(SyncManagerChangeProcessingTest,AddBookmarks)3266 TEST_F(SyncManagerChangeProcessingTest, AddBookmarks) {
3267 int64 type_root = GetIdForDataType(BOOKMARKS);
3268 int64 folder_id = kInvalidId;
3269 int64 child_id = kInvalidId;
3270
3271 // Create a folder and a bookmark under it.
3272 {
3273 syncable::WriteTransaction trans(
3274 FROM_HERE, syncable::SYNCER, share()->directory.get());
3275 syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root);
3276 ASSERT_TRUE(root.good());
3277
3278 syncable::MutableEntry folder(&trans, syncable::CREATE,
3279 BOOKMARKS, root.GetId(), "folder");
3280 ASSERT_TRUE(folder.good());
3281 SetNodeProperties(&folder);
3282 folder.PutIsDir(true);
3283 folder_id = folder.GetMetahandle();
3284
3285 syncable::MutableEntry child(&trans, syncable::CREATE,
3286 BOOKMARKS, folder.GetId(), "child");
3287 ASSERT_TRUE(child.good());
3288 SetNodeProperties(&child);
3289 child_id = child.GetMetahandle();
3290 }
3291
3292 // The closing of the above scope will delete the transaction. Its processed
3293 // changes should be waiting for us in a member of the test harness.
3294 EXPECT_EQ(2UL, GetChangeListSize());
3295
3296 // We don't need to check these return values here. The function will add a
3297 // non-fatal failure if these changes are not found.
3298 size_t folder_change_pos =
3299 FindChangeInList(folder_id, ChangeRecord::ACTION_ADD);
3300 size_t child_change_pos =
3301 FindChangeInList(child_id, ChangeRecord::ACTION_ADD);
3302
3303 // Parents are delivered before children.
3304 EXPECT_LT(folder_change_pos, child_change_pos);
3305 }
3306
3307 // Test moving a bookmark into an empty folder.
TEST_F(SyncManagerChangeProcessingTest,MoveBookmarkIntoEmptyFolder)3308 TEST_F(SyncManagerChangeProcessingTest, MoveBookmarkIntoEmptyFolder) {
3309 int64 type_root = GetIdForDataType(BOOKMARKS);
3310 int64 folder_b_id = kInvalidId;
3311 int64 child_id = kInvalidId;
3312
3313 // Create two folders. Place a child under folder A.
3314 {
3315 syncable::WriteTransaction trans(
3316 FROM_HERE, syncable::SYNCER, share()->directory.get());
3317 syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root);
3318 ASSERT_TRUE(root.good());
3319
3320 syncable::MutableEntry folder_a(&trans, syncable::CREATE,
3321 BOOKMARKS, root.GetId(), "folderA");
3322 ASSERT_TRUE(folder_a.good());
3323 SetNodeProperties(&folder_a);
3324 folder_a.PutIsDir(true);
3325
3326 syncable::MutableEntry folder_b(&trans, syncable::CREATE,
3327 BOOKMARKS, root.GetId(), "folderB");
3328 ASSERT_TRUE(folder_b.good());
3329 SetNodeProperties(&folder_b);
3330 folder_b.PutIsDir(true);
3331 folder_b_id = folder_b.GetMetahandle();
3332
3333 syncable::MutableEntry child(&trans, syncable::CREATE,
3334 BOOKMARKS, folder_a.GetId(),
3335 "child");
3336 ASSERT_TRUE(child.good());
3337 SetNodeProperties(&child);
3338 child_id = child.GetMetahandle();
3339 }
3340
3341 // Close that transaction. The above was to setup the initial scenario. The
3342 // real test starts now.
3343
3344 // Move the child from folder A to folder B.
3345 {
3346 syncable::WriteTransaction trans(
3347 FROM_HERE, syncable::SYNCER, share()->directory.get());
3348
3349 syncable::Entry folder_b(&trans, syncable::GET_BY_HANDLE, folder_b_id);
3350 syncable::MutableEntry child(&trans, syncable::GET_BY_HANDLE, child_id);
3351
3352 child.PutParentId(folder_b.GetId());
3353 }
3354
3355 EXPECT_EQ(1UL, GetChangeListSize());
3356
3357 // Verify that this was detected as a real change. An early version of the
3358 // UniquePosition code had a bug where moves from one folder to another were
3359 // ignored unless the moved node's UniquePosition value was also changed in
3360 // some way.
3361 FindChangeInList(child_id, ChangeRecord::ACTION_UPDATE);
3362 }
3363
3364 // Test moving a bookmark into a non-empty folder.
TEST_F(SyncManagerChangeProcessingTest,MoveIntoPopulatedFolder)3365 TEST_F(SyncManagerChangeProcessingTest, MoveIntoPopulatedFolder) {
3366 int64 type_root = GetIdForDataType(BOOKMARKS);
3367 int64 child_a_id = kInvalidId;
3368 int64 child_b_id = kInvalidId;
3369
3370 // Create two folders. Place one child each under folder A and folder B.
3371 {
3372 syncable::WriteTransaction trans(
3373 FROM_HERE, syncable::SYNCER, share()->directory.get());
3374 syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root);
3375 ASSERT_TRUE(root.good());
3376
3377 syncable::MutableEntry folder_a(&trans, syncable::CREATE,
3378 BOOKMARKS, root.GetId(), "folderA");
3379 ASSERT_TRUE(folder_a.good());
3380 SetNodeProperties(&folder_a);
3381 folder_a.PutIsDir(true);
3382
3383 syncable::MutableEntry folder_b(&trans, syncable::CREATE,
3384 BOOKMARKS, root.GetId(), "folderB");
3385 ASSERT_TRUE(folder_b.good());
3386 SetNodeProperties(&folder_b);
3387 folder_b.PutIsDir(true);
3388
3389 syncable::MutableEntry child_a(&trans, syncable::CREATE,
3390 BOOKMARKS, folder_a.GetId(),
3391 "childA");
3392 ASSERT_TRUE(child_a.good());
3393 SetNodeProperties(&child_a);
3394 child_a_id = child_a.GetMetahandle();
3395
3396 syncable::MutableEntry child_b(&trans, syncable::CREATE,
3397 BOOKMARKS, folder_b.GetId(),
3398 "childB");
3399 SetNodeProperties(&child_b);
3400 child_b_id = child_b.GetMetahandle();
3401
3402 }
3403
3404 // Close that transaction. The above was to setup the initial scenario. The
3405 // real test starts now.
3406
3407 {
3408 syncable::WriteTransaction trans(
3409 FROM_HERE, syncable::SYNCER, share()->directory.get());
3410
3411 syncable::MutableEntry child_a(&trans, syncable::GET_BY_HANDLE, child_a_id);
3412 syncable::MutableEntry child_b(&trans, syncable::GET_BY_HANDLE, child_b_id);
3413
3414 // Move child A from folder A to folder B and update its position.
3415 child_a.PutParentId(child_b.GetParentId());
3416 child_a.PutPredecessor(child_b.GetId());
3417 }
3418
3419 EXPECT_EQ(1UL, GetChangeListSize());
3420
3421 // Verify that only child a is in the change list.
3422 // (This function will add a failure if the lookup fails.)
3423 FindChangeInList(child_a_id, ChangeRecord::ACTION_UPDATE);
3424 }
3425
3426 // Tests the ordering of deletion changes.
TEST_F(SyncManagerChangeProcessingTest,DeletionsAndChanges)3427 TEST_F(SyncManagerChangeProcessingTest, DeletionsAndChanges) {
3428 int64 type_root = GetIdForDataType(BOOKMARKS);
3429 int64 folder_a_id = kInvalidId;
3430 int64 folder_b_id = kInvalidId;
3431 int64 child_id = kInvalidId;
3432
3433 // Create two folders. Place a child under folder A.
3434 {
3435 syncable::WriteTransaction trans(
3436 FROM_HERE, syncable::SYNCER, share()->directory.get());
3437 syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root);
3438 ASSERT_TRUE(root.good());
3439
3440 syncable::MutableEntry folder_a(&trans, syncable::CREATE,
3441 BOOKMARKS, root.GetId(), "folderA");
3442 ASSERT_TRUE(folder_a.good());
3443 SetNodeProperties(&folder_a);
3444 folder_a.PutIsDir(true);
3445 folder_a_id = folder_a.GetMetahandle();
3446
3447 syncable::MutableEntry folder_b(&trans, syncable::CREATE,
3448 BOOKMARKS, root.GetId(), "folderB");
3449 ASSERT_TRUE(folder_b.good());
3450 SetNodeProperties(&folder_b);
3451 folder_b.PutIsDir(true);
3452 folder_b_id = folder_b.GetMetahandle();
3453
3454 syncable::MutableEntry child(&trans, syncable::CREATE,
3455 BOOKMARKS, folder_a.GetId(),
3456 "child");
3457 ASSERT_TRUE(child.good());
3458 SetNodeProperties(&child);
3459 child_id = child.GetMetahandle();
3460 }
3461
3462 // Close that transaction. The above was to setup the initial scenario. The
3463 // real test starts now.
3464
3465 {
3466 syncable::WriteTransaction trans(
3467 FROM_HERE, syncable::SYNCER, share()->directory.get());
3468
3469 syncable::MutableEntry folder_a(
3470 &trans, syncable::GET_BY_HANDLE, folder_a_id);
3471 syncable::MutableEntry folder_b(
3472 &trans, syncable::GET_BY_HANDLE, folder_b_id);
3473 syncable::MutableEntry child(&trans, syncable::GET_BY_HANDLE, child_id);
3474
3475 // Delete folder B and its child.
3476 child.PutIsDel(true);
3477 folder_b.PutIsDel(true);
3478
3479 // Make an unrelated change to folder A.
3480 folder_a.PutNonUniqueName("NewNameA");
3481 }
3482
3483 EXPECT_EQ(3UL, GetChangeListSize());
3484
3485 size_t folder_a_pos =
3486 FindChangeInList(folder_a_id, ChangeRecord::ACTION_UPDATE);
3487 size_t folder_b_pos =
3488 FindChangeInList(folder_b_id, ChangeRecord::ACTION_DELETE);
3489 size_t child_pos = FindChangeInList(child_id, ChangeRecord::ACTION_DELETE);
3490
3491 // Deletes should appear before updates.
3492 EXPECT_LT(child_pos, folder_a_pos);
3493 EXPECT_LT(folder_b_pos, folder_a_pos);
3494 }
3495
3496 // During initialization SyncManagerImpl loads sqlite database. If it fails to
3497 // do so it should fail initialization. This test verifies this behavior.
3498 // Test reuses SyncManagerImpl initialization from SyncManagerTest but overrides
3499 // InternalComponentsFactory to return DirectoryBackingStore that always fails
3500 // to load.
3501 class SyncManagerInitInvalidStorageTest : public SyncManagerTest {
3502 public:
SyncManagerInitInvalidStorageTest()3503 SyncManagerInitInvalidStorageTest() {
3504 }
3505
GetFactory()3506 virtual InternalComponentsFactory* GetFactory() OVERRIDE {
3507 return new TestInternalComponentsFactory(GetSwitches(), STORAGE_INVALID);
3508 }
3509 };
3510
3511 // SyncManagerInitInvalidStorageTest::GetFactory will return
3512 // DirectoryBackingStore that ensures that SyncManagerImpl::OpenDirectory fails.
3513 // SyncManagerImpl initialization is done in SyncManagerTest::SetUp. This test's
3514 // task is to ensure that SyncManagerImpl reported initialization failure in
3515 // OnInitializationComplete callback.
TEST_F(SyncManagerInitInvalidStorageTest,FailToOpenDatabase)3516 TEST_F(SyncManagerInitInvalidStorageTest, FailToOpenDatabase) {
3517 EXPECT_FALSE(initialization_succeeded_);
3518 }
3519
3520 } // namespace
3521