Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / password_manager / native_backend_gnome_x_unittest.cc
blob01ef6ea5f084b1a9dbe426657f3c63645834dfde
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.
5 #include <stdarg.h>
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;
32 namespace {
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 {
40 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),
46 password(password) {}
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;
77 return true;
80 std::string keyring;
81 std::string display_name;
82 std::string password;
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";
97 return false;
100 gboolean mock_gnome_keyring_is_available() {
101 return true;
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,
110 gpointer data,
111 GDestroyNotify destroy_data,
112 ...) {
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;
121 va_list ap;
122 va_start(ap, destroy_data);
123 char* name;
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 << "'";
130 } else {
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;
137 va_end(ap);
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);
149 return nullptr;
152 // GnomeKeyringResult, data
153 callback(GNOME_KEYRING_RESULT_OK, data);
154 return nullptr;
157 gpointer mock_gnome_keyring_delete_password(
158 const GnomeKeyringPasswordSchema* schema,
159 GnomeKeyringOperationDoneCallback callback,
160 gpointer data,
161 GDestroyNotify destroy_data,
162 ...) {
163 MockKeyringItem::attribute_query query;
164 va_list ap;
165 va_start(ap, destroy_data);
166 char* name;
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 << "'";
173 } else {
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;
180 va_end(ap);
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));
187 deleted = true;
190 // GnomeKeyringResult, data
191 callback(deleted ? GNOME_KEYRING_RESULT_OK
192 : GNOME_KEYRING_RESULT_NO_MATCH, data);
193 return nullptr;
196 gpointer mock_gnome_keyring_find_items(
197 GnomeKeyringItemType type,
198 GnomeKeyringAttributeList* attributes,
199 GnomeKeyringOperationGetListCallback callback,
200 gpointer data,
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) {
207 query.push_back(
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 << "'";
212 } else {
213 query.push_back(
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());
227 found->item_id = i;
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();
232 ++it) {
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());
237 } else {
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);
252 while (element) {
253 GnomeKeyringFound* found = static_cast<GnomeKeyringFound*>(element->data);
254 free(found->keyring);
255 gnome_keyring_attribute_list_free(found->attributes);
256 free(found->secret);
257 delete found;
258 element = g_list_next(element);
260 g_list_free(results);
261 return nullptr;
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 {
270 public:
271 static bool LoadMockGnomeKeyring() {
272 if (!LoadGnomeKeyring())
273 return false;
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;
282 return true;
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,
321 bool result) {
322 EXPECT_TRUE(result);
323 CheckPasswordChanges(*expected, *actual);
326 void CheckTrue(bool result) {
327 EXPECT_TRUE(result);
330 } // anonymous namespace
332 class NativeBackendGnomeTest : public testing::Test {
333 protected:
334 enum UpdateType { // Used in CheckPSLUpdate().
335 UPDATE_BY_UPDATELOGIN,
336 UPDATE_BY_ADDLOGIN,
338 enum RemoveBetweenMethod { // Used in CheckRemoveLoginsBetween().
339 CREATED,
340 SYNCED,
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();
410 db_thread_.Stop();
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,
430 uint32_t value) {
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,
498 const GURL& url,
499 const PasswordForm::Scheme& scheme,
500 PasswordForm* result) {
501 NativeBackendGnome backend(321);
502 backend.Init();
504 BrowserThread::PostTask(
505 BrowserThread::DB,
506 FROM_HERE,
507 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
508 base::Unretained(&backend),
509 credentials));
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(
523 BrowserThread::DB,
524 FROM_HERE,
525 base::Bind(&NativeBackendGnome::GetLogins,
526 base::Unretained(&backend),
527 target_form,
528 &form_list),
529 base::Bind(&CheckTrue));
531 RunBothThreads();
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())
539 return false;
540 EXPECT_EQ(1u, form_list.size());
541 if (result)
542 *result = *form_list[0];
543 return true;
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);
553 backend.Init();
555 // Add |form_facebook_| to saved logins.
556 BrowserThread::PostTaskAndReplyWithResult(
557 BrowserThread::DB,
558 FROM_HERE,
559 base::Bind(&NativeBackendGnome::AddLogin,
560 base::Unretained(&backend),
561 form_facebook_),
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(
573 BrowserThread::DB,
574 FROM_HERE,
575 base::Bind(&NativeBackendGnome::GetLogins,
576 base::Unretained(&backend),
577 m_facebook_lookup,
578 &form_list),
579 base::Bind(&CheckTrue));
580 RunBothThreads();
581 EXPECT_EQ(1u, mock_keyring_items.size());
582 EXPECT_EQ(1u, form_list.size());
583 PasswordForm m_facebook = *form_list[0];
584 form_list.clear();
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(
590 BrowserThread::DB,
591 FROM_HERE,
592 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
593 base::Unretained(&backend),
594 m_facebook));
595 RunBothThreads();
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(
611 BrowserThread::DB,
612 FROM_HERE,
613 base::Bind(&NativeBackendGnome::UpdateLogin,
614 base::Unretained(&backend),
615 new_facebook,
616 &changes),
617 base::Bind(&CheckPasswordChangesWithResult,
618 &expected_changes, &changes));
619 break;
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(
626 BrowserThread::DB,
627 FROM_HERE,
628 base::Bind(&NativeBackendGnome::AddLogin,
629 base::Unretained(&backend),
630 new_facebook),
631 base::Bind(&CheckPasswordChanges, expected_changes));
632 break;
635 RunBothThreads();
636 EXPECT_EQ(2u, mock_keyring_items.size());
638 // Check that m.facebook.com login was not modified by the update.
639 BrowserThread::PostTaskAndReplyWithResult(
640 BrowserThread::DB,
641 FROM_HERE,
642 base::Bind(&NativeBackendGnome::GetLogins,
643 base::Unretained(&backend),
644 m_facebook_lookup,
645 &form_list),
646 base::Bind(&CheckTrue));
647 RunBothThreads();
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())
652 index_non_psl = 1;
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);
656 form_list.clear();
658 // Check that www.facebook.com login was modified by the update.
659 BrowserThread::PostTaskAndReplyWithResult(
660 BrowserThread::DB,
661 FROM_HERE,
662 base::Bind(&NativeBackendGnome::GetLogins,
663 base::Unretained(&backend),
664 form_facebook_,
665 &form_list),
666 base::Bind(&CheckTrue));
667 RunBothThreads();
668 // There should be two results -- the exact one, and the PSL-matched one.
669 EXPECT_EQ(2u, form_list.size());
670 index_non_psl = 0;
671 if (!form_list[index_non_psl]->original_signon_realm.empty())
672 index_non_psl = 1;
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);
699 backend.Init();
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;
710 } else {
711 form_google_.date_synced = now;
712 form_isc_.date_synced = next_day;
715 BrowserThread::PostTask(
716 BrowserThread::DB,
717 FROM_HERE,
718 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
719 base::Unretained(&backend),
720 form_google_));
721 BrowserThread::PostTask(
722 BrowserThread::DB,
723 FROM_HERE,
724 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
725 base::Unretained(&backend),
726 form_isc_));
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(
738 BrowserThread::DB,
739 FROM_HERE,
740 base::Bind(method,
741 base::Unretained(&backend),
742 base::Time(),
743 next_day,
744 &changes),
745 base::Bind(
746 &CheckPasswordChangesWithResult, &expected_changes, &changes));
747 RunBothThreads();
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");
753 // Remove form_isc_.
754 expected_changes.clear();
755 expected_changes.push_back(
756 PasswordStoreChange(PasswordStoreChange::REMOVE, form_isc_));
757 BrowserThread::PostTaskAndReplyWithResult(
758 BrowserThread::DB,
759 FROM_HERE,
760 base::Bind(method,
761 base::Unretained(&backend),
762 next_day,
763 base::Time(),
764 &changes),
765 base::Bind(
766 &CheckPasswordChangesWithResult, &expected_changes, &changes));
767 RunBothThreads();
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);
785 backend.Init();
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_))));
795 RunBothThreads();
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);
804 backend.Init();
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));
818 RunBothThreads();
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) {
830 PasswordForm result;
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
839 // m-facebook.com.
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);
873 backend.Init();
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_));
881 RunBothThreads();
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");
891 // Update login
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),
899 new_form_google,
900 &changes),
901 base::Bind(&CheckPasswordChangesWithResult, &expected_changes, &changes));
902 RunBothThreads();
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);
911 backend.Init();
913 BrowserThread::PostTask(
914 BrowserThread::DB, FROM_HERE,
915 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
916 base::Unretained(&backend), form_google_));
918 RunBothThreads();
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));
933 RunBothThreads();
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);
941 backend.Init();
943 BrowserThread::PostTask(
944 BrowserThread::DB, FROM_HERE,
945 base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
946 base::Unretained(&backend), form_google_));
948 RunBothThreads();
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));
966 RunBothThreads();
968 EXPECT_EQ(0u, mock_keyring_items.size());
971 TEST_F(NativeBackendGnomeTest, RemoveNonexistentLogin) {
972 NativeBackendGnome backend(42);
973 backend.Init();
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_));
981 RunBothThreads();
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));
1004 RunBothThreads();
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);
1016 backend.Init();
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_));
1024 RunBothThreads();
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),
1036 form_isc_,
1037 &changes),
1038 base::Bind(&CheckPasswordChangesWithResult,
1039 base::Owned(new PasswordStoreChangeList), &changes));
1040 RunBothThreads();
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);
1049 backend.Init();
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_));
1056 RunBothThreads();
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),
1069 form_google_,
1070 &changes),
1071 base::Bind(&CheckPasswordChangesWithResult, &expected_changes, &changes));
1072 RunBothThreads();
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);
1081 backend.Init();
1083 PasswordStoreChangeList changes;
1084 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
1085 form_google_));
1086 BrowserThread::PostTaskAndReplyWithResult(
1087 BrowserThread::DB, FROM_HERE,
1088 base::Bind(&NativeBackendGnome::AddLogin,
1089 base::Unretained(&backend), form_google_),
1090 base::Bind(&CheckPasswordChanges, changes));
1092 changes.clear();
1093 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
1094 form_google_));
1095 form_google_.times_used++;
1096 form_google_.submit_element = UTF8ToUTF16("submit2");
1097 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
1098 form_google_));
1100 BrowserThread::PostTaskAndReplyWithResult(
1101 BrowserThread::DB, FROM_HERE,
1102 base::Bind(&NativeBackendGnome::AddLogin,
1103 base::Unretained(&backend), form_google_),
1104 base::Bind(&CheckPasswordChanges, changes));
1106 RunBothThreads();
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);
1115 backend.Init();
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,
1139 &form_list),
1140 base::Bind(&CheckTrue));
1142 RunBothThreads();
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);
1158 backend.Init();
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_));
1175 RunBothThreads();
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));
1196 RunBothThreads();
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.