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