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
80using base::ExpectDictStringValue;
81using testing::_;
82using testing::DoAll;
83using testing::InSequence;
84using testing::Return;
85using testing::SaveArg;
86using testing::StrictMock;
87
88namespace syncer {
89
90using sessions::SyncSessionSnapshot;
91using syncable::GET_BY_HANDLE;
92using syncable::IS_DEL;
93using syncable::IS_UNSYNCED;
94using syncable::NON_UNIQUE_NAME;
95using syncable::SPECIFICS;
96using syncable::kEncryptedString;
97
98namespace {
99
100void 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
110void 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.
120int64 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.
136int64 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
149int64 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.
163int64 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.
190int64 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
221class SyncApiTest : public testing::Test {
222 public:
223  virtual void SetUp() {
224    test_user_share_.SetUp();
225  }
226
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
236TEST_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
254TEST_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
278TEST_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
317TEST_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.
336TEST_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
416TEST_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
450TEST_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
493TEST_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
510TEST_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
529namespace {
530
531void 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
590TEST_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
602TEST_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
614TEST_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.
628TEST_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.
641TEST_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.
663TEST_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
701namespace {
702
703class TestHttpPostProviderInterface : public HttpPostProviderInterface {
704 public:
705  virtual ~TestHttpPostProviderInterface() {}
706
707  virtual void SetExtraRequestHeaders(const char* headers) OVERRIDE {}
708  virtual void SetURL(const char* url, int port) OVERRIDE {}
709  virtual void SetPostPayload(const char* content_type,
710                              int content_length,
711                              const char* content) OVERRIDE {}
712  virtual bool MakeSynchronousPost(int* error_code, int* response_code)
713      OVERRIDE {
714    return false;
715  }
716  virtual int GetResponseContentLength() const OVERRIDE {
717    return 0;
718  }
719  virtual const char* GetResponseContent() const OVERRIDE {
720    return "";
721  }
722  virtual const std::string GetResponseHeaderValue(
723      const std::string& name) const OVERRIDE {
724    return std::string();
725  }
726  virtual void Abort() OVERRIDE {}
727};
728
729class TestHttpPostProviderFactory : public HttpPostProviderFactory {
730 public:
731  virtual ~TestHttpPostProviderFactory() {}
732  virtual void Init(const std::string& user_agent) OVERRIDE { }
733  virtual HttpPostProviderInterface* Create() OVERRIDE {
734    return new TestHttpPostProviderInterface();
735  }
736  virtual void Destroy(HttpPostProviderInterface* http) OVERRIDE {
737    delete static_cast<TestHttpPostProviderInterface*>(http);
738  }
739};
740
741class 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
757class 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
776class 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
790  SyncManagerTest()
791      : sync_manager_("Test sync manager") {
792    switches_.encryption_method =
793        InternalComponentsFactory::ENCRYPTION_KEYSTORE;
794  }
795
796  virtual ~SyncManagerTest() {
797  }
798
799  // Test implementation.
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
862  void TearDown() {
863    sync_manager_.RemoveObserver(&manager_observer_);
864    sync_manager_.ShutdownOnSyncThread();
865    PumpLoop();
866  }
867
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
880  virtual void OnChangesApplied(
881      ModelType model_type,
882      int64 model_version,
883      const BaseTransaction* trans,
884      const ImmutableChangeRecordList& changes) OVERRIDE {}
885
886  virtual void OnChangesComplete(ModelType model_type) OVERRIDE {}
887
888  // Helper methods.
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
925  int64 GetIdForDataType(ModelType type) {
926    if (type_roots_.count(type) == 0)
927      return 0;
928    return type_roots_[type];
929  }
930
931  void PumpLoop() {
932    message_loop_.RunUntilIdle();
933  }
934
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
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.
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
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.
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.
978  ModelTypeSet GetEncryptedTypes() {
979    ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
980    return GetEncryptedTypesWithTrans(&trans);
981  }
982
983  ModelTypeSet GetEncryptedTypesWithTrans(BaseTransaction* trans) {
984    return trans->GetDirectory()->GetNigoriHandler()->
985        GetEncryptedTypes(trans->GetWrappedTrans());
986  }
987
988  void SimulateInvalidatorStateChangeForTest(InvalidatorState state) {
989    DCHECK(sync_manager_.thread_checker_.CalledOnValidThread());
990    sync_manager_.OnInvalidatorStateChange(state);
991  }
992
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
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
1013  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
1037TEST_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
1055TEST_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
1081void 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
1099class SyncManagerGetNodesByIdTest : public SyncManagerTest {
1100 protected:
1101  virtual ~SyncManagerGetNodesByIdTest() {}
1102
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
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
1200TEST_F(SyncManagerGetNodesByIdTest, GetNodeSummariesById) {
1201  RunGetNodesByIdTest("getNodeSummariesById", false);
1202}
1203
1204TEST_F(SyncManagerGetNodesByIdTest, GetNodeDetailsById) {
1205  RunGetNodesByIdTest("getNodeDetailsById", true);
1206}
1207
1208TEST_F(SyncManagerGetNodesByIdTest, GetNodeSummariesByIdFailure) {
1209  RunGetNodesByIdFailureTest("getNodeSummariesById");
1210}
1211
1212TEST_F(SyncManagerGetNodesByIdTest, GetNodeDetailsByIdFailure) {
1213  RunGetNodesByIdFailureTest("getNodeDetailsById");
1214}
1215
1216TEST_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
1239TEST_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
1285TEST_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.
1328TEST_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
1381TEST_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
1420TEST_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.
1447TEST_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.
1464TEST_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
1491TEST_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
1501TEST_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)
1610TEST_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)
1637TEST_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)
1672TEST_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)
1731TEST_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)
1779TEST_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)
1862TEST_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.
1915TEST_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
1951TEST_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
1994TEST_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.
2009TEST_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(..).
2115TEST_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.
2149TEST_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.
2289TEST_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.
2324TEST_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.
2376TEST_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.
2415TEST_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.
2447TEST_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.
2483TEST_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.
2540TEST_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).
2578TEST_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.
2637TEST_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.
2689TEST_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.
2751TEST_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
2788class MockSyncScheduler : public FakeSyncScheduler {
2789 public:
2790  MockSyncScheduler() : FakeSyncScheduler() {}
2791  virtual ~MockSyncScheduler() {}
2792
2793  MOCK_METHOD1(Start, void(SyncScheduler::Mode));
2794  MOCK_METHOD1(ScheduleConfiguration, void(const ConfigurationParams&));
2795};
2796
2797class ComponentsFactory : public TestInternalComponentsFactory {
2798 public:
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) {}
2805  virtual ~ComponentsFactory() {}
2806
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
2820class SyncManagerTestWithMockScheduler : public SyncManagerTest {
2821 public:
2822  SyncManagerTestWithMockScheduler() : scheduler_(NULL) {}
2823  virtual InternalComponentsFactory* GetFactory() OVERRIDE {
2824    scheduler_ = new MockSyncScheduler();
2825    return new ComponentsFactory(GetSwitches(), scheduler_, &session_context_);
2826  }
2827
2828  MockSyncScheduler* scheduler() { return scheduler_; }
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.
2841TEST_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>(&params));
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.
2889TEST_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>(&params));
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.
2947TEST_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.
3020TEST_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.
3062TEST_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.
3206class SyncManagerChangeProcessingTest : public SyncManagerTest {
3207 public:
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
3216  virtual void OnChangesComplete(ModelType model_type) OVERRIDE {}
3217
3218  const ImmutableChangeRecordList& GetRecentChangeList() {
3219    return last_changes_;
3220  }
3221
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.
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.
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.
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.
3266TEST_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.
3308TEST_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.
3365TEST_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.
3427TEST_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.
3501class SyncManagerInitInvalidStorageTest : public SyncManagerTest {
3502 public:
3503  SyncManagerInitInvalidStorageTest() {
3504  }
3505
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.
3516TEST_F(SyncManagerInitInvalidStorageTest, FailToOpenDatabase) {
3517  EXPECT_FALSE(initialization_succeeded_);
3518}
3519
3520}  // namespace
3521