Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(42)

Side by Side Diff: chrome/browser/sync/engine/syncapi_unittest.cc

Issue 7624009: Original patch by rlarocque@chromium.org at http://codereview.chromium.org/7633077/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix windows build pt5 Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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/compiler_specific.h"
14 #include "base/format_macros.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/message_loop.h"
17 #include "base/scoped_temp_dir.h"
18 #include "base/string_number_conversions.h"
19 #include "base/stringprintf.h"
20 #include "base/tracked.h"
21 #include "base/utf_string_conversions.h"
22 #include "base/values.h"
23 #include "chrome/browser/password_manager/encryptor.h"
24 #include "chrome/browser/sync/engine/http_post_provider_factory.h"
25 #include "chrome/browser/sync/engine/http_post_provider_interface.h"
26 #include "chrome/browser/sync/engine/model_safe_worker.h"
27 #include "chrome/browser/sync/engine/nigori_util.h"
28 #include "chrome/browser/sync/engine/syncapi.h"
29 #include "chrome/browser/sync/js/js_arg_list.h"
30 #include "chrome/browser/sync/js/js_backend.h"
31 #include "chrome/browser/sync/js/js_event_handler.h"
32 #include "chrome/browser/sync/js/js_reply_handler.h"
33 #include "chrome/browser/sync/js/js_test_util.h"
34 #include "chrome/browser/sync/notifier/sync_notifier.h"
35 #include "chrome/browser/sync/notifier/sync_notifier_observer.h"
36 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
37 #include "chrome/browser/sync/protocol/password_specifics.pb.h"
38 #include "chrome/browser/sync/protocol/sync.pb.h"
39 #include "chrome/browser/sync/protocol/proto_value_conversions.h"
40 #include "chrome/browser/sync/sessions/sync_session.h"
41 #include "chrome/browser/sync/syncable/directory_manager.h"
42 #include "chrome/browser/sync/syncable/syncable.h"
43 #include "chrome/browser/sync/syncable/syncable_id.h"
44 #include "chrome/browser/sync/util/cryptographer.h"
45 #include "chrome/test/base/values_test_util.h"
46 #include "chrome/test/sync/engine/test_user_share.h"
47 #include "content/browser/browser_thread.h"
48 #include "testing/gmock/include/gmock/gmock.h"
49 #include "testing/gtest/include/gtest/gtest.h"
50
51 using browser_sync::Cryptographer;
52 using browser_sync::HasArgsAsList;
53 using browser_sync::HasDetailsAsDictionary;
54 using browser_sync::KeyParams;
55 using browser_sync::JsArgList;
56 using browser_sync::JsBackend;
57 using browser_sync::JsEventHandler;
58 using browser_sync::JsReplyHandler;
59 using browser_sync::MockJsEventHandler;
60 using browser_sync::MockJsReplyHandler;
61 using browser_sync::ModelSafeRoutingInfo;
62 using browser_sync::ModelSafeWorker;
63 using browser_sync::ModelSafeWorkerRegistrar;
64 using browser_sync::sessions::SyncSessionSnapshot;
65 using browser_sync::WeakHandle;
66 using syncable::ModelType;
67 using syncable::ModelTypeSet;
68 using test::ExpectDictDictionaryValue;
69 using test::ExpectDictStringValue;
70 using testing::_;
71 using testing::AnyNumber;
72 using testing::AtLeast;
73 using testing::InSequence;
74 using testing::Invoke;
75 using testing::SaveArg;
76 using testing::StrictMock;
77
78 namespace sync_api {
79
80 namespace {
81
82 void ExpectInt64Value(int64 expected_value,
83 const DictionaryValue& value, const std::string& key) {
84 std::string int64_str;
85 EXPECT_TRUE(value.GetString(key, &int64_str));
86 int64 val = 0;
87 EXPECT_TRUE(base::StringToInt64(int64_str, &val));
88 EXPECT_EQ(expected_value, val);
89 }
90
91 // Makes a non-folder child of the root node. Returns the id of the
92 // newly-created node.
93 int64 MakeNode(UserShare* share,
94 ModelType model_type,
95 const std::string& client_tag) {
96 WriteTransaction trans(FROM_HERE, share);
97 ReadNode root_node(&trans);
98 root_node.InitByRootLookup();
99 WriteNode node(&trans);
100 EXPECT_TRUE(node.InitUniqueByCreation(model_type, root_node, client_tag));
101 node.SetIsFolder(false);
102 return node.GetId();
103 }
104
105 // Makes a non-folder child of a non-root node. Returns the id of the
106 // newly-created node.
107 int64 MakeNodeWithParent(UserShare* share,
108 ModelType model_type,
109 const std::string& client_tag,
110 int64 parent_id) {
111 WriteTransaction trans(FROM_HERE, share);
112 ReadNode parent_node(&trans);
113 EXPECT_TRUE(parent_node.InitByIdLookup(parent_id));
114 WriteNode node(&trans);
115 EXPECT_TRUE(node.InitUniqueByCreation(model_type, parent_node, client_tag));
116 node.SetIsFolder(false);
117 return node.GetId();
118 }
119
120 // Makes a folder child of a non-root node. Returns the id of the
121 // newly-created node.
122 int64 MakeFolderWithParent(UserShare* share,
123 ModelType model_type,
124 int64 parent_id,
125 BaseNode* predecessor) {
126 WriteTransaction trans(FROM_HERE, share);
127 ReadNode parent_node(&trans);
128 EXPECT_TRUE(parent_node.InitByIdLookup(parent_id));
129 WriteNode node(&trans);
130 EXPECT_TRUE(node.InitByCreation(model_type, parent_node, predecessor));
131 node.SetIsFolder(true);
132 return node.GetId();
133 }
134
135 // Creates the "synced" root node for a particular datatype. We use the syncable
136 // methods here so that the syncer treats these nodes as if they were already
137 // received from the server.
138 int64 MakeServerNodeForType(UserShare* share,
139 ModelType model_type) {
140 sync_pb::EntitySpecifics specifics;
141 syncable::AddDefaultExtensionValue(model_type, &specifics);
142 syncable::ScopedDirLookup dir(share->dir_manager.get(), share->name);
143 EXPECT_TRUE(dir.good());
144 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir);
145 // Attempt to lookup by nigori tag.
146 std::string type_tag = syncable::ModelTypeToRootTag(model_type);
147 syncable::Id node_id = syncable::Id::CreateFromServerId(type_tag);
148 syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
149 node_id);
150 EXPECT_TRUE(entry.good());
151 entry.Put(syncable::BASE_VERSION, 1);
152 entry.Put(syncable::SERVER_VERSION, 1);
153 entry.Put(syncable::IS_UNAPPLIED_UPDATE, false);
154 entry.Put(syncable::SERVER_PARENT_ID, syncable::kNullId);
155 entry.Put(syncable::SERVER_IS_DIR, true);
156 entry.Put(syncable::IS_DIR, true);
157 entry.Put(syncable::SERVER_SPECIFICS, specifics);
158 entry.Put(syncable::UNIQUE_SERVER_TAG, type_tag);
159 entry.Put(syncable::NON_UNIQUE_NAME, type_tag);
160 entry.Put(syncable::IS_DEL, false);
161 entry.Put(syncable::SPECIFICS, specifics);
162 return entry.Get(syncable::META_HANDLE);
163 }
164
165 } // namespace
166
167 class SyncApiTest : public testing::Test {
168 public:
169 virtual void SetUp() {
170 test_user_share_.SetUp();
171 }
172
173 virtual void TearDown() {
174 test_user_share_.TearDown();
175 }
176
177 protected:
178 browser_sync::TestUserShare test_user_share_;
179 };
180
181 TEST_F(SyncApiTest, SanityCheckTest) {
182 {
183 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
184 EXPECT_TRUE(trans.GetWrappedTrans() != NULL);
185 }
186 {
187 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
188 EXPECT_TRUE(trans.GetWrappedTrans() != NULL);
189 }
190 {
191 // No entries but root should exist
192 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
193 ReadNode node(&trans);
194 // Metahandle 1 can be root, sanity check 2
195 EXPECT_FALSE(node.InitByIdLookup(2));
196 }
197 }
198
199 TEST_F(SyncApiTest, BasicTagWrite) {
200 {
201 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
202 ReadNode root_node(&trans);
203 root_node.InitByRootLookup();
204 EXPECT_EQ(root_node.GetFirstChildId(), 0);
205 }
206
207 ignore_result(MakeNode(test_user_share_.user_share(),
208 syncable::BOOKMARKS, "testtag"));
209
210 {
211 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
212 ReadNode node(&trans);
213 EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS,
214 "testtag"));
215
216 ReadNode root_node(&trans);
217 root_node.InitByRootLookup();
218 EXPECT_NE(node.GetId(), 0);
219 EXPECT_EQ(node.GetId(), root_node.GetFirstChildId());
220 }
221 }
222
223 TEST_F(SyncApiTest, GenerateSyncableHash) {
224 EXPECT_EQ("OyaXV5mEzrPS4wbogmtKvRfekAI=",
225 BaseNode::GenerateSyncableHash(syncable::BOOKMARKS, "tag1"));
226 EXPECT_EQ("iNFQtRFQb+IZcn1kKUJEZDDkLs4=",
227 BaseNode::GenerateSyncableHash(syncable::PREFERENCES, "tag1"));
228 EXPECT_EQ("gO1cPZQXaM73sHOvSA+tKCKFs58=",
229 BaseNode::GenerateSyncableHash(syncable::AUTOFILL, "tag1"));
230
231 EXPECT_EQ("A0eYIHXM1/jVwKDDp12Up20IkKY=",
232 BaseNode::GenerateSyncableHash(syncable::BOOKMARKS, "tag2"));
233 EXPECT_EQ("XYxkF7bhS4eItStFgiOIAU23swI=",
234 BaseNode::GenerateSyncableHash(syncable::PREFERENCES, "tag2"));
235 EXPECT_EQ("GFiWzo5NGhjLlN+OyCfhy28DJTQ=",
236 BaseNode::GenerateSyncableHash(syncable::AUTOFILL, "tag2"));
237 }
238
239 TEST_F(SyncApiTest, ModelTypesSiloed) {
240 {
241 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
242 ReadNode root_node(&trans);
243 root_node.InitByRootLookup();
244 EXPECT_EQ(root_node.GetFirstChildId(), 0);
245 }
246
247 ignore_result(MakeNode(test_user_share_.user_share(),
248 syncable::BOOKMARKS, "collideme"));
249 ignore_result(MakeNode(test_user_share_.user_share(),
250 syncable::PREFERENCES, "collideme"));
251 ignore_result(MakeNode(test_user_share_.user_share(),
252 syncable::AUTOFILL, "collideme"));
253
254 {
255 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
256
257 ReadNode bookmarknode(&trans);
258 EXPECT_TRUE(bookmarknode.InitByClientTagLookup(syncable::BOOKMARKS,
259 "collideme"));
260
261 ReadNode prefnode(&trans);
262 EXPECT_TRUE(prefnode.InitByClientTagLookup(syncable::PREFERENCES,
263 "collideme"));
264
265 ReadNode autofillnode(&trans);
266 EXPECT_TRUE(autofillnode.InitByClientTagLookup(syncable::AUTOFILL,
267 "collideme"));
268
269 EXPECT_NE(bookmarknode.GetId(), prefnode.GetId());
270 EXPECT_NE(autofillnode.GetId(), prefnode.GetId());
271 EXPECT_NE(bookmarknode.GetId(), autofillnode.GetId());
272 }
273 }
274
275 TEST_F(SyncApiTest, ReadMissingTagsFails) {
276 {
277 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
278 ReadNode node(&trans);
279 EXPECT_FALSE(node.InitByClientTagLookup(syncable::BOOKMARKS,
280 "testtag"));
281 }
282 {
283 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
284 WriteNode node(&trans);
285 EXPECT_FALSE(node.InitByClientTagLookup(syncable::BOOKMARKS,
286 "testtag"));
287 }
288 }
289
290 // TODO(chron): Hook this all up to the server and write full integration tests
291 // for update->undelete behavior.
292 TEST_F(SyncApiTest, TestDeleteBehavior) {
293 int64 node_id;
294 int64 folder_id;
295 std::string test_title("test1");
296
297 {
298 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
299 ReadNode root_node(&trans);
300 root_node.InitByRootLookup();
301
302 // we'll use this spare folder later
303 WriteNode folder_node(&trans);
304 EXPECT_TRUE(folder_node.InitByCreation(syncable::BOOKMARKS,
305 root_node, NULL));
306 folder_id = folder_node.GetId();
307
308 WriteNode wnode(&trans);
309 EXPECT_TRUE(wnode.InitUniqueByCreation(syncable::BOOKMARKS,
310 root_node, "testtag"));
311 wnode.SetIsFolder(false);
312 wnode.SetTitle(UTF8ToWide(test_title));
313
314 node_id = wnode.GetId();
315 }
316
317 // Ensure we can delete something with a tag.
318 {
319 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
320 WriteNode wnode(&trans);
321 EXPECT_TRUE(wnode.InitByClientTagLookup(syncable::BOOKMARKS,
322 "testtag"));
323 EXPECT_FALSE(wnode.GetIsFolder());
324 EXPECT_EQ(wnode.GetTitle(), test_title);
325
326 wnode.Remove();
327 }
328
329 // Lookup of a node which was deleted should return failure,
330 // but have found some data about the node.
331 {
332 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
333 ReadNode node(&trans);
334 EXPECT_FALSE(node.InitByClientTagLookup(syncable::BOOKMARKS,
335 "testtag"));
336 // Note that for proper function of this API this doesn't need to be
337 // filled, we're checking just to make sure the DB worked in this test.
338 EXPECT_EQ(node.GetTitle(), test_title);
339 }
340
341 {
342 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
343 ReadNode folder_node(&trans);
344 EXPECT_TRUE(folder_node.InitByIdLookup(folder_id));
345
346 WriteNode wnode(&trans);
347 // This will undelete the tag.
348 EXPECT_TRUE(wnode.InitUniqueByCreation(syncable::BOOKMARKS,
349 folder_node, "testtag"));
350 EXPECT_EQ(wnode.GetIsFolder(), false);
351 EXPECT_EQ(wnode.GetParentId(), folder_node.GetId());
352 EXPECT_EQ(wnode.GetId(), node_id);
353 EXPECT_NE(wnode.GetTitle(), test_title); // Title should be cleared
354 wnode.SetTitle(UTF8ToWide(test_title));
355 }
356
357 // Now look up should work.
358 {
359 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
360 ReadNode node(&trans);
361 EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS,
362 "testtag"));
363 EXPECT_EQ(node.GetTitle(), test_title);
364 EXPECT_EQ(node.GetModelType(), syncable::BOOKMARKS);
365 }
366 }
367
368 TEST_F(SyncApiTest, WriteAndReadPassword) {
369 KeyParams params = {"localhost", "username", "passphrase"};
370 {
371 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
372 trans.GetCryptographer()->AddKey(params);
373 }
374 {
375 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
376 ReadNode root_node(&trans);
377 root_node.InitByRootLookup();
378
379 WriteNode password_node(&trans);
380 EXPECT_TRUE(password_node.InitUniqueByCreation(syncable::PASSWORDS,
381 root_node, "foo"));
382 sync_pb::PasswordSpecificsData data;
383 data.set_password_value("secret");
384 password_node.SetPasswordSpecifics(data);
385 }
386 {
387 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
388 ReadNode root_node(&trans);
389 root_node.InitByRootLookup();
390
391 ReadNode password_node(&trans);
392 EXPECT_TRUE(password_node.InitByClientTagLookup(syncable::PASSWORDS,
393 "foo"));
394 const sync_pb::PasswordSpecificsData& data =
395 password_node.GetPasswordSpecifics();
396 EXPECT_EQ("secret", data.password_value());
397 }
398 }
399
400 TEST_F(SyncApiTest, BaseNodeSetSpecifics) {
401 int64 child_id = MakeNode(test_user_share_.user_share(),
402 syncable::BOOKMARKS, "testtag");
403 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
404 WriteNode node(&trans);
405 EXPECT_TRUE(node.InitByIdLookup(child_id));
406
407 sync_pb::EntitySpecifics entity_specifics;
408 entity_specifics.MutableExtension(sync_pb::bookmark)->
409 set_url("http://www.google.com");
410
411 EXPECT_NE(entity_specifics.SerializeAsString(),
412 node.GetEntitySpecifics().SerializeAsString());
413 node.SetEntitySpecifics(entity_specifics);
414 EXPECT_EQ(entity_specifics.SerializeAsString(),
415 node.GetEntitySpecifics().SerializeAsString());
416 }
417
418 TEST_F(SyncApiTest, BaseNodeSetSpecificsPreservesUnknownFields) {
419 int64 child_id = MakeNode(test_user_share_.user_share(),
420 syncable::BOOKMARKS, "testtag");
421 WriteTransaction trans(FROM_HERE, test_user_share_.user_share());
422 WriteNode node(&trans);
423 EXPECT_TRUE(node.InitByIdLookup(child_id));
424 EXPECT_TRUE(node.GetEntitySpecifics().unknown_fields().empty());
425
426 sync_pb::EntitySpecifics entity_specifics;
427 entity_specifics.MutableExtension(sync_pb::bookmark)->
428 set_url("http://www.google.com");
429 entity_specifics.mutable_unknown_fields()->AddFixed32(5, 100);
430 node.SetEntitySpecifics(entity_specifics);
431 EXPECT_FALSE(node.GetEntitySpecifics().unknown_fields().empty());
432
433 entity_specifics.mutable_unknown_fields()->Clear();
434 node.SetEntitySpecifics(entity_specifics);
435 EXPECT_FALSE(node.GetEntitySpecifics().unknown_fields().empty());
436 }
437
438 namespace {
439
440 void CheckNodeValue(const BaseNode& node, const DictionaryValue& value,
441 bool is_detailed) {
442 ExpectInt64Value(node.GetId(), value, "id");
443 {
444 bool is_folder = false;
445 EXPECT_TRUE(value.GetBoolean("isFolder", &is_folder));
446 EXPECT_EQ(node.GetIsFolder(), is_folder);
447 }
448 ExpectDictStringValue(node.GetTitle(), value, "title");
449 {
450 ModelType expected_model_type = node.GetModelType();
451 std::string type_str;
452 EXPECT_TRUE(value.GetString("type", &type_str));
453 if (expected_model_type >= syncable::FIRST_REAL_MODEL_TYPE) {
454 ModelType model_type =
455 syncable::ModelTypeFromString(type_str);
456 EXPECT_EQ(expected_model_type, model_type);
457 } else if (expected_model_type == syncable::TOP_LEVEL_FOLDER) {
458 EXPECT_EQ("Top-level folder", type_str);
459 } else if (expected_model_type == syncable::UNSPECIFIED) {
460 EXPECT_EQ("Unspecified", type_str);
461 } else {
462 ADD_FAILURE();
463 }
464 }
465 if (is_detailed) {
466 ExpectInt64Value(node.GetParentId(), value, "parentId");
467 ExpectInt64Value(node.GetModificationTime(), value, "modificationTime");
468 ExpectInt64Value(node.GetExternalId(), value, "externalId");
469 ExpectInt64Value(node.GetPredecessorId(), value, "predecessorId");
470 ExpectInt64Value(node.GetSuccessorId(), value, "successorId");
471 ExpectInt64Value(node.GetFirstChildId(), value, "firstChildId");
472 {
473 scoped_ptr<DictionaryValue> expected_entry(node.GetEntry()->ToValue());
474 Value* entry = NULL;
475 EXPECT_TRUE(value.Get("entry", &entry));
476 EXPECT_TRUE(Value::Equals(entry, expected_entry.get()));
477 }
478 EXPECT_EQ(11u, value.size());
479 } else {
480 EXPECT_EQ(4u, value.size());
481 }
482 }
483
484 } // namespace
485
486 TEST_F(SyncApiTest, BaseNodeGetSummaryAsValue) {
487 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
488 ReadNode node(&trans);
489 node.InitByRootLookup();
490 scoped_ptr<DictionaryValue> details(node.GetSummaryAsValue());
491 if (details.get()) {
492 CheckNodeValue(node, *details, false);
493 } else {
494 ADD_FAILURE();
495 }
496 }
497
498 TEST_F(SyncApiTest, BaseNodeGetDetailsAsValue) {
499 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
500 ReadNode node(&trans);
501 node.InitByRootLookup();
502 scoped_ptr<DictionaryValue> details(node.GetDetailsAsValue());
503 if (details.get()) {
504 CheckNodeValue(node, *details, true);
505 } else {
506 ADD_FAILURE();
507 }
508 }
509
510 namespace {
511
512 void ExpectChangeRecordActionValue(SyncManager::ChangeRecord::Action
513 expected_value,
514 const DictionaryValue& value,
515 const std::string& key) {
516 std::string str_value;
517 EXPECT_TRUE(value.GetString(key, &str_value));
518 switch (expected_value) {
519 case SyncManager::ChangeRecord::ACTION_ADD:
520 EXPECT_EQ("Add", str_value);
521 break;
522 case SyncManager::ChangeRecord::ACTION_UPDATE:
523 EXPECT_EQ("Update", str_value);
524 break;
525 case SyncManager::ChangeRecord::ACTION_DELETE:
526 EXPECT_EQ("Delete", str_value);
527 break;
528 default:
529 NOTREACHED();
530 break;
531 }
532 }
533
534 void CheckNonDeleteChangeRecordValue(const SyncManager::ChangeRecord& record,
535 const DictionaryValue& value,
536 BaseTransaction* trans) {
537 EXPECT_NE(SyncManager::ChangeRecord::ACTION_DELETE, record.action);
538 ExpectChangeRecordActionValue(record.action, value, "action");
539 {
540 ReadNode node(trans);
541 EXPECT_TRUE(node.InitByIdLookup(record.id));
542 scoped_ptr<DictionaryValue> expected_details(node.GetDetailsAsValue());
543 ExpectDictDictionaryValue(*expected_details, value, "node");
544 }
545 }
546
547 void CheckDeleteChangeRecordValue(const SyncManager::ChangeRecord& record,
548 const DictionaryValue& value) {
549 EXPECT_EQ(SyncManager::ChangeRecord::ACTION_DELETE, record.action);
550 ExpectChangeRecordActionValue(record.action, value, "action");
551 DictionaryValue* node_value = NULL;
552 EXPECT_TRUE(value.GetDictionary("node", &node_value));
553 if (node_value) {
554 ExpectInt64Value(record.id, *node_value, "id");
555 scoped_ptr<DictionaryValue> expected_specifics_value(
556 browser_sync::EntitySpecificsToValue(record.specifics));
557 ExpectDictDictionaryValue(*expected_specifics_value,
558 *node_value, "specifics");
559 scoped_ptr<DictionaryValue> expected_extra_value;
560 if (record.extra.get()) {
561 expected_extra_value.reset(record.extra->ToValue());
562 }
563 Value* extra_value = NULL;
564 EXPECT_EQ(record.extra.get() != NULL,
565 node_value->Get("extra", &extra_value));
566 EXPECT_TRUE(Value::Equals(extra_value, expected_extra_value.get()));
567 }
568 }
569
570 class MockExtraChangeRecordData
571 : public SyncManager::ExtraPasswordChangeRecordData {
572 public:
573 MOCK_CONST_METHOD0(ToValue, DictionaryValue*());
574 };
575
576 } // namespace
577
578 TEST_F(SyncApiTest, ChangeRecordToValue) {
579 int64 child_id = MakeNode(test_user_share_.user_share(),
580 syncable::BOOKMARKS, "testtag");
581 sync_pb::EntitySpecifics child_specifics;
582 {
583 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
584 ReadNode node(&trans);
585 EXPECT_TRUE(node.InitByIdLookup(child_id));
586 child_specifics = node.GetEntry()->Get(syncable::SPECIFICS);
587 }
588
589 // Add
590 {
591 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
592 SyncManager::ChangeRecord record;
593 record.action = SyncManager::ChangeRecord::ACTION_ADD;
594 record.id = 1;
595 record.specifics = child_specifics;
596 record.extra.reset(new StrictMock<MockExtraChangeRecordData>());
597 scoped_ptr<DictionaryValue> value(record.ToValue(&trans));
598 CheckNonDeleteChangeRecordValue(record, *value, &trans);
599 }
600
601 // Update
602 {
603 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
604 SyncManager::ChangeRecord record;
605 record.action = SyncManager::ChangeRecord::ACTION_UPDATE;
606 record.id = child_id;
607 record.specifics = child_specifics;
608 record.extra.reset(new StrictMock<MockExtraChangeRecordData>());
609 scoped_ptr<DictionaryValue> value(record.ToValue(&trans));
610 CheckNonDeleteChangeRecordValue(record, *value, &trans);
611 }
612
613 // Delete (no extra)
614 {
615 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
616 SyncManager::ChangeRecord record;
617 record.action = SyncManager::ChangeRecord::ACTION_DELETE;
618 record.id = child_id + 1;
619 record.specifics = child_specifics;
620 scoped_ptr<DictionaryValue> value(record.ToValue(&trans));
621 CheckDeleteChangeRecordValue(record, *value);
622 }
623
624 // Delete (with extra)
625 {
626 ReadTransaction trans(FROM_HERE, test_user_share_.user_share());
627 SyncManager::ChangeRecord record;
628 record.action = SyncManager::ChangeRecord::ACTION_DELETE;
629 record.id = child_id + 1;
630 record.specifics = child_specifics;
631
632 DictionaryValue extra_value;
633 extra_value.SetString("foo", "bar");
634 scoped_ptr<StrictMock<MockExtraChangeRecordData> > extra(
635 new StrictMock<MockExtraChangeRecordData>());
636 EXPECT_CALL(*extra, ToValue()).Times(2).WillRepeatedly(
637 Invoke(&extra_value, &DictionaryValue::DeepCopy));
638
639 record.extra.reset(extra.release());
640 scoped_ptr<DictionaryValue> value(record.ToValue(&trans));
641 CheckDeleteChangeRecordValue(record, *value);
642 }
643 }
644
645 namespace {
646
647 class TestHttpPostProviderInterface : public HttpPostProviderInterface {
648 public:
649 virtual ~TestHttpPostProviderInterface() {}
650
651 virtual void SetUserAgent(const char* user_agent) OVERRIDE {}
652 virtual void SetExtraRequestHeaders(const char* headers) OVERRIDE {}
653 virtual void SetURL(const char* url, int port) OVERRIDE {}
654 virtual void SetPostPayload(const char* content_type,
655 int content_length,
656 const char* content) OVERRIDE {}
657 virtual bool MakeSynchronousPost(int* os_error_code, int* response_code)
658 OVERRIDE {
659 return false;
660 }
661 virtual int GetResponseContentLength() const OVERRIDE {
662 return 0;
663 }
664 virtual const char* GetResponseContent() const OVERRIDE {
665 return "";
666 }
667 virtual const std::string GetResponseHeaderValue(
668 const std::string& name) const OVERRIDE {
669 return "";
670 }
671 virtual void Abort() OVERRIDE {}
672 };
673
674 class TestHttpPostProviderFactory : public HttpPostProviderFactory {
675 public:
676 virtual ~TestHttpPostProviderFactory() {}
677 virtual HttpPostProviderInterface* Create() OVERRIDE {
678 return new TestHttpPostProviderInterface();
679 }
680 virtual void Destroy(HttpPostProviderInterface* http) OVERRIDE {
681 delete http;
682 }
683 };
684
685 class SyncManagerObserverMock : public SyncManager::Observer {
686 public:
687 MOCK_METHOD4(OnChangesApplied,
688 void(ModelType,
689 const BaseTransaction*,
690 const SyncManager::ChangeRecord*,
691 int)); // NOLINT
692 MOCK_METHOD1(OnChangesComplete, void(ModelType)); // NOLINT
693 MOCK_METHOD1(OnSyncCycleCompleted,
694 void(const SyncSessionSnapshot*)); // NOLINT
695 MOCK_METHOD1(OnInitializationComplete,
696 void(const WeakHandle<JsBackend>&)); // NOLINT
697 MOCK_METHOD1(OnAuthError, void(const GoogleServiceAuthError&)); // NOLINT
698 MOCK_METHOD1(OnPassphraseRequired,
699 void(sync_api::PassphraseRequiredReason)); // NOLINT
700 MOCK_METHOD1(OnPassphraseAccepted, void(const std::string&)); // NOLINT
701 MOCK_METHOD0(OnStopSyncingPermanently, void()); // NOLINT
702 MOCK_METHOD1(OnUpdatedToken, void(const std::string&)); // NOLINT
703 MOCK_METHOD1(OnMigrationNeededForTypes, void(const ModelTypeSet&));
704 MOCK_METHOD0(OnClearServerDataFailed, void()); // NOLINT
705 MOCK_METHOD0(OnClearServerDataSucceeded, void()); // NOLINT
706 MOCK_METHOD1(OnEncryptionComplete, void(const ModelTypeSet&)); // NOLINT
707 };
708
709 class SyncNotifierMock : public sync_notifier::SyncNotifier {
710 public:
711 MOCK_METHOD1(AddObserver, void(sync_notifier::SyncNotifierObserver*));
712 MOCK_METHOD1(RemoveObserver, void(sync_notifier::SyncNotifierObserver*));
713 MOCK_METHOD1(SetUniqueId, void(const std::string&));
714 MOCK_METHOD1(SetState, void(const std::string&));
715 MOCK_METHOD2(UpdateCredentials,
716 void(const std::string&, const std::string&));
717 MOCK_METHOD1(UpdateEnabledTypes,
718 void(const syncable::ModelTypeSet&));
719 MOCK_METHOD0(SendNotification, void());
720 };
721
722 class SyncManagerTest : public testing::Test,
723 public ModelSafeWorkerRegistrar {
724 protected:
725 SyncManagerTest()
726 : ui_thread_(BrowserThread::UI, &ui_loop_),
727 sync_notifier_mock_(NULL),
728 sync_manager_("Test sync manager"),
729 sync_notifier_observer_(NULL),
730 update_enabled_types_call_count_(0) {}
731
732 virtual ~SyncManagerTest() {
733 EXPECT_FALSE(sync_notifier_mock_);
734 }
735
736 // Test implementation.
737 void SetUp() {
738 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
739
740 SyncCredentials credentials;
741 credentials.email = "foo@bar.com";
742 credentials.sync_token = "sometoken";
743
744 sync_notifier_mock_ = new StrictMock<SyncNotifierMock>();
745 EXPECT_CALL(*sync_notifier_mock_, AddObserver(_)).
746 WillOnce(Invoke(this, &SyncManagerTest::SyncNotifierAddObserver));
747 EXPECT_CALL(*sync_notifier_mock_, SetUniqueId(_));
748 EXPECT_CALL(*sync_notifier_mock_, SetState(""));
749 EXPECT_CALL(*sync_notifier_mock_,
750 UpdateCredentials(credentials.email, credentials.sync_token));
751 EXPECT_CALL(*sync_notifier_mock_, UpdateEnabledTypes(_)).
752 Times(AtLeast(1)).
753 WillRepeatedly(
754 Invoke(this, &SyncManagerTest::SyncNotifierUpdateEnabledTypes));
755 EXPECT_CALL(*sync_notifier_mock_, RemoveObserver(_)).
756 WillOnce(Invoke(this, &SyncManagerTest::SyncNotifierRemoveObserver));
757
758 sync_manager_.AddObserver(&observer_);
759 EXPECT_CALL(observer_, OnInitializationComplete(_)).
760 WillOnce(SaveArg<0>(&js_backend_));
761
762 EXPECT_FALSE(sync_notifier_observer_);
763 EXPECT_FALSE(js_backend_.IsInitialized());
764
765 // Takes ownership of |sync_notifier_mock_|.
766 sync_manager_.Init(temp_dir_.path(),
767 WeakHandle<JsEventHandler>(),
768 "bogus", 0, false,
769 new TestHttpPostProviderFactory(), this, "bogus",
770 credentials, sync_notifier_mock_, "",
771 true /* setup_for_test_mode */);
772
773 EXPECT_TRUE(sync_notifier_observer_);
774 EXPECT_TRUE(js_backend_.IsInitialized());
775
776 EXPECT_EQ(1, update_enabled_types_call_count_);
777
778 ModelSafeRoutingInfo routes;
779 GetModelSafeRoutingInfo(&routes);
780 for (ModelSafeRoutingInfo::iterator i = routes.begin(); i != routes.end();
781 ++i) {
782 EXPECT_CALL(observer_, OnChangesApplied(i->first, _, _, 1))
783 .RetiresOnSaturation();
784 EXPECT_CALL(observer_, OnChangesComplete(i->first))
785 .RetiresOnSaturation();
786 type_roots_[i->first] = MakeServerNodeForType(
787 sync_manager_.GetUserShare(), i->first);
788 }
789 PumpLoop();
790 }
791
792 void TearDown() {
793 sync_manager_.RemoveObserver(&observer_);
794 sync_manager_.Shutdown();
795 sync_notifier_mock_ = NULL;
796 EXPECT_FALSE(sync_notifier_observer_);
797 PumpLoop();
798 }
799
800 // ModelSafeWorkerRegistrar implementation.
801 virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) {
802 NOTIMPLEMENTED();
803 out->clear();
804 }
805 virtual void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
806 (*out)[syncable::NIGORI] = browser_sync::GROUP_PASSIVE;
807 (*out)[syncable::BOOKMARKS] = browser_sync::GROUP_PASSIVE;
808 (*out)[syncable::THEMES] = browser_sync::GROUP_PASSIVE;
809 (*out)[syncable::SESSIONS] = browser_sync::GROUP_PASSIVE;
810 (*out)[syncable::PASSWORDS] = browser_sync::GROUP_PASSIVE;
811 }
812
813 // Helper methods.
814 bool SetUpEncryption() {
815 // Mock the Mac Keychain service. The real Keychain can block on user input.
816 #if defined(OS_MACOSX)
817 Encryptor::UseMockKeychain(true);
818 #endif
819
820 // We need to create the nigori node as if it were an applied server update.
821 UserShare* share = sync_manager_.GetUserShare();
822 int64 nigori_id = GetIdForDataType(syncable::NIGORI);
823 if (nigori_id == kInvalidId)
824 return false;
825
826 // Set the nigori cryptographer information.
827 WriteTransaction trans(FROM_HERE, share);
828 Cryptographer* cryptographer = trans.GetCryptographer();
829 if (!cryptographer)
830 return false;
831 KeyParams params = {"localhost", "dummy", "foobar"};
832 cryptographer->AddKey(params);
833 sync_pb::NigoriSpecifics nigori;
834 cryptographer->GetKeys(nigori.mutable_encrypted());
835 WriteNode node(&trans);
836 EXPECT_TRUE(node.InitByIdLookup(nigori_id));
837 node.SetNigoriSpecifics(nigori);
838 return cryptographer->is_ready();
839 }
840
841 int64 GetIdForDataType(ModelType type) {
842 if (type_roots_.count(type) == 0)
843 return 0;
844 return type_roots_[type];
845 }
846
847 void SyncNotifierAddObserver(
848 sync_notifier::SyncNotifierObserver* sync_notifier_observer) {
849 EXPECT_EQ(NULL, sync_notifier_observer_);
850 sync_notifier_observer_ = sync_notifier_observer;
851 }
852
853 void SyncNotifierRemoveObserver(
854 sync_notifier::SyncNotifierObserver* sync_notifier_observer) {
855 EXPECT_EQ(sync_notifier_observer_, sync_notifier_observer);
856 sync_notifier_observer_ = NULL;
857 }
858
859 void SyncNotifierUpdateEnabledTypes(
860 const syncable::ModelTypeSet& types) {
861 ModelSafeRoutingInfo routes;
862 GetModelSafeRoutingInfo(&routes);
863 syncable::ModelTypeSet expected_types;
864 for (ModelSafeRoutingInfo::const_iterator it = routes.begin();
865 it != routes.end(); ++it) {
866 expected_types.insert(it->first);
867 }
868 EXPECT_EQ(expected_types, types);
869 ++update_enabled_types_call_count_;
870 }
871
872 void PumpLoop() {
873 ui_loop_.RunAllPending();
874 }
875
876 void SendJsMessage(const std::string& name, const JsArgList& args,
877 const WeakHandle<JsReplyHandler>& reply_handler) {
878 js_backend_.Call(FROM_HERE, &JsBackend::ProcessJsMessage,
879 name, args, reply_handler);
880 PumpLoop();
881 }
882
883 void SetJsEventHandler(const WeakHandle<JsEventHandler>& event_handler) {
884 js_backend_.Call(FROM_HERE, &JsBackend::SetJsEventHandler,
885 event_handler);
886 PumpLoop();
887 }
888
889 private:
890 // Needed by |ui_thread_|.
891 MessageLoopForUI ui_loop_;
892 // Needed by |sync_manager_|.
893 BrowserThread ui_thread_;
894 // Needed by |sync_manager_|.
895 ScopedTempDir temp_dir_;
896 // Sync Id's for the roots of the enabled datatypes.
897 std::map<ModelType, int64> type_roots_;
898 StrictMock<SyncNotifierMock>* sync_notifier_mock_;
899
900 protected:
901 SyncManager sync_manager_;
902 WeakHandle<JsBackend> js_backend_;
903 StrictMock<SyncManagerObserverMock> observer_;
904 sync_notifier::SyncNotifierObserver* sync_notifier_observer_;
905 int update_enabled_types_call_count_;
906 };
907
908 TEST_F(SyncManagerTest, UpdateEnabledTypes) {
909 EXPECT_EQ(1, update_enabled_types_call_count_);
910 // Triggers SyncNotifierUpdateEnabledTypes.
911 sync_manager_.UpdateEnabledTypes();
912 EXPECT_EQ(2, update_enabled_types_call_count_);
913 }
914
915 TEST_F(SyncManagerTest, ProcessJsMessage) {
916 const JsArgList kNoArgs;
917
918 StrictMock<MockJsReplyHandler> reply_handler;
919
920 ListValue false_args;
921 false_args.Append(Value::CreateBooleanValue(false));
922
923 EXPECT_CALL(reply_handler,
924 HandleJsReply("getNotificationState",
925 HasArgsAsList(false_args)));
926
927 // This message should be dropped.
928 SendJsMessage("unknownMessage", kNoArgs, reply_handler.AsWeakHandle());
929
930 SendJsMessage("getNotificationState", kNoArgs, reply_handler.AsWeakHandle());
931 }
932
933 TEST_F(SyncManagerTest, ProcessJsMessageGetRootNodeDetails) {
934 const JsArgList kNoArgs;
935
936 StrictMock<MockJsReplyHandler> reply_handler;
937
938 JsArgList return_args;
939
940 EXPECT_CALL(reply_handler,
941 HandleJsReply("getRootNodeDetails", _))
942 .WillOnce(SaveArg<1>(&return_args));
943
944 SendJsMessage("getRootNodeDetails", kNoArgs, reply_handler.AsWeakHandle());
945
946 EXPECT_EQ(1u, return_args.Get().GetSize());
947 DictionaryValue* node_info = NULL;
948 EXPECT_TRUE(return_args.Get().GetDictionary(0, &node_info));
949 if (node_info) {
950 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
951 ReadNode node(&trans);
952 node.InitByRootLookup();
953 CheckNodeValue(node, *node_info, true);
954 } else {
955 ADD_FAILURE();
956 }
957 }
958
959 void CheckGetNodesByIdReturnArgs(const SyncManager& sync_manager,
960 const JsArgList& return_args,
961 int64 id,
962 bool is_detailed) {
963 EXPECT_EQ(1u, return_args.Get().GetSize());
964 ListValue* nodes = NULL;
965 ASSERT_TRUE(return_args.Get().GetList(0, &nodes));
966 ASSERT_TRUE(nodes);
967 EXPECT_EQ(1u, nodes->GetSize());
968 DictionaryValue* node_info = NULL;
969 EXPECT_TRUE(nodes->GetDictionary(0, &node_info));
970 ASSERT_TRUE(node_info);
971 ReadTransaction trans(FROM_HERE, sync_manager.GetUserShare());
972 ReadNode node(&trans);
973 EXPECT_TRUE(node.InitByIdLookup(id));
974 CheckNodeValue(node, *node_info, is_detailed);
975 }
976
977 class SyncManagerGetNodesByIdTest : public SyncManagerTest {
978 protected:
979 virtual ~SyncManagerGetNodesByIdTest() {}
980
981 void RunGetNodesByIdTest(const char* message_name, bool is_detailed) {
982 int64 root_id = kInvalidId;
983 {
984 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
985 ReadNode root_node(&trans);
986 root_node.InitByRootLookup();
987 root_id = root_node.GetId();
988 }
989
990 int64 child_id =
991 MakeNode(sync_manager_.GetUserShare(),
992 syncable::BOOKMARKS, "testtag");
993
994 StrictMock<MockJsReplyHandler> reply_handler;
995
996 JsArgList return_args;
997
998 const int64 ids[] = { root_id, child_id };
999
1000 EXPECT_CALL(reply_handler,
1001 HandleJsReply(message_name, _))
1002 .Times(arraysize(ids)).WillRepeatedly(SaveArg<1>(&return_args));
1003
1004 for (size_t i = 0; i < arraysize(ids); ++i) {
1005 ListValue args;
1006 ListValue* id_values = new ListValue();
1007 args.Append(id_values);
1008 id_values->Append(Value::CreateStringValue(base::Int64ToString(ids[i])));
1009 SendJsMessage(message_name,
1010 JsArgList(&args), reply_handler.AsWeakHandle());
1011
1012 CheckGetNodesByIdReturnArgs(sync_manager_, return_args,
1013 ids[i], is_detailed);
1014 }
1015 }
1016
1017 void RunGetNodesByIdFailureTest(const char* message_name) {
1018 StrictMock<MockJsReplyHandler> reply_handler;
1019
1020 ListValue empty_list_args;
1021 empty_list_args.Append(new ListValue());
1022
1023 EXPECT_CALL(reply_handler,
1024 HandleJsReply(message_name,
1025 HasArgsAsList(empty_list_args)))
1026 .Times(6);
1027
1028 {
1029 ListValue args;
1030 SendJsMessage(message_name,
1031 JsArgList(&args), reply_handler.AsWeakHandle());
1032 }
1033
1034 {
1035 ListValue args;
1036 args.Append(new ListValue());
1037 SendJsMessage(message_name,
1038 JsArgList(&args), reply_handler.AsWeakHandle());
1039 }
1040
1041 {
1042 ListValue args;
1043 ListValue* ids = new ListValue();
1044 args.Append(ids);
1045 ids->Append(Value::CreateStringValue(""));
1046 SendJsMessage(message_name,
1047 JsArgList(&args), reply_handler.AsWeakHandle());
1048 }
1049
1050 {
1051 ListValue args;
1052 ListValue* ids = new ListValue();
1053 args.Append(ids);
1054 ids->Append(Value::CreateStringValue("nonsense"));
1055 SendJsMessage(message_name,
1056 JsArgList(&args), reply_handler.AsWeakHandle());
1057 }
1058
1059 {
1060 ListValue args;
1061 ListValue* ids = new ListValue();
1062 args.Append(ids);
1063 ids->Append(Value::CreateStringValue("0"));
1064 SendJsMessage(message_name,
1065 JsArgList(&args), reply_handler.AsWeakHandle());
1066 }
1067
1068 {
1069 ListValue args;
1070 ListValue* ids = new ListValue();
1071 args.Append(ids);
1072 ids->Append(Value::CreateStringValue("9999"));
1073 SendJsMessage(message_name,
1074 JsArgList(&args), reply_handler.AsWeakHandle());
1075 }
1076 }
1077 };
1078
1079 TEST_F(SyncManagerGetNodesByIdTest, GetNodeSummariesById) {
1080 RunGetNodesByIdTest("getNodeSummariesById", false);
1081 }
1082
1083 TEST_F(SyncManagerGetNodesByIdTest, GetNodeDetailsById) {
1084 RunGetNodesByIdTest("getNodeDetailsById", true);
1085 }
1086
1087 TEST_F(SyncManagerGetNodesByIdTest, GetNodeSummariesByIdFailure) {
1088 RunGetNodesByIdFailureTest("getNodeSummariesById");
1089 }
1090
1091 TEST_F(SyncManagerGetNodesByIdTest, GetNodeDetailsByIdFailure) {
1092 RunGetNodesByIdFailureTest("getNodeDetailsById");
1093 }
1094
1095 TEST_F(SyncManagerTest, GetChildNodeIds) {
1096 StrictMock<MockJsReplyHandler> reply_handler;
1097
1098 JsArgList return_args;
1099
1100 EXPECT_CALL(reply_handler,
1101 HandleJsReply("getChildNodeIds", _))
1102 .Times(1).WillRepeatedly(SaveArg<1>(&return_args));
1103
1104 {
1105 ListValue args;
1106 args.Append(Value::CreateStringValue("1"));
1107 SendJsMessage("getChildNodeIds",
1108 JsArgList(&args), reply_handler.AsWeakHandle());
1109 }
1110
1111 EXPECT_EQ(1u, return_args.Get().GetSize());
1112 ListValue* nodes = NULL;
1113 ASSERT_TRUE(return_args.Get().GetList(0, &nodes));
1114 ASSERT_TRUE(nodes);
1115 EXPECT_EQ(5u, nodes->GetSize());
1116 }
1117
1118 TEST_F(SyncManagerTest, GetChildNodeIdsFailure) {
1119 StrictMock<MockJsReplyHandler> reply_handler;
1120
1121 ListValue empty_list_args;
1122 empty_list_args.Append(new ListValue());
1123
1124 EXPECT_CALL(reply_handler,
1125 HandleJsReply("getChildNodeIds",
1126 HasArgsAsList(empty_list_args)))
1127 .Times(5);
1128
1129 {
1130 ListValue args;
1131 SendJsMessage("getChildNodeIds",
1132 JsArgList(&args), reply_handler.AsWeakHandle());
1133 }
1134
1135 {
1136 ListValue args;
1137 args.Append(Value::CreateStringValue(""));
1138 SendJsMessage("getChildNodeIds",
1139 JsArgList(&args), reply_handler.AsWeakHandle());
1140 }
1141
1142 {
1143 ListValue args;
1144 args.Append(Value::CreateStringValue("nonsense"));
1145 SendJsMessage("getChildNodeIds",
1146 JsArgList(&args), reply_handler.AsWeakHandle());
1147 }
1148
1149 {
1150 ListValue args;
1151 args.Append(Value::CreateStringValue("0"));
1152 SendJsMessage("getChildNodeIds",
1153 JsArgList(&args), reply_handler.AsWeakHandle());
1154 }
1155
1156 {
1157 ListValue args;
1158 args.Append(Value::CreateStringValue("9999"));
1159 SendJsMessage("getChildNodeIds",
1160 JsArgList(&args), reply_handler.AsWeakHandle());
1161 }
1162 }
1163
1164 // TODO(akalin): Add unit tests for findNodesContainingString message.
1165
1166 TEST_F(SyncManagerTest, OnNotificationStateChange) {
1167 InSequence dummy;
1168 StrictMock<MockJsEventHandler> event_handler;
1169
1170 DictionaryValue true_details;
1171 true_details.SetBoolean("enabled", true);
1172 DictionaryValue false_details;
1173 false_details.SetBoolean("enabled", false);
1174
1175 EXPECT_CALL(event_handler,
1176 HandleJsEvent("onNotificationStateChange",
1177 HasDetailsAsDictionary(true_details)));
1178 EXPECT_CALL(event_handler,
1179 HandleJsEvent("onNotificationStateChange",
1180 HasDetailsAsDictionary(false_details)));
1181
1182 sync_manager_.TriggerOnNotificationStateChangeForTest(true);
1183 sync_manager_.TriggerOnNotificationStateChangeForTest(false);
1184
1185 SetJsEventHandler(event_handler.AsWeakHandle());
1186 sync_manager_.TriggerOnNotificationStateChangeForTest(true);
1187 sync_manager_.TriggerOnNotificationStateChangeForTest(false);
1188 SetJsEventHandler(WeakHandle<JsEventHandler>());
1189
1190 sync_manager_.TriggerOnNotificationStateChangeForTest(true);
1191 sync_manager_.TriggerOnNotificationStateChangeForTest(false);
1192
1193 // Should trigger the replies.
1194 PumpLoop();
1195 }
1196
1197 TEST_F(SyncManagerTest, OnIncomingNotification) {
1198 StrictMock<MockJsEventHandler> event_handler;
1199
1200 const syncable::ModelTypeBitSet empty_model_types;
1201 syncable::ModelTypeBitSet model_types;
1202 model_types.set(syncable::BOOKMARKS);
1203 model_types.set(syncable::THEMES);
1204
1205 // Build expected_args to have a single argument with the string
1206 // equivalents of model_types.
1207 DictionaryValue expected_details;
1208 {
1209 ListValue* model_type_list = new ListValue();
1210 expected_details.Set("changedTypes", model_type_list);
1211 for (int i = syncable::FIRST_REAL_MODEL_TYPE;
1212 i < syncable::MODEL_TYPE_COUNT; ++i) {
1213 if (model_types[i]) {
1214 model_type_list->Append(
1215 Value::CreateStringValue(
1216 syncable::ModelTypeToString(
1217 syncable::ModelTypeFromInt(i))));
1218 }
1219 }
1220 }
1221
1222 EXPECT_CALL(event_handler,
1223 HandleJsEvent("onIncomingNotification",
1224 HasDetailsAsDictionary(expected_details)));
1225
1226 sync_manager_.TriggerOnIncomingNotificationForTest(empty_model_types);
1227 sync_manager_.TriggerOnIncomingNotificationForTest(model_types);
1228
1229 SetJsEventHandler(event_handler.AsWeakHandle());
1230 sync_manager_.TriggerOnIncomingNotificationForTest(model_types);
1231 SetJsEventHandler(WeakHandle<JsEventHandler>());
1232
1233 sync_manager_.TriggerOnIncomingNotificationForTest(empty_model_types);
1234 sync_manager_.TriggerOnIncomingNotificationForTest(model_types);
1235
1236 // Should trigger the replies.
1237 PumpLoop();
1238 }
1239
1240 TEST_F(SyncManagerTest, RefreshEncryptionReady) {
1241 EXPECT_TRUE(SetUpEncryption());
1242 sync_manager_.RefreshEncryption();
1243 syncable::ModelTypeSet encrypted_types =
1244 sync_manager_.GetEncryptedDataTypes();
1245 EXPECT_EQ(1U, encrypted_types.count(syncable::PASSWORDS));
1246 }
1247
1248 // Attempt to refresh encryption when nigori not downloaded.
1249 TEST_F(SyncManagerTest, RefreshEncryptionNotReady) {
1250 // Don't set up encryption (no nigori node created).
1251 sync_manager_.RefreshEncryption(); // Should fail.
1252 syncable::ModelTypeSet encrypted_types =
1253 sync_manager_.GetEncryptedDataTypes();
1254 EXPECT_EQ(1U, encrypted_types.count(syncable::PASSWORDS)); // Hardcoded.
1255 }
1256
1257 TEST_F(SyncManagerTest, EncryptDataTypesWithNoData) {
1258 EXPECT_TRUE(SetUpEncryption());
1259 ModelTypeSet encrypted_types;
1260 encrypted_types.insert(syncable::BOOKMARKS);
1261 // Even though Passwords isn't marked for encryption, it's enabled, so it
1262 // should automatically be added to the response of OnEncryptionComplete.
1263 ModelTypeSet expected_types = encrypted_types;
1264 expected_types.insert(syncable::PASSWORDS);
1265 EXPECT_CALL(observer_, OnEncryptionComplete(expected_types));
1266 sync_manager_.EncryptDataTypes(encrypted_types);
1267 {
1268 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1269 EXPECT_EQ(expected_types,
1270 GetEncryptedTypes(&trans));
1271 }
1272 }
1273
1274 TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
1275 size_t batch_size = 5;
1276 EXPECT_TRUE(SetUpEncryption());
1277
1278 // Create some unencrypted unsynced data.
1279 int64 folder = MakeFolderWithParent(sync_manager_.GetUserShare(),
1280 syncable::BOOKMARKS,
1281 GetIdForDataType(syncable::BOOKMARKS),
1282 NULL);
1283 // First batch_size nodes are children of folder.
1284 size_t i;
1285 for (i = 0; i < batch_size; ++i) {
1286 MakeNodeWithParent(sync_manager_.GetUserShare(), syncable::BOOKMARKS,
1287 base::StringPrintf("%"PRIuS"", i), folder);
1288 }
1289 // Next batch_size nodes are a different type and on their own.
1290 for (; i < 2*batch_size; ++i) {
1291 MakeNodeWithParent(sync_manager_.GetUserShare(), syncable::SESSIONS,
1292 base::StringPrintf("%"PRIuS"", i),
1293 GetIdForDataType(syncable::SESSIONS));
1294 }
1295 // Last batch_size nodes are a third type that will not need encryption.
1296 for (; i < 3*batch_size; ++i) {
1297 MakeNodeWithParent(sync_manager_.GetUserShare(), syncable::THEMES,
1298 base::StringPrintf("%"PRIuS"", i),
1299 GetIdForDataType(syncable::THEMES));
1300 }
1301
1302 {
1303 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1304 EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
1305 trans.GetCryptographer(),
1306 syncable::BOOKMARKS,
1307 false /* not encrypted */));
1308 EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
1309 trans.GetCryptographer(),
1310 syncable::SESSIONS,
1311 false /* not encrypted */));
1312 EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
1313 trans.GetCryptographer(),
1314 syncable::THEMES,
1315 false /* not encrypted */));
1316 }
1317
1318 ModelTypeSet encrypted_types;
1319 encrypted_types.insert(syncable::BOOKMARKS);
1320 encrypted_types.insert(syncable::SESSIONS);
1321 encrypted_types.insert(syncable::PASSWORDS);
1322 EXPECT_CALL(observer_, OnEncryptionComplete(encrypted_types));
1323 sync_manager_.EncryptDataTypes(encrypted_types);
1324
1325 {
1326 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1327 EXPECT_EQ(encrypted_types, GetEncryptedTypes(&trans));
1328 EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
1329 trans.GetCryptographer(),
1330 syncable::BOOKMARKS,
1331 true /* is encrypted */));
1332 EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
1333 trans.GetCryptographer(),
1334 syncable::SESSIONS,
1335 true /* is encrypted */));
1336 EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
1337 trans.GetCryptographer(),
1338 syncable::THEMES,
1339 false /* not encrypted */));
1340 }
1341
1342 // Trigger's a ReEncryptEverything with new passphrase.
1343 testing::Mock::VerifyAndClearExpectations(&observer_);
1344 EXPECT_CALL(observer_, OnPassphraseAccepted(_)).Times(1);
1345 EXPECT_CALL(observer_, OnEncryptionComplete(encrypted_types)).Times(1);
1346 sync_manager_.SetPassphrase("new_passphrase", true);
1347 {
1348 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1349 EXPECT_EQ(encrypted_types, GetEncryptedTypes(&trans));
1350 EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
1351 trans.GetCryptographer(),
1352 syncable::BOOKMARKS,
1353 true /* is encrypted */));
1354 EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
1355 trans.GetCryptographer(),
1356 syncable::SESSIONS,
1357 true /* is encrypted */));
1358 EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
1359 trans.GetCryptographer(),
1360 syncable::THEMES,
1361 false /* not encrypted */));
1362 }
1363 // Calling EncryptDataTypes with an empty encrypted types should not trigger
1364 // a reencryption and should just notify immediately.
1365 // TODO(zea): add logic to ensure nothing was written.
1366 testing::Mock::VerifyAndClearExpectations(&observer_);
1367 EXPECT_CALL(observer_, OnPassphraseAccepted(_)).Times(0);
1368 EXPECT_CALL(observer_, OnEncryptionComplete(encrypted_types)).Times(1);
1369 sync_manager_.EncryptDataTypes(encrypted_types);
1370 }
1371
1372 TEST_F(SyncManagerTest, SetPassphraseWithPassword) {
1373 EXPECT_TRUE(SetUpEncryption());
1374 {
1375 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1376 ReadNode root_node(&trans);
1377 root_node.InitByRootLookup();
1378
1379 WriteNode password_node(&trans);
1380 EXPECT_TRUE(password_node.InitUniqueByCreation(syncable::PASSWORDS,
1381 root_node, "foo"));
1382 sync_pb::PasswordSpecificsData data;
1383 data.set_password_value("secret");
1384 password_node.SetPasswordSpecifics(data);
1385 }
1386 EXPECT_CALL(observer_, OnPassphraseAccepted(_));
1387 EXPECT_CALL(observer_, OnEncryptionComplete(_));
1388 sync_manager_.SetPassphrase("new_passphrase", true);
1389 {
1390 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1391 ReadNode password_node(&trans);
1392 EXPECT_TRUE(password_node.InitByClientTagLookup(syncable::PASSWORDS,
1393 "foo"));
1394 const sync_pb::PasswordSpecificsData& data =
1395 password_node.GetPasswordSpecifics();
1396 EXPECT_EQ("secret", data.password_value());
1397 }
1398 }
1399
1400 TEST_F(SyncManagerTest, SetPassphraseWithEmptyPasswordNode) {
1401 EXPECT_TRUE(SetUpEncryption());
1402 int64 node_id = 0;
1403 std::string tag = "foo";
1404 {
1405 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1406 ReadNode root_node(&trans);
1407 root_node.InitByRootLookup();
1408
1409 WriteNode password_node(&trans);
1410 EXPECT_TRUE(password_node.InitUniqueByCreation(syncable::PASSWORDS,
1411 root_node, tag));
1412 node_id = password_node.GetId();
1413 }
1414 EXPECT_CALL(observer_, OnPassphraseAccepted(_));
1415 EXPECT_CALL(observer_, OnEncryptionComplete(_));
1416 sync_manager_.SetPassphrase("new_passphrase", true);
1417 {
1418 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1419 ReadNode password_node(&trans);
1420 EXPECT_FALSE(password_node.InitByClientTagLookup(syncable::PASSWORDS,
1421 tag));
1422 }
1423 {
1424 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
1425 ReadNode password_node(&trans);
1426 EXPECT_FALSE(password_node.InitByIdLookup(node_id));
1427 }
1428 }
1429
1430 } // namespace
1431
1432 } // namespace browser_sync
OLDNEW
« no previous file with comments | « chrome/browser/sync/engine/syncapi_mock.h ('k') | chrome/browser/sync/glue/app_change_processor.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698