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/location.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/time/time.h"
17 #include "chrome/browser/password_manager/native_backend_gnome_x.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "components/autofill/core/common/password_form.h"
20 #include "components/password_manager/core/browser/psl_matching_helper.h"
21 #include "components/password_manager/core/common/password_manager_pref_names.h"
22 #include "content/public/test/test_browser_thread.h"
23 #include "testing/gtest/include/gtest/gtest.h"
25 using autofill::PasswordForm
;
26 using base::UTF8ToUTF16
;
27 using base::UTF16ToUTF8
;
28 using content::BrowserThread
;
29 using password_manager::PasswordStoreChange
;
30 using password_manager::PasswordStoreChangeList
;
34 // What follows is a very simple implementation of the subset of the GNOME
35 // Keyring API that we actually use. It gets substituted for the real one by
36 // MockGnomeKeyringLoader, which hooks into the facility normally used to load
37 // the GNOME Keyring library at runtime to avoid a static dependency on it.
39 struct MockKeyringItem
{
41 MockKeyringItem(const char* keyring
,
42 const std::string
& display_name
,
43 const std::string
& password
)
44 : keyring(keyring
? keyring
: "login"),
45 display_name(display_name
),
48 struct ItemAttribute
{
49 ItemAttribute() : type(UINT32
), value_uint32(0) {}
50 explicit ItemAttribute(uint32_t value
)
51 : type(UINT32
), value_uint32(value
) {}
52 explicit ItemAttribute(const std::string
& value
)
53 : type(STRING
), value_string(value
) {}
55 bool Equals(const ItemAttribute
& x
) const {
56 if (type
!= x
.type
) return false;
57 return (type
== STRING
) ? value_string
== x
.value_string
58 : value_uint32
== x
.value_uint32
;
61 enum Type
{ UINT32
, STRING
} type
;
62 uint32_t value_uint32
;
63 std::string value_string
;
66 typedef std::map
<std::string
, ItemAttribute
> attribute_map
;
67 typedef std::vector
<std::pair
<std::string
, ItemAttribute
> > attribute_query
;
69 bool Matches(const attribute_query
& query
) const {
70 // The real GNOME Keyring doesn't match empty queries.
71 if (query
.empty()) return false;
72 for (size_t i
= 0; i
< query
.size(); ++i
) {
73 attribute_map::const_iterator match
= attributes
.find(query
[i
].first
);
74 if (match
== attributes
.end()) return false;
75 if (!match
->second
.Equals(query
[i
].second
)) return false;
81 std::string display_name
;
84 attribute_map attributes
;
87 // The list of all keyring items we have stored.
88 std::vector
<MockKeyringItem
> mock_keyring_items
;
89 bool mock_keyring_reject_local_ids
= false;
91 bool IsStringAttribute(const GnomeKeyringPasswordSchema
* schema
,
92 const std::string
& name
) {
93 for (size_t i
= 0; schema
->attributes
[i
].name
; ++i
)
94 if (name
== schema
->attributes
[i
].name
)
95 return schema
->attributes
[i
].type
== GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
;
96 NOTREACHED() << "Requested type of nonexistent attribute";
100 gboolean
mock_gnome_keyring_is_available() {
104 gpointer
mock_gnome_keyring_store_password(
105 const GnomeKeyringPasswordSchema
* schema
,
106 const gchar
* keyring
,
107 const gchar
* display_name
,
108 const gchar
* password
,
109 GnomeKeyringOperationDoneCallback callback
,
111 GDestroyNotify destroy_data
,
113 mock_keyring_items
.push_back(
114 MockKeyringItem(keyring
, display_name
, password
));
115 MockKeyringItem
* item
= &mock_keyring_items
.back();
116 const std::string keyring_desc
=
117 keyring
? base::StringPrintf("keyring %s", keyring
)
118 : std::string("default keyring");
119 VLOG(1) << "Adding item with origin " << display_name
120 << " to " << keyring_desc
;
122 va_start(ap
, destroy_data
);
124 while ((name
= va_arg(ap
, gchar
*))) {
125 if (IsStringAttribute(schema
, name
)) {
126 item
->attributes
[name
] =
127 MockKeyringItem::ItemAttribute(va_arg(ap
, gchar
*));
128 VLOG(1) << "Adding item attribute " << name
129 << ", value '" << item
->attributes
[name
].value_string
<< "'";
131 item
->attributes
[name
] =
132 MockKeyringItem::ItemAttribute(va_arg(ap
, uint32_t));
133 VLOG(1) << "Adding item attribute " << name
134 << ", value " << item
->attributes
[name
].value_uint32
;
138 // As a hack to ease testing migration, make it possible to reject the new
139 // format for the app string. This way we can add them easily to migrate.
140 if (mock_keyring_reject_local_ids
) {
141 MockKeyringItem::attribute_map::iterator it
=
142 item
->attributes
.find("application");
143 if (it
!= item
->attributes
.end() &&
144 it
->second
.type
== MockKeyringItem::ItemAttribute::STRING
&&
145 base::StringPiece(it
->second
.value_string
).starts_with("chrome-")) {
146 mock_keyring_items
.pop_back();
147 // GnomeKeyringResult, data
148 callback(GNOME_KEYRING_RESULT_IO_ERROR
, data
);
152 // GnomeKeyringResult, data
153 callback(GNOME_KEYRING_RESULT_OK
, data
);
157 gpointer
mock_gnome_keyring_delete_password(
158 const GnomeKeyringPasswordSchema
* schema
,
159 GnomeKeyringOperationDoneCallback callback
,
161 GDestroyNotify destroy_data
,
163 MockKeyringItem::attribute_query query
;
165 va_start(ap
, destroy_data
);
167 while ((name
= va_arg(ap
, gchar
*))) {
168 if (IsStringAttribute(schema
, name
)) {
169 query
.push_back(make_pair(std::string(name
),
170 MockKeyringItem::ItemAttribute(va_arg(ap
, gchar
*))));
171 VLOG(1) << "Querying with item attribute " << name
172 << ", value '" << query
.back().second
.value_string
<< "'";
174 query
.push_back(make_pair(std::string(name
),
175 MockKeyringItem::ItemAttribute(va_arg(ap
, uint32_t))));
176 VLOG(1) << "Querying with item attribute " << name
177 << ", value " << query
.back().second
.value_uint32
;
181 bool deleted
= false;
182 for (size_t i
= mock_keyring_items
.size(); i
> 0; --i
) {
183 const MockKeyringItem
* item
= &mock_keyring_items
[i
- 1];
184 if (item
->Matches(query
)) {
185 VLOG(1) << "Deleting item with origin " << item
->display_name
;
186 mock_keyring_items
.erase(mock_keyring_items
.begin() + (i
- 1));
190 // GnomeKeyringResult, data
191 callback(deleted
? GNOME_KEYRING_RESULT_OK
192 : GNOME_KEYRING_RESULT_NO_MATCH
, data
);
196 gpointer
mock_gnome_keyring_find_items(
197 GnomeKeyringItemType type
,
198 GnomeKeyringAttributeList
* attributes
,
199 GnomeKeyringOperationGetListCallback callback
,
201 GDestroyNotify destroy_data
) {
202 MockKeyringItem::attribute_query query
;
203 for (size_t i
= 0; i
< attributes
->len
; ++i
) {
204 GnomeKeyringAttribute attribute
=
205 g_array_index(attributes
, GnomeKeyringAttribute
, i
);
206 if (attribute
.type
== GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
) {
208 make_pair(std::string(attribute
.name
),
209 MockKeyringItem::ItemAttribute(attribute
.value
.string
)));
210 VLOG(1) << "Querying with item attribute " << attribute
.name
211 << ", value '" << query
.back().second
.value_string
<< "'";
214 make_pair(std::string(attribute
.name
),
215 MockKeyringItem::ItemAttribute(attribute
.value
.integer
)));
216 VLOG(1) << "Querying with item attribute " << attribute
.name
<< ", value "
217 << query
.back().second
.value_uint32
;
220 // Find matches and add them to a list of results.
221 GList
* results
= nullptr;
222 for (size_t i
= 0; i
< mock_keyring_items
.size(); ++i
) {
223 const MockKeyringItem
* item
= &mock_keyring_items
[i
];
224 if (item
->Matches(query
)) {
225 GnomeKeyringFound
* found
= new GnomeKeyringFound
;
226 found
->keyring
= strdup(item
->keyring
.c_str());
228 found
->attributes
= gnome_keyring_attribute_list_new();
229 for (MockKeyringItem::attribute_map::const_iterator it
=
230 item
->attributes
.begin();
231 it
!= item
->attributes
.end();
233 if (it
->second
.type
== MockKeyringItem::ItemAttribute::STRING
) {
234 gnome_keyring_attribute_list_append_string(
235 found
->attributes
, it
->first
.c_str(),
236 it
->second
.value_string
.c_str());
238 gnome_keyring_attribute_list_append_uint32(
239 found
->attributes
, it
->first
.c_str(),
240 it
->second
.value_uint32
);
243 found
->secret
= strdup(item
->password
.c_str());
244 results
= g_list_prepend(results
, found
);
247 // GnomeKeyringResult, GList*, data
248 callback(results
? GNOME_KEYRING_RESULT_OK
249 : GNOME_KEYRING_RESULT_NO_MATCH
, results
, data
);
250 // Now free the list of results.
251 GList
* element
= g_list_first(results
);
253 GnomeKeyringFound
* found
= static_cast<GnomeKeyringFound
*>(element
->data
);
254 free(found
->keyring
);
255 gnome_keyring_attribute_list_free(found
->attributes
);
258 element
= g_list_next(element
);
260 g_list_free(results
);
264 const gchar
* mock_gnome_keyring_result_to_message(GnomeKeyringResult res
) {
265 return "mock keyring simulating failure";
268 // Inherit to get access to protected fields.
269 class MockGnomeKeyringLoader
: public GnomeKeyringLoader
{
271 static bool LoadMockGnomeKeyring() {
272 if (!LoadGnomeKeyring())
274 #define GNOME_KEYRING_ASSIGN_POINTER(name) \
275 gnome_keyring_##name = &mock_gnome_keyring_##name;
276 GNOME_KEYRING_FOR_EACH_MOCKED_FUNC(GNOME_KEYRING_ASSIGN_POINTER
)
277 #undef GNOME_KEYRING_ASSIGN_POINTER
278 keyring_loaded
= true;
279 // Reset the state of the mock library.
280 mock_keyring_items
.clear();
281 mock_keyring_reject_local_ids
= false;
286 void CheckPasswordChanges(const PasswordStoreChangeList
& expected_list
,
287 const PasswordStoreChangeList
& actual_list
) {
288 ASSERT_EQ(expected_list
.size(), actual_list
.size());
289 for (size_t i
= 0; i
< expected_list
.size(); ++i
) {
290 EXPECT_EQ(expected_list
[i
].type(), actual_list
[i
].type());
291 const PasswordForm
& expected
= expected_list
[i
].form();
292 const PasswordForm
& actual
= actual_list
[i
].form();
294 EXPECT_EQ(expected
.origin
, actual
.origin
);
295 EXPECT_EQ(expected
.password_value
, actual
.password_value
);
296 EXPECT_EQ(expected
.action
, actual
.action
);
297 EXPECT_EQ(expected
.username_element
, actual
.username_element
);
298 EXPECT_EQ(expected
.username_value
, actual
.username_value
);
299 EXPECT_EQ(expected
.password_element
, actual
.password_element
);
300 EXPECT_EQ(expected
.submit_element
, actual
.submit_element
);
301 EXPECT_EQ(expected
.signon_realm
, actual
.signon_realm
);
302 EXPECT_EQ(expected
.ssl_valid
, actual
.ssl_valid
);
303 EXPECT_EQ(expected
.preferred
, actual
.preferred
);
304 EXPECT_EQ(expected
.date_created
, actual
.date_created
);
305 EXPECT_EQ(expected
.blacklisted_by_user
, actual
.blacklisted_by_user
);
306 EXPECT_EQ(expected
.type
, actual
.type
);
307 EXPECT_EQ(expected
.times_used
, actual
.times_used
);
308 EXPECT_EQ(expected
.scheme
, actual
.scheme
);
309 EXPECT_EQ(expected
.date_synced
, actual
.date_synced
);
310 EXPECT_EQ(expected
.display_name
, actual
.display_name
);
311 EXPECT_EQ(expected
.icon_url
, actual
.icon_url
);
312 EXPECT_EQ(expected
.federation_url
, actual
.federation_url
);
313 EXPECT_EQ(expected
.skip_zero_click
, actual
.skip_zero_click
);
314 EXPECT_EQ(expected
.generation_upload_status
,
315 actual
.generation_upload_status
);
319 void CheckPasswordChangesWithResult(const PasswordStoreChangeList
* expected
,
320 const PasswordStoreChangeList
* actual
,
323 CheckPasswordChanges(*expected
, *actual
);
326 void CheckTrue(bool result
) {
330 } // anonymous namespace
332 class NativeBackendGnomeTest
: public testing::Test
{
334 enum UpdateType
{ // Used in CheckPSLUpdate().
335 UPDATE_BY_UPDATELOGIN
,
338 enum RemoveBetweenMethod
{ // Used in CheckRemoveLoginsBetween().
343 NativeBackendGnomeTest()
344 : ui_thread_(BrowserThread::UI
, &message_loop_
),
345 db_thread_(BrowserThread::DB
) {
348 void SetUp() override
{
349 ASSERT_TRUE(db_thread_
.Start());
351 ASSERT_TRUE(MockGnomeKeyringLoader::LoadMockGnomeKeyring());
353 form_google_
.origin
= GURL("http://www.google.com/");
354 form_google_
.action
= GURL("http://www.google.com/login");
355 form_google_
.username_element
= UTF8ToUTF16("user");
356 form_google_
.username_value
= UTF8ToUTF16("joeschmoe");
357 form_google_
.password_element
= UTF8ToUTF16("pass");
358 form_google_
.password_value
= UTF8ToUTF16("seekrit");
359 form_google_
.submit_element
= UTF8ToUTF16("submit");
360 form_google_
.signon_realm
= "http://www.google.com/";
361 form_google_
.type
= PasswordForm::TYPE_GENERATED
;
362 form_google_
.date_created
= base::Time::Now();
363 form_google_
.date_synced
= base::Time::Now();
364 form_google_
.display_name
= UTF8ToUTF16("Joe Schmoe");
365 form_google_
.icon_url
= GURL("http://www.google.com/icon");
366 form_google_
.federation_url
= GURL("http://www.google.com/federation_url");
367 form_google_
.skip_zero_click
= true;
368 form_google_
.generation_upload_status
= PasswordForm::POSITIVE_SIGNAL_SENT
;
369 form_google_
.form_data
.name
= UTF8ToUTF16("form_name");
371 form_facebook_
.origin
= GURL("http://www.facebook.com/");
372 form_facebook_
.action
= GURL("http://www.facebook.com/login");
373 form_facebook_
.username_element
= UTF8ToUTF16("user");
374 form_facebook_
.username_value
= UTF8ToUTF16("a");
375 form_facebook_
.password_element
= UTF8ToUTF16("password");
376 form_facebook_
.password_value
= UTF8ToUTF16("b");
377 form_facebook_
.submit_element
= UTF8ToUTF16("submit");
378 form_facebook_
.signon_realm
= "http://www.facebook.com/";
379 form_facebook_
.date_created
= base::Time::Now();
380 form_facebook_
.date_synced
= base::Time::Now();
381 form_facebook_
.display_name
= UTF8ToUTF16("Joe Schmoe");
382 form_facebook_
.icon_url
= GURL("http://www.facebook.com/icon");
383 form_facebook_
.federation_url
= GURL("http://www.facebook.com/federation");
384 form_facebook_
.skip_zero_click
= true;
385 form_facebook_
.generation_upload_status
= PasswordForm::NO_SIGNAL_SENT
;
387 form_isc_
.origin
= GURL("http://www.isc.org/");
388 form_isc_
.action
= GURL("http://www.isc.org/auth");
389 form_isc_
.username_element
= UTF8ToUTF16("id");
390 form_isc_
.username_value
= UTF8ToUTF16("janedoe");
391 form_isc_
.password_element
= UTF8ToUTF16("passwd");
392 form_isc_
.password_value
= UTF8ToUTF16("ihazabukkit");
393 form_isc_
.submit_element
= UTF8ToUTF16("login");
394 form_isc_
.signon_realm
= "http://www.isc.org/";
395 form_isc_
.date_created
= base::Time::Now();
396 form_isc_
.date_synced
= base::Time::Now();
398 other_auth_
.origin
= GURL("http://www.example.com/");
399 other_auth_
.username_value
= UTF8ToUTF16("username");
400 other_auth_
.password_value
= UTF8ToUTF16("pass");
401 other_auth_
.signon_realm
= "http://www.example.com/Realm";
402 other_auth_
.date_created
= base::Time::Now();
403 other_auth_
.date_synced
= base::Time::Now();
406 void TearDown() override
{
407 base::ThreadTaskRunnerHandle::Get()->PostTask(
408 FROM_HERE
, base::MessageLoop::QuitClosure());
409 base::MessageLoop::current()->Run();
413 void RunBothThreads() {
414 // First we post a message to the DB thread that will run after all other
415 // messages that have been posted to the DB thread (we don't expect more
416 // to be posted), which posts a message to the UI thread to quit the loop.
417 // That way we can run both loops and be sure that the UI thread loop will
418 // quit so we can get on with the rest of the test.
419 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
420 base::Bind(&PostQuitTask
, &message_loop_
));
421 base::MessageLoop::current()->Run();
424 static void PostQuitTask(base::MessageLoop
* loop
) {
425 loop
->task_runner()->PostTask(FROM_HERE
, base::MessageLoop::QuitClosure());
428 void CheckUint32Attribute(const MockKeyringItem
* item
,
429 const std::string
& attribute
,
431 MockKeyringItem::attribute_map::const_iterator it
=
432 item
->attributes
.find(attribute
);
433 EXPECT_NE(item
->attributes
.end(), it
);
434 if (it
!= item
->attributes
.end()) {
435 EXPECT_EQ(MockKeyringItem::ItemAttribute::UINT32
, it
->second
.type
);
436 EXPECT_EQ(value
, it
->second
.value_uint32
);
440 void CheckStringAttribute(const MockKeyringItem
* item
,
441 const std::string
& attribute
,
442 const std::string
& value
) {
443 MockKeyringItem::attribute_map::const_iterator it
=
444 item
->attributes
.find(attribute
);
445 EXPECT_NE(item
->attributes
.end(), it
);
446 if (it
!= item
->attributes
.end()) {
447 EXPECT_EQ(MockKeyringItem::ItemAttribute::STRING
, it
->second
.type
);
448 EXPECT_EQ(value
, it
->second
.value_string
);
452 void CheckMockKeyringItem(const MockKeyringItem
* item
,
453 const PasswordForm
& form
,
454 const std::string
& app_string
) {
455 // We always add items to the login keyring.
456 EXPECT_EQ("login", item
->keyring
);
457 EXPECT_EQ(form
.origin
.spec(), item
->display_name
);
458 EXPECT_EQ(UTF16ToUTF8(form
.password_value
), item
->password
);
459 EXPECT_EQ(22u, item
->attributes
.size());
460 CheckStringAttribute(item
, "origin_url", form
.origin
.spec());
461 CheckStringAttribute(item
, "action_url", form
.action
.spec());
462 CheckStringAttribute(item
, "username_element",
463 UTF16ToUTF8(form
.username_element
));
464 CheckStringAttribute(item
, "username_value",
465 UTF16ToUTF8(form
.username_value
));
466 CheckStringAttribute(item
, "password_element",
467 UTF16ToUTF8(form
.password_element
));
468 CheckStringAttribute(item
, "submit_element",
469 UTF16ToUTF8(form
.submit_element
));
470 CheckStringAttribute(item
, "signon_realm", form
.signon_realm
);
471 CheckUint32Attribute(item
, "ssl_valid", form
.ssl_valid
);
472 CheckUint32Attribute(item
, "preferred", form
.preferred
);
473 // We don't check the date created. It varies.
474 CheckUint32Attribute(item
, "blacklisted_by_user", form
.blacklisted_by_user
);
475 CheckUint32Attribute(item
, "type", form
.type
);
476 CheckUint32Attribute(item
, "times_used", form
.times_used
);
477 CheckUint32Attribute(item
, "scheme", form
.scheme
);
478 CheckStringAttribute(item
, "date_synced", base::Int64ToString(
479 form
.date_synced
.ToInternalValue()));
480 CheckStringAttribute(item
, "display_name", UTF16ToUTF8(form
.display_name
));
481 CheckStringAttribute(item
, "avatar_url", form
.icon_url
.spec());
482 CheckStringAttribute(item
, "federation_url", form
.federation_url
.spec());
483 CheckUint32Attribute(item
, "skip_zero_click", form
.skip_zero_click
);
484 CheckUint32Attribute(item
, "generation_upload_status",
485 form
.generation_upload_status
);
486 CheckStringAttribute(item
, "application", app_string
);
487 autofill::FormData actual
;
488 DeserializeFormDataFromBase64String(
489 item
->attributes
.at("form_data").value_string
, &actual
);
490 EXPECT_TRUE(form
.form_data
.SameFormAs(actual
));
493 // Saves |credentials| and then gets logins matching |url| and |scheme|.
494 // Returns true when something is found, and in such case copies the result to
495 // |result| when |result| is not NULL. (Note that there can be max. 1 result,
496 // derived from |credentials|.)
497 bool CheckCredentialAvailability(const PasswordForm
& credentials
,
499 const PasswordForm::Scheme
& scheme
,
500 PasswordForm
* result
) {
501 NativeBackendGnome
backend(321);
504 BrowserThread::PostTask(
507 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
508 base::Unretained(&backend
),
511 PasswordForm target_form
;
512 target_form
.origin
= url
;
513 target_form
.signon_realm
= url
.spec();
514 if (scheme
!= PasswordForm::SCHEME_HTML
) {
515 // For non-HTML forms, the realm used for authentication
516 // (http://tools.ietf.org/html/rfc1945#section-10.2) is appended to the
517 // signon_realm. Just use a default value for now.
518 target_form
.signon_realm
.append("Realm");
519 target_form
.scheme
= scheme
;
521 ScopedVector
<autofill::PasswordForm
> form_list
;
522 BrowserThread::PostTaskAndReplyWithResult(
525 base::Bind(&NativeBackendGnome::GetLogins
,
526 base::Unretained(&backend
),
529 base::Bind(&CheckTrue
));
533 EXPECT_EQ(1u, mock_keyring_items
.size());
534 if (mock_keyring_items
.size() > 0)
535 CheckMockKeyringItem(&mock_keyring_items
[0], credentials
, "chrome-321");
536 mock_keyring_items
.clear();
538 if (form_list
.empty())
540 EXPECT_EQ(1u, form_list
.size());
542 *result
= *form_list
[0];
546 // Test that updating does not use PSL matching: Add a www.facebook.com
547 // password, then use PSL matching to get a copy of it for m.facebook.com, and
548 // add that copy as well. Now update the www.facebook.com password -- the
549 // m.facebook.com password should not get updated. Depending on the argument,
550 // the credential update is done via UpdateLogin or AddLogin.
551 void CheckPSLUpdate(UpdateType update_type
) {
552 NativeBackendGnome
backend(321);
555 // Add |form_facebook_| to saved logins.
556 BrowserThread::PostTaskAndReplyWithResult(
559 base::Bind(&NativeBackendGnome::AddLogin
,
560 base::Unretained(&backend
),
562 base::Bind(&CheckPasswordChanges
,
563 PasswordStoreChangeList(1, PasswordStoreChange(
564 PasswordStoreChange::ADD
, form_facebook_
))));
566 // Get the PSL-matched copy of the saved login for m.facebook.
567 const GURL
kMobileURL("http://m.facebook.com/");
568 PasswordForm m_facebook_lookup
;
569 m_facebook_lookup
.origin
= kMobileURL
;
570 m_facebook_lookup
.signon_realm
= kMobileURL
.spec();
571 ScopedVector
<autofill::PasswordForm
> form_list
;
572 BrowserThread::PostTaskAndReplyWithResult(
575 base::Bind(&NativeBackendGnome::GetLogins
,
576 base::Unretained(&backend
),
579 base::Bind(&CheckTrue
));
581 EXPECT_EQ(1u, mock_keyring_items
.size());
582 EXPECT_EQ(1u, form_list
.size());
583 PasswordForm m_facebook
= *form_list
[0];
585 EXPECT_EQ(kMobileURL
, m_facebook
.origin
);
586 EXPECT_EQ(kMobileURL
.spec(), m_facebook
.signon_realm
);
588 // Add the PSL-matched copy to saved logins.
589 BrowserThread::PostTask(
592 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
593 base::Unretained(&backend
),
596 EXPECT_EQ(2u, mock_keyring_items
.size());
598 // Update www.facebook.com login.
599 PasswordForm
new_facebook(form_facebook_
);
600 const base::string16
kOldPassword(form_facebook_
.password_value
);
601 const base::string16
kNewPassword(UTF8ToUTF16("new_b"));
602 EXPECT_NE(kOldPassword
, kNewPassword
);
603 new_facebook
.password_value
= kNewPassword
;
604 PasswordStoreChangeList changes
;
605 PasswordStoreChangeList expected_changes
;
606 switch (update_type
) {
607 case UPDATE_BY_UPDATELOGIN
:
608 expected_changes
.push_back(
609 PasswordStoreChange(PasswordStoreChange::UPDATE
, new_facebook
));
610 BrowserThread::PostTaskAndReplyWithResult(
613 base::Bind(&NativeBackendGnome::UpdateLogin
,
614 base::Unretained(&backend
),
617 base::Bind(&CheckPasswordChangesWithResult
,
618 &expected_changes
, &changes
));
620 case UPDATE_BY_ADDLOGIN
:
621 expected_changes
.push_back(
622 PasswordStoreChange(PasswordStoreChange::REMOVE
, form_facebook_
));
623 expected_changes
.push_back(
624 PasswordStoreChange(PasswordStoreChange::ADD
, new_facebook
));
625 BrowserThread::PostTaskAndReplyWithResult(
628 base::Bind(&NativeBackendGnome::AddLogin
,
629 base::Unretained(&backend
),
631 base::Bind(&CheckPasswordChanges
, expected_changes
));
636 EXPECT_EQ(2u, mock_keyring_items
.size());
638 // Check that m.facebook.com login was not modified by the update.
639 BrowserThread::PostTaskAndReplyWithResult(
642 base::Bind(&NativeBackendGnome::GetLogins
,
643 base::Unretained(&backend
),
646 base::Bind(&CheckTrue
));
648 // There should be two results -- the exact one, and the PSL-matched one.
649 EXPECT_EQ(2u, form_list
.size());
650 size_t index_non_psl
= 0;
651 if (!form_list
[index_non_psl
]->original_signon_realm
.empty())
653 EXPECT_EQ(kMobileURL
, form_list
[index_non_psl
]->origin
);
654 EXPECT_EQ(kMobileURL
.spec(), form_list
[index_non_psl
]->signon_realm
);
655 EXPECT_EQ(kOldPassword
, form_list
[index_non_psl
]->password_value
);
658 // Check that www.facebook.com login was modified by the update.
659 BrowserThread::PostTaskAndReplyWithResult(
662 base::Bind(&NativeBackendGnome::GetLogins
,
663 base::Unretained(&backend
),
666 base::Bind(&CheckTrue
));
668 // There should be two results -- the exact one, and the PSL-matched one.
669 EXPECT_EQ(2u, form_list
.size());
671 if (!form_list
[index_non_psl
]->original_signon_realm
.empty())
673 EXPECT_EQ(form_facebook_
.origin
, form_list
[index_non_psl
]->origin
);
674 EXPECT_EQ(form_facebook_
.signon_realm
,
675 form_list
[index_non_psl
]->signon_realm
);
676 EXPECT_EQ(kNewPassword
, form_list
[index_non_psl
]->password_value
);
679 void CheckMatchingWithScheme(const PasswordForm::Scheme
& scheme
) {
680 other_auth_
.scheme
= scheme
;
682 // Don't match a non-HTML form with an HTML form.
683 EXPECT_FALSE(CheckCredentialAvailability(
684 other_auth_
, GURL("http://www.example.com"),
685 PasswordForm::SCHEME_HTML
, nullptr));
686 // Don't match an HTML form with non-HTML auth form.
687 EXPECT_FALSE(CheckCredentialAvailability(
688 form_google_
, GURL("http://www.google.com/"), scheme
, nullptr));
689 // Don't match two different non-HTML auth forms with different origin.
690 EXPECT_FALSE(CheckCredentialAvailability(
691 other_auth_
, GURL("http://first.example.com"), scheme
, nullptr));
692 // Do match non-HTML forms from the same origin.
693 EXPECT_TRUE(CheckCredentialAvailability(
694 other_auth_
, GURL("http://www.example.com/"), scheme
, nullptr));
697 void CheckRemoveLoginsBetween(RemoveBetweenMethod date_to_test
) {
698 NativeBackendGnome
backend(42);
701 base::Time now
= base::Time::Now();
702 base::Time next_day
= now
+ base::TimeDelta::FromDays(1);
703 form_google_
.date_synced
= base::Time();
704 form_isc_
.date_synced
= base::Time();
705 form_google_
.date_created
= now
;
706 form_isc_
.date_created
= now
;
707 if (date_to_test
== CREATED
) {
708 form_google_
.date_created
= now
;
709 form_isc_
.date_created
= next_day
;
711 form_google_
.date_synced
= now
;
712 form_isc_
.date_synced
= next_day
;
715 BrowserThread::PostTask(
718 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
719 base::Unretained(&backend
),
721 BrowserThread::PostTask(
724 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
725 base::Unretained(&backend
),
728 PasswordStoreChangeList expected_changes
;
729 expected_changes
.push_back(
730 PasswordStoreChange(PasswordStoreChange::REMOVE
, form_google_
));
731 PasswordStoreChangeList changes
;
732 bool (NativeBackendGnome::*method
)(
733 base::Time
, base::Time
, password_manager::PasswordStoreChangeList
*) =
734 date_to_test
== CREATED
735 ? &NativeBackendGnome::RemoveLoginsCreatedBetween
736 : &NativeBackendGnome::RemoveLoginsSyncedBetween
;
737 BrowserThread::PostTaskAndReplyWithResult(
741 base::Unretained(&backend
),
746 &CheckPasswordChangesWithResult
, &expected_changes
, &changes
));
749 EXPECT_EQ(1u, mock_keyring_items
.size());
750 if (mock_keyring_items
.size() > 0)
751 CheckMockKeyringItem(&mock_keyring_items
[0], form_isc_
, "chrome-42");
754 expected_changes
.clear();
755 expected_changes
.push_back(
756 PasswordStoreChange(PasswordStoreChange::REMOVE
, form_isc_
));
757 BrowserThread::PostTaskAndReplyWithResult(
761 base::Unretained(&backend
),
766 &CheckPasswordChangesWithResult
, &expected_changes
, &changes
));
769 EXPECT_EQ(0u, mock_keyring_items
.size());
772 base::MessageLoopForUI message_loop_
;
773 content::TestBrowserThread ui_thread_
;
774 content::TestBrowserThread db_thread_
;
776 // Provide some test forms to avoid having to set them up in each test.
777 PasswordForm form_google_
;
778 PasswordForm form_facebook_
;
779 PasswordForm form_isc_
;
780 PasswordForm other_auth_
;
783 TEST_F(NativeBackendGnomeTest
, BasicAddLogin
) {
784 NativeBackendGnome
backend(42);
787 BrowserThread::PostTaskAndReplyWithResult(
788 BrowserThread::DB
, FROM_HERE
,
789 base::Bind(&NativeBackendGnome::AddLogin
,
790 base::Unretained(&backend
), form_google_
),
791 base::Bind(&CheckPasswordChanges
,
792 PasswordStoreChangeList(1, PasswordStoreChange(
793 PasswordStoreChange::ADD
, form_google_
))));
797 EXPECT_EQ(1u, mock_keyring_items
.size());
798 if (mock_keyring_items
.size() > 0)
799 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
802 TEST_F(NativeBackendGnomeTest
, BasicListLogins
) {
803 NativeBackendGnome
backend(42);
806 BrowserThread::PostTask(
807 BrowserThread::DB
, FROM_HERE
,
808 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
809 base::Unretained(&backend
), form_google_
));
811 ScopedVector
<autofill::PasswordForm
> form_list
;
812 BrowserThread::PostTaskAndReplyWithResult(
813 BrowserThread::DB
, FROM_HERE
,
814 base::Bind(&NativeBackendGnome::GetAutofillableLogins
,
815 base::Unretained(&backend
), &form_list
),
816 base::Bind(&CheckTrue
));
820 // Quick check that we got something back.
821 EXPECT_EQ(1u, form_list
.size());
823 EXPECT_EQ(1u, mock_keyring_items
.size());
824 if (mock_keyring_items
.size() > 0)
825 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
828 // Save a password for www.facebook.com and see it suggested for m.facebook.com.
829 TEST_F(NativeBackendGnomeTest
, PSLMatchingPositive
) {
831 const GURL
kMobileURL("http://m.facebook.com/");
832 EXPECT_TRUE(CheckCredentialAvailability(
833 form_facebook_
, kMobileURL
, PasswordForm::SCHEME_HTML
, &result
));
834 EXPECT_EQ(kMobileURL
, result
.origin
);
835 EXPECT_EQ(kMobileURL
.spec(), result
.signon_realm
);
838 // Save a password for www.facebook.com and see it not suggested for
840 TEST_F(NativeBackendGnomeTest
, PSLMatchingNegativeDomainMismatch
) {
841 EXPECT_FALSE(CheckCredentialAvailability(
842 form_facebook_
, GURL("http://m-facebook.com/"),
843 PasswordForm::SCHEME_HTML
, nullptr));
846 // Test PSL matching is off for domains excluded from it.
847 TEST_F(NativeBackendGnomeTest
, PSLMatchingDisabledDomains
) {
848 EXPECT_FALSE(CheckCredentialAvailability(
849 form_google_
, GURL("http://one.google.com/"),
850 PasswordForm::SCHEME_HTML
, nullptr));
853 // Make sure PSL matches aren't available for non-HTML forms.
854 TEST_F(NativeBackendGnomeTest
, PSLMatchingDisabledForNonHTMLForms
) {
855 CheckMatchingWithScheme(PasswordForm::SCHEME_BASIC
);
856 CheckMatchingWithScheme(PasswordForm::SCHEME_DIGEST
);
857 CheckMatchingWithScheme(PasswordForm::SCHEME_OTHER
);
861 TEST_F(NativeBackendGnomeTest
, PSLUpdatingStrictUpdateLogin
) {
862 CheckPSLUpdate(UPDATE_BY_UPDATELOGIN
);
865 TEST_F(NativeBackendGnomeTest
, PSLUpdatingStrictAddLogin
) {
866 // TODO(vabr): if AddLogin becomes no longer valid for existing logins, then
867 // just delete this test.
868 CheckPSLUpdate(UPDATE_BY_ADDLOGIN
);
871 TEST_F(NativeBackendGnomeTest
, BasicUpdateLogin
) {
872 NativeBackendGnome
backend(42);
875 // First add google login.
876 BrowserThread::PostTask(
877 BrowserThread::DB
, FROM_HERE
,
878 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
879 base::Unretained(&backend
), form_google_
));
883 PasswordForm
new_form_google(form_google_
);
884 new_form_google
.times_used
= 1;
885 new_form_google
.action
= GURL("http://www.google.com/different/login");
887 EXPECT_EQ(1u, mock_keyring_items
.size());
888 if (mock_keyring_items
.size() > 0)
889 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
892 PasswordStoreChangeList changes
;
893 PasswordStoreChangeList
expected_changes(
894 1, PasswordStoreChange(PasswordStoreChange::UPDATE
, new_form_google
));
895 BrowserThread::PostTaskAndReplyWithResult(
896 BrowserThread::DB
, FROM_HERE
,
897 base::Bind(&NativeBackendGnome::UpdateLogin
,
898 base::Unretained(&backend
),
901 base::Bind(&CheckPasswordChangesWithResult
, &expected_changes
, &changes
));
904 EXPECT_EQ(1u, mock_keyring_items
.size());
905 if (mock_keyring_items
.size() > 0)
906 CheckMockKeyringItem(&mock_keyring_items
[0], new_form_google
, "chrome-42");
909 TEST_F(NativeBackendGnomeTest
, BasicRemoveLogin
) {
910 NativeBackendGnome
backend(42);
913 BrowserThread::PostTask(
914 BrowserThread::DB
, FROM_HERE
,
915 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
916 base::Unretained(&backend
), form_google_
));
920 EXPECT_EQ(1u, mock_keyring_items
.size());
921 if (mock_keyring_items
.size() > 0)
922 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
924 PasswordStoreChangeList changes
;
925 PasswordStoreChangeList
expected_changes(
926 1, PasswordStoreChange(PasswordStoreChange::REMOVE
, form_google_
));
927 BrowserThread::PostTaskAndReplyWithResult(
928 BrowserThread::DB
, FROM_HERE
,
929 base::Bind(&NativeBackendGnome::RemoveLogin
,
930 base::Unretained(&backend
), form_google_
, &changes
),
931 base::Bind(&CheckPasswordChangesWithResult
, &expected_changes
, &changes
));
935 EXPECT_EQ(0u, mock_keyring_items
.size());
938 // Verify fix for http://crbug.com/408783.
939 TEST_F(NativeBackendGnomeTest
, RemoveLoginActionMismatch
) {
940 NativeBackendGnome
backend(42);
943 BrowserThread::PostTask(
944 BrowserThread::DB
, FROM_HERE
,
945 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
946 base::Unretained(&backend
), form_google_
));
950 EXPECT_EQ(1u, mock_keyring_items
.size());
951 if (mock_keyring_items
.size() > 0)
952 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
954 // Action url match not required for removal.
955 form_google_
.action
= GURL("https://some.other.url.com/path");
957 PasswordStoreChangeList changes
;
958 PasswordStoreChangeList
expected_changes(
959 1, PasswordStoreChange(PasswordStoreChange::REMOVE
, form_google_
));
960 BrowserThread::PostTaskAndReplyWithResult(
961 BrowserThread::DB
, FROM_HERE
,
962 base::Bind(&NativeBackendGnome::RemoveLogin
,
963 base::Unretained(&backend
), form_google_
, &changes
),
964 base::Bind(&CheckPasswordChangesWithResult
, &expected_changes
, &changes
));
968 EXPECT_EQ(0u, mock_keyring_items
.size());
971 TEST_F(NativeBackendGnomeTest
, RemoveNonexistentLogin
) {
972 NativeBackendGnome
backend(42);
975 // First add an unrelated login.
976 BrowserThread::PostTask(
977 BrowserThread::DB
, FROM_HERE
,
978 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
979 base::Unretained(&backend
), form_google_
));
983 EXPECT_EQ(1u, mock_keyring_items
.size());
984 if (mock_keyring_items
.size() > 0)
985 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
987 // Attempt to remove a login that doesn't exist.
988 PasswordStoreChangeList changes
;
989 BrowserThread::PostTaskAndReplyWithResult(
990 BrowserThread::DB
, FROM_HERE
,
991 base::Bind(&NativeBackendGnome::RemoveLogin
,
992 base::Unretained(&backend
), form_isc_
, &changes
),
993 base::Bind(&CheckPasswordChangesWithResult
,
994 base::Owned(new PasswordStoreChangeList
), &changes
));
996 // Make sure we can still get the first form back.
997 ScopedVector
<autofill::PasswordForm
> form_list
;
998 BrowserThread::PostTaskAndReplyWithResult(
999 BrowserThread::DB
, FROM_HERE
,
1000 base::Bind(&NativeBackendGnome::GetAutofillableLogins
,
1001 base::Unretained(&backend
), &form_list
),
1002 base::Bind(&CheckTrue
));
1006 // Quick check that we got something back.
1007 EXPECT_EQ(1u, form_list
.size());
1009 EXPECT_EQ(1u, mock_keyring_items
.size());
1010 if (mock_keyring_items
.size() > 0)
1011 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
1014 TEST_F(NativeBackendGnomeTest
, UpdateNonexistentLogin
) {
1015 NativeBackendGnome
backend(42);
1018 // First add an unrelated login.
1019 BrowserThread::PostTask(
1020 BrowserThread::DB
, FROM_HERE
,
1021 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
1022 base::Unretained(&backend
), form_google_
));
1026 EXPECT_EQ(1u, mock_keyring_items
.size());
1027 if (mock_keyring_items
.size() > 0)
1028 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
1030 // Attempt to update a login that doesn't exist.
1031 PasswordStoreChangeList changes
;
1032 BrowserThread::PostTaskAndReplyWithResult(
1033 BrowserThread::DB
, FROM_HERE
,
1034 base::Bind(&NativeBackendGnome::UpdateLogin
,
1035 base::Unretained(&backend
),
1038 base::Bind(&CheckPasswordChangesWithResult
,
1039 base::Owned(new PasswordStoreChangeList
), &changes
));
1042 EXPECT_EQ(1u, mock_keyring_items
.size());
1043 if (mock_keyring_items
.size() > 0)
1044 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
1047 TEST_F(NativeBackendGnomeTest
, UpdateSameLogin
) {
1048 NativeBackendGnome
backend(42);
1051 // First add an unrelated login.
1052 BrowserThread::PostTask(
1053 BrowserThread::DB
, FROM_HERE
,
1054 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
1055 base::Unretained(&backend
), form_google_
));
1058 EXPECT_EQ(1u, mock_keyring_items
.size());
1059 if (mock_keyring_items
.size() > 0)
1060 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
1062 // Attempt to update the same login without changing anything.
1063 PasswordStoreChangeList changes
;
1064 PasswordStoreChangeList expected_changes
;
1065 BrowserThread::PostTaskAndReplyWithResult(
1066 BrowserThread::DB
, FROM_HERE
,
1067 base::Bind(&NativeBackendGnome::UpdateLogin
,
1068 base::Unretained(&backend
),
1071 base::Bind(&CheckPasswordChangesWithResult
, &expected_changes
, &changes
));
1074 EXPECT_EQ(1u, mock_keyring_items
.size());
1075 if (mock_keyring_items
.size() > 0)
1076 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
1079 TEST_F(NativeBackendGnomeTest
, AddDuplicateLogin
) {
1080 NativeBackendGnome
backend(42);
1083 PasswordStoreChangeList changes
;
1084 changes
.push_back(PasswordStoreChange(PasswordStoreChange::ADD
,
1086 BrowserThread::PostTaskAndReplyWithResult(
1087 BrowserThread::DB
, FROM_HERE
,
1088 base::Bind(&NativeBackendGnome::AddLogin
,
1089 base::Unretained(&backend
), form_google_
),
1090 base::Bind(&CheckPasswordChanges
, changes
));
1093 changes
.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE
,
1095 form_google_
.times_used
++;
1096 form_google_
.submit_element
= UTF8ToUTF16("submit2");
1097 changes
.push_back(PasswordStoreChange(PasswordStoreChange::ADD
,
1100 BrowserThread::PostTaskAndReplyWithResult(
1101 BrowserThread::DB
, FROM_HERE
,
1102 base::Bind(&NativeBackendGnome::AddLogin
,
1103 base::Unretained(&backend
), form_google_
),
1104 base::Bind(&CheckPasswordChanges
, changes
));
1108 EXPECT_EQ(1u, mock_keyring_items
.size());
1109 if (mock_keyring_items
.size() > 0)
1110 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
1113 TEST_F(NativeBackendGnomeTest
, AndroidCredentials
) {
1114 NativeBackendGnome
backend(42);
1117 PasswordForm observed_android_form
;
1118 observed_android_form
.scheme
= PasswordForm::SCHEME_HTML
;
1119 observed_android_form
.signon_realm
=
1120 "android://7x7IDboo8u9YKraUsbmVkuf1-@net.rateflix.app/";
1121 PasswordForm saved_android_form
= observed_android_form
;
1122 saved_android_form
.username_value
= base::UTF8ToUTF16("randomusername");
1123 saved_android_form
.password_value
= base::UTF8ToUTF16("password");
1124 saved_android_form
.date_created
= base::Time::Now();
1126 BrowserThread::PostTaskAndReplyWithResult(
1127 BrowserThread::DB
, FROM_HERE
,
1128 base::Bind(&NativeBackendGnome::AddLogin
,
1129 base::Unretained(&backend
), saved_android_form
),
1130 base::Bind(&CheckPasswordChanges
,
1131 PasswordStoreChangeList(1, PasswordStoreChange(
1132 PasswordStoreChange::ADD
, saved_android_form
))));
1134 ScopedVector
<autofill::PasswordForm
> form_list
;
1135 BrowserThread::PostTaskAndReplyWithResult(
1136 BrowserThread::DB
, FROM_HERE
,
1137 base::Bind(&NativeBackendGnome::GetLogins
,
1138 base::Unretained(&backend
), observed_android_form
,
1140 base::Bind(&CheckTrue
));
1144 EXPECT_EQ(1u, form_list
.size());
1145 EXPECT_EQ(saved_android_form
, *form_list
[0]);
1148 TEST_F(NativeBackendGnomeTest
, RemoveLoginsCreatedBetween
) {
1149 CheckRemoveLoginsBetween(CREATED
);
1152 TEST_F(NativeBackendGnomeTest
, RemoveLoginsSyncedBetween
) {
1153 CheckRemoveLoginsBetween(SYNCED
);
1156 TEST_F(NativeBackendGnomeTest
, ReadDuplicateForms
) {
1157 NativeBackendGnome
backend(42);
1160 // Add 2 slightly different password forms.
1161 const char unique_string
[] = "unique_unique_string";
1162 const char unique_string_replacement
[] = "uniKue_unique_string";
1163 form_google_
.origin
=
1164 GURL(std::string("http://www.google.com/") + unique_string
);
1165 BrowserThread::PostTask(
1166 BrowserThread::DB
, FROM_HERE
,
1167 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
1168 base::Unretained(&backend
), form_google_
));
1169 form_google_
.origin
=
1170 GURL(std::string("http://www.google.com/") + unique_string_replacement
);
1171 BrowserThread::PostTask(
1172 BrowserThread::DB
, FROM_HERE
,
1173 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin
),
1174 base::Unretained(&backend
), form_google_
));
1177 // Read the raw value back. Change the |unique_string| to
1178 // |unique_string_replacement| so the forms become unique.
1179 ASSERT_EQ(2u, mock_keyring_items
.size());
1180 MockKeyringItem::attribute_map::iterator it
=
1181 mock_keyring_items
[0].attributes
.find("origin_url");
1182 ASSERT_NE(mock_keyring_items
[0].attributes
.end(), it
);
1183 size_t position
= it
->second
.value_string
.find(unique_string
);
1184 ASSERT_NE(std::string::npos
, position
) << it
->second
.value_string
;
1185 it
->second
.value_string
.replace(
1186 position
, std::string(unique_string_replacement
).length(),
1187 unique_string_replacement
);
1189 // Now test that GetAutofillableLogins returns only one form.
1190 ScopedVector
<autofill::PasswordForm
> form_list
;
1191 BrowserThread::PostTaskAndReplyWithResult(
1192 BrowserThread::DB
, FROM_HERE
,
1193 base::Bind(&NativeBackendGnome::GetAutofillableLogins
,
1194 base::Unretained(&backend
), &form_list
),
1195 base::Bind(&CheckTrue
));
1198 EXPECT_EQ(1u, form_list
.size());
1199 EXPECT_EQ(form_google_
, *form_list
[0]);
1201 EXPECT_EQ(1u, mock_keyring_items
.size());
1202 if (mock_keyring_items
.size() > 0)
1203 CheckMockKeyringItem(&mock_keyring_items
[0], form_google_
, "chrome-42");
1206 // TODO(mdm): add more basic tests here at some point.