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 #include <stdarg.h> |
| 6 |
| 7 #include "base/basictypes.h" |
| 8 #include "base/stl_util-inl.h" |
| 9 #include "base/string_util.h" |
| 10 #include "base/time.h" |
| 11 #include "base/utf_string_conversions.h" |
| 12 #include "chrome/browser/password_manager/native_backend_gnome_x.h" |
| 13 #include "chrome/browser/prefs/pref_service.h" |
| 14 #include "chrome/common/pref_names.h" |
| 15 #include "chrome/test/testing_profile.h" |
| 16 #include "testing/gtest/include/gtest/gtest.h" |
| 17 |
| 18 using webkit_glue::PasswordForm; |
| 19 |
| 20 namespace { |
| 21 |
| 22 // What follows is a very simple implementation of the subset of the GNOME |
| 23 // Keyring API that we actually use. It gets substituted for the real one by |
| 24 // MockGnomeKeyringLoader, which hooks into the facility normally used to load |
| 25 // the GNOME Keyring library at runtime to avoid a static dependency on it. |
| 26 |
| 27 struct MockKeyringItem { |
| 28 MockKeyringItem() {} |
| 29 MockKeyringItem(const char* keyring, |
| 30 const std::string& display_name, |
| 31 const std::string& password) |
| 32 : keyring(keyring ? keyring : "login"), |
| 33 display_name(display_name), |
| 34 password(password) {} |
| 35 |
| 36 struct ItemAttribute { |
| 37 ItemAttribute() : type(UINT32), value_uint32(0) {} |
| 38 explicit ItemAttribute(uint32_t value) |
| 39 : type(UINT32), value_uint32(value) {} |
| 40 explicit ItemAttribute(const std::string& value) |
| 41 : type(STRING), value_string(value) {} |
| 42 |
| 43 bool Equals(const ItemAttribute& x) const { |
| 44 if (type != x.type) return false; |
| 45 return (type == STRING) ? value_string == x.value_string |
| 46 : value_uint32 == x.value_uint32; |
| 47 } |
| 48 |
| 49 enum { UINT32, STRING } type; |
| 50 uint32_t value_uint32; |
| 51 std::string value_string; |
| 52 }; |
| 53 |
| 54 typedef std::map<std::string, ItemAttribute> attribute_map; |
| 55 typedef std::vector<std::pair<std::string, ItemAttribute> > attribute_query; |
| 56 |
| 57 bool Matches(const attribute_query& query) const { |
| 58 // The real GNOME Keyring doesn't match empty queries. |
| 59 if (query.empty()) return false; |
| 60 for (size_t i = 0; i < query.size(); ++i) { |
| 61 attribute_map::const_iterator match = attributes.find(query[i].first); |
| 62 if (match == attributes.end()) return false; |
| 63 if (!match->second.Equals(query[i].second)) return false; |
| 64 } |
| 65 return true; |
| 66 } |
| 67 |
| 68 std::string keyring; |
| 69 std::string display_name; |
| 70 std::string password; |
| 71 |
| 72 attribute_map attributes; |
| 73 }; |
| 74 |
| 75 // The list of all keyring items we have stored. |
| 76 std::vector<MockKeyringItem> mock_keyring_items; |
| 77 bool mock_keyring_reject_local_ids = false; |
| 78 |
| 79 bool IsStringAttribute(const GnomeKeyringPasswordSchema* schema, |
| 80 const std::string& name) { |
| 81 for (size_t i = 0; schema->attributes[i].name; ++i) |
| 82 if (name == schema->attributes[i].name) |
| 83 return schema->attributes[i].type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING; |
| 84 NOTREACHED() << "Requested type of nonexistent attribute"; |
| 85 return false; |
| 86 } |
| 87 |
| 88 gboolean mock_gnome_keyring_is_available() { |
| 89 return true; |
| 90 } |
| 91 |
| 92 gpointer mock_gnome_keyring_store_password( |
| 93 const GnomeKeyringPasswordSchema* schema, |
| 94 const gchar* keyring, |
| 95 const gchar* display_name, |
| 96 const gchar* password, |
| 97 GnomeKeyringOperationDoneCallback callback, |
| 98 gpointer data, |
| 99 GDestroyNotify destroy_data, |
| 100 ...) { |
| 101 mock_keyring_items.push_back( |
| 102 MockKeyringItem(keyring, display_name, password)); |
| 103 MockKeyringItem* item = &mock_keyring_items.back(); |
| 104 const std::string keyring_desc = keyring ? StringPrintf("keyring %s", keyring) |
| 105 : std::string("default keyring"); |
| 106 VLOG(1) << "Adding item with origin " << display_name |
| 107 << " to " << keyring_desc; |
| 108 va_list ap; |
| 109 va_start(ap, destroy_data); |
| 110 char* name; |
| 111 while ((name = va_arg(ap, gchar*))) { |
| 112 if (IsStringAttribute(schema, name)) { |
| 113 item->attributes[name] = |
| 114 MockKeyringItem::ItemAttribute(va_arg(ap, gchar*)); |
| 115 VLOG(1) << "Adding item attribute " << name |
| 116 << ", value '" << item->attributes[name].value_string << "'"; |
| 117 } else { |
| 118 item->attributes[name] = |
| 119 MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t)); |
| 120 VLOG(1) << "Adding item attribute " << name |
| 121 << ", value " << item->attributes[name].value_uint32; |
| 122 } |
| 123 } |
| 124 va_end(ap); |
| 125 // As a hack to ease testing migration, make it possible to reject the new |
| 126 // format for the app string. This way we can add them easily to migrate. |
| 127 if (mock_keyring_reject_local_ids) { |
| 128 MockKeyringItem::attribute_map::iterator it = |
| 129 item->attributes.find("application"); |
| 130 if (it != item->attributes.end() && |
| 131 it->second.type == MockKeyringItem::ItemAttribute::STRING && |
| 132 base::StringPiece(it->second.value_string).starts_with("chrome-")) { |
| 133 mock_keyring_items.pop_back(); |
| 134 // GnomeKeyringResult, data |
| 135 callback(GNOME_KEYRING_RESULT_IO_ERROR, data); |
| 136 return NULL; |
| 137 } |
| 138 } |
| 139 // GnomeKeyringResult, data |
| 140 callback(GNOME_KEYRING_RESULT_OK, data); |
| 141 return NULL; |
| 142 } |
| 143 |
| 144 gpointer mock_gnome_keyring_delete_password( |
| 145 const GnomeKeyringPasswordSchema* schema, |
| 146 GnomeKeyringOperationDoneCallback callback, |
| 147 gpointer data, |
| 148 GDestroyNotify destroy_data, |
| 149 ...) { |
| 150 MockKeyringItem::attribute_query query; |
| 151 va_list ap; |
| 152 va_start(ap, destroy_data); |
| 153 char* name; |
| 154 while ((name = va_arg(ap, gchar*))) { |
| 155 if (IsStringAttribute(schema, name)) { |
| 156 query.push_back(make_pair(std::string(name), |
| 157 MockKeyringItem::ItemAttribute(va_arg(ap, gchar*)))); |
| 158 VLOG(1) << "Querying with item attribute " << name |
| 159 << ", value '" << query.back().second.value_string << "'"; |
| 160 } else { |
| 161 query.push_back(make_pair(std::string(name), |
| 162 MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t)))); |
| 163 VLOG(1) << "Querying with item attribute " << name |
| 164 << ", value " << query.back().second.value_uint32; |
| 165 } |
| 166 } |
| 167 va_end(ap); |
| 168 bool deleted = false; |
| 169 for (size_t i = mock_keyring_items.size(); i > 0; --i) { |
| 170 const MockKeyringItem* item = &mock_keyring_items[i - 1]; |
| 171 if (item->Matches(query)) { |
| 172 VLOG(1) << "Deleting item with origin " << item->display_name; |
| 173 mock_keyring_items.erase(mock_keyring_items.begin() + (i - 1)); |
| 174 deleted = true; |
| 175 } |
| 176 } |
| 177 // GnomeKeyringResult, data |
| 178 callback(deleted ? GNOME_KEYRING_RESULT_OK |
| 179 : GNOME_KEYRING_RESULT_NO_MATCH, data); |
| 180 return NULL; |
| 181 } |
| 182 |
| 183 gpointer mock_gnome_keyring_find_itemsv( |
| 184 GnomeKeyringItemType type, |
| 185 GnomeKeyringOperationGetListCallback callback, |
| 186 gpointer data, |
| 187 GDestroyNotify destroy_data, |
| 188 ...) { |
| 189 MockKeyringItem::attribute_query query; |
| 190 va_list ap; |
| 191 va_start(ap, destroy_data); |
| 192 char* name; |
| 193 while ((name = va_arg(ap, gchar*))) { |
| 194 // Really a GnomeKeyringAttributeType, but promoted to int through ... |
| 195 if (va_arg(ap, int) == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) { |
| 196 query.push_back(make_pair(std::string(name), |
| 197 MockKeyringItem::ItemAttribute(va_arg(ap, gchar*)))); |
| 198 VLOG(1) << "Querying with item attribute " << name |
| 199 << ", value '" << query.back().second.value_string << "'"; |
| 200 } else { |
| 201 query.push_back(make_pair(std::string(name), |
| 202 MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t)))); |
| 203 VLOG(1) << "Querying with item attribute " << name |
| 204 << ", value " << query.back().second.value_uint32; |
| 205 } |
| 206 } |
| 207 va_end(ap); |
| 208 // Find matches and add them to a list of results. |
| 209 GList* results = NULL; |
| 210 for (size_t i = 0; i < mock_keyring_items.size(); ++i) { |
| 211 const MockKeyringItem* item = &mock_keyring_items[i]; |
| 212 if (item->Matches(query)) { |
| 213 GnomeKeyringFound* found = new GnomeKeyringFound; |
| 214 found->keyring = strdup(item->keyring.c_str()); |
| 215 found->item_id = i; |
| 216 found->attributes = gnome_keyring_attribute_list_new(); |
| 217 for (MockKeyringItem::attribute_map::const_iterator it = |
| 218 item->attributes.begin(); |
| 219 it != item->attributes.end(); |
| 220 ++it) { |
| 221 if (it->second.type == MockKeyringItem::ItemAttribute::STRING) { |
| 222 gnome_keyring_attribute_list_append_string( |
| 223 found->attributes, it->first.c_str(), |
| 224 it->second.value_string.c_str()); |
| 225 } else { |
| 226 gnome_keyring_attribute_list_append_uint32( |
| 227 found->attributes, it->first.c_str(), |
| 228 it->second.value_uint32); |
| 229 } |
| 230 } |
| 231 found->secret = strdup(item->password.c_str()); |
| 232 results = g_list_prepend(results, found); |
| 233 } |
| 234 } |
| 235 // GnomeKeyringResult, GList*, data |
| 236 callback(results ? GNOME_KEYRING_RESULT_OK |
| 237 : GNOME_KEYRING_RESULT_NO_MATCH, results, data); |
| 238 // Now free the list of results. |
| 239 GList* element = g_list_first(results); |
| 240 while (element) { |
| 241 GnomeKeyringFound* found = static_cast<GnomeKeyringFound*>(element->data); |
| 242 free(found->keyring); |
| 243 gnome_keyring_attribute_list_free(found->attributes); |
| 244 free(found->secret); |
| 245 element = g_list_next(element); |
| 246 } |
| 247 g_list_free(results); |
| 248 return NULL; |
| 249 } |
| 250 |
| 251 const gchar* mock_gnome_keyring_result_to_message(GnomeKeyringResult res) { |
| 252 return "mock keyring simulating failure"; |
| 253 } |
| 254 |
| 255 // Inherit to get access to protected fields. |
| 256 class MockGnomeKeyringLoader : public GnomeKeyringLoader { |
| 257 public: |
| 258 static bool LoadMockGnomeKeyring() { |
| 259 #define GNOME_KEYRING_ASSIGN_POINTER(name) \ |
| 260 gnome_keyring_##name = &mock_gnome_keyring_##name; |
| 261 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_ASSIGN_POINTER) |
| 262 #undef GNOME_KEYRING_ASSIGN_POINTER |
| 263 keyring_loaded = true; |
| 264 // Reset the state of the mock library. |
| 265 mock_keyring_items.clear(); |
| 266 mock_keyring_reject_local_ids = false; |
| 267 return true; |
| 268 } |
| 269 }; |
| 270 |
| 271 } // anonymous namespace |
| 272 |
| 273 // NativeBackendGnome isn't reference counted, but in these unit tests that |
| 274 // won't be a problem as it always outlives the threads we post tasks to. |
| 275 template<> |
| 276 struct RunnableMethodTraits<NativeBackendGnome> { |
| 277 void RetainCallee(NativeBackendGnome*) {} |
| 278 void ReleaseCallee(NativeBackendGnome*) {} |
| 279 }; |
| 280 |
| 281 class NativeBackendGnomeTest : public testing::Test { |
| 282 protected: |
| 283 NativeBackendGnomeTest() |
| 284 : ui_thread_(BrowserThread::UI, &message_loop_), |
| 285 db_thread_(BrowserThread::DB) { |
| 286 } |
| 287 |
| 288 virtual void SetUp() { |
| 289 ASSERT_TRUE(db_thread_.Start()); |
| 290 |
| 291 MockGnomeKeyringLoader::LoadMockGnomeKeyring(); |
| 292 profile_.reset(new TestingProfile()); |
| 293 |
| 294 form_google_.origin = GURL("http://www.google.com/"); |
| 295 form_google_.action = GURL("http://www.google.com/login"); |
| 296 form_google_.username_element = UTF8ToUTF16("user"); |
| 297 form_google_.username_value = UTF8ToUTF16("joeschmoe"); |
| 298 form_google_.password_element = UTF8ToUTF16("pass"); |
| 299 form_google_.password_value = UTF8ToUTF16("seekrit"); |
| 300 form_google_.submit_element = UTF8ToUTF16("submit"); |
| 301 form_google_.signon_realm = "Google"; |
| 302 |
| 303 form_isc_.origin = GURL("http://www.isc.org/"); |
| 304 form_isc_.action = GURL("http://www.isc.org/auth"); |
| 305 form_isc_.username_element = UTF8ToUTF16("id"); |
| 306 form_isc_.username_value = UTF8ToUTF16("janedoe"); |
| 307 form_isc_.password_element = UTF8ToUTF16("passwd"); |
| 308 form_isc_.password_value = UTF8ToUTF16("ihazabukkit"); |
| 309 form_isc_.submit_element = UTF8ToUTF16("login"); |
| 310 form_isc_.signon_realm = "ISC"; |
| 311 } |
| 312 |
| 313 virtual void TearDown() { |
| 314 MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask); |
| 315 MessageLoop::current()->Run(); |
| 316 db_thread_.Stop(); |
| 317 } |
| 318 |
| 319 void RunBothThreads() { |
| 320 // First we post a message to the DB thread that will run after all other |
| 321 // messages that have been posted to the DB thread (we don't expect more |
| 322 // to be posted), which posts a message to the UI thread to quit the loop. |
| 323 // That way we can run both loops and be sure that the UI thread loop will |
| 324 // quit so we can get on with the rest of the test. |
| 325 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 326 NewRunnableFunction(&PostQuitTask, &message_loop_)); |
| 327 MessageLoop::current()->Run(); |
| 328 } |
| 329 |
| 330 static void PostQuitTask(MessageLoop* loop) { |
| 331 loop->PostTask(FROM_HERE, new MessageLoop::QuitTask); |
| 332 } |
| 333 |
| 334 void CheckUint32Attribute(const MockKeyringItem* item, |
| 335 const std::string& attribute, |
| 336 uint32_t value) { |
| 337 MockKeyringItem::attribute_map::const_iterator it = |
| 338 item->attributes.find(attribute); |
| 339 EXPECT_NE(item->attributes.end(), it); |
| 340 if (it != item->attributes.end()) { |
| 341 EXPECT_EQ(MockKeyringItem::ItemAttribute::UINT32, it->second.type); |
| 342 EXPECT_EQ(value, it->second.value_uint32); |
| 343 } |
| 344 } |
| 345 |
| 346 void CheckStringAttribute(const MockKeyringItem* item, |
| 347 const std::string& attribute, |
| 348 const std::string& value) { |
| 349 MockKeyringItem::attribute_map::const_iterator it = |
| 350 item->attributes.find(attribute); |
| 351 EXPECT_NE(item->attributes.end(), it); |
| 352 if (it != item->attributes.end()) { |
| 353 EXPECT_EQ(MockKeyringItem::ItemAttribute::STRING, it->second.type); |
| 354 EXPECT_EQ(value, it->second.value_string); |
| 355 } |
| 356 } |
| 357 |
| 358 void CheckMockKeyringItem(const MockKeyringItem* item, |
| 359 const PasswordForm& form, |
| 360 const std::string& app_string) { |
| 361 // We always add items to the login keyring. |
| 362 EXPECT_EQ("login", item->keyring); |
| 363 EXPECT_EQ(form.origin.spec(), item->display_name); |
| 364 EXPECT_EQ(UTF16ToUTF8(form.password_value), item->password); |
| 365 EXPECT_EQ(13u, item->attributes.size()); |
| 366 CheckStringAttribute(item, "origin_url", form.origin.spec()); |
| 367 CheckStringAttribute(item, "action_url", form.action.spec()); |
| 368 CheckStringAttribute(item, "username_element", |
| 369 UTF16ToUTF8(form.username_element)); |
| 370 CheckStringAttribute(item, "username_value", |
| 371 UTF16ToUTF8(form.username_value)); |
| 372 CheckStringAttribute(item, "password_element", |
| 373 UTF16ToUTF8(form.password_element)); |
| 374 CheckStringAttribute(item, "submit_element", |
| 375 UTF16ToUTF8(form.submit_element)); |
| 376 CheckStringAttribute(item, "signon_realm", form.signon_realm); |
| 377 CheckUint32Attribute(item, "ssl_valid", form.ssl_valid); |
| 378 CheckUint32Attribute(item, "preferred", form.preferred); |
| 379 // We don't check the date created. It varies. |
| 380 CheckUint32Attribute(item, "blacklisted_by_user", form.blacklisted_by_user); |
| 381 CheckUint32Attribute(item, "scheme", form.scheme); |
| 382 CheckStringAttribute(item, "application", app_string); |
| 383 } |
| 384 |
| 385 MessageLoopForUI message_loop_; |
| 386 BrowserThread ui_thread_; |
| 387 BrowserThread db_thread_; |
| 388 |
| 389 scoped_ptr<TestingProfile> profile_; |
| 390 |
| 391 // Provide some test forms to avoid having to set them up in each test. |
| 392 PasswordForm form_google_; |
| 393 PasswordForm form_isc_; |
| 394 }; |
| 395 |
| 396 TEST_F(NativeBackendGnomeTest, BasicAddLogin) { |
| 397 // Pretend that the migration has already taken place. |
| 398 profile_->GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); |
| 399 |
| 400 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 401 backend.Init(); |
| 402 |
| 403 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 404 NewRunnableMethod(&backend, |
| 405 &NativeBackendGnome::AddLogin, |
| 406 form_google_)); |
| 407 |
| 408 RunBothThreads(); |
| 409 |
| 410 EXPECT_EQ(1u, mock_keyring_items.size()); |
| 411 if (mock_keyring_items.size() > 0) |
| 412 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42"); |
| 413 } |
| 414 |
| 415 TEST_F(NativeBackendGnomeTest, BasicListLogins) { |
| 416 // Pretend that the migration has already taken place. |
| 417 profile_->GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); |
| 418 |
| 419 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 420 backend.Init(); |
| 421 |
| 422 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 423 NewRunnableMethod(&backend, |
| 424 &NativeBackendGnome::AddLogin, |
| 425 form_google_)); |
| 426 |
| 427 std::vector<PasswordForm*> form_list; |
| 428 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 429 NewRunnableMethod(&backend, |
| 430 &NativeBackendGnome::GetAutofillableLogins, |
| 431 &form_list)); |
| 432 |
| 433 RunBothThreads(); |
| 434 |
| 435 // Quick check that we got something back. |
| 436 EXPECT_EQ(1u, form_list.size()); |
| 437 STLDeleteElements(&form_list); |
| 438 |
| 439 EXPECT_EQ(1u, mock_keyring_items.size()); |
| 440 if (mock_keyring_items.size() > 0) |
| 441 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42"); |
| 442 } |
| 443 |
| 444 TEST_F(NativeBackendGnomeTest, BasicRemoveLogin) { |
| 445 // Pretend that the migration has already taken place. |
| 446 profile_->GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); |
| 447 |
| 448 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 449 backend.Init(); |
| 450 |
| 451 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 452 NewRunnableMethod(&backend, |
| 453 &NativeBackendGnome::AddLogin, |
| 454 form_google_)); |
| 455 |
| 456 RunBothThreads(); |
| 457 |
| 458 EXPECT_EQ(1u, mock_keyring_items.size()); |
| 459 if (mock_keyring_items.size() > 0) |
| 460 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42"); |
| 461 |
| 462 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 463 NewRunnableMethod(&backend, |
| 464 &NativeBackendGnome::RemoveLogin, |
| 465 form_google_)); |
| 466 |
| 467 RunBothThreads(); |
| 468 |
| 469 EXPECT_EQ(0u, mock_keyring_items.size()); |
| 470 } |
| 471 |
| 472 TEST_F(NativeBackendGnomeTest, RemoveNonexistentLogin) { |
| 473 // Pretend that the migration has already taken place. |
| 474 profile_->GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); |
| 475 |
| 476 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 477 backend.Init(); |
| 478 |
| 479 // First add an unrelated login. |
| 480 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 481 NewRunnableMethod(&backend, |
| 482 &NativeBackendGnome::AddLogin, |
| 483 form_google_)); |
| 484 |
| 485 RunBothThreads(); |
| 486 |
| 487 EXPECT_EQ(1u, mock_keyring_items.size()); |
| 488 if (mock_keyring_items.size() > 0) |
| 489 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42"); |
| 490 |
| 491 // Attempt to remove a login that doesn't exist. |
| 492 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 493 NewRunnableMethod(&backend, |
| 494 &NativeBackendGnome::RemoveLogin, |
| 495 form_isc_)); |
| 496 |
| 497 // Make sure we can still get the first form back. |
| 498 std::vector<PasswordForm*> form_list; |
| 499 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 500 NewRunnableMethod(&backend, |
| 501 &NativeBackendGnome::GetAutofillableLogins, |
| 502 &form_list)); |
| 503 |
| 504 RunBothThreads(); |
| 505 |
| 506 // Quick check that we got something back. |
| 507 EXPECT_EQ(1u, form_list.size()); |
| 508 STLDeleteElements(&form_list); |
| 509 |
| 510 EXPECT_EQ(1u, mock_keyring_items.size()); |
| 511 if (mock_keyring_items.size() > 0) |
| 512 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42"); |
| 513 } |
| 514 |
| 515 TEST_F(NativeBackendGnomeTest, AddDuplicateLogin) { |
| 516 // Pretend that the migration has already taken place. |
| 517 profile_->GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); |
| 518 |
| 519 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 520 backend.Init(); |
| 521 |
| 522 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 523 NewRunnableMethod(&backend, |
| 524 &NativeBackendGnome::AddLogin, |
| 525 form_google_)); |
| 526 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 527 NewRunnableMethod(&backend, |
| 528 &NativeBackendGnome::AddLogin, |
| 529 form_google_)); |
| 530 |
| 531 RunBothThreads(); |
| 532 |
| 533 EXPECT_EQ(1u, mock_keyring_items.size()); |
| 534 if (mock_keyring_items.size() > 0) |
| 535 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42"); |
| 536 } |
| 537 |
| 538 // TODO(mdm): add more basic (i.e. non-migration) tests here at some point. |
| 539 |
| 540 TEST_F(NativeBackendGnomeTest, MigrateOneLogin) { |
| 541 // Reject attempts to migrate so we can populate the store. |
| 542 mock_keyring_reject_local_ids = true; |
| 543 |
| 544 { |
| 545 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 546 backend.Init(); |
| 547 |
| 548 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 549 NewRunnableMethod(&backend, |
| 550 &NativeBackendGnome::AddLogin, |
| 551 form_google_)); |
| 552 |
| 553 // Make sure we can get the form back even when migration is failing. |
| 554 std::vector<PasswordForm*> form_list; |
| 555 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 556 NewRunnableMethod(&backend, |
| 557 &NativeBackendGnome::GetAutofillableLogins, |
| 558 &form_list)); |
| 559 |
| 560 RunBothThreads(); |
| 561 |
| 562 // Quick check that we got something back. |
| 563 EXPECT_EQ(1u, form_list.size()); |
| 564 STLDeleteElements(&form_list); |
| 565 } |
| 566 |
| 567 EXPECT_EQ(1u, mock_keyring_items.size()); |
| 568 if (mock_keyring_items.size() > 0) |
| 569 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome"); |
| 570 |
| 571 // Now allow the migration. |
| 572 mock_keyring_reject_local_ids = false; |
| 573 |
| 574 { |
| 575 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 576 backend.Init(); |
| 577 |
| 578 // This should not trigger migration because there will be no results. |
| 579 std::vector<PasswordForm*> form_list; |
| 580 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 581 NewRunnableMethod(&backend, |
| 582 &NativeBackendGnome::GetBlacklistLogins, |
| 583 &form_list)); |
| 584 |
| 585 RunBothThreads(); |
| 586 |
| 587 // Check that we got nothing back. |
| 588 EXPECT_EQ(0u, form_list.size()); |
| 589 STLDeleteElements(&form_list); |
| 590 } |
| 591 |
| 592 // Check that the keyring is unmodified. |
| 593 EXPECT_EQ(1u, mock_keyring_items.size()); |
| 594 if (mock_keyring_items.size() > 0) |
| 595 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome"); |
| 596 |
| 597 // Check that we haven't set the persistent preference. |
| 598 EXPECT_FALSE( |
| 599 profile_->GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId)); |
| 600 |
| 601 { |
| 602 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 603 backend.Init(); |
| 604 |
| 605 // Trigger the migration by looking something up. |
| 606 std::vector<PasswordForm*> form_list; |
| 607 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 608 NewRunnableMethod(&backend, |
| 609 &NativeBackendGnome::GetAutofillableLogins, |
| 610 &form_list)); |
| 611 |
| 612 RunBothThreads(); |
| 613 |
| 614 // Quick check that we got something back. |
| 615 EXPECT_EQ(1u, form_list.size()); |
| 616 STLDeleteElements(&form_list); |
| 617 } |
| 618 |
| 619 EXPECT_EQ(2u, mock_keyring_items.size()); |
| 620 if (mock_keyring_items.size() > 0) |
| 621 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome"); |
| 622 if (mock_keyring_items.size() > 1) |
| 623 CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42"); |
| 624 |
| 625 // Check that we have set the persistent preference. |
| 626 EXPECT_TRUE( |
| 627 profile_->GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId)); |
| 628 } |
| 629 |
| 630 TEST_F(NativeBackendGnomeTest, MigrateToMultipleProfiles) { |
| 631 // Reject attempts to migrate so we can populate the store. |
| 632 mock_keyring_reject_local_ids = true; |
| 633 |
| 634 { |
| 635 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 636 backend.Init(); |
| 637 |
| 638 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 639 NewRunnableMethod(&backend, |
| 640 &NativeBackendGnome::AddLogin, |
| 641 form_google_)); |
| 642 |
| 643 RunBothThreads(); |
| 644 } |
| 645 |
| 646 EXPECT_EQ(1u, mock_keyring_items.size()); |
| 647 if (mock_keyring_items.size() > 0) |
| 648 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome"); |
| 649 |
| 650 // Now allow the migration. |
| 651 mock_keyring_reject_local_ids = false; |
| 652 |
| 653 { |
| 654 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 655 backend.Init(); |
| 656 |
| 657 // Trigger the migration by looking something up. |
| 658 std::vector<PasswordForm*> form_list; |
| 659 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 660 NewRunnableMethod(&backend, |
| 661 &NativeBackendGnome::GetAutofillableLogins, |
| 662 &form_list)); |
| 663 |
| 664 RunBothThreads(); |
| 665 |
| 666 // Quick check that we got something back. |
| 667 EXPECT_EQ(1u, form_list.size()); |
| 668 STLDeleteElements(&form_list); |
| 669 } |
| 670 |
| 671 EXPECT_EQ(2u, mock_keyring_items.size()); |
| 672 if (mock_keyring_items.size() > 0) |
| 673 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome"); |
| 674 if (mock_keyring_items.size() > 1) |
| 675 CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42"); |
| 676 |
| 677 // Check that we have set the persistent preference. |
| 678 EXPECT_TRUE( |
| 679 profile_->GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId)); |
| 680 |
| 681 // Normally we'd actually have a different profile. But in the test just reset |
| 682 // the profile's persistent pref; we pass in the local profile id anyway. |
| 683 profile_->GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, false); |
| 684 |
| 685 { |
| 686 NativeBackendGnome backend(24, profile_->GetPrefs()); |
| 687 backend.Init(); |
| 688 |
| 689 // Trigger the migration by looking something up. |
| 690 std::vector<PasswordForm*> form_list; |
| 691 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 692 NewRunnableMethod(&backend, |
| 693 &NativeBackendGnome::GetAutofillableLogins, |
| 694 &form_list)); |
| 695 |
| 696 RunBothThreads(); |
| 697 |
| 698 // Quick check that we got something back. |
| 699 EXPECT_EQ(1u, form_list.size()); |
| 700 STLDeleteElements(&form_list); |
| 701 } |
| 702 |
| 703 EXPECT_EQ(3u, mock_keyring_items.size()); |
| 704 if (mock_keyring_items.size() > 0) |
| 705 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome"); |
| 706 if (mock_keyring_items.size() > 1) |
| 707 CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42"); |
| 708 if (mock_keyring_items.size() > 2) |
| 709 CheckMockKeyringItem(&mock_keyring_items[2], form_google_, "chrome-24"); |
| 710 } |
| 711 |
| 712 TEST_F(NativeBackendGnomeTest, NoMigrationWithPrefSet) { |
| 713 // Reject attempts to migrate so we can populate the store. |
| 714 mock_keyring_reject_local_ids = true; |
| 715 |
| 716 { |
| 717 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 718 backend.Init(); |
| 719 |
| 720 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 721 NewRunnableMethod(&backend, |
| 722 &NativeBackendGnome::AddLogin, |
| 723 form_google_)); |
| 724 |
| 725 RunBothThreads(); |
| 726 } |
| 727 |
| 728 EXPECT_EQ(1u, mock_keyring_items.size()); |
| 729 if (mock_keyring_items.size() > 0) |
| 730 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome"); |
| 731 |
| 732 // Now allow migration, but also pretend that the it has already taken place. |
| 733 mock_keyring_reject_local_ids = false; |
| 734 profile_->GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); |
| 735 |
| 736 { |
| 737 NativeBackendGnome backend(42, profile_->GetPrefs()); |
| 738 backend.Init(); |
| 739 |
| 740 // Trigger the migration by adding a new login. |
| 741 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 742 NewRunnableMethod(&backend, |
| 743 &NativeBackendGnome::AddLogin, |
| 744 form_isc_)); |
| 745 |
| 746 // Look up all logins; we expect only the one we added. |
| 747 std::vector<PasswordForm*> form_list; |
| 748 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 749 NewRunnableMethod(&backend, |
| 750 &NativeBackendGnome::GetAutofillableLogins, |
| 751 &form_list)); |
| 752 |
| 753 RunBothThreads(); |
| 754 |
| 755 // Quick check that we got the right thing back. |
| 756 EXPECT_EQ(1u, form_list.size()); |
| 757 if (form_list.size() > 0) |
| 758 EXPECT_EQ(form_isc_.signon_realm, form_list[0]->signon_realm); |
| 759 STLDeleteElements(&form_list); |
| 760 } |
| 761 |
| 762 EXPECT_EQ(2u, mock_keyring_items.size()); |
| 763 if (mock_keyring_items.size() > 0) |
| 764 CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome"); |
| 765 if (mock_keyring_items.size() > 1) |
| 766 CheckMockKeyringItem(&mock_keyring_items[1], form_isc_, "chrome-42"); |
| 767 } |
OLD | NEW |