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