ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / password_manager / native_backend_libsecret_unittest.cc
blobc0687dedba0c4ced1aa27e89f970ca3fa7b4e94d
1 // Copyright (c) 2015 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/prefs/pref_service.h"
9 #include "base/stl_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/password_manager/native_backend_libsecret.h"
16 #include "chrome/test/base/testing_profile.h"
17 #include "components/autofill/core/common/password_form.h"
18 #include "components/password_manager/core/browser/psl_matching_helper.h"
19 #include "components/password_manager/core/common/password_manager_pref_names.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 using autofill::PasswordForm;
23 using base::UTF8ToUTF16;
24 using base::UTF16ToUTF8;
25 using password_manager::PasswordStoreChange;
26 using password_manager::PasswordStoreChangeList;
28 namespace {
30 // What follows is a very simple implementation of the subset of the Libsecret
31 // API that we actually use. It gets substituted for the real one by
32 // MockLibsecretLoader, which hooks into the facility normally used to load
33 // the libsecret library at runtime to avoid a static dependency on it.
35 struct MockSecretValue {
36 gchar* password;
37 explicit MockSecretValue(gchar* password) : password(password) {}
38 ~MockSecretValue() { g_free(password); }
41 struct MockSecretItem {
42 MockSecretValue* value;
43 GHashTable* attributes;
45 MockSecretItem(MockSecretValue* value, GHashTable* attributes)
46 : value(value), attributes(attributes) {}
47 ~MockSecretItem() {
48 delete value;
49 g_hash_table_destroy(attributes);
52 void RemoveAttribute(const char* keyname) {
53 g_hash_table_remove(attributes, keyname);
57 bool Matches(MockSecretItem* item, GHashTable* query) {
58 GHashTable* attributes = item->attributes;
59 GHashTableIter iter;
60 gchar* name;
61 gchar* query_value;
62 g_hash_table_iter_init(&iter, query);
64 while (g_hash_table_iter_next(&iter, reinterpret_cast<gpointer*>(&name),
65 reinterpret_cast<gpointer*>(&query_value))) {
66 gchar* value = static_cast<gchar*>(g_hash_table_lookup(attributes, name));
67 if (value == nullptr || strcmp(value, query_value) != 0)
68 return false;
70 return true;
73 bool IsStringAttribute(const SecretSchema* schema, const std::string& name) {
74 for (size_t i = 0; schema->attributes[i].name; ++i)
75 if (name == schema->attributes[i].name)
76 return schema->attributes[i].type == SECRET_SCHEMA_ATTRIBUTE_STRING;
77 NOTREACHED() << "Requested type of nonexistent attribute";
78 return false;
81 // The list of all libsecret items we have stored.
82 ScopedVector<MockSecretItem>* global_mock_libsecret_items;
83 bool global_mock_libsecret_reject_local_ids = false;
85 gboolean mock_secret_password_store_sync(const SecretSchema* schema,
86 const gchar* collection,
87 const gchar* label,
88 const gchar* password,
89 GCancellable* cancellable,
90 GError** error,
91 ...) {
92 GHashTable* attributes =
93 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
94 va_list ap;
95 va_start(ap, error);
96 char* name;
97 while ((name = va_arg(ap, gchar*))) {
98 char* value;
99 if (IsStringAttribute(schema, name)) {
100 value = g_strdup(va_arg(ap, gchar*));
101 VLOG(1) << "Adding item attribute " << name << ", value '" << value
102 << "'";
103 } else {
104 uint32_t intvalue = va_arg(ap, uint32_t);
105 VLOG(1) << "Adding item attribute " << name << ", value " << intvalue;
106 value = g_strdup_printf("%u", intvalue);
108 g_hash_table_insert(attributes, g_strdup(name), value);
110 va_end(ap);
111 MockSecretValue* secret_value = new MockSecretValue(g_strdup(password));
112 MockSecretItem* item = new MockSecretItem(secret_value, attributes);
113 global_mock_libsecret_items->push_back(item);
114 return true;
117 GList* mock_secret_service_search_sync(SecretService* service,
118 const SecretSchema* schema,
119 GHashTable* attributes,
120 SecretSearchFlags flags,
121 GCancellable* cancellable,
122 GError** error) {
123 GList* result = nullptr;
124 for (MockSecretItem* item : *global_mock_libsecret_items) {
125 if (Matches(item, attributes))
126 result = g_list_append(result, item);
128 return result;
131 gboolean mock_secret_password_clear_sync(const SecretSchema* schema,
132 GCancellable* cancellable,
133 GError** error,
134 ...) {
135 GHashTable* attributes =
136 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
137 va_list ap;
138 va_start(ap, error);
139 char* name;
140 while ((name = va_arg(ap, gchar*))) {
141 char* value;
142 if (IsStringAttribute(schema, name)) {
143 value = g_strdup(va_arg(ap, gchar*));
144 VLOG(1) << "Adding item attribute " << name << ", value '" << value
145 << "'";
146 } else {
147 uint32_t intvalue = va_arg(ap, uint32_t);
148 VLOG(1) << "Adding item attribute " << name << ", value " << intvalue;
149 value = g_strdup_printf("%u", intvalue);
151 g_hash_table_insert(attributes, g_strdup(name), value);
153 va_end(ap);
154 for (uint32_t i = 0; i < global_mock_libsecret_items->size();)
155 if (Matches((*global_mock_libsecret_items)[i], attributes)) {
156 global_mock_libsecret_items->erase(global_mock_libsecret_items->begin() +
158 } else {
159 ++i;
161 g_hash_table_unref(attributes);
162 return true;
165 MockSecretValue* mock_secret_item_get_secret(MockSecretItem* self) {
166 return self->value;
169 const gchar* mock_secret_value_get_text(MockSecretValue* value) {
170 return value->password;
173 GHashTable* mock_secret_item_get_attributes(MockSecretItem* self) {
174 // Libsecret backend will make unreference of received attributes, so in
175 // order to save them we need to increase their reference number.
176 g_hash_table_ref(self->attributes);
177 return self->attributes;
180 gboolean mock_secret_item_load_secret_sync(MockSecretItem* self,
181 GCancellable* cancellable,
182 GError** error) {
183 return true;
186 void mock_secret_value_unref(gpointer value) {
189 // Inherit to get access to protected fields.
190 class MockLibsecretLoader : public LibsecretLoader {
191 public:
192 static bool LoadMockLibsecret() {
193 secret_password_store_sync = &mock_secret_password_store_sync;
194 secret_service_search_sync = &mock_secret_service_search_sync;
195 secret_password_clear_sync = &mock_secret_password_clear_sync;
196 secret_item_get_secret =
197 (decltype(&::secret_item_get_secret)) & mock_secret_item_get_secret;
198 secret_value_get_text =
199 (decltype(&::secret_value_get_text)) & mock_secret_value_get_text;
200 secret_item_get_attributes = (decltype(&::secret_item_get_attributes)) &
201 mock_secret_item_get_attributes;
202 secret_item_load_secret_sync = (decltype(&::secret_item_load_secret_sync)) &
203 mock_secret_item_load_secret_sync;
204 secret_value_unref =
205 (decltype(&::secret_value_unref)) & mock_secret_value_unref;
206 libsecret_loaded = true;
207 // Reset the state of the mock library.
208 global_mock_libsecret_items->clear();
209 global_mock_libsecret_reject_local_ids = false;
210 return true;
214 void CheckPasswordChanges(const PasswordStoreChangeList& expected_list,
215 const PasswordStoreChangeList& actual_list) {
216 ASSERT_EQ(expected_list.size(), actual_list.size());
217 for (size_t i = 0; i < expected_list.size(); ++i) {
218 EXPECT_EQ(expected_list[i].type(), actual_list[i].type());
219 const PasswordForm& expected = expected_list[i].form();
220 const PasswordForm& actual = actual_list[i].form();
222 EXPECT_EQ(expected.origin, actual.origin);
223 EXPECT_EQ(expected.password_value, actual.password_value);
224 EXPECT_EQ(expected.action, actual.action);
225 EXPECT_EQ(expected.username_element, actual.username_element);
226 EXPECT_EQ(expected.username_value, actual.username_value);
227 EXPECT_EQ(expected.password_element, actual.password_element);
228 EXPECT_EQ(expected.submit_element, actual.submit_element);
229 EXPECT_EQ(expected.signon_realm, actual.signon_realm);
230 EXPECT_EQ(expected.ssl_valid, actual.ssl_valid);
231 EXPECT_EQ(expected.preferred, actual.preferred);
232 EXPECT_EQ(expected.date_created, actual.date_created);
233 EXPECT_EQ(expected.blacklisted_by_user, actual.blacklisted_by_user);
234 EXPECT_EQ(expected.type, actual.type);
235 EXPECT_EQ(expected.times_used, actual.times_used);
236 EXPECT_EQ(expected.scheme, actual.scheme);
237 EXPECT_EQ(expected.date_synced, actual.date_synced);
238 EXPECT_EQ(expected.display_name, actual.display_name);
239 EXPECT_EQ(expected.avatar_url, actual.avatar_url);
240 EXPECT_EQ(expected.federation_url, actual.federation_url);
241 EXPECT_EQ(expected.skip_zero_click, actual.skip_zero_click);
242 EXPECT_EQ(expected.generation_upload_status,
243 actual.generation_upload_status);
247 } // anonymous namespace
249 class NativeBackendLibsecretTest : public testing::Test {
250 protected:
251 enum UpdateType { // Used in CheckPSLUpdate().
252 UPDATE_BY_UPDATELOGIN,
253 UPDATE_BY_ADDLOGIN,
255 enum RemoveBetweenMethod { // Used in CheckRemoveLoginsBetween().
256 CREATED,
257 SYNCED,
260 NativeBackendLibsecretTest() {}
262 void SetUp() override {
263 ASSERT_FALSE(global_mock_libsecret_items);
264 global_mock_libsecret_items = &mock_libsecret_items_;
266 ASSERT_TRUE(MockLibsecretLoader::LoadMockLibsecret());
268 form_google_.origin = GURL("http://www.google.com/");
269 form_google_.action = GURL("http://www.google.com/login");
270 form_google_.username_element = UTF8ToUTF16("user");
271 form_google_.username_value = UTF8ToUTF16("joeschmoe");
272 form_google_.password_element = UTF8ToUTF16("pass");
273 form_google_.password_value = UTF8ToUTF16("seekrit");
274 form_google_.submit_element = UTF8ToUTF16("submit");
275 form_google_.signon_realm = "http://www.google.com/";
276 form_google_.type = PasswordForm::TYPE_GENERATED;
277 form_google_.date_created = base::Time::Now();
278 form_google_.date_synced = base::Time::Now();
279 form_google_.display_name = UTF8ToUTF16("Joe Schmoe");
280 form_google_.avatar_url = GURL("http://www.google.com/avatar");
281 form_google_.federation_url = GURL("http://www.google.com/federation_url");
282 form_google_.skip_zero_click = true;
283 form_google_.generation_upload_status = PasswordForm::POSITIVE_SIGNAL_SENT;
285 form_facebook_.origin = GURL("http://www.facebook.com/");
286 form_facebook_.action = GURL("http://www.facebook.com/login");
287 form_facebook_.username_element = UTF8ToUTF16("user");
288 form_facebook_.username_value = UTF8ToUTF16("a");
289 form_facebook_.password_element = UTF8ToUTF16("password");
290 form_facebook_.password_value = UTF8ToUTF16("b");
291 form_facebook_.submit_element = UTF8ToUTF16("submit");
292 form_facebook_.signon_realm = "http://www.facebook.com/";
293 form_facebook_.date_created = base::Time::Now();
294 form_facebook_.date_synced = base::Time::Now();
295 form_facebook_.display_name = UTF8ToUTF16("Joe Schmoe");
296 form_facebook_.avatar_url = GURL("http://www.facebook.com/avatar");
297 form_facebook_.federation_url = GURL("http://www.facebook.com/federation");
298 form_facebook_.skip_zero_click = true;
299 form_facebook_.generation_upload_status = PasswordForm::NO_SIGNAL_SENT;
301 form_isc_.origin = GURL("http://www.isc.org/");
302 form_isc_.action = GURL("http://www.isc.org/auth");
303 form_isc_.username_element = UTF8ToUTF16("id");
304 form_isc_.username_value = UTF8ToUTF16("janedoe");
305 form_isc_.password_element = UTF8ToUTF16("passwd");
306 form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
307 form_isc_.submit_element = UTF8ToUTF16("login");
308 form_isc_.signon_realm = "http://www.isc.org/";
309 form_isc_.date_created = base::Time::Now();
310 form_isc_.date_synced = base::Time::Now();
312 other_auth_.origin = GURL("http://www.example.com/");
313 other_auth_.username_value = UTF8ToUTF16("username");
314 other_auth_.password_value = UTF8ToUTF16("pass");
315 other_auth_.signon_realm = "http://www.example.com/Realm";
316 other_auth_.date_created = base::Time::Now();
317 other_auth_.date_synced = base::Time::Now();
320 void TearDown() override {
321 base::MessageLoop::current()->PostTask(FROM_HERE,
322 base::MessageLoop::QuitClosure());
323 base::MessageLoop::current()->Run();
324 ASSERT_TRUE(global_mock_libsecret_items);
325 global_mock_libsecret_items = nullptr;
328 void RunUIThread() { base::MessageLoop::current()->Run(); }
330 void CheckUint32Attribute(const MockSecretItem* item,
331 const std::string& attribute,
332 uint32_t value) {
333 gpointer item_value =
334 g_hash_table_lookup(item->attributes, attribute.c_str());
335 EXPECT_TRUE(item_value) << " in attribute " << attribute;
336 if (item_value) {
337 uint32_t int_value;
338 bool conversion_ok = base::StringToUint((char*)item_value, &int_value);
339 EXPECT_TRUE(conversion_ok);
340 EXPECT_EQ(value, int_value);
344 void CheckStringAttribute(const MockSecretItem* item,
345 const std::string& attribute,
346 const std::string& value) {
347 gpointer item_value =
348 g_hash_table_lookup(item->attributes, attribute.c_str());
349 EXPECT_TRUE(item_value) << " in attribute " << attribute;
350 if (item_value) {
351 EXPECT_EQ(value, static_cast<char*>(item_value));
355 void CheckMockSecretItem(const MockSecretItem* item,
356 const PasswordForm& form,
357 const std::string& app_string) {
358 EXPECT_EQ(UTF16ToUTF8(form.password_value), item->value->password);
359 EXPECT_EQ(21u, g_hash_table_size(item->attributes));
360 CheckStringAttribute(item, "origin_url", form.origin.spec());
361 CheckStringAttribute(item, "action_url", form.action.spec());
362 CheckStringAttribute(item, "username_element",
363 UTF16ToUTF8(form.username_element));
364 CheckStringAttribute(item, "username_value",
365 UTF16ToUTF8(form.username_value));
366 CheckStringAttribute(item, "password_element",
367 UTF16ToUTF8(form.password_element));
368 CheckStringAttribute(item, "submit_element",
369 UTF16ToUTF8(form.submit_element));
370 CheckStringAttribute(item, "signon_realm", form.signon_realm);
371 CheckUint32Attribute(item, "ssl_valid", form.ssl_valid);
372 CheckUint32Attribute(item, "preferred", form.preferred);
373 // We don't check the date created. It varies.
374 CheckUint32Attribute(item, "blacklisted_by_user", form.blacklisted_by_user);
375 CheckUint32Attribute(item, "type", form.type);
376 CheckUint32Attribute(item, "times_used", form.times_used);
377 CheckUint32Attribute(item, "scheme", form.scheme);
378 CheckStringAttribute(
379 item, "date_synced",
380 base::Int64ToString(form.date_synced.ToInternalValue()));
381 CheckStringAttribute(item, "display_name", UTF16ToUTF8(form.display_name));
382 CheckStringAttribute(item, "avatar_url", form.avatar_url.spec());
383 CheckStringAttribute(item, "federation_url", form.federation_url.spec());
384 CheckUint32Attribute(item, "skip_zero_click", form.skip_zero_click);
385 CheckUint32Attribute(item, "generation_upload_status",
386 form.generation_upload_status);
387 CheckStringAttribute(item, "application", app_string);
390 // Saves |credentials| and then gets logins matching |url| and |scheme|.
391 // Returns true when something is found, and in such case copies the result to
392 // |result| when |result| is not nullptr. (Note that there can be max. 1
393 // result derived from |credentials|.)
394 bool CheckCredentialAvailability(const PasswordForm& credentials,
395 const GURL& url,
396 const PasswordForm::Scheme& scheme,
397 PasswordForm* result) {
398 NativeBackendLibsecret backend(321);
400 backend.AddLogin(credentials);
402 PasswordForm target_form;
403 target_form.origin = url;
404 target_form.signon_realm = url.spec();
405 if (scheme != PasswordForm::SCHEME_HTML) {
406 // For non-HTML forms, the realm used for authentication
407 // (http://tools.ietf.org/html/rfc1945#section-10.2) is appended to the
408 // signon_realm. Just use a default value for now.
409 target_form.signon_realm.append("Realm");
410 target_form.scheme = scheme;
412 ScopedVector<autofill::PasswordForm> form_list;
413 backend.GetLogins(target_form, &form_list);
415 EXPECT_EQ(1u, global_mock_libsecret_items->size());
416 if (!global_mock_libsecret_items->empty())
417 CheckMockSecretItem((*global_mock_libsecret_items)[0], credentials,
418 "chrome-321");
419 global_mock_libsecret_items->clear();
421 if (form_list.empty())
422 return false;
423 EXPECT_EQ(1u, form_list.size());
424 if (result)
425 *result = *form_list[0];
426 return true;
429 // Test that updating does not use PSL matching: Add a www.facebook.com
430 // password, then use PSL matching to get a copy of it for m.facebook.com, and
431 // add that copy as well. Now update the www.facebook.com password -- the
432 // m.facebook.com password should not get updated. Depending on the argument,
433 // the credential update is done via UpdateLogin or AddLogin.
434 void CheckPSLUpdate(UpdateType update_type) {
435 NativeBackendLibsecret backend(321);
437 backend.AddLogin(form_facebook_);
439 // Get the PSL-matched copy of the saved login for m.facebook.
440 const GURL kMobileURL("http://m.facebook.com/");
441 PasswordForm m_facebook_lookup;
442 m_facebook_lookup.origin = kMobileURL;
443 m_facebook_lookup.signon_realm = kMobileURL.spec();
444 ScopedVector<autofill::PasswordForm> form_list;
445 backend.GetLogins(m_facebook_lookup, &form_list);
447 EXPECT_EQ(1u, global_mock_libsecret_items->size());
448 EXPECT_EQ(1u, form_list.size());
449 PasswordForm m_facebook = *form_list[0];
450 form_list.clear();
451 EXPECT_EQ(kMobileURL, m_facebook.origin);
452 EXPECT_EQ(kMobileURL.spec(), m_facebook.signon_realm);
454 // Add the PSL-matched copy to saved logins.
455 backend.AddLogin(m_facebook);
456 EXPECT_EQ(2u, global_mock_libsecret_items->size());
458 // Update www.facebook.com login.
459 PasswordForm new_facebook(form_facebook_);
460 const base::string16 kOldPassword(form_facebook_.password_value);
461 const base::string16 kNewPassword(UTF8ToUTF16("new_b"));
462 EXPECT_NE(kOldPassword, kNewPassword);
463 new_facebook.password_value = kNewPassword;
464 scoped_ptr<PasswordStoreChangeList> not_used(new PasswordStoreChangeList());
465 switch (update_type) {
466 case UPDATE_BY_UPDATELOGIN:
467 backend.UpdateLogin(new_facebook, not_used.get());
468 break;
469 case UPDATE_BY_ADDLOGIN:
470 backend.AddLogin(new_facebook);
471 break;
474 EXPECT_EQ(2u, global_mock_libsecret_items->size());
476 // Check that m.facebook.com login was not modified by the update.
477 backend.GetLogins(m_facebook_lookup, &form_list);
479 // There should be two results -- the exact one, and the PSL-matched one.
480 EXPECT_EQ(2u, form_list.size());
481 size_t index_non_psl = 0;
482 if (!form_list[index_non_psl]->original_signon_realm.empty())
483 index_non_psl = 1;
484 EXPECT_EQ(kMobileURL, form_list[index_non_psl]->origin);
485 EXPECT_EQ(kMobileURL.spec(), form_list[index_non_psl]->signon_realm);
486 EXPECT_EQ(kOldPassword, form_list[index_non_psl]->password_value);
487 form_list.clear();
489 // Check that www.facebook.com login was modified by the update.
490 backend.GetLogins(form_facebook_, &form_list);
491 // There should be two results -- the exact one, and the PSL-matched one.
492 EXPECT_EQ(2u, form_list.size());
493 index_non_psl = 0;
494 if (!form_list[index_non_psl]->original_signon_realm.empty())
495 index_non_psl = 1;
496 EXPECT_EQ(form_facebook_.origin, form_list[index_non_psl]->origin);
497 EXPECT_EQ(form_facebook_.signon_realm,
498 form_list[index_non_psl]->signon_realm);
499 EXPECT_EQ(kNewPassword, form_list[index_non_psl]->password_value);
500 form_list.clear();
503 // Checks various types of matching for forms with a non-HTML |scheme|.
504 void CheckMatchingWithScheme(const PasswordForm::Scheme& scheme) {
505 ASSERT_NE(PasswordForm::SCHEME_HTML, scheme);
506 other_auth_.scheme = scheme;
508 // Don't match a non-HTML form with an HTML form.
509 EXPECT_FALSE(
510 CheckCredentialAvailability(other_auth_, GURL("http://www.example.com"),
511 PasswordForm::SCHEME_HTML, nullptr));
512 // Don't match an HTML form with non-HTML auth form.
513 EXPECT_FALSE(CheckCredentialAvailability(
514 form_google_, GURL("http://www.google.com/"), scheme, nullptr));
515 // Don't match two different non-HTML auth forms with different origin.
516 EXPECT_FALSE(CheckCredentialAvailability(
517 other_auth_, GURL("http://first.example.com"), scheme, nullptr));
518 // Do match non-HTML forms from the same origin.
519 EXPECT_TRUE(CheckCredentialAvailability(
520 other_auth_, GURL("http://www.example.com/"), scheme, nullptr));
523 void CheckRemoveLoginsBetween(RemoveBetweenMethod date_to_test) {
524 NativeBackendLibsecret backend(42);
526 base::Time now = base::Time::Now();
527 base::Time next_day = now + base::TimeDelta::FromDays(1);
528 form_google_.date_synced = base::Time();
529 form_isc_.date_synced = base::Time();
530 form_google_.date_created = now;
531 form_isc_.date_created = now;
532 if (date_to_test == CREATED) {
533 form_google_.date_created = now;
534 form_isc_.date_created = next_day;
535 } else {
536 form_google_.date_synced = now;
537 form_isc_.date_synced = next_day;
540 backend.AddLogin(form_google_);
541 backend.AddLogin(form_isc_);
543 PasswordStoreChangeList expected_changes;
544 expected_changes.push_back(
545 PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_));
546 PasswordStoreChangeList changes;
547 bool (NativeBackendLibsecret::*method)(
548 base::Time, base::Time, password_manager::PasswordStoreChangeList*) =
549 date_to_test == CREATED
550 ? &NativeBackendLibsecret::RemoveLoginsCreatedBetween
551 : &NativeBackendLibsecret::RemoveLoginsSyncedBetween;
553 EXPECT_TRUE(base::Bind(method, base::Unretained(&backend), base::Time(),
554 next_day, &changes).Run());
555 CheckPasswordChanges(expected_changes, changes);
557 EXPECT_EQ(1u, global_mock_libsecret_items->size());
558 if (!global_mock_libsecret_items->empty() > 0)
559 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_isc_,
560 "chrome-42");
562 // Remove form_isc_.
563 expected_changes.clear();
564 expected_changes.push_back(
565 PasswordStoreChange(PasswordStoreChange::REMOVE, form_isc_));
567 EXPECT_TRUE(base::Bind(method, base::Unretained(&backend), next_day,
568 base::Time(), &changes).Run());
569 CheckPasswordChanges(expected_changes, changes);
571 EXPECT_TRUE(global_mock_libsecret_items->empty());
574 base::MessageLoopForUI message_loop_;
576 // Provide some test forms to avoid having to set them up in each test.
577 PasswordForm form_google_;
578 PasswordForm form_facebook_;
579 PasswordForm form_isc_;
580 PasswordForm other_auth_;
582 ScopedVector<MockSecretItem> mock_libsecret_items_;
585 TEST_F(NativeBackendLibsecretTest, BasicAddLogin) {
586 NativeBackendLibsecret backend(42);
588 backend.AddLogin(form_google_);
590 EXPECT_EQ(1u, global_mock_libsecret_items->size());
591 if (!global_mock_libsecret_items->empty())
592 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_google_,
593 "chrome-42");
596 TEST_F(NativeBackendLibsecretTest, BasicListLogins) {
597 NativeBackendLibsecret backend(42);
599 backend.AddLogin(form_google_);
601 ScopedVector<autofill::PasswordForm> form_list;
602 backend.GetAutofillableLogins(&form_list);
604 // Quick check that we got something back.
605 EXPECT_EQ(1u, form_list.size());
606 form_list.clear();
608 EXPECT_EQ(1u, global_mock_libsecret_items->size());
609 if (!global_mock_libsecret_items->empty())
610 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_google_,
611 "chrome-42");
614 // Save a password for www.facebook.com and see it suggested for m.facebook.com.
615 TEST_F(NativeBackendLibsecretTest, PSLMatchingPositive) {
616 PasswordForm result;
617 const GURL kMobileURL("http://m.facebook.com/");
618 EXPECT_TRUE(CheckCredentialAvailability(form_facebook_, kMobileURL,
619 PasswordForm::SCHEME_HTML, &result));
620 EXPECT_EQ(kMobileURL, result.origin);
621 EXPECT_EQ(kMobileURL.spec(), result.signon_realm);
624 // Save a password for www.facebook.com and see it not suggested for
625 // m-facebook.com.
626 TEST_F(NativeBackendLibsecretTest, PSLMatchingNegativeDomainMismatch) {
627 EXPECT_FALSE(CheckCredentialAvailability(form_facebook_,
628 GURL("http://m-facebook.com/"),
629 PasswordForm::SCHEME_HTML, nullptr));
632 // Test PSL matching is off for domains excluded from it.
633 TEST_F(NativeBackendLibsecretTest, PSLMatchingDisabledDomains) {
634 EXPECT_FALSE(CheckCredentialAvailability(form_google_,
635 GURL("http://one.google.com/"),
636 PasswordForm::SCHEME_HTML, nullptr));
639 // Make sure PSL matches aren't available for non-HTML forms.
640 TEST_F(NativeBackendLibsecretTest, PSLMatchingDisabledForNonHTMLForms) {
641 CheckMatchingWithScheme(PasswordForm::SCHEME_BASIC);
642 CheckMatchingWithScheme(PasswordForm::SCHEME_DIGEST);
643 CheckMatchingWithScheme(PasswordForm::SCHEME_OTHER);
646 TEST_F(NativeBackendLibsecretTest, PSLUpdatingStrictUpdateLogin) {
647 CheckPSLUpdate(UPDATE_BY_UPDATELOGIN);
650 TEST_F(NativeBackendLibsecretTest, PSLUpdatingStrictAddLogin) {
651 // TODO(vabr): if AddLogin becomes no longer valid for existing logins, then
652 // just delete this test.
653 CheckPSLUpdate(UPDATE_BY_ADDLOGIN);
656 TEST_F(NativeBackendLibsecretTest, BasicUpdateLogin) {
657 NativeBackendLibsecret backend(42);
659 backend.AddLogin(form_google_);
661 PasswordForm new_form_google(form_google_);
662 new_form_google.times_used = 1;
663 new_form_google.action = GURL("http://www.google.com/different/login");
665 EXPECT_EQ(1u, global_mock_libsecret_items->size());
666 if (!global_mock_libsecret_items->empty())
667 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_google_,
668 "chrome-42");
670 // Update login
671 PasswordStoreChangeList changes;
672 backend.UpdateLogin(new_form_google, &changes);
674 ASSERT_EQ(1u, changes.size());
675 EXPECT_EQ(PasswordStoreChange::UPDATE, changes.front().type());
676 EXPECT_EQ(new_form_google, changes.front().form());
677 EXPECT_EQ(1u, global_mock_libsecret_items->size());
678 if (!global_mock_libsecret_items->empty())
679 CheckMockSecretItem((*global_mock_libsecret_items)[0], new_form_google,
680 "chrome-42");
683 TEST_F(NativeBackendLibsecretTest, BasicRemoveLogin) {
684 NativeBackendLibsecret backend(42);
686 backend.AddLogin(form_google_);
688 EXPECT_EQ(1u, global_mock_libsecret_items->size());
689 if (!global_mock_libsecret_items->empty())
690 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_google_,
691 "chrome-42");
693 backend.RemoveLogin(form_google_);
695 EXPECT_TRUE(global_mock_libsecret_items->empty());
698 // Verify fix for http://crbug.com/408783.
699 TEST_F(NativeBackendLibsecretTest, RemoveLoginActionMismatch) {
700 NativeBackendLibsecret backend(42);
702 backend.AddLogin(form_google_);
704 EXPECT_EQ(1u, global_mock_libsecret_items->size());
705 if (!global_mock_libsecret_items->empty())
706 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_google_,
707 "chrome-42");
709 // Action url match not required for removal.
710 form_google_.action = GURL("https://some.other.url.com/path");
712 backend.RemoveLogin(form_google_);
714 EXPECT_TRUE(global_mock_libsecret_items->empty());
717 TEST_F(NativeBackendLibsecretTest, RemoveNonexistentLogin) {
718 NativeBackendLibsecret backend(42);
720 // First add an unrelated login.
721 backend.AddLogin(form_google_);
723 EXPECT_EQ(1u, global_mock_libsecret_items->size());
724 if (!global_mock_libsecret_items->empty())
725 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_google_,
726 "chrome-42");
728 // Attempt to remove a login that doesn't exist.
729 backend.RemoveLogin(form_isc_);
731 // Make sure we can still get the first form back.
732 ScopedVector<autofill::PasswordForm> form_list;
733 backend.GetAutofillableLogins(&form_list);
735 // Quick check that we got something back.
736 EXPECT_EQ(1u, form_list.size());
737 form_list.clear();
739 EXPECT_EQ(1u, global_mock_libsecret_items->size());
740 if (!global_mock_libsecret_items->empty())
741 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_google_,
742 "chrome-42");
745 TEST_F(NativeBackendLibsecretTest, UpdateNonexistentLogin) {
746 NativeBackendLibsecret backend(42);
748 // First add an unrelated login.
749 backend.AddLogin(form_google_);
751 EXPECT_EQ(1u, global_mock_libsecret_items->size());
752 if (!global_mock_libsecret_items->empty())
753 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_google_,
754 "chrome-42");
756 // Attempt to update a login that doesn't exist.
757 PasswordStoreChangeList changes;
758 backend.UpdateLogin(form_isc_, &changes);
760 EXPECT_EQ(PasswordStoreChangeList(), changes);
761 EXPECT_EQ(1u, global_mock_libsecret_items->size());
762 if (!global_mock_libsecret_items->empty())
763 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_google_,
764 "chrome-42");
767 TEST_F(NativeBackendLibsecretTest, AddDuplicateLogin) {
768 NativeBackendLibsecret backend(42);
770 PasswordStoreChangeList expected_changes, actual_changes;
771 expected_changes.push_back(
772 PasswordStoreChange(PasswordStoreChange::ADD, form_google_));
773 actual_changes = backend.AddLogin(form_google_);
774 CheckPasswordChanges(expected_changes, actual_changes);
776 expected_changes.clear();
777 expected_changes.push_back(
778 PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_));
779 form_google_.times_used++;
780 expected_changes.push_back(
781 PasswordStoreChange(PasswordStoreChange::ADD, form_google_));
783 actual_changes = backend.AddLogin(form_google_);
784 CheckPasswordChanges(expected_changes, actual_changes);
786 EXPECT_EQ(1u, global_mock_libsecret_items->size());
787 if (!global_mock_libsecret_items->empty())
788 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_google_,
789 "chrome-42");
792 TEST_F(NativeBackendLibsecretTest, ListLoginsAppends) {
793 NativeBackendLibsecret backend(42);
795 backend.AddLogin(form_google_);
797 // Send the same request twice with the same list both times.
798 ScopedVector<autofill::PasswordForm> form_list;
799 backend.GetAutofillableLogins(&form_list);
800 backend.GetAutofillableLogins(&form_list);
802 // Quick check that we got two results back.
803 EXPECT_EQ(2u, form_list.size());
804 form_list.clear();
806 EXPECT_EQ(1u, global_mock_libsecret_items->size());
807 if (!global_mock_libsecret_items->empty())
808 CheckMockSecretItem((*global_mock_libsecret_items)[0], form_google_,
809 "chrome-42");
812 TEST_F(NativeBackendLibsecretTest, AndroidCredentials) {
813 NativeBackendLibsecret backend(42);
814 backend.Init();
816 PasswordForm observed_android_form;
817 observed_android_form.scheme = PasswordForm::SCHEME_HTML;
818 observed_android_form.signon_realm =
819 "android://7x7IDboo8u9YKraUsbmVkuf1-@net.rateflix.app/";
820 PasswordForm saved_android_form = observed_android_form;
821 saved_android_form.username_value = base::UTF8ToUTF16("randomusername");
822 saved_android_form.password_value = base::UTF8ToUTF16("password");
823 saved_android_form.date_created = base::Time::Now();
825 backend.AddLogin(saved_android_form);
827 ScopedVector<autofill::PasswordForm> form_list;
828 backend.GetAutofillableLogins(&form_list);
830 EXPECT_EQ(1u, form_list.size());
831 EXPECT_EQ(saved_android_form, *form_list[0]);
835 TEST_F(NativeBackendLibsecretTest, RemoveLoginsCreatedBetween) {
836 CheckRemoveLoginsBetween(CREATED);
839 TEST_F(NativeBackendLibsecretTest, RemoveLoginsSyncedBetween) {
840 CheckRemoveLoginsBetween(SYNCED);
843 TEST_F(NativeBackendLibsecretTest, SomeKeyringAttributesAreMissing) {
844 // Absent attributes should be filled with default values.
845 NativeBackendLibsecret backend(42);
847 backend.AddLogin(form_google_);
849 EXPECT_EQ(1u, global_mock_libsecret_items->size());
850 // Remove a string attribute.
851 (*global_mock_libsecret_items)[0]->RemoveAttribute("avatar_url");
852 // Remove an integer attribute.
853 (*global_mock_libsecret_items)[0]->RemoveAttribute("ssl_valid");
855 ScopedVector<autofill::PasswordForm> form_list;
856 backend.GetAutofillableLogins(&form_list);
858 EXPECT_EQ(1u, form_list.size());
859 EXPECT_EQ(GURL(""), form_list[0]->avatar_url);
860 EXPECT_FALSE(form_list[0]->ssl_valid);
863 // TODO(mdm): add more basic tests here at some point.