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/test/base/testing_profile.h"
16 #include "components/autofill/core/common/password_form.h"
17 #include "components/password_manager/core/browser/psl_matching_helper.h"
18 #include "components/password_manager/core/common/password_manager_pref_names.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 enum UpdateType
{ // Used in CheckPSLUpdate().
286 UPDATE_BY_UPDATELOGIN
,
290 NativeBackendGnomeTest()
291 : ui_thread_(BrowserThread::UI
, &message_loop_
),
292 db_thread_(BrowserThread::DB
) {
295 virtual void SetUp() {
296 ASSERT_TRUE(db_thread_
.Start());
298 ASSERT_TRUE(MockGnomeKeyringLoader::LoadMockGnomeKeyring());
300 form_google_
.origin
= GURL("http://www.google.com/");
301 form_google_
.action
= GURL("http://www.google.com/login");
302 form_google_
.username_element
= UTF8ToUTF16("user");
303 form_google_
.username_value
= UTF8ToUTF16("joeschmoe");
304 form_google_
.password_element
= UTF8ToUTF16("pass");
305 form_google_
.password_value
= UTF8ToUTF16("seekrit");
306 form_google_
.submit_element
= UTF8ToUTF16("submit");
307 form_google_
.signon_realm
= "http://www.google.com/";
308 form_google_
.type
= PasswordForm::TYPE_GENERATED
;
310 form_facebook_
.origin
= GURL("http://www.facebook.com/");
311 form_facebook_
.action
= GURL("http://www.facebook.com/login");
312 form_facebook_
.username_element
= UTF8ToUTF16("user");
313 form_facebook_
.username_value
= UTF8ToUTF16("a");
314 form_facebook_
.password_element
= UTF8ToUTF16("password");
315 form_facebook_
.password_value
= UTF8ToUTF16("b");
316 form_facebook_
.submit_element
= UTF8ToUTF16("submit");
317 form_facebook_
.signon_realm
= "http://www.facebook.com/";
319 form_isc_
.origin
= GURL("http://www.isc.org/");
320 form_isc_
.action
= GURL("http://www.isc.org/auth");
321 form_isc_
.username_element
= UTF8ToUTF16("id");
322 form_isc_
.username_value
= UTF8ToUTF16("janedoe");
323 form_isc_
.password_element
= UTF8ToUTF16("passwd");
324 form_isc_
.password_value
= UTF8ToUTF16("ihazabukkit");
325 form_isc_
.submit_element
= UTF8ToUTF16("login");
326 form_isc_
.signon_realm
= "http://www.isc.org/";
329 virtual void TearDown() {
330 base::MessageLoop::current()->PostTask(FROM_HERE
,
331 base::MessageLoop::QuitClosure());
332 base::MessageLoop::current()->Run();
336 void RunBothThreads() {
337 // First we post a message to the DB thread that will run after all other
338 // messages that have been posted to the DB thread (we don't expect more
339 // to be posted), which posts a message to the UI thread to quit the loop.
340 // That way we can run both loops and be sure that the UI thread loop will
341 // quit so we can get on with the rest of the test.
342 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
343 base::Bind(&PostQuitTask
, &message_loop_
));
344 base::MessageLoop::current()->Run();
347 static void PostQuitTask(base::MessageLoop
* loop
) {
348 loop
->PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
351 void CheckUint32Attribute(const MockKeyringItem
* item
,
352 const std::string
& attribute
,
354 MockKeyringItem::attribute_map::const_iterator it
=
355 item
->attributes
.find(attribute
);
356 EXPECT_NE(item
->attributes
.end(), it
);
357 if (it
!= item
->attributes
.end()) {
358 EXPECT_EQ(MockKeyringItem::ItemAttribute::UINT32
, it
->second
.type
);
359 EXPECT_EQ(value
, it
->second
.value_uint32
);
363 void CheckStringAttribute(const MockKeyringItem
* item
,
364 const std::string
& attribute
,
365 const std::string
& value
) {
366 MockKeyringItem::attribute_map::const_iterator it
=
367 item
->attributes
.find(attribute
);
368 EXPECT_NE(item
->attributes
.end(), it
);
369 if (it
!= item
->attributes
.end()) {
370 EXPECT_EQ(MockKeyringItem::ItemAttribute::STRING
, it
->second
.type
);
371 EXPECT_EQ(value
, it
->second
.value_string
);
375 void CheckMockKeyringItem(const MockKeyringItem
* item
,
376 const PasswordForm
& form
,
377 const std::string
& app_string
) {
378 // We always add items to the login keyring.
379 EXPECT_EQ("login", item
->keyring
);
380 EXPECT_EQ(form
.origin
.spec(), item
->display_name
);
381 EXPECT_EQ(UTF16ToUTF8(form
.password_value
), item
->password
);
382 EXPECT_EQ(15u, item
->attributes
.size());
383 CheckStringAttribute(item
, "origin_url", form
.origin
.spec());
384 CheckStringAttribute(item
, "action_url", form
.action
.spec());
385 CheckStringAttribute(item
, "username_element",
386 UTF16ToUTF8(form
.username_element
));
387 CheckStringAttribute(item
, "username_value",
388 UTF16ToUTF8(form
.username_value
));
389 CheckStringAttribute(item
, "password_element",
390 UTF16ToUTF8(form
.password_element
));
391 CheckStringAttribute(item
, "submit_element",
392 UTF16ToUTF8(form
.submit_element
));
393 CheckStringAttribute(item
, "signon_realm", form
.signon_realm
);
394 CheckUint32Attribute(item
, "ssl_valid", form
.ssl_valid
);
395 CheckUint32Attribute(item
, "preferred", form
.preferred
);
396 // We don't check the date created. It varies.
397 CheckUint32Attribute(item
, "blacklisted_by_user", form
.blacklisted_by_user
);
398 CheckUint32Attribute(item
, "type", form
.type
);
399 CheckUint32Attribute(item
, "times_used", form
.times_used
);
400 CheckUint32Attribute(item
, "scheme", form
.scheme
);
401 CheckStringAttribute(item
, "application", app_string
);
404 // Saves |credentials| and then gets login for origin and realm |url|. Returns
405 // true when something is found, and in such case copies the result to
406 // |result| when |result| is not NULL. (Note that there can be max. 1 result,
407 // derived from |credentials|.)
408 bool CheckCredentialAvailability(const PasswordForm
& credentials
,
410 PasswordForm
* result
) {
411 NativeBackendGnome
backend(321);
414 BrowserThread::PostTask(
417 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
418 base::Unretained(&backend
),
421 PasswordForm target_form
;
422 target_form
.origin
= url
;
423 target_form
.signon_realm
= url
.spec();
424 std::vector
<PasswordForm
*> form_list
;
425 BrowserThread::PostTask(
428 base::Bind(base::IgnoreResult(&NativeBackendGnome::GetLogins
),
429 base::Unretained(&backend
),
435 EXPECT_EQ(1u, mock_keyring_items
.size());
436 if (mock_keyring_items
.size() > 0)
437 CheckMockKeyringItem(&mock_keyring_items
[0], credentials
, "chrome-321");
439 if (form_list
.empty())
441 EXPECT_EQ(1u, form_list
.size());
443 *result
= *form_list
[0];
444 STLDeleteElements(&form_list
);
448 // Test that updating does not use PSL matching: Add a www.facebook.com
449 // password, then use PSL matching to get a copy of it for m.facebook.com, and
450 // add that copy as well. Now update the www.facebook.com password -- the
451 // m.facebook.com password should not get updated. Depending on the argument,
452 // the credential update is done via UpdateLogin or AddLogin.
453 void CheckPSLUpdate(UpdateType update_type
) {
454 password_manager::PSLMatchingHelper helper
;
455 ASSERT_TRUE(helper
.IsMatchingEnabled());
457 NativeBackendGnome
backend(321);
460 // Add |form_facebook_| to saved logins.
461 BrowserThread::PostTask(
464 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
465 base::Unretained(&backend
),
468 // Get the PSL-matched copy of the saved login for m.facebook.
469 const GURL
kMobileURL("http://m.facebook.com/");
470 PasswordForm m_facebook_lookup
;
471 m_facebook_lookup
.origin
= kMobileURL
;
472 m_facebook_lookup
.signon_realm
= kMobileURL
.spec();
473 std::vector
<PasswordForm
*> form_list
;
474 BrowserThread::PostTask(
477 base::Bind(base::IgnoreResult(&NativeBackendGnome::GetLogins
),
478 base::Unretained(&backend
),
482 EXPECT_EQ(1u, mock_keyring_items
.size());
483 EXPECT_EQ(1u, form_list
.size());
484 PasswordForm m_facebook
= *form_list
[0];
485 STLDeleteElements(&form_list
);
486 EXPECT_EQ(kMobileURL
, m_facebook
.origin
);
487 EXPECT_EQ(kMobileURL
.spec(), m_facebook
.signon_realm
);
489 // Add the PSL-matched copy to saved logins.
490 BrowserThread::PostTask(
493 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
494 base::Unretained(&backend
),
497 EXPECT_EQ(2u, mock_keyring_items
.size());
499 // Update www.facebook.com login.
500 PasswordForm
new_facebook(form_facebook_
);
501 const base::string16
kOldPassword(form_facebook_
.password_value
);
502 const base::string16
kNewPassword(UTF8ToUTF16("new_b"));
503 EXPECT_NE(kOldPassword
, kNewPassword
);
504 new_facebook
.password_value
= kNewPassword
;
505 switch (update_type
) {
506 case UPDATE_BY_UPDATELOGIN
:
507 BrowserThread::PostTask(
510 base::Bind(base::IgnoreResult(&NativeBackendGnome::UpdateLogin
),
511 base::Unretained(&backend
),
514 case UPDATE_BY_ADDLOGIN
:
515 BrowserThread::PostTask(
518 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
519 base::Unretained(&backend
),
525 EXPECT_EQ(2u, mock_keyring_items
.size());
527 // Check that m.facebook.com login was not modified by the update.
528 BrowserThread::PostTask(
531 base::Bind(base::IgnoreResult(&NativeBackendGnome::GetLogins
),
532 base::Unretained(&backend
),
536 // There should be two results -- the exact one, and the PSL-matched one.
537 EXPECT_EQ(2u, form_list
.size());
538 size_t index_non_psl
= 0;
539 if (!form_list
[index_non_psl
]->original_signon_realm
.empty())
541 EXPECT_EQ(kMobileURL
, form_list
[index_non_psl
]->origin
);
542 EXPECT_EQ(kMobileURL
.spec(), form_list
[index_non_psl
]->signon_realm
);
543 EXPECT_EQ(kOldPassword
, form_list
[index_non_psl
]->password_value
);
544 STLDeleteElements(&form_list
);
546 // Check that www.facebook.com login was modified by the update.
547 BrowserThread::PostTask(
550 base::Bind(base::IgnoreResult(&NativeBackendGnome::GetLogins
),
551 base::Unretained(&backend
),
555 // There should be two results -- the exact one, and the PSL-matched one.
556 EXPECT_EQ(2u, form_list
.size());
558 if (!form_list
[index_non_psl
]->original_signon_realm
.empty())
560 EXPECT_EQ(form_facebook_
.origin
, form_list
[index_non_psl
]->origin
);
561 EXPECT_EQ(form_facebook_
.signon_realm
,
562 form_list
[index_non_psl
]->signon_realm
);
563 EXPECT_EQ(kNewPassword
, form_list
[index_non_psl
]->password_value
);
564 STLDeleteElements(&form_list
);
567 base::MessageLoopForUI message_loop_
;
568 content::TestBrowserThread ui_thread_
;
569 content::TestBrowserThread db_thread_
;
571 // Provide some test forms to avoid having to set them up in each test.
572 PasswordForm form_google_
;
573 PasswordForm form_facebook_
;
574 PasswordForm form_isc_
;
577 TEST_F(NativeBackendGnomeTest
, BasicAddLogin
) {
578 NativeBackendGnome
backend(42);
581 BrowserThread::PostTask(
582 BrowserThread::DB
, FROM_HERE
,
583 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
584 base::Unretained(&backend
), form_google_
));
588 EXPECT_EQ(1u, mock_keyring_items
.size());
589 if (mock_keyring_items
.size() > 0)
590 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
593 TEST_F(NativeBackendGnomeTest
, BasicListLogins
) {
594 NativeBackendGnome
backend(42);
597 BrowserThread::PostTask(
598 BrowserThread::DB
, FROM_HERE
,
599 base::Bind(base::IgnoreResult( &NativeBackendGnome::AddLogin
),
600 base::Unretained(&backend
), form_google_
));
602 std::vector
<PasswordForm
*> form_list
;
603 BrowserThread::PostTask(
604 BrowserThread::DB
, FROM_HERE
,
606 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
607 base::Unretained(&backend
), &form_list
));
611 // Quick check that we got something back.
612 EXPECT_EQ(1u, form_list
.size());
613 STLDeleteElements(&form_list
);
615 EXPECT_EQ(1u, mock_keyring_items
.size());
616 if (mock_keyring_items
.size() > 0)
617 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
620 // Save a password for www.facebook.com and see it suggested for m.facebook.com.
621 TEST_F(NativeBackendGnomeTest
, PSLMatchingPositive
) {
623 const GURL
kMobileURL("http://m.facebook.com/");
624 password_manager::PSLMatchingHelper helper
;
625 ASSERT_TRUE(helper
.IsMatchingEnabled());
626 EXPECT_TRUE(CheckCredentialAvailability(form_facebook_
, kMobileURL
, &result
));
627 EXPECT_EQ(kMobileURL
, result
.origin
);
628 EXPECT_EQ(kMobileURL
.spec(), result
.signon_realm
);
631 // Save a password for www.facebook.com and see it not suggested for
633 TEST_F(NativeBackendGnomeTest
, PSLMatchingNegativeDomainMismatch
) {
634 password_manager::PSLMatchingHelper helper
;
635 ASSERT_TRUE(helper
.IsMatchingEnabled());
636 EXPECT_FALSE(CheckCredentialAvailability(
637 form_facebook_
, GURL("http://m-facebook.com/"), NULL
));
640 // Test PSL matching is off for domains excluded from it.
641 TEST_F(NativeBackendGnomeTest
, PSLMatchingDisabledDomains
) {
642 password_manager::PSLMatchingHelper helper
;
643 ASSERT_TRUE(helper
.IsMatchingEnabled());
644 EXPECT_FALSE(CheckCredentialAvailability(
645 form_google_
, GURL("http://one.google.com/"), NULL
));
648 TEST_F(NativeBackendGnomeTest
, PSLUpdatingStrictUpdateLogin
) {
649 CheckPSLUpdate(UPDATE_BY_UPDATELOGIN
);
652 TEST_F(NativeBackendGnomeTest
, PSLUpdatingStrictAddLogin
) {
653 // TODO(vabr): if AddLogin becomes no longer valid for existing logins, then
654 // just delete this test.
655 CheckPSLUpdate(UPDATE_BY_ADDLOGIN
);
658 TEST_F(NativeBackendGnomeTest
, BasicUpdateLogin
) {
659 NativeBackendGnome
backend(42);
662 // First add google login.
663 BrowserThread::PostTask(
664 BrowserThread::DB
, FROM_HERE
,
665 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
666 base::Unretained(&backend
), form_google_
));
670 PasswordForm
new_form_google(form_google_
);
671 new_form_google
.times_used
= 1;
672 new_form_google
.action
= GURL("http://www.google.com/different/login");
674 EXPECT_EQ(1u, mock_keyring_items
.size());
675 if (mock_keyring_items
.size() > 0)
676 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
679 BrowserThread::PostTask(
680 BrowserThread::DB
, FROM_HERE
,
681 base::Bind(base::IgnoreResult(&NativeBackendGnome::UpdateLogin
),
682 base::Unretained(&backend
), new_form_google
));
686 EXPECT_EQ(1u, mock_keyring_items
.size());
687 if (mock_keyring_items
.size() > 0)
688 CheckMockKeyringItem(&mock_keyring_items
[0], new_form_google
, "chrome-42");
691 TEST_F(NativeBackendGnomeTest
, BasicRemoveLogin
) {
692 NativeBackendGnome
backend(42);
695 BrowserThread::PostTask(
696 BrowserThread::DB
, FROM_HERE
,
697 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
698 base::Unretained(&backend
), form_google_
));
702 EXPECT_EQ(1u, mock_keyring_items
.size());
703 if (mock_keyring_items
.size() > 0)
704 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
706 BrowserThread::PostTask(
707 BrowserThread::DB
, FROM_HERE
,
708 base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin
),
709 base::Unretained(&backend
), form_google_
));
713 EXPECT_EQ(0u, mock_keyring_items
.size());
716 TEST_F(NativeBackendGnomeTest
, RemoveNonexistentLogin
) {
717 NativeBackendGnome
backend(42);
720 // First add an unrelated login.
721 BrowserThread::PostTask(
722 BrowserThread::DB
, FROM_HERE
,
723 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
724 base::Unretained(&backend
), form_google_
));
728 EXPECT_EQ(1u, mock_keyring_items
.size());
729 if (mock_keyring_items
.size() > 0)
730 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
732 // Attempt to remove a login that doesn't exist.
733 BrowserThread::PostTask(
734 BrowserThread::DB
, FROM_HERE
,
735 base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin
),
736 base::Unretained(&backend
), form_isc_
));
738 // Make sure we can still get the first form back.
739 std::vector
<PasswordForm
*> form_list
;
740 BrowserThread::PostTask(
741 BrowserThread::DB
, FROM_HERE
,
743 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
744 base::Unretained(&backend
), &form_list
));
748 // Quick check that we got something back.
749 EXPECT_EQ(1u, form_list
.size());
750 STLDeleteElements(&form_list
);
752 EXPECT_EQ(1u, mock_keyring_items
.size());
753 if (mock_keyring_items
.size() > 0)
754 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
757 TEST_F(NativeBackendGnomeTest
, AddDuplicateLogin
) {
758 NativeBackendGnome
backend(42);
761 BrowserThread::PostTask(
762 BrowserThread::DB
, FROM_HERE
,
763 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
764 base::Unretained(&backend
), form_google_
));
765 BrowserThread::PostTask(
766 BrowserThread::DB
, FROM_HERE
,
767 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
768 base::Unretained(&backend
), form_google_
));
772 EXPECT_EQ(1u, mock_keyring_items
.size());
773 if (mock_keyring_items
.size() > 0)
774 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
777 TEST_F(NativeBackendGnomeTest
, ListLoginsAppends
) {
778 NativeBackendGnome
backend(42);
781 BrowserThread::PostTask(
782 BrowserThread::DB
, FROM_HERE
,
783 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
784 base::Unretained(&backend
), form_google_
));
786 // Send the same request twice with the same list both times.
787 std::vector
<PasswordForm
*> form_list
;
788 BrowserThread::PostTask(
789 BrowserThread::DB
, FROM_HERE
,
791 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
792 base::Unretained(&backend
), &form_list
));
793 BrowserThread::PostTask(
794 BrowserThread::DB
, FROM_HERE
,
796 base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins
),
797 base::Unretained(&backend
), &form_list
));
801 // Quick check that we got two results back.
802 EXPECT_EQ(2u, form_list
.size());
803 STLDeleteElements(&form_list
);
805 EXPECT_EQ(1u, mock_keyring_items
.size());
806 if (mock_keyring_items
.size() > 0)
807 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
810 // TODO(mdm): add more basic tests here at some point.