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.
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/browser/password_manager/psl_matching_helper.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "components/autofill/core/common/password_form.h"
19 #include "content/public/test/test_browser_thread.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 using autofill::PasswordForm
;
23 using base::UTF8ToUTF16
;
24 using base::UTF16ToUTF8
;
25 using content::BrowserThread
;
29 // What follows is a very simple implementation of the subset of the GNOME
30 // Keyring API that we actually use. It gets substituted for the real one by
31 // MockGnomeKeyringLoader, which hooks into the facility normally used to load
32 // the GNOME Keyring library at runtime to avoid a static dependency on it.
34 struct MockKeyringItem
{
36 MockKeyringItem(const char* keyring
,
37 const std::string
& display_name
,
38 const std::string
& password
)
39 : keyring(keyring
? keyring
: "login"),
40 display_name(display_name
),
43 struct ItemAttribute
{
44 ItemAttribute() : type(UINT32
), value_uint32(0) {}
45 explicit ItemAttribute(uint32_t value
)
46 : type(UINT32
), value_uint32(value
) {}
47 explicit ItemAttribute(const std::string
& value
)
48 : type(STRING
), value_string(value
) {}
50 bool Equals(const ItemAttribute
& x
) const {
51 if (type
!= x
.type
) return false;
52 return (type
== STRING
) ? value_string
== x
.value_string
53 : value_uint32
== x
.value_uint32
;
56 enum Type
{ UINT32
, STRING
} type
;
57 uint32_t value_uint32
;
58 std::string value_string
;
61 typedef std::map
<std::string
, ItemAttribute
> attribute_map
;
62 typedef std::vector
<std::pair
<std::string
, ItemAttribute
> > attribute_query
;
64 bool Matches(const attribute_query
& query
) const {
65 // The real GNOME Keyring doesn't match empty queries.
66 if (query
.empty()) return false;
67 for (size_t i
= 0; i
< query
.size(); ++i
) {
68 attribute_map::const_iterator match
= attributes
.find(query
[i
].first
);
69 if (match
== attributes
.end()) return false;
70 if (!match
->second
.Equals(query
[i
].second
)) return false;
76 std::string display_name
;
79 attribute_map attributes
;
82 // The list of all keyring items we have stored.
83 std::vector
<MockKeyringItem
> mock_keyring_items
;
84 bool mock_keyring_reject_local_ids
= false;
86 bool IsStringAttribute(const GnomeKeyringPasswordSchema
* schema
,
87 const std::string
& name
) {
88 for (size_t i
= 0; schema
->attributes
[i
].name
; ++i
)
89 if (name
== schema
->attributes
[i
].name
)
90 return schema
->attributes
[i
].type
== GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
;
91 NOTREACHED() << "Requested type of nonexistent attribute";
95 gboolean
mock_gnome_keyring_is_available() {
99 gpointer
mock_gnome_keyring_store_password(
100 const GnomeKeyringPasswordSchema
* schema
,
101 const gchar
* keyring
,
102 const gchar
* display_name
,
103 const gchar
* password
,
104 GnomeKeyringOperationDoneCallback callback
,
106 GDestroyNotify destroy_data
,
108 mock_keyring_items
.push_back(
109 MockKeyringItem(keyring
, display_name
, password
));
110 MockKeyringItem
* item
= &mock_keyring_items
.back();
111 const std::string keyring_desc
=
112 keyring
? base::StringPrintf("keyring %s", keyring
)
113 : std::string("default keyring");
114 VLOG(1) << "Adding item with origin " << display_name
115 << " to " << keyring_desc
;
117 va_start(ap
, destroy_data
);
119 while ((name
= va_arg(ap
, gchar
*))) {
120 if (IsStringAttribute(schema
, name
)) {
121 item
->attributes
[name
] =
122 MockKeyringItem::ItemAttribute(va_arg(ap
, gchar
*));
123 VLOG(1) << "Adding item attribute " << name
124 << ", value '" << item
->attributes
[name
].value_string
<< "'";
126 item
->attributes
[name
] =
127 MockKeyringItem::ItemAttribute(va_arg(ap
, uint32_t));
128 VLOG(1) << "Adding item attribute " << name
129 << ", value " << item
->attributes
[name
].value_uint32
;
133 // As a hack to ease testing migration, make it possible to reject the new
134 // format for the app string. This way we can add them easily to migrate.
135 if (mock_keyring_reject_local_ids
) {
136 MockKeyringItem::attribute_map::iterator it
=
137 item
->attributes
.find("application");
138 if (it
!= item
->attributes
.end() &&
139 it
->second
.type
== MockKeyringItem::ItemAttribute::STRING
&&
140 base::StringPiece(it
->second
.value_string
).starts_with("chrome-")) {
141 mock_keyring_items
.pop_back();
142 // GnomeKeyringResult, data
143 callback(GNOME_KEYRING_RESULT_IO_ERROR
, data
);
147 // GnomeKeyringResult, data
148 callback(GNOME_KEYRING_RESULT_OK
, data
);
152 gpointer
mock_gnome_keyring_delete_password(
153 const GnomeKeyringPasswordSchema
* schema
,
154 GnomeKeyringOperationDoneCallback callback
,
156 GDestroyNotify destroy_data
,
158 MockKeyringItem::attribute_query query
;
160 va_start(ap
, destroy_data
);
162 while ((name
= va_arg(ap
, gchar
*))) {
163 if (IsStringAttribute(schema
, name
)) {
164 query
.push_back(make_pair(std::string(name
),
165 MockKeyringItem::ItemAttribute(va_arg(ap
, gchar
*))));
166 VLOG(1) << "Querying with item attribute " << name
167 << ", value '" << query
.back().second
.value_string
<< "'";
169 query
.push_back(make_pair(std::string(name
),
170 MockKeyringItem::ItemAttribute(va_arg(ap
, uint32_t))));
171 VLOG(1) << "Querying with item attribute " << name
172 << ", value " << query
.back().second
.value_uint32
;
176 bool deleted
= false;
177 for (size_t i
= mock_keyring_items
.size(); i
> 0; --i
) {
178 const MockKeyringItem
* item
= &mock_keyring_items
[i
- 1];
179 if (item
->Matches(query
)) {
180 VLOG(1) << "Deleting item with origin " << item
->display_name
;
181 mock_keyring_items
.erase(mock_keyring_items
.begin() + (i
- 1));
185 // GnomeKeyringResult, data
186 callback(deleted
? GNOME_KEYRING_RESULT_OK
187 : GNOME_KEYRING_RESULT_NO_MATCH
, data
);
191 gpointer
mock_gnome_keyring_find_items(
192 GnomeKeyringItemType type
,
193 GnomeKeyringAttributeList
* attributes
,
194 GnomeKeyringOperationGetListCallback callback
,
196 GDestroyNotify destroy_data
) {
197 MockKeyringItem::attribute_query query
;
198 for (size_t i
= 0; i
< attributes
->len
; ++i
) {
199 GnomeKeyringAttribute attribute
=
200 g_array_index(attributes
, GnomeKeyringAttribute
, i
);
201 if (attribute
.type
== GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
) {
203 make_pair(std::string(attribute
.name
),
204 MockKeyringItem::ItemAttribute(attribute
.value
.string
)));
205 VLOG(1) << "Querying with item attribute " << attribute
.name
206 << ", value '" << query
.back().second
.value_string
<< "'";
209 make_pair(std::string(attribute
.name
),
210 MockKeyringItem::ItemAttribute(attribute
.value
.integer
)));
211 VLOG(1) << "Querying with item attribute " << attribute
.name
<< ", value "
212 << query
.back().second
.value_uint32
;
215 // Find matches and add them to a list of results.
216 GList
* results
= NULL
;
217 for (size_t i
= 0; i
< mock_keyring_items
.size(); ++i
) {
218 const MockKeyringItem
* item
= &mock_keyring_items
[i
];
219 if (item
->Matches(query
)) {
220 GnomeKeyringFound
* found
= new GnomeKeyringFound
;
221 found
->keyring
= strdup(item
->keyring
.c_str());
223 found
->attributes
= gnome_keyring_attribute_list_new();
224 for (MockKeyringItem::attribute_map::const_iterator it
=
225 item
->attributes
.begin();
226 it
!= item
->attributes
.end();
228 if (it
->second
.type
== MockKeyringItem::ItemAttribute::STRING
) {
229 gnome_keyring_attribute_list_append_string(
230 found
->attributes
, it
->first
.c_str(),
231 it
->second
.value_string
.c_str());
233 gnome_keyring_attribute_list_append_uint32(
234 found
->attributes
, it
->first
.c_str(),
235 it
->second
.value_uint32
);
238 found
->secret
= strdup(item
->password
.c_str());
239 results
= g_list_prepend(results
, found
);
242 // GnomeKeyringResult, GList*, data
243 callback(results
? GNOME_KEYRING_RESULT_OK
244 : GNOME_KEYRING_RESULT_NO_MATCH
, results
, data
);
245 // Now free the list of results.
246 GList
* element
= g_list_first(results
);
248 GnomeKeyringFound
* found
= static_cast<GnomeKeyringFound
*>(element
->data
);
249 free(found
->keyring
);
250 gnome_keyring_attribute_list_free(found
->attributes
);
253 element
= g_list_next(element
);
255 g_list_free(results
);
259 const gchar
* mock_gnome_keyring_result_to_message(GnomeKeyringResult res
) {
260 return "mock keyring simulating failure";
263 // Inherit to get access to protected fields.
264 class MockGnomeKeyringLoader
: public GnomeKeyringLoader
{
266 static bool LoadMockGnomeKeyring() {
267 if (!LoadGnomeKeyring())
269 #define GNOME_KEYRING_ASSIGN_POINTER(name) \
270 gnome_keyring_##name = &mock_gnome_keyring_##name;
271 GNOME_KEYRING_FOR_EACH_MOCKED_FUNC(GNOME_KEYRING_ASSIGN_POINTER
)
272 #undef GNOME_KEYRING_ASSIGN_POINTER
273 keyring_loaded
= true;
274 // Reset the state of the mock library.
275 mock_keyring_items
.clear();
276 mock_keyring_reject_local_ids
= false;
281 } // anonymous namespace
283 class NativeBackendGnomeTest
: public testing::Test
{
285 NativeBackendGnomeTest()
286 : ui_thread_(BrowserThread::UI
, &message_loop_
),
287 db_thread_(BrowserThread::DB
) {
290 virtual void SetUp() {
291 ASSERT_TRUE(db_thread_
.Start());
293 ASSERT_TRUE(MockGnomeKeyringLoader::LoadMockGnomeKeyring());
295 form_google_
.origin
= GURL("http://www.google.com/");
296 form_google_
.action
= GURL("http://www.google.com/login");
297 form_google_
.username_element
= UTF8ToUTF16("user");
298 form_google_
.username_value
= UTF8ToUTF16("joeschmoe");
299 form_google_
.password_element
= UTF8ToUTF16("pass");
300 form_google_
.password_value
= UTF8ToUTF16("seekrit");
301 form_google_
.submit_element
= UTF8ToUTF16("submit");
302 form_google_
.signon_realm
= "http://www.google.com/";
304 form_facebook_
.origin
= GURL("http://www.facebook.com/");
305 form_facebook_
.action
= GURL("http://www.facebook.com/login");
306 form_facebook_
.username_element
= UTF8ToUTF16("user");
307 form_facebook_
.username_value
= UTF8ToUTF16("a");
308 form_facebook_
.password_element
= UTF8ToUTF16("password");
309 form_facebook_
.password_value
= UTF8ToUTF16("b");
310 form_facebook_
.submit_element
= UTF8ToUTF16("submit");
311 form_facebook_
.signon_realm
= "http://www.facebook.com/";
313 form_isc_
.origin
= GURL("http://www.isc.org/");
314 form_isc_
.action
= GURL("http://www.isc.org/auth");
315 form_isc_
.username_element
= UTF8ToUTF16("id");
316 form_isc_
.username_value
= UTF8ToUTF16("janedoe");
317 form_isc_
.password_element
= UTF8ToUTF16("passwd");
318 form_isc_
.password_value
= UTF8ToUTF16("ihazabukkit");
319 form_isc_
.submit_element
= UTF8ToUTF16("login");
320 form_isc_
.signon_realm
= "http://www.isc.org/";
323 virtual void TearDown() {
324 base::MessageLoop::current()->PostTask(FROM_HERE
,
325 base::MessageLoop::QuitClosure());
326 base::MessageLoop::current()->Run();
330 void RunBothThreads() {
331 // First we post a message to the DB thread that will run after all other
332 // messages that have been posted to the DB thread (we don't expect more
333 // to be posted), which posts a message to the UI thread to quit the loop.
334 // That way we can run both loops and be sure that the UI thread loop will
335 // quit so we can get on with the rest of the test.
336 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
337 base::Bind(&PostQuitTask
, &message_loop_
));
338 base::MessageLoop::current()->Run();
341 static void PostQuitTask(base::MessageLoop
* loop
) {
342 loop
->PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
345 void CheckUint32Attribute(const MockKeyringItem
* item
,
346 const std::string
& attribute
,
348 MockKeyringItem::attribute_map::const_iterator it
=
349 item
->attributes
.find(attribute
);
350 EXPECT_NE(item
->attributes
.end(), it
);
351 if (it
!= item
->attributes
.end()) {
352 EXPECT_EQ(MockKeyringItem::ItemAttribute::UINT32
, it
->second
.type
);
353 EXPECT_EQ(value
, it
->second
.value_uint32
);
357 void CheckStringAttribute(const MockKeyringItem
* item
,
358 const std::string
& attribute
,
359 const std::string
& value
) {
360 MockKeyringItem::attribute_map::const_iterator it
=
361 item
->attributes
.find(attribute
);
362 EXPECT_NE(item
->attributes
.end(), it
);
363 if (it
!= item
->attributes
.end()) {
364 EXPECT_EQ(MockKeyringItem::ItemAttribute::STRING
, it
->second
.type
);
365 EXPECT_EQ(value
, it
->second
.value_string
);
369 void CheckMockKeyringItem(const MockKeyringItem
* item
,
370 const PasswordForm
& form
,
371 const std::string
& app_string
) {
372 // We always add items to the login keyring.
373 EXPECT_EQ("login", item
->keyring
);
374 EXPECT_EQ(form
.origin
.spec(), item
->display_name
);
375 EXPECT_EQ(UTF16ToUTF8(form
.password_value
), item
->password
);
376 EXPECT_EQ(13u, item
->attributes
.size());
377 CheckStringAttribute(item
, "origin_url", form
.origin
.spec());
378 CheckStringAttribute(item
, "action_url", form
.action
.spec());
379 CheckStringAttribute(item
, "username_element",
380 UTF16ToUTF8(form
.username_element
));
381 CheckStringAttribute(item
, "username_value",
382 UTF16ToUTF8(form
.username_value
));
383 CheckStringAttribute(item
, "password_element",
384 UTF16ToUTF8(form
.password_element
));
385 CheckStringAttribute(item
, "submit_element",
386 UTF16ToUTF8(form
.submit_element
));
387 CheckStringAttribute(item
, "signon_realm", form
.signon_realm
);
388 CheckUint32Attribute(item
, "ssl_valid", form
.ssl_valid
);
389 CheckUint32Attribute(item
, "preferred", form
.preferred
);
390 // We don't check the date created. It varies.
391 CheckUint32Attribute(item
, "blacklisted_by_user", form
.blacklisted_by_user
);
392 CheckUint32Attribute(item
, "scheme", form
.scheme
);
393 CheckStringAttribute(item
, "application", app_string
);
396 // Checks (using EXPECT_* macros), that |credentials| are accessible for
397 // filling in for a page with |origin| iff
398 // |should_credential_be_available_to_url| is true.
399 void CheckCredentialAvailability(const PasswordForm
& credentials
,
400 const std::string
& url
,
401 bool should_credential_be_available_to_url
) {
402 PSLMatchingHelper helper
;
403 ASSERT_TRUE(helper
.IsMatchingEnabled())
404 << "PSL matching needs to be enabled.";
406 NativeBackendGnome
backend(321, profile_
.GetPrefs());
409 BrowserThread::PostTask(
412 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
413 base::Unretained(&backend
),
416 PasswordForm target_form
;
417 target_form
.origin
= GURL(url
);
418 target_form
.signon_realm
= url
;
419 std::vector
<PasswordForm
*> form_list
;
420 BrowserThread::PostTask(
423 base::Bind(base::IgnoreResult(&NativeBackendGnome::GetLogins
),
424 base::Unretained(&backend
),
430 EXPECT_EQ(1u, mock_keyring_items
.size());
431 if (mock_keyring_items
.size() > 0)
432 CheckMockKeyringItem(&mock_keyring_items
[0], credentials
, "chrome-321");
434 if (should_credential_be_available_to_url
)
435 EXPECT_EQ(1u, form_list
.size());
437 EXPECT_EQ(0u, form_list
.size());
438 STLDeleteElements(&form_list
);
441 base::MessageLoopForUI message_loop_
;
442 content::TestBrowserThread ui_thread_
;
443 content::TestBrowserThread db_thread_
;
445 TestingProfile profile_
;
447 // Provide some test forms to avoid having to set them up in each test.
448 PasswordForm form_google_
;
449 PasswordForm form_facebook_
;
450 PasswordForm form_isc_
;
453 TEST_F(NativeBackendGnomeTest
, BasicAddLogin
) {
454 // Pretend that the migration has already taken place.
455 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
457 NativeBackendGnome
backend(42, profile_
.GetPrefs());
460 BrowserThread::PostTask(
461 BrowserThread::DB
, FROM_HERE
,
462 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
463 base::Unretained(&backend
), form_google_
));
467 EXPECT_EQ(1u, mock_keyring_items
.size());
468 if (mock_keyring_items
.size() > 0)
469 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
472 TEST_F(NativeBackendGnomeTest
, BasicListLogins
) {
473 // Pretend that the migration has already taken place.
474 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
476 NativeBackendGnome
backend(42, profile_
.GetPrefs());
479 BrowserThread::PostTask(
480 BrowserThread::DB
, FROM_HERE
,
481 base::Bind(base::IgnoreResult( &NativeBackendGnome::AddLogin
),
482 base::Unretained(&backend
), form_google_
));
484 std::vector
<PasswordForm
*> form_list
;
485 BrowserThread::PostTask(
486 BrowserThread::DB
, FROM_HERE
,
488 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
489 base::Unretained(&backend
), &form_list
));
493 // Quick check that we got something back.
494 EXPECT_EQ(1u, form_list
.size());
495 STLDeleteElements(&form_list
);
497 EXPECT_EQ(1u, mock_keyring_items
.size());
498 if (mock_keyring_items
.size() > 0)
499 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
502 // Save a password for www.facebook.com and see it suggested for m.facebook.com.
503 TEST_F(NativeBackendGnomeTest
, PSLMatchingPositive
) {
504 CheckCredentialAvailability(form_facebook_
,
505 "http://m.facebook.com/",
506 /*should_credential_be_available_to_url=*/true);
509 // Save a password for www.facebook.com and see it not suggested for
511 TEST_F(NativeBackendGnomeTest
, PSLMatchingNegativeDomainMismatch
) {
512 CheckCredentialAvailability(form_facebook_
,
513 "http://m-facebook.com/",
514 /*should_credential_be_available_to_url=*/false);
517 // Test PSL matching is off for domains excluded from it.
518 TEST_F(NativeBackendGnomeTest
, PSLMatchingDisabledDomains
) {
519 CheckCredentialAvailability(form_google_
,
520 "http://one.google.com/",
521 /*should_credential_be_available_to_url=*/false);
524 TEST_F(NativeBackendGnomeTest
, BasicRemoveLogin
) {
525 // Pretend that the migration has already taken place.
526 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
528 NativeBackendGnome
backend(42, profile_
.GetPrefs());
531 BrowserThread::PostTask(
532 BrowserThread::DB
, FROM_HERE
,
533 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
534 base::Unretained(&backend
), form_google_
));
538 EXPECT_EQ(1u, mock_keyring_items
.size());
539 if (mock_keyring_items
.size() > 0)
540 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
542 BrowserThread::PostTask(
543 BrowserThread::DB
, FROM_HERE
,
544 base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin
),
545 base::Unretained(&backend
), form_google_
));
549 EXPECT_EQ(0u, mock_keyring_items
.size());
552 TEST_F(NativeBackendGnomeTest
, RemoveNonexistentLogin
) {
553 // Pretend that the migration has already taken place.
554 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
556 NativeBackendGnome
backend(42, profile_
.GetPrefs());
559 // First add an unrelated login.
560 BrowserThread::PostTask(
561 BrowserThread::DB
, FROM_HERE
,
562 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
563 base::Unretained(&backend
), form_google_
));
567 EXPECT_EQ(1u, mock_keyring_items
.size());
568 if (mock_keyring_items
.size() > 0)
569 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
571 // Attempt to remove a login that doesn't exist.
572 BrowserThread::PostTask(
573 BrowserThread::DB
, FROM_HERE
,
574 base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin
),
575 base::Unretained(&backend
), form_isc_
));
577 // Make sure we can still get the first form back.
578 std::vector
<PasswordForm
*> form_list
;
579 BrowserThread::PostTask(
580 BrowserThread::DB
, FROM_HERE
,
582 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
583 base::Unretained(&backend
), &form_list
));
587 // Quick check that we got something back.
588 EXPECT_EQ(1u, form_list
.size());
589 STLDeleteElements(&form_list
);
591 EXPECT_EQ(1u, mock_keyring_items
.size());
592 if (mock_keyring_items
.size() > 0)
593 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
596 TEST_F(NativeBackendGnomeTest
, AddDuplicateLogin
) {
597 // Pretend that the migration has already taken place.
598 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
600 NativeBackendGnome
backend(42, profile_
.GetPrefs());
603 BrowserThread::PostTask(
604 BrowserThread::DB
, FROM_HERE
,
605 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
606 base::Unretained(&backend
), form_google_
));
607 BrowserThread::PostTask(
608 BrowserThread::DB
, FROM_HERE
,
609 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
610 base::Unretained(&backend
), form_google_
));
614 EXPECT_EQ(1u, mock_keyring_items
.size());
615 if (mock_keyring_items
.size() > 0)
616 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
619 TEST_F(NativeBackendGnomeTest
, ListLoginsAppends
) {
620 // Pretend that the migration has already taken place.
621 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
623 NativeBackendGnome
backend(42, profile_
.GetPrefs());
626 BrowserThread::PostTask(
627 BrowserThread::DB
, FROM_HERE
,
628 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
629 base::Unretained(&backend
), form_google_
));
631 // Send the same request twice with the same list both times.
632 std::vector
<PasswordForm
*> form_list
;
633 BrowserThread::PostTask(
634 BrowserThread::DB
, FROM_HERE
,
636 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
637 base::Unretained(&backend
), &form_list
));
638 BrowserThread::PostTask(
639 BrowserThread::DB
, FROM_HERE
,
641 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
642 base::Unretained(&backend
), &form_list
));
646 // Quick check that we got two results back.
647 EXPECT_EQ(2u, form_list
.size());
648 STLDeleteElements(&form_list
);
650 EXPECT_EQ(1u, mock_keyring_items
.size());
651 if (mock_keyring_items
.size() > 0)
652 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
655 // TODO(mdm): add more basic (i.e. non-migration) tests here at some point.
657 TEST_F(NativeBackendGnomeTest
, DISABLED_MigrateOneLogin
) {
658 // Reject attempts to migrate so we can populate the store.
659 mock_keyring_reject_local_ids
= true;
662 NativeBackendGnome
backend(42, profile_
.GetPrefs());
665 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
666 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
667 base::Unretained(&backend
), form_google_
));
669 // Make sure we can get the form back even when migration is failing.
670 std::vector
<PasswordForm
*> form_list
;
671 BrowserThread::PostTask(
672 BrowserThread::DB
, FROM_HERE
,
674 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
675 base::Unretained(&backend
), &form_list
));
679 // Quick check that we got something back.
680 EXPECT_EQ(1u, form_list
.size());
681 STLDeleteElements(&form_list
);
684 EXPECT_EQ(1u, mock_keyring_items
.size());
685 if (mock_keyring_items
.size() > 0)
686 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
688 // Now allow the migration.
689 mock_keyring_reject_local_ids
= false;
692 NativeBackendGnome
backend(42, profile_
.GetPrefs());
695 // This should not trigger migration because there will be no results.
696 std::vector
<PasswordForm
*> form_list
;
697 BrowserThread::PostTask(
698 BrowserThread::DB
, FROM_HERE
,
699 base::Bind(base::IgnoreResult(&NativeBackendGnome::GetBlacklistLogins
),
700 base::Unretained(&backend
), &form_list
));
704 // Check that we got nothing back.
705 EXPECT_EQ(0u, form_list
.size());
706 STLDeleteElements(&form_list
);
709 // Check that the keyring is unmodified.
710 EXPECT_EQ(1u, mock_keyring_items
.size());
711 if (mock_keyring_items
.size() > 0)
712 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
714 // Check that we haven't set the persistent preference.
716 profile_
.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId
));
719 NativeBackendGnome
backend(42, profile_
.GetPrefs());
722 // Trigger the migration by looking something up.
723 std::vector
<PasswordForm
*> form_list
;
724 BrowserThread::PostTask(
725 BrowserThread::DB
, FROM_HERE
,
727 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
728 base::Unretained(&backend
), &form_list
));
732 // Quick check that we got something back.
733 EXPECT_EQ(1u, form_list
.size());
734 STLDeleteElements(&form_list
);
737 EXPECT_EQ(2u, mock_keyring_items
.size());
738 if (mock_keyring_items
.size() > 0)
739 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
740 if (mock_keyring_items
.size() > 1)
741 CheckMockKeyringItem(&mock_keyring_items
[1], form_google_
, "chrome-42");
743 // Check that we have set the persistent preference.
745 profile_
.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId
));
748 TEST_F(NativeBackendGnomeTest
, DISABLED_MigrateToMultipleProfiles
) {
749 // Reject attempts to migrate so we can populate the store.
750 mock_keyring_reject_local_ids
= true;
753 NativeBackendGnome
backend(42, profile_
.GetPrefs());
756 BrowserThread::PostTask(
757 BrowserThread::DB
, FROM_HERE
,
758 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
759 base::Unretained(&backend
), form_google_
));
764 EXPECT_EQ(1u, mock_keyring_items
.size());
765 if (mock_keyring_items
.size() > 0)
766 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
768 // Now allow the migration.
769 mock_keyring_reject_local_ids
= false;
772 NativeBackendGnome
backend(42, profile_
.GetPrefs());
775 // Trigger the migration by looking something up.
776 std::vector
<PasswordForm
*> form_list
;
777 BrowserThread::PostTask(
778 BrowserThread::DB
, FROM_HERE
,
780 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
781 base::Unretained(&backend
), &form_list
));
785 // Quick check that we got something back.
786 EXPECT_EQ(1u, form_list
.size());
787 STLDeleteElements(&form_list
);
790 EXPECT_EQ(2u, mock_keyring_items
.size());
791 if (mock_keyring_items
.size() > 0)
792 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
793 if (mock_keyring_items
.size() > 1)
794 CheckMockKeyringItem(&mock_keyring_items
[1], form_google_
, "chrome-42");
796 // Check that we have set the persistent preference.
798 profile_
.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId
));
800 // Normally we'd actually have a different profile. But in the test just reset
801 // the profile's persistent pref; we pass in the local profile id anyway.
802 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, false);
805 NativeBackendGnome
backend(24, profile_
.GetPrefs());
808 // Trigger the migration by looking something up.
809 std::vector
<PasswordForm
*> form_list
;
810 BrowserThread::PostTask(
811 BrowserThread::DB
, FROM_HERE
,
813 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
814 base::Unretained(&backend
), &form_list
));
818 // Quick check that we got something back.
819 EXPECT_EQ(1u, form_list
.size());
820 STLDeleteElements(&form_list
);
823 EXPECT_EQ(3u, mock_keyring_items
.size());
824 if (mock_keyring_items
.size() > 0)
825 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
826 if (mock_keyring_items
.size() > 1)
827 CheckMockKeyringItem(&mock_keyring_items
[1], form_google_
, "chrome-42");
828 if (mock_keyring_items
.size() > 2)
829 CheckMockKeyringItem(&mock_keyring_items
[2], form_google_
, "chrome-24");
832 TEST_F(NativeBackendGnomeTest
, DISABLED_NoMigrationWithPrefSet
) {
833 // Reject attempts to migrate so we can populate the store.
834 mock_keyring_reject_local_ids
= true;
837 NativeBackendGnome
backend(42, profile_
.GetPrefs());
840 BrowserThread::PostTask(
841 BrowserThread::DB
, FROM_HERE
,
842 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
843 base::Unretained(&backend
), form_google_
));
848 EXPECT_EQ(1u, mock_keyring_items
.size());
849 if (mock_keyring_items
.size() > 0)
850 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
852 // Now allow migration, but also pretend that the it has already taken place.
853 mock_keyring_reject_local_ids
= false;
854 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, true);
857 NativeBackendGnome
backend(42, profile_
.GetPrefs());
860 // Trigger the migration by adding a new login.
861 BrowserThread::PostTask(
862 BrowserThread::DB
, FROM_HERE
,
863 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
864 base::Unretained(&backend
), form_isc_
));
866 // Look up all logins; we expect only the one we added.
867 std::vector
<PasswordForm
*> form_list
;
868 BrowserThread::PostTask(
869 BrowserThread::DB
, FROM_HERE
,
871 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
872 base::Unretained(&backend
), &form_list
));
876 // Quick check that we got the right thing back.
877 EXPECT_EQ(1u, form_list
.size());
878 if (form_list
.size() > 0)
879 EXPECT_EQ(form_isc_
.signon_realm
, form_list
[0]->signon_realm
);
880 STLDeleteElements(&form_list
);
883 EXPECT_EQ(2u, mock_keyring_items
.size());
884 if (mock_keyring_items
.size() > 0)
885 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
886 if (mock_keyring_items
.size() > 1)
887 CheckMockKeyringItem(&mock_keyring_items
[1], form_isc_
, "chrome-42");
890 TEST_F(NativeBackendGnomeTest
, DISABLED_DeleteMigratedPasswordIsIsolated
) {
891 // Reject attempts to migrate so we can populate the store.
892 mock_keyring_reject_local_ids
= true;
895 NativeBackendGnome
backend(42, profile_
.GetPrefs());
898 BrowserThread::PostTask(
899 BrowserThread::DB
, FROM_HERE
,
900 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
901 base::Unretained(&backend
), form_google_
));
906 EXPECT_EQ(1u, mock_keyring_items
.size());
907 if (mock_keyring_items
.size() > 0)
908 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
910 // Now allow the migration.
911 mock_keyring_reject_local_ids
= false;
914 NativeBackendGnome
backend(42, profile_
.GetPrefs());
917 // Trigger the migration by looking something up.
918 std::vector
<PasswordForm
*> form_list
;
919 BrowserThread::PostTask(
920 BrowserThread::DB
, FROM_HERE
,
922 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
923 base::Unretained(&backend
), &form_list
));
927 // Quick check that we got something back.
928 EXPECT_EQ(1u, form_list
.size());
929 STLDeleteElements(&form_list
);
932 EXPECT_EQ(2u, mock_keyring_items
.size());
933 if (mock_keyring_items
.size() > 0)
934 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
935 if (mock_keyring_items
.size() > 1)
936 CheckMockKeyringItem(&mock_keyring_items
[1], form_google_
, "chrome-42");
938 // Check that we have set the persistent preference.
940 profile_
.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId
));
942 // Normally we'd actually have a different profile. But in the test just reset
943 // the profile's persistent pref; we pass in the local profile id anyway.
944 profile_
.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId
, false);
947 NativeBackendGnome
backend(24, profile_
.GetPrefs());
950 // Trigger the migration by looking something up.
951 std::vector
<PasswordForm
*> form_list
;
952 BrowserThread::PostTask(
953 BrowserThread::DB
, FROM_HERE
,
955 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
956 base::Unretained(&backend
), &form_list
));
960 // Quick check that we got something back.
961 EXPECT_EQ(1u, form_list
.size());
962 STLDeleteElements(&form_list
);
964 // There should be three passwords now.
965 EXPECT_EQ(3u, mock_keyring_items
.size());
966 if (mock_keyring_items
.size() > 0)
967 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
968 if (mock_keyring_items
.size() > 1)
969 CheckMockKeyringItem(&mock_keyring_items
[1], form_google_
, "chrome-42");
970 if (mock_keyring_items
.size() > 2)
971 CheckMockKeyringItem(&mock_keyring_items
[2], form_google_
, "chrome-24");
973 // Now delete the password from this second profile.
974 BrowserThread::PostTask(
975 BrowserThread::DB
, FROM_HERE
,
976 base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin
),
977 base::Unretained(&backend
), form_google_
));
981 // The other two copies of the password in different profiles should remain.
982 EXPECT_EQ(2u, mock_keyring_items
.size());
983 if (mock_keyring_items
.size() > 0)
984 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome");
985 if (mock_keyring_items
.size() > 1)
986 CheckMockKeyringItem(&mock_keyring_items
[1], form_google_
, "chrome-42");