Home | History | Annotate | Download | only in engine
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Unit tests for the SyncApi. Note that a lot of the underlying
      6 // functionality is provided by the Syncable layer, which has its own
      7 // unit tests. We'll test SyncApi specific things in this harness.
      8 
      9 #include <map>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/format_macros.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/memory/scoped_temp_dir.h"
     15 #include "base/message_loop.h"
     16 #include "base/string_number_conversions.h"
     17 #include "base/string_util.h"
     18 #include "base/utf_string_conversions.h"
     19 #include "base/values.h"
     20 #include "chrome/browser/sync/engine/http_post_provider_factory.h"
     21 #include "chrome/browser/sync/engine/http_post_provider_interface.h"
     22 #include "chrome/browser/sync/engine/model_safe_worker.h"
     23 #include "chrome/browser/sync/engine/syncapi.h"
     24 #include "chrome/browser/sync/js_arg_list.h"
     25 #include "chrome/browser/sync/js_backend.h"
     26 #include "chrome/browser/sync/js_event_handler.h"
     27 #include "chrome/browser/sync/js_event_router.h"
     28 #include "chrome/browser/sync/js_test_util.h"
     29 #include "chrome/browser/sync/notifier/sync_notifier.h"
     30 #include "chrome/browser/sync/notifier/sync_notifier_observer.h"
     31 #include "chrome/browser/sync/protocol/password_specifics.pb.h"
     32 #include "chrome/browser/sync/protocol/proto_value_conversions.h"
     33 #include "chrome/browser/sync/sessions/sync_session.h"
     34 #include "chrome/browser/sync/syncable/directory_manager.h"
     35 #include "chrome/browser/sync/syncable/nigori_util.h"
     36 #include "chrome/browser/sync/syncable/syncable.h"
     37 #include "chrome/browser/sync/syncable/syncable_id.h"
     38 #include "chrome/browser/sync/util/cryptographer.h"
     39 #include "chrome/test/sync/engine/test_user_share.h"
     40 #include "chrome/test/values_test_util.h"
     41 #include "content/browser/browser_thread.h"
     42 #include "testing/gmock/include/gmock/gmock.h"
     43 #include "testing/gtest/include/gtest/gtest.h"
     44 
     45 using browser_sync::Cryptographer;
     46 using browser_sync::HasArgsAsList;
     47 using browser_sync::KeyParams;
     48 using browser_sync::JsArgList;
     49 using browser_sync::MockJsEventHandler;
     50 using browser_sync::MockJsEventRouter;
     51 using browser_sync::ModelSafeRoutingInfo;
     52 using browser_sync::ModelSafeWorker;
     53 using browser_sync::ModelSafeWorkerRegistrar;
     54 using browser_sync::sessions::SyncSessionSnapshot;
     55 using syncable::ModelType;
     56 using syncable::ModelTypeSet;
     57 using test::ExpectDictionaryValue;
     58 using test::ExpectStringValue;
     59 using testing::_;
     60 using testing::AtLeast;
     61 using testing::Invoke;
     62 using testing::SaveArg;
     63 using testing::StrictMock;
     64 
     65 namespace sync_api {
     66 
     67 namespace {
     68 
     69 void ExpectInt64Value(int64 expected_value,
     70                       const DictionaryValue& value, const std::string& key) {
     71   std::string int64_str;
     72   EXPECT_TRUE(value.GetString(key, &int64_str));
     73   int64 val = 0;
     74   EXPECT_TRUE(base::StringToInt64(int64_str, &val));
     75   EXPECT_EQ(expected_value, val);
     76 }
     77 
     78 // Makes a non-folder child of the root node.  Returns the id of the
     79 // newly-created node.
     80 int64 MakeNode(UserShare* share,
     81                ModelType model_type,
     82                const std::string& client_tag) {
     83   WriteTransaction trans(share);
     84   ReadNode root_node(&trans);
     85   root_node.InitByRootLookup();
     86   WriteNode node(&trans);
     87   EXPECT_TRUE(node.InitUniqueByCreation(model_type, root_node, client_tag));
     88   node.SetIsFolder(false);
     89   return node.GetId();
     90 }
     91 
     92 // Make a folder as a child of the root node. Returns the id of the
     93 // newly-created node.
     94 int64 MakeFolder(UserShare* share,
     95                  syncable::ModelType model_type,
     96                  const std::string& client_tag) {
     97   WriteTransaction trans(share);
     98   ReadNode root_node(&trans);
     99   root_node.InitByRootLookup();
    100   WriteNode node(&trans);
    101   EXPECT_TRUE(node.InitUniqueByCreation(model_type, root_node, client_tag));
    102   node.SetIsFolder(true);
    103   return node.GetId();
    104 }
    105 
    106 // Makes a non-folder child of a non-root node. Returns the id of the
    107 // newly-created node.
    108 int64 MakeNodeWithParent(UserShare* share,
    109                          ModelType model_type,
    110                          const std::string& client_tag,
    111                          int64 parent_id) {
    112   WriteTransaction trans(share);
    113   ReadNode parent_node(&trans);
    114   parent_node.InitByIdLookup(parent_id);
    115   WriteNode node(&trans);
    116   EXPECT_TRUE(node.InitUniqueByCreation(model_type, parent_node, client_tag));
    117   node.SetIsFolder(false);
    118   return node.GetId();
    119 }
    120 
    121 // Makes a folder child of a non-root node. Returns the id of the
    122 // newly-created node.
    123 int64 MakeFolderWithParent(UserShare* share,
    124                            ModelType model_type,
    125                            int64 parent_id,
    126                            BaseNode* predecessor) {
    127   WriteTransaction trans(share);
    128   ReadNode parent_node(&trans);
    129   parent_node.InitByIdLookup(parent_id);
    130   WriteNode node(&trans);
    131   EXPECT_TRUE(node.InitByCreation(model_type, parent_node, predecessor));
    132   node.SetIsFolder(true);
    133   return node.GetId();
    134 }
    135 
    136 // Creates the "synced" root node for a particular datatype. We use the syncable
    137 // methods here so that the syncer treats these nodes as if they were already
    138 // received from the server.
    139 int64 MakeServerNodeForType(UserShare* share,
    140                             ModelType model_type) {
    141   sync_pb::EntitySpecifics specifics;
    142   syncable::AddDefaultExtensionValue(model_type, &specifics);
    143   syncable::ScopedDirLookup dir(share->dir_manager.get(), share->name);
    144   EXPECT_TRUE(dir.good());
    145   syncable::WriteTransaction trans(dir, syncable::UNITTEST, __FILE__, __LINE__);
    146   // Attempt to lookup by nigori tag.
    147   std::string type_tag = syncable::ModelTypeToRootTag(model_type);
    148   syncable::Id node_id = syncable::Id::CreateFromServerId(type_tag);
    149   syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
    150                                node_id);
    151   EXPECT_TRUE(entry.good());
    152   entry.Put(syncable::BASE_VERSION, 1);
    153   entry.Put(syncable::SERVER_VERSION, 1);
    154   entry.Put(syncable::IS_UNAPPLIED_UPDATE, false);
    155   entry.Put(syncable::SERVER_PARENT_ID, syncable::kNullId);
    156   entry.Put(syncable::SERVER_IS_DIR, true);
    157   entry.Put(syncable::IS_DIR, true);
    158   entry.Put(syncable::SERVER_SPECIFICS, specifics);
    159   entry.Put(syncable::UNIQUE_SERVER_TAG, type_tag);
    160   entry.Put(syncable::NON_UNIQUE_NAME, type_tag);
    161   entry.Put(syncable::IS_DEL, false);
    162   entry.Put(syncable::SPECIFICS, specifics);
    163   return entry.Get(syncable::META_HANDLE);
    164 }
    165 
    166 }  // namespace
    167 
    168 class SyncApiTest : public testing::Test {
    169  public:
    170   virtual void SetUp() {
    171     test_user_share_.SetUp();
    172   }
    173 
    174   virtual void TearDown() {
    175     test_user_share_.TearDown();
    176   }
    177 
    178  protected:
    179   browser_sync::TestUserShare test_user_share_;
    180 };
    181 
    182 TEST_F(SyncApiTest, SanityCheckTest) {
    183   {
    184     ReadTransaction trans(test_user_share_.user_share());
    185     EXPECT_TRUE(trans.GetWrappedTrans() != NULL);
    186   }
    187   {
    188     WriteTransaction trans(test_user_share_.user_share());
    189     EXPECT_TRUE(trans.GetWrappedTrans() != NULL);
    190   }
    191   {
    192     // No entries but root should exist
    193     ReadTransaction trans(test_user_share_.user_share());
    194     ReadNode node(&trans);
    195     // Metahandle 1 can be root, sanity check 2
    196     EXPECT_FALSE(node.InitByIdLookup(2));
    197   }
    198 }
    199 
    200 TEST_F(SyncApiTest, BasicTagWrite) {
    201   {
    202     ReadTransaction trans(test_user_share_.user_share());
    203     ReadNode root_node(&trans);
    204     root_node.InitByRootLookup();
    205     EXPECT_EQ(root_node.GetFirstChildId(), 0);
    206   }
    207 
    208   ignore_result(MakeNode(test_user_share_.user_share(),
    209                          syncable::BOOKMARKS, "testtag"));
    210 
    211   {
    212     ReadTransaction trans(test_user_share_.user_share());
    213     ReadNode node(&trans);
    214     EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS,
    215         "testtag"));
    216 
    217     ReadNode root_node(&trans);
    218     root_node.InitByRootLookup();
    219     EXPECT_NE(node.GetId(), 0);
    220     EXPECT_EQ(node.GetId(), root_node.GetFirstChildId());
    221   }
    222 }
    223 
    224 TEST_F(SyncApiTest, GenerateSyncableHash) {
    225   EXPECT_EQ("OyaXV5mEzrPS4wbogmtKvRfekAI=",
    226       BaseNode::GenerateSyncableHash(syncable::BOOKMARKS, "tag1"));
    227   EXPECT_EQ("iNFQtRFQb+IZcn1kKUJEZDDkLs4=",
    228       BaseNode::GenerateSyncableHash(syncable::PREFERENCES, "tag1"));
    229   EXPECT_EQ("gO1cPZQXaM73sHOvSA+tKCKFs58=",
    230       BaseNode::GenerateSyncableHash(syncable::AUTOFILL, "tag1"));
    231 
    232   EXPECT_EQ("A0eYIHXM1/jVwKDDp12Up20IkKY=",
    233       BaseNode::GenerateSyncableHash(syncable::BOOKMARKS, "tag2"));
    234   EXPECT_EQ("XYxkF7bhS4eItStFgiOIAU23swI=",
    235       BaseNode::GenerateSyncableHash(syncable::PREFERENCES, "tag2"));
    236   EXPECT_EQ("GFiWzo5NGhjLlN+OyCfhy28DJTQ=",
    237       BaseNode::GenerateSyncableHash(syncable::AUTOFILL, "tag2"));
    238 }
    239 
    240 TEST_F(SyncApiTest, ModelTypesSiloed) {
    241   {
    242     WriteTransaction trans(test_user_share_.user_share());
    243     ReadNode root_node(&trans);
    244     root_node.InitByRootLookup();
    245     EXPECT_EQ(root_node.GetFirstChildId(), 0);
    246   }
    247 
    248   ignore_result(MakeNode(test_user_share_.user_share(),
    249                          syncable::BOOKMARKS, "collideme"));
    250   ignore_result(MakeNode(test_user_share_.user_share(),
    251                          syncable::PREFERENCES, "collideme"));
    252   ignore_result(MakeNode(test_user_share_.user_share(),
    253                          syncable::AUTOFILL, "collideme"));
    254 
    255   {
    256     ReadTransaction trans(test_user_share_.user_share());
    257 
    258     ReadNode bookmarknode(&trans);
    259     EXPECT_TRUE(bookmarknode.InitByClientTagLookup(syncable::BOOKMARKS,
    260         "collideme"));
    261 
    262     ReadNode prefnode(&trans);
    263     EXPECT_TRUE(prefnode.InitByClientTagLookup(syncable::PREFERENCES,
    264         "collideme"));
    265 
    266     ReadNode autofillnode(&trans);
    267     EXPECT_TRUE(autofillnode.InitByClientTagLookup(syncable::AUTOFILL,
    268         "collideme"));
    269 
    270     EXPECT_NE(bookmarknode.GetId(), prefnode.GetId());
    271     EXPECT_NE(autofillnode.GetId(), prefnode.GetId());
    272     EXPECT_NE(bookmarknode.GetId(), autofillnode.GetId());
    273   }
    274 }
    275 
    276 TEST_F(SyncApiTest, ReadMissingTagsFails) {
    277   {
    278     ReadTransaction trans(test_user_share_.user_share());
    279     ReadNode node(&trans);
    280     EXPECT_FALSE(node.InitByClientTagLookup(syncable::BOOKMARKS,
    281         "testtag"));
    282   }
    283   {
    284     WriteTransaction trans(test_user_share_.user_share());
    285     WriteNode node(&trans);
    286     EXPECT_FALSE(node.InitByClientTagLookup(syncable::BOOKMARKS,
    287         "testtag"));
    288   }
    289 }
    290 
    291 // TODO(chron): Hook this all up to the server and write full integration tests
    292 //              for update->undelete behavior.
    293 TEST_F(SyncApiTest, TestDeleteBehavior) {
    294   int64 node_id;
    295   int64 folder_id;
    296   std::wstring test_title(L"test1");
    297 
    298   {
    299     WriteTransaction trans(test_user_share_.user_share());
    300     ReadNode root_node(&trans);
    301     root_node.InitByRootLookup();
    302 
    303     // we'll use this spare folder later
    304     WriteNode folder_node(&trans);
    305     EXPECT_TRUE(folder_node.InitByCreation(syncable::BOOKMARKS,
    306         root_node, NULL));
    307     folder_id = folder_node.GetId();
    308 
    309     WriteNode wnode(&trans);
    310     EXPECT_TRUE(wnode.InitUniqueByCreation(syncable::BOOKMARKS,
    311         root_node, "testtag"));
    312     wnode.SetIsFolder(false);
    313     wnode.SetTitle(test_title);
    314 
    315     node_id = wnode.GetId();
    316   }
    317 
    318   // Ensure we can delete something with a tag.
    319   {
    320     WriteTransaction trans(test_user_share_.user_share());
    321     WriteNode wnode(&trans);
    322     EXPECT_TRUE(wnode.InitByClientTagLookup(syncable::BOOKMARKS,
    323         "testtag"));
    324     EXPECT_FALSE(wnode.GetIsFolder());
    325     EXPECT_EQ(wnode.GetTitle(), test_title);
    326 
    327     wnode.Remove();
    328   }
    329 
    330   // Lookup of a node which was deleted should return failure,
    331   // but have found some data about the node.
    332   {
    333     ReadTransaction trans(test_user_share_.user_share());
    334     ReadNode node(&trans);
    335     EXPECT_FALSE(node.InitByClientTagLookup(syncable::BOOKMARKS,
    336         "testtag"));
    337     // Note that for proper function of this API this doesn't need to be
    338     // filled, we're checking just to make sure the DB worked in this test.
    339     EXPECT_EQ(node.GetTitle(), test_title);
    340   }
    341 
    342   {
    343     WriteTransaction trans(test_user_share_.user_share());
    344     ReadNode folder_node(&trans);
    345     EXPECT_TRUE(folder_node.InitByIdLookup(folder_id));
    346 
    347     WriteNode wnode(&trans);
    348     // This will undelete the tag.
    349     EXPECT_TRUE(wnode.InitUniqueByCreation(syncable::BOOKMARKS,
    350         folder_node, "testtag"));
    351     EXPECT_EQ(wnode.GetIsFolder(), false);
    352     EXPECT_EQ(wnode.GetParentId(), folder_node.GetId());
    353     EXPECT_EQ(wnode.GetId(), node_id);
    354     EXPECT_NE(wnode.GetTitle(), test_title);  // Title should be cleared
    355     wnode.SetTitle(test_title);
    356   }
    357 
    358   // Now look up should work.
    359   {
    360     ReadTransaction trans(test_user_share_.user_share());
    361     ReadNode node(&trans);
    362     EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS,
    363         "testtag"));
    364     EXPECT_EQ(node.GetTitle(), test_title);
    365     EXPECT_EQ(node.GetModelType(), syncable::BOOKMARKS);
    366   }
    367 }
    368 
    369 TEST_F(SyncApiTest, WriteAndReadPassword) {
    370   KeyParams params = {"localhost", "username", "passphrase"};
    371   {
    372     ReadTransaction trans(test_user_share_.user_share());
    373     trans.GetCryptographer()->AddKey(params);
    374   }
    375   {
    376     WriteTransaction trans(test_user_share_.user_share());
    377     ReadNode root_node(&trans);
    378     root_node.InitByRootLookup();
    379 
    380     WriteNode password_node(&trans);
    381     EXPECT_TRUE(password_node.InitUniqueByCreation(syncable::PASSWORDS,
    382                                                    root_node, "foo"));
    383     sync_pb::PasswordSpecificsData data;
    384     data.set_password_value("secret");
    385     password_node.SetPasswordSpecifics(data);
    386   }
    387   {
    388     ReadTransaction trans(test_user_share_.user_share());
    389     ReadNode root_node(&trans);
    390     root_node.InitByRootLookup();
    391 
    392     ReadNode password_node(&trans);
    393     EXPECT_TRUE(password_node.InitByClientTagLookup(syncable::PASSWORDS,
    394                                                     "foo"));
    395     const sync_pb::PasswordSpecificsData& data =
    396         password_node.GetPasswordSpecifics();
    397     EXPECT_EQ("secret", data.password_value());
    398   }
    399 }
    400 
    401 namespace {
    402 
    403 void CheckNodeValue(const BaseNode& node, const DictionaryValue& value) {
    404   ExpectInt64Value(node.GetId(), value, "id");
    405   ExpectInt64Value(node.GetModificationTime(), value, "modificationTime");
    406   ExpectInt64Value(node.GetParentId(), value, "parentId");
    407   {
    408     bool is_folder = false;
    409     EXPECT_TRUE(value.GetBoolean("isFolder", &is_folder));
    410     EXPECT_EQ(node.GetIsFolder(), is_folder);
    411   }
    412   ExpectStringValue(WideToUTF8(node.GetTitle()), value, "title");
    413   {
    414     ModelType expected_model_type = node.GetModelType();
    415     std::string type_str;
    416     EXPECT_TRUE(value.GetString("type", &type_str));
    417     if (expected_model_type >= syncable::FIRST_REAL_MODEL_TYPE) {
    418       ModelType model_type =
    419           syncable::ModelTypeFromString(type_str);
    420       EXPECT_EQ(expected_model_type, model_type);
    421     } else if (expected_model_type == syncable::TOP_LEVEL_FOLDER) {
    422       EXPECT_EQ("Top-level folder", type_str);
    423     } else if (expected_model_type == syncable::UNSPECIFIED) {
    424       EXPECT_EQ("Unspecified", type_str);
    425     } else {
    426       ADD_FAILURE();
    427     }
    428   }
    429   ExpectInt64Value(node.GetExternalId(), value, "externalId");
    430   ExpectInt64Value(node.GetPredecessorId(), value, "predecessorId");
    431   ExpectInt64Value(node.GetSuccessorId(), value, "successorId");
    432   ExpectInt64Value(node.GetFirstChildId(), value, "firstChildId");
    433   {
    434     scoped_ptr<DictionaryValue> expected_entry(node.GetEntry()->ToValue());
    435     Value* entry = NULL;
    436     EXPECT_TRUE(value.Get("entry", &entry));
    437     EXPECT_TRUE(Value::Equals(entry, expected_entry.get()));
    438   }
    439   EXPECT_EQ(11u, value.size());
    440 }
    441 
    442 }  // namespace
    443 
    444 TEST_F(SyncApiTest, BaseNodeToValue) {
    445   ReadTransaction trans(test_user_share_.user_share());
    446   ReadNode node(&trans);
    447   node.InitByRootLookup();
    448   scoped_ptr<DictionaryValue> value(node.ToValue());
    449   if (value.get()) {
    450     CheckNodeValue(node, *value);
    451   } else {
    452     ADD_FAILURE();
    453   }
    454 }
    455 
    456 namespace {
    457 
    458 void ExpectChangeRecordActionValue(SyncManager::ChangeRecord::Action
    459                                        expected_value,
    460                                    const DictionaryValue& value,
    461                                    const std::string& key) {
    462   std::string str_value;
    463   EXPECT_TRUE(value.GetString(key, &str_value));
    464   switch (expected_value) {
    465     case SyncManager::ChangeRecord::ACTION_ADD:
    466       EXPECT_EQ("Add", str_value);
    467       break;
    468     case SyncManager::ChangeRecord::ACTION_UPDATE:
    469       EXPECT_EQ("Update", str_value);
    470       break;
    471     case SyncManager::ChangeRecord::ACTION_DELETE:
    472       EXPECT_EQ("Delete", str_value);
    473       break;
    474     default:
    475       NOTREACHED();
    476       break;
    477   }
    478 }
    479 
    480 void CheckNonDeleteChangeRecordValue(const SyncManager::ChangeRecord& record,
    481                                      const DictionaryValue& value,
    482                                      BaseTransaction* trans) {
    483   EXPECT_NE(SyncManager::ChangeRecord::ACTION_DELETE, record.action);
    484   ExpectChangeRecordActionValue(record.action, value, "action");
    485   {
    486     ReadNode node(trans);
    487     EXPECT_TRUE(node.InitByIdLookup(record.id));
    488     scoped_ptr<DictionaryValue> expected_node_value(node.ToValue());
    489     ExpectDictionaryValue(*expected_node_value, value, "node");
    490   }
    491 }
    492 
    493 void CheckDeleteChangeRecordValue(const SyncManager::ChangeRecord& record,
    494                                   const DictionaryValue& value) {
    495   EXPECT_EQ(SyncManager::ChangeRecord::ACTION_DELETE, record.action);
    496   ExpectChangeRecordActionValue(record.action, value, "action");
    497   DictionaryValue* node_value = NULL;
    498   EXPECT_TRUE(value.GetDictionary("node", &node_value));
    499   if (node_value) {
    500     ExpectInt64Value(record.id, *node_value, "id");
    501     scoped_ptr<DictionaryValue> expected_specifics_value(
    502         browser_sync::EntitySpecificsToValue(record.specifics));
    503     ExpectDictionaryValue(*expected_specifics_value,
    504                           *node_value, "specifics");
    505     scoped_ptr<DictionaryValue> expected_extra_value;
    506     if (record.extra.get()) {
    507       expected_extra_value.reset(record.extra->ToValue());
    508     }
    509     Value* extra_value = NULL;
    510     EXPECT_EQ(record.extra.get() != NULL,
    511               node_value->Get("extra", &extra_value));
    512     EXPECT_TRUE(Value::Equals(extra_value, expected_extra_value.get()));
    513   }
    514 }
    515 
    516 class MockExtraChangeRecordData
    517     : public SyncManager::ExtraPasswordChangeRecordData {
    518  public:
    519   MOCK_CONST_METHOD0(ToValue, DictionaryValue*());
    520 };
    521 
    522 }  // namespace
    523 
    524 TEST_F(SyncApiTest, ChangeRecordToValue) {
    525   int64 child_id = MakeNode(test_user_share_.user_share(),
    526                             syncable::BOOKMARKS, "testtag");
    527   sync_pb::EntitySpecifics child_specifics;
    528   {
    529     ReadTransaction trans(test_user_share_.user_share());
    530     ReadNode node(&trans);
    531     EXPECT_TRUE(node.InitByIdLookup(child_id));
    532     child_specifics = node.GetEntry()->Get(syncable::SPECIFICS);
    533   }
    534 
    535   // Add
    536   {
    537     ReadTransaction trans(test_user_share_.user_share());
    538     SyncManager::ChangeRecord record;
    539     record.action = SyncManager::ChangeRecord::ACTION_ADD;
    540     record.id = 1;
    541     record.specifics = child_specifics;
    542     record.extra.reset(new StrictMock<MockExtraChangeRecordData>());
    543     scoped_ptr<DictionaryValue> value(record.ToValue(&trans));
    544     CheckNonDeleteChangeRecordValue(record, *value, &trans);
    545   }
    546 
    547   // Update
    548   {
    549     ReadTransaction trans(test_user_share_.user_share());
    550     SyncManager::ChangeRecord record;
    551     record.action = SyncManager::ChangeRecord::ACTION_UPDATE;
    552     record.id = child_id;
    553     record.specifics = child_specifics;
    554     record.extra.reset(new StrictMock<MockExtraChangeRecordData>());
    555     scoped_ptr<DictionaryValue> value(record.ToValue(&trans));
    556     CheckNonDeleteChangeRecordValue(record, *value, &trans);
    557   }
    558 
    559   // Delete (no extra)
    560   {
    561     ReadTransaction trans(test_user_share_.user_share());
    562     SyncManager::ChangeRecord record;
    563     record.action = SyncManager::ChangeRecord::ACTION_DELETE;
    564     record.id = child_id + 1;
    565     record.specifics = child_specifics;
    566     scoped_ptr<DictionaryValue> value(record.ToValue(&trans));
    567     CheckDeleteChangeRecordValue(record, *value);
    568   }
    569 
    570   // Delete (with extra)
    571   {
    572     ReadTransaction trans(test_user_share_.user_share());
    573     SyncManager::ChangeRecord record;
    574     record.action = SyncManager::ChangeRecord::ACTION_DELETE;
    575     record.id = child_id + 1;
    576     record.specifics = child_specifics;
    577 
    578     DictionaryValue extra_value;
    579     extra_value.SetString("foo", "bar");
    580     scoped_ptr<StrictMock<MockExtraChangeRecordData> > extra(
    581         new StrictMock<MockExtraChangeRecordData>());
    582     EXPECT_CALL(*extra, ToValue()).Times(2).WillRepeatedly(
    583         Invoke(&extra_value, &DictionaryValue::DeepCopy));
    584 
    585     record.extra.reset(extra.release());
    586     scoped_ptr<DictionaryValue> value(record.ToValue(&trans));
    587     CheckDeleteChangeRecordValue(record, *value);
    588   }
    589 }
    590 
    591 namespace {
    592 
    593 class TestHttpPostProviderFactory : public HttpPostProviderFactory {
    594  public:
    595   virtual ~TestHttpPostProviderFactory() {}
    596   virtual HttpPostProviderInterface* Create() {
    597     NOTREACHED();
    598     return NULL;
    599   }
    600   virtual void Destroy(HttpPostProviderInterface* http) {
    601     NOTREACHED();
    602   }
    603 };
    604 
    605 class SyncManagerObserverMock : public SyncManager::Observer {
    606  public:
    607   MOCK_METHOD4(OnChangesApplied,
    608                void(ModelType,
    609                     const BaseTransaction*,
    610                     const SyncManager::ChangeRecord*,
    611                     int));  // NOLINT
    612   MOCK_METHOD1(OnChangesComplete, void(ModelType));  // NOLINT
    613   MOCK_METHOD1(OnSyncCycleCompleted,
    614                void(const SyncSessionSnapshot*));  // NOLINT
    615   MOCK_METHOD0(OnInitializationComplete, void());  // NOLINT
    616   MOCK_METHOD1(OnAuthError, void(const GoogleServiceAuthError&));  // NOLINT
    617   MOCK_METHOD1(OnPassphraseRequired, void(bool));  // NOLINT
    618   MOCK_METHOD0(OnPassphraseFailed, void());  // NOLINT
    619   MOCK_METHOD1(OnPassphraseAccepted, void(const std::string&));  // NOLINT
    620   MOCK_METHOD0(OnStopSyncingPermanently, void());  // NOLINT
    621   MOCK_METHOD1(OnUpdatedToken, void(const std::string&));  // NOLINT
    622   MOCK_METHOD1(OnMigrationNeededForTypes, void(const ModelTypeSet&));
    623   MOCK_METHOD0(OnClearServerDataFailed, void());  // NOLINT
    624   MOCK_METHOD0(OnClearServerDataSucceeded, void());  // NOLINT
    625   MOCK_METHOD1(OnEncryptionComplete, void(const ModelTypeSet&));  // NOLINT
    626 };
    627 
    628 class SyncNotifierMock : public sync_notifier::SyncNotifier {
    629  public:
    630   MOCK_METHOD1(AddObserver, void(sync_notifier::SyncNotifierObserver*));
    631   MOCK_METHOD1(RemoveObserver, void(sync_notifier::SyncNotifierObserver*));
    632   MOCK_METHOD1(SetState, void(const std::string&));
    633   MOCK_METHOD2(UpdateCredentials,
    634                void(const std::string&, const std::string&));
    635   MOCK_METHOD1(UpdateEnabledTypes,
    636                void(const syncable::ModelTypeSet&));
    637   MOCK_METHOD0(SendNotification, void());
    638 };
    639 
    640 class SyncManagerTest : public testing::Test,
    641                         public ModelSafeWorkerRegistrar {
    642  protected:
    643   SyncManagerTest()
    644       : ui_thread_(BrowserThread::UI, &ui_loop_),
    645         sync_notifier_observer_(NULL),
    646         update_enabled_types_call_count_(0) {}
    647 
    648   // Test implementation.
    649   void SetUp() {
    650     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    651 
    652     SyncCredentials credentials;
    653     credentials.email = "foo (at) bar.com";
    654     credentials.sync_token = "sometoken";
    655 
    656     sync_notifier_mock_.reset(new StrictMock<SyncNotifierMock>());
    657     EXPECT_CALL(*sync_notifier_mock_, AddObserver(_)).
    658         WillOnce(Invoke(this, &SyncManagerTest::SyncNotifierAddObserver));
    659     EXPECT_CALL(*sync_notifier_mock_, SetState(""));
    660     EXPECT_CALL(*sync_notifier_mock_,
    661                 UpdateCredentials(credentials.email, credentials.sync_token));
    662     EXPECT_CALL(*sync_notifier_mock_, UpdateEnabledTypes(_)).
    663         Times(AtLeast(1)).
    664         WillRepeatedly(
    665             Invoke(this, &SyncManagerTest::SyncNotifierUpdateEnabledTypes));
    666     EXPECT_CALL(*sync_notifier_mock_, RemoveObserver(_)).
    667         WillOnce(Invoke(this, &SyncManagerTest::SyncNotifierRemoveObserver));
    668 
    669     EXPECT_FALSE(sync_notifier_observer_);
    670 
    671     sync_manager_.Init(temp_dir_.path(), "bogus", 0, false,
    672                        new TestHttpPostProviderFactory(), this, "bogus",
    673                        credentials, sync_notifier_mock_.get(), "",
    674                        true /* setup_for_test_mode */);
    675 
    676     EXPECT_TRUE(sync_notifier_observer_);
    677     sync_manager_.AddObserver(&observer_);
    678 
    679     EXPECT_EQ(1, update_enabled_types_call_count_);
    680 
    681     ModelSafeRoutingInfo routes;
    682     GetModelSafeRoutingInfo(&routes);
    683     for (ModelSafeRoutingInfo::iterator i = routes.begin(); i != routes.end();
    684          ++i) {
    685       EXPECT_CALL(observer_, OnChangesApplied(i->first, _, _, 1))
    686           .RetiresOnSaturation();
    687       EXPECT_CALL(observer_, OnChangesComplete(i->first))
    688           .RetiresOnSaturation();
    689       type_roots_[i->first] = MakeServerNodeForType(
    690           sync_manager_.GetUserShare(), i->first);
    691     }
    692   }
    693 
    694   void TearDown() {
    695     sync_manager_.RemoveObserver(&observer_);
    696     sync_manager_.Shutdown();
    697     EXPECT_FALSE(sync_notifier_observer_);
    698   }
    699 
    700   // ModelSafeWorkerRegistrar implementation.
    701   virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) {
    702     NOTIMPLEMENTED();
    703     out->clear();
    704   }
    705   virtual void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
    706     (*out)[syncable::NIGORI] = browser_sync::GROUP_PASSIVE;
    707     (*out)[syncable::BOOKMARKS] = browser_sync::GROUP_PASSIVE;
    708     (*out)[syncable::THEMES] = browser_sync::GROUP_PASSIVE;
    709     (*out)[syncable::SESSIONS] = browser_sync::GROUP_PASSIVE;
    710     (*out)[syncable::PASSWORDS] = browser_sync::GROUP_PASSIVE;
    711   }
    712 
    713   // Helper methods.
    714   bool SetUpEncryption() {
    715     // We need to create the nigori node as if it were an applied server update.
    716     UserShare* share = sync_manager_.GetUserShare();
    717     int64 nigori_id = GetIdForDataType(syncable::NIGORI);
    718     if (nigori_id == kInvalidId)
    719       return false;
    720 
    721     // Set the nigori cryptographer information.
    722     WriteTransaction trans(share);
    723     Cryptographer* cryptographer = trans.GetCryptographer();
    724     if (!cryptographer)
    725       return false;
    726     KeyParams params = {"localhost", "dummy", "foobar"};
    727     cryptographer->AddKey(params);
    728     sync_pb::NigoriSpecifics nigori;
    729     cryptographer->GetKeys(nigori.mutable_encrypted());
    730     WriteNode node(&trans);
    731     node.InitByIdLookup(nigori_id);
    732     node.SetNigoriSpecifics(nigori);
    733     return cryptographer->is_ready();
    734   }
    735 
    736   int64 GetIdForDataType(ModelType type) {
    737     if (type_roots_.count(type) == 0)
    738       return 0;
    739     return type_roots_[type];
    740   }
    741 
    742   void SyncNotifierAddObserver(
    743       sync_notifier::SyncNotifierObserver* sync_notifier_observer) {
    744     EXPECT_EQ(NULL, sync_notifier_observer_);
    745     sync_notifier_observer_ = sync_notifier_observer;
    746   }
    747 
    748   void SyncNotifierRemoveObserver(
    749       sync_notifier::SyncNotifierObserver* sync_notifier_observer) {
    750     EXPECT_EQ(sync_notifier_observer_, sync_notifier_observer);
    751     sync_notifier_observer_ = NULL;
    752   }
    753 
    754   void SyncNotifierUpdateEnabledTypes(
    755       const syncable::ModelTypeSet& types) {
    756     ModelSafeRoutingInfo routes;
    757     GetModelSafeRoutingInfo(&routes);
    758     syncable::ModelTypeSet expected_types;
    759     for (ModelSafeRoutingInfo::const_iterator it = routes.begin();
    760          it != routes.end(); ++it) {
    761       expected_types.insert(it->first);
    762     }
    763     EXPECT_EQ(expected_types, types);
    764     ++update_enabled_types_call_count_;
    765   }
    766 
    767  private:
    768   // Needed by |ui_thread_|.
    769   MessageLoopForUI ui_loop_;
    770   // Needed by |sync_manager_|.
    771   BrowserThread ui_thread_;
    772   // Needed by |sync_manager_|.
    773   ScopedTempDir temp_dir_;
    774   // Sync Id's for the roots of the enabled datatypes.
    775   std::map<ModelType, int64> type_roots_;
    776   scoped_ptr<StrictMock<SyncNotifierMock> > sync_notifier_mock_;
    777 
    778  protected:
    779   SyncManager sync_manager_;
    780   StrictMock<SyncManagerObserverMock> observer_;
    781   sync_notifier::SyncNotifierObserver* sync_notifier_observer_;
    782   int update_enabled_types_call_count_;
    783 };
    784 
    785 TEST_F(SyncManagerTest, UpdateEnabledTypes) {
    786   EXPECT_EQ(1, update_enabled_types_call_count_);
    787   // Triggers SyncNotifierUpdateEnabledTypes.
    788   sync_manager_.UpdateEnabledTypes();
    789   EXPECT_EQ(2, update_enabled_types_call_count_);
    790 }
    791 
    792 TEST_F(SyncManagerTest, ParentJsEventRouter) {
    793   StrictMock<MockJsEventRouter> event_router;
    794   browser_sync::JsBackend* js_backend = sync_manager_.GetJsBackend();
    795   EXPECT_EQ(NULL, js_backend->GetParentJsEventRouter());
    796   js_backend->SetParentJsEventRouter(&event_router);
    797   EXPECT_EQ(&event_router, js_backend->GetParentJsEventRouter());
    798   js_backend->RemoveParentJsEventRouter();
    799   EXPECT_EQ(NULL, js_backend->GetParentJsEventRouter());
    800 }
    801 
    802 TEST_F(SyncManagerTest, ProcessMessage) {
    803   const JsArgList kNoArgs;
    804 
    805   browser_sync::JsBackend* js_backend = sync_manager_.GetJsBackend();
    806 
    807   // Messages sent without any parent router should be dropped.
    808   {
    809     StrictMock<MockJsEventHandler> event_handler;
    810     js_backend->ProcessMessage("unknownMessage",
    811                                kNoArgs, &event_handler);
    812     js_backend->ProcessMessage("getNotificationState",
    813                                kNoArgs, &event_handler);
    814   }
    815 
    816   {
    817     StrictMock<MockJsEventHandler> event_handler;
    818     StrictMock<MockJsEventRouter> event_router;
    819 
    820     ListValue false_args;
    821     false_args.Append(Value::CreateBooleanValue(false));
    822 
    823     EXPECT_CALL(event_router,
    824                 RouteJsEvent("onGetNotificationStateFinished",
    825                              HasArgsAsList(false_args), &event_handler));
    826 
    827     js_backend->SetParentJsEventRouter(&event_router);
    828 
    829     // This message should be dropped.
    830     js_backend->ProcessMessage("unknownMessage",
    831                                  kNoArgs, &event_handler);
    832 
    833     // This should trigger the reply.
    834     js_backend->ProcessMessage("getNotificationState",
    835                                  kNoArgs, &event_handler);
    836 
    837     js_backend->RemoveParentJsEventRouter();
    838   }
    839 
    840   // Messages sent after a parent router has been removed should be
    841   // dropped.
    842   {
    843     StrictMock<MockJsEventHandler> event_handler;
    844     js_backend->ProcessMessage("unknownMessage",
    845                                  kNoArgs, &event_handler);
    846     js_backend->ProcessMessage("getNotificationState",
    847                                  kNoArgs, &event_handler);
    848   }
    849 }
    850 
    851 TEST_F(SyncManagerTest, ProcessMessageGetRootNode) {
    852   const JsArgList kNoArgs;
    853 
    854   browser_sync::JsBackend* js_backend = sync_manager_.GetJsBackend();
    855 
    856   StrictMock<MockJsEventHandler> event_handler;
    857   StrictMock<MockJsEventRouter> event_router;
    858 
    859   JsArgList return_args;
    860 
    861   EXPECT_CALL(event_router,
    862               RouteJsEvent("onGetRootNodeFinished", _, &event_handler)).
    863       WillOnce(SaveArg<1>(&return_args));
    864 
    865   js_backend->SetParentJsEventRouter(&event_router);
    866 
    867   // Should trigger the reply.
    868   js_backend->ProcessMessage("getRootNode", kNoArgs, &event_handler);
    869 
    870   EXPECT_EQ(1u, return_args.Get().GetSize());
    871   DictionaryValue* node_info = NULL;
    872   EXPECT_TRUE(return_args.Get().GetDictionary(0, &node_info));
    873   if (node_info) {
    874     ReadTransaction trans(sync_manager_.GetUserShare());
    875     ReadNode node(&trans);
    876     node.InitByRootLookup();
    877     CheckNodeValue(node, *node_info);
    878   } else {
    879     ADD_FAILURE();
    880   }
    881 
    882   js_backend->RemoveParentJsEventRouter();
    883 }
    884 
    885 void CheckGetNodeByIdReturnArgs(const SyncManager& sync_manager,
    886                                 const JsArgList& return_args,
    887                                 int64 id) {
    888   EXPECT_EQ(1u, return_args.Get().GetSize());
    889   DictionaryValue* node_info = NULL;
    890   EXPECT_TRUE(return_args.Get().GetDictionary(0, &node_info));
    891   if (node_info) {
    892     ReadTransaction trans(sync_manager.GetUserShare());
    893     ReadNode node(&trans);
    894     node.InitByIdLookup(id);
    895     CheckNodeValue(node, *node_info);
    896   } else {
    897     ADD_FAILURE();
    898   }
    899 }
    900 
    901 TEST_F(SyncManagerTest, ProcessMessageGetNodeById) {
    902   int64 child_id =
    903       MakeNode(sync_manager_.GetUserShare(), syncable::BOOKMARKS, "testtag");
    904 
    905   browser_sync::JsBackend* js_backend = sync_manager_.GetJsBackend();
    906 
    907   StrictMock<MockJsEventHandler> event_handler;
    908   StrictMock<MockJsEventRouter> event_router;
    909 
    910   JsArgList return_args;
    911 
    912   EXPECT_CALL(event_router,
    913               RouteJsEvent("onGetNodeByIdFinished", _, &event_handler))
    914       .Times(2).WillRepeatedly(SaveArg<1>(&return_args));
    915 
    916   js_backend->SetParentJsEventRouter(&event_router);
    917 
    918   // Should trigger the reply.
    919   {
    920     ListValue args;
    921     args.Append(Value::CreateStringValue("1"));
    922     js_backend->ProcessMessage("getNodeById", JsArgList(args), &event_handler);
    923   }
    924 
    925   CheckGetNodeByIdReturnArgs(sync_manager_, return_args, 1);
    926 
    927   // Should trigger another reply.
    928   {
    929     ListValue args;
    930     args.Append(Value::CreateStringValue(base::Int64ToString(child_id)));
    931     js_backend->ProcessMessage("getNodeById", JsArgList(args), &event_handler);
    932   }
    933 
    934   CheckGetNodeByIdReturnArgs(sync_manager_, return_args, child_id);
    935 
    936   js_backend->RemoveParentJsEventRouter();
    937 }
    938 
    939 TEST_F(SyncManagerTest, ProcessMessageGetNodeByIdFailure) {
    940   browser_sync::JsBackend* js_backend = sync_manager_.GetJsBackend();
    941 
    942   StrictMock<MockJsEventHandler> event_handler;
    943   StrictMock<MockJsEventRouter> event_router;
    944 
    945   ListValue null_args;
    946   null_args.Append(Value::CreateNullValue());
    947 
    948   EXPECT_CALL(event_router,
    949               RouteJsEvent("onGetNodeByIdFinished",
    950                            HasArgsAsList(null_args), &event_handler))
    951       .Times(5);
    952 
    953   js_backend->SetParentJsEventRouter(&event_router);
    954 
    955   {
    956     ListValue args;
    957     js_backend->ProcessMessage("getNodeById", JsArgList(args), &event_handler);
    958   }
    959 
    960   {
    961     ListValue args;
    962     args.Append(Value::CreateStringValue(""));
    963     js_backend->ProcessMessage("getNodeById", JsArgList(args), &event_handler);
    964   }
    965 
    966   {
    967     ListValue args;
    968     args.Append(Value::CreateStringValue("nonsense"));
    969     js_backend->ProcessMessage("getNodeById", JsArgList(args), &event_handler);
    970   }
    971 
    972   {
    973     ListValue args;
    974     args.Append(Value::CreateStringValue("nonsense"));
    975     js_backend->ProcessMessage("getNodeById", JsArgList(args), &event_handler);
    976   }
    977 
    978   {
    979     ListValue args;
    980     args.Append(Value::CreateStringValue("0"));
    981     js_backend->ProcessMessage("getNodeById", JsArgList(args), &event_handler);
    982   }
    983 
    984   // TODO(akalin): Figure out how to test InitByIdLookup() failure.
    985 
    986   js_backend->RemoveParentJsEventRouter();
    987 }
    988 
    989 TEST_F(SyncManagerTest, OnNotificationStateChange) {
    990   StrictMock<MockJsEventRouter> event_router;
    991 
    992   ListValue true_args;
    993   true_args.Append(Value::CreateBooleanValue(true));
    994   ListValue false_args;
    995   false_args.Append(Value::CreateBooleanValue(false));
    996 
    997   EXPECT_CALL(event_router,
    998               RouteJsEvent("onSyncNotificationStateChange",
    999                            HasArgsAsList(true_args), NULL));
   1000   EXPECT_CALL(event_router,
   1001               RouteJsEvent("onSyncNotificationStateChange",
   1002                            HasArgsAsList(false_args), NULL));
   1003 
   1004   browser_sync::JsBackend* js_backend = sync_manager_.GetJsBackend();
   1005 
   1006   sync_manager_.TriggerOnNotificationStateChangeForTest(true);
   1007   sync_manager_.TriggerOnNotificationStateChangeForTest(false);
   1008 
   1009   js_backend->SetParentJsEventRouter(&event_router);
   1010   sync_manager_.TriggerOnNotificationStateChangeForTest(true);
   1011   sync_manager_.TriggerOnNotificationStateChangeForTest(false);
   1012   js_backend->RemoveParentJsEventRouter();
   1013 
   1014   sync_manager_.TriggerOnNotificationStateChangeForTest(true);
   1015   sync_manager_.TriggerOnNotificationStateChangeForTest(false);
   1016 }
   1017 
   1018 TEST_F(SyncManagerTest, OnIncomingNotification) {
   1019   StrictMock<MockJsEventRouter> event_router;
   1020 
   1021   const syncable::ModelTypeBitSet empty_model_types;
   1022   syncable::ModelTypeBitSet model_types;
   1023   model_types.set(syncable::BOOKMARKS);
   1024   model_types.set(syncable::THEMES);
   1025 
   1026   // Build expected_args to have a single argument with the string
   1027   // equivalents of model_types.
   1028   ListValue expected_args;
   1029   {
   1030     ListValue* model_type_list = new ListValue();
   1031     expected_args.Append(model_type_list);
   1032     for (int i = syncable::FIRST_REAL_MODEL_TYPE;
   1033          i < syncable::MODEL_TYPE_COUNT; ++i) {
   1034       if (model_types[i]) {
   1035         model_type_list->Append(
   1036             Value::CreateStringValue(
   1037                 syncable::ModelTypeToString(
   1038                     syncable::ModelTypeFromInt(i))));
   1039       }
   1040     }
   1041   }
   1042 
   1043   EXPECT_CALL(event_router,
   1044               RouteJsEvent("onSyncIncomingNotification",
   1045                            HasArgsAsList(expected_args), NULL));
   1046 
   1047   browser_sync::JsBackend* js_backend = sync_manager_.GetJsBackend();
   1048 
   1049   sync_manager_.TriggerOnIncomingNotificationForTest(empty_model_types);
   1050   sync_manager_.TriggerOnIncomingNotificationForTest(model_types);
   1051 
   1052   js_backend->SetParentJsEventRouter(&event_router);
   1053   sync_manager_.TriggerOnIncomingNotificationForTest(model_types);
   1054   js_backend->RemoveParentJsEventRouter();
   1055 
   1056   sync_manager_.TriggerOnIncomingNotificationForTest(empty_model_types);
   1057   sync_manager_.TriggerOnIncomingNotificationForTest(model_types);
   1058 }
   1059 
   1060 TEST_F(SyncManagerTest, EncryptDataTypesWithNoData) {
   1061   EXPECT_TRUE(SetUpEncryption());
   1062   ModelTypeSet encrypted_types;
   1063   encrypted_types.insert(syncable::BOOKMARKS);
   1064   // Even though Passwords isn't marked for encryption, it's enabled, so it
   1065   // should automatically be added to the response of OnEncryptionComplete.
   1066   ModelTypeSet expected_types = encrypted_types;
   1067   expected_types.insert(syncable::PASSWORDS);
   1068   EXPECT_CALL(observer_, OnEncryptionComplete(expected_types));
   1069   sync_manager_.EncryptDataTypes(encrypted_types);
   1070   {
   1071     ReadTransaction trans(sync_manager_.GetUserShare());
   1072     EXPECT_EQ(encrypted_types,
   1073               GetEncryptedDataTypes(trans.GetWrappedTrans()));
   1074   }
   1075 }
   1076 
   1077 TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
   1078   size_t batch_size = 5;
   1079   EXPECT_TRUE(SetUpEncryption());
   1080 
   1081   // Create some unencrypted unsynced data.
   1082   int64 folder = MakeFolderWithParent(sync_manager_.GetUserShare(),
   1083                                       syncable::BOOKMARKS,
   1084                                       GetIdForDataType(syncable::BOOKMARKS),
   1085                                       NULL);
   1086   // First batch_size nodes are children of folder.
   1087   size_t i;
   1088   for (i = 0; i < batch_size; ++i) {
   1089     MakeNodeWithParent(sync_manager_.GetUserShare(), syncable::BOOKMARKS,
   1090                        StringPrintf("%"PRIuS"", i), folder);
   1091   }
   1092   // Next batch_size nodes are a different type and on their own.
   1093   for (; i < 2*batch_size; ++i) {
   1094     MakeNodeWithParent(sync_manager_.GetUserShare(), syncable::SESSIONS,
   1095                        StringPrintf("%"PRIuS"", i),
   1096                        GetIdForDataType(syncable::SESSIONS));
   1097   }
   1098   // Last batch_size nodes are a third type that will not need encryption.
   1099   for (; i < 3*batch_size; ++i) {
   1100     MakeNodeWithParent(sync_manager_.GetUserShare(), syncable::THEMES,
   1101                        StringPrintf("%"PRIuS"", i),
   1102                        GetIdForDataType(syncable::THEMES));
   1103   }
   1104 
   1105   {
   1106     ReadTransaction trans(sync_manager_.GetUserShare());
   1107     EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
   1108                                                    syncable::BOOKMARKS,
   1109                                                    false /* not encrypted */));
   1110     EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
   1111                                                    syncable::SESSIONS,
   1112                                                    false /* not encrypted */));
   1113     EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
   1114                                                    syncable::THEMES,
   1115                                                    false /* not encrypted */));
   1116   }
   1117 
   1118   ModelTypeSet encrypted_types;
   1119   encrypted_types.insert(syncable::BOOKMARKS);
   1120   encrypted_types.insert(syncable::SESSIONS);
   1121   encrypted_types.insert(syncable::PASSWORDS);
   1122   EXPECT_CALL(observer_, OnEncryptionComplete(encrypted_types));
   1123   sync_manager_.EncryptDataTypes(encrypted_types);
   1124 
   1125   {
   1126     ReadTransaction trans(sync_manager_.GetUserShare());
   1127     encrypted_types.erase(syncable::PASSWORDS);  // Not stored in nigori node.
   1128     EXPECT_EQ(encrypted_types,
   1129               GetEncryptedDataTypes(trans.GetWrappedTrans()));
   1130     EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
   1131                                                    syncable::BOOKMARKS,
   1132                                                    true /* is encrypted */));
   1133     EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
   1134                                                    syncable::SESSIONS,
   1135                                                    true /* is encrypted */));
   1136     EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
   1137                                                    syncable::THEMES,
   1138                                                    false /* not encrypted */));
   1139   }
   1140 }
   1141 
   1142 }  // namespace
   1143 
   1144 }  // namespace browser_sync
   1145