Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / password_manager / native_backend_gnome_x_unittest.cc
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.
4
5 #include <stdarg.h>
6
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_gnome_x.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 "content/public/test/test_browser_thread.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 using autofill::PasswordForm;
24 using base::UTF8ToUTF16;
25 using base::UTF16ToUTF8;
26 using content::BrowserThread;
27 using password_manager::PasswordStoreChange;
28 using password_manager::PasswordStoreChangeList;
29
30 namespace {
31
32 // What follows is a very simple implementation of the subset of the GNOME
33 // Keyring API that we actually use. It gets substituted for the real one by
34 // MockGnomeKeyringLoader, which hooks into the facility normally used to load
35 // the GNOME Keyring library at runtime to avoid a static dependency on it.
36
37 struct MockKeyringItem {
38   MockKeyringItem() {}
39   MockKeyringItem(const char* keyring,
40                   const std::string& display_name,
41                   const std::string& password)
42     : keyring(keyring ? keyring : "login"),
43       display_name(display_name),
44       password(password) {}
45
46   struct ItemAttribute {
47     ItemAttribute() : type(UINT32), value_uint32(0) {}
48     explicit ItemAttribute(uint32_t value)
49       : type(UINT32), value_uint32(value) {}
50     explicit ItemAttribute(const std::string& value)
51       : type(STRING), value_string(value) {}
52
53     bool Equals(const ItemAttribute& x) const {
54       if (type != x.type) return false;
55       return (type == STRING) ? value_string == x.value_string
56                               : value_uint32 == x.value_uint32;
57     }
58
59     enum Type { UINT32, STRING } type;
60     uint32_t value_uint32;
61     std::string value_string;
62   };
63
64   typedef std::map<std::string, ItemAttribute> attribute_map;
65   typedef std::vector<std::pair<std::string, ItemAttribute> > attribute_query;
66
67   bool Matches(const attribute_query& query) const {
68     // The real GNOME Keyring doesn't match empty queries.
69     if (query.empty()) return false;
70     for (size_t i = 0; i < query.size(); ++i) {
71       attribute_map::const_iterator match = attributes.find(query[i].first);
72       if (match == attributes.end()) return false;
73       if (!match->second.Equals(query[i].second)) return false;
74     }
75     return true;
76   }
77
78   std::string keyring;
79   std::string display_name;
80   std::string password;
81
82   attribute_map attributes;
83 };
84
85 // The list of all keyring items we have stored.
86 std::vector<MockKeyringItem> mock_keyring_items;
87 bool mock_keyring_reject_local_ids = false;
88
89 bool IsStringAttribute(const GnomeKeyringPasswordSchema* schema,
90                        const std::string& name) {
91   for (size_t i = 0; schema->attributes[i].name; ++i)
92     if (name == schema->attributes[i].name)
93       return schema->attributes[i].type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
94   NOTREACHED() << "Requested type of nonexistent attribute";
95   return false;
96 }
97
98 gboolean mock_gnome_keyring_is_available() {
99   return true;
100 }
101
102 gpointer mock_gnome_keyring_store_password(
103     const GnomeKeyringPasswordSchema* schema,
104     const gchar* keyring,
105     const gchar* display_name,
106     const gchar* password,
107     GnomeKeyringOperationDoneCallback callback,
108     gpointer data,
109     GDestroyNotify destroy_data,
110     ...) {
111   mock_keyring_items.push_back(
112       MockKeyringItem(keyring, display_name, password));
113   MockKeyringItem* item = &mock_keyring_items.back();
114   const std::string keyring_desc =
115       keyring ? base::StringPrintf("keyring %s", keyring)
116               : std::string("default keyring");
117   VLOG(1) << "Adding item with origin " << display_name
118           << " to " << keyring_desc;
119   va_list ap;
120   va_start(ap, destroy_data);
121   char* name;
122   while ((name = va_arg(ap, gchar*))) {
123     if (IsStringAttribute(schema, name)) {
124       item->attributes[name] =
125           MockKeyringItem::ItemAttribute(va_arg(ap, gchar*));
126       VLOG(1) << "Adding item attribute " << name
127               << ", value '" << item->attributes[name].value_string << "'";
128     } else {
129       item->attributes[name] =
130           MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t));
131       VLOG(1) << "Adding item attribute " << name
132               << ", value " << item->attributes[name].value_uint32;
133     }
134   }
135   va_end(ap);
136   // As a hack to ease testing migration, make it possible to reject the new
137   // format for the app string. This way we can add them easily to migrate.
138   if (mock_keyring_reject_local_ids) {
139     MockKeyringItem::attribute_map::iterator it =
140         item->attributes.find("application");
141     if (it != item->attributes.end() &&
142         it->second.type == MockKeyringItem::ItemAttribute::STRING &&
143         base::StringPiece(it->second.value_string).starts_with("chrome-")) {
144       mock_keyring_items.pop_back();
145       // GnomeKeyringResult, data
146       callback(GNOME_KEYRING_RESULT_IO_ERROR, data);
147       return NULL;
148     }
149   }
150   // GnomeKeyringResult, data
151   callback(GNOME_KEYRING_RESULT_OK, data);
152   return NULL;
153 }
154
155 gpointer mock_gnome_keyring_delete_password(
156     const GnomeKeyringPasswordSchema* schema,
157     GnomeKeyringOperationDoneCallback callback,
158     gpointer data,
159     GDestroyNotify destroy_data,
160     ...) {
161   MockKeyringItem::attribute_query query;
162   va_list ap;
163   va_start(ap, destroy_data);
164   char* name;
165   while ((name = va_arg(ap, gchar*))) {
166     if (IsStringAttribute(schema, name)) {
167       query.push_back(make_pair(std::string(name),
168           MockKeyringItem::ItemAttribute(va_arg(ap, gchar*))));
169       VLOG(1) << "Querying with item attribute " << name
170               << ", value '" << query.back().second.value_string << "'";
171     } else {
172       query.push_back(make_pair(std::string(name),
173           MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t))));
174       VLOG(1) << "Querying with item attribute " << name
175               << ", value " << query.back().second.value_uint32;
176     }
177   }
178   va_end(ap);
179   bool deleted = false;
180   for (size_t i = mock_keyring_items.size(); i > 0; --i) {
181     const MockKeyringItem* item = &mock_keyring_items[i - 1];
182     if (item->Matches(query)) {
183       VLOG(1) << "Deleting item with origin " <<  item->display_name;
184       mock_keyring_items.erase(mock_keyring_items.begin() + (i - 1));
185       deleted = true;
186     }
187   }
188   // GnomeKeyringResult, data
189   callback(deleted ? GNOME_KEYRING_RESULT_OK
190                    : GNOME_KEYRING_RESULT_NO_MATCH, data);
191   return NULL;
192 }
193
194 gpointer mock_gnome_keyring_find_items(
195     GnomeKeyringItemType type,
196     GnomeKeyringAttributeList* attributes,
197     GnomeKeyringOperationGetListCallback callback,
198     gpointer data,
199     GDestroyNotify destroy_data) {
200   MockKeyringItem::attribute_query query;
201   for (size_t i = 0; i < attributes->len; ++i) {
202     GnomeKeyringAttribute attribute =
203         g_array_index(attributes, GnomeKeyringAttribute, i);
204     if (attribute.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) {
205       query.push_back(
206           make_pair(std::string(attribute.name),
207                     MockKeyringItem::ItemAttribute(attribute.value.string)));
208       VLOG(1) << "Querying with item attribute " << attribute.name
209               << ", value '" << query.back().second.value_string << "'";
210     } else {
211       query.push_back(
212           make_pair(std::string(attribute.name),
213                     MockKeyringItem::ItemAttribute(attribute.value.integer)));
214       VLOG(1) << "Querying with item attribute " << attribute.name << ", value "
215               << query.back().second.value_uint32;
216     }
217   }
218   // Find matches and add them to a list of results.
219   GList* results = NULL;
220   for (size_t i = 0; i < mock_keyring_items.size(); ++i) {
221     const MockKeyringItem* item = &mock_keyring_items[i];
222     if (item->Matches(query)) {
223       GnomeKeyringFound* found = new GnomeKeyringFound;
224       found->keyring = strdup(item->keyring.c_str());
225       found->item_id = i;
226       found->attributes = gnome_keyring_attribute_list_new();
227       for (MockKeyringItem::attribute_map::const_iterator it =
228                item->attributes.begin();
229            it != item->attributes.end();
230            ++it) {
231         if (it->second.type == MockKeyringItem::ItemAttribute::STRING) {
232           gnome_keyring_attribute_list_append_string(
233               found->attributes, it->first.c_str(),
234               it->second.value_string.c_str());
235         } else {
236           gnome_keyring_attribute_list_append_uint32(
237               found->attributes, it->first.c_str(),
238               it->second.value_uint32);
239         }
240       }
241       found->secret = strdup(item->password.c_str());
242       results = g_list_prepend(results, found);
243     }
244   }
245   // GnomeKeyringResult, GList*, data
246   callback(results ? GNOME_KEYRING_RESULT_OK
247                    : GNOME_KEYRING_RESULT_NO_MATCH, results, data);
248   // Now free the list of results.
249   GList* element = g_list_first(results);
250   while (element) {
251     GnomeKeyringFound* found = static_cast<GnomeKeyringFound*>(element->data);
252     free(found->keyring);
253     gnome_keyring_attribute_list_free(found->attributes);
254     free(found->secret);
255     delete found;
256     element = g_list_next(element);
257   }
258   g_list_free(results);
259   return NULL;
260 }
261
262 const gchar* mock_gnome_keyring_result_to_message(GnomeKeyringResult res) {
263   return "mock keyring simulating failure";
264 }
265
266 // Inherit to get access to protected fields.
267 class MockGnomeKeyringLoader : public GnomeKeyringLoader {
268  public:
269   static bool LoadMockGnomeKeyring() {
270     if (!LoadGnomeKeyring())
271       return false;
272 #define GNOME_KEYRING_ASSIGN_POINTER(name) \
273   gnome_keyring_##name = &mock_gnome_keyring_##name;
274     GNOME_KEYRING_FOR_EACH_MOCKED_FUNC(GNOME_KEYRING_ASSIGN_POINTER)
275 #undef GNOME_KEYRING_ASSIGN_POINTER
276     keyring_loaded = true;
277     // Reset the state of the mock library.
278     mock_keyring_items.clear();
279     mock_keyring_reject_local_ids = false;
280     return true;
281   }
282 };
283
284 void CheckPasswordChanges(const PasswordStoreChangeList& expected_list,
285                           const PasswordStoreChangeList& actual_list) {
286   ASSERT_EQ(expected_list.size(), actual_list.size());
287   for (size_t i = 0; i < expected_list.size(); ++i) {
288     EXPECT_EQ(expected_list[i].type(), actual_list[i].type());
289     const PasswordForm& expected = expected_list[i].form();
290     const PasswordForm& actual = actual_list[i].form();
291
292     EXPECT_EQ(expected.origin, actual.origin);
293     EXPECT_EQ(expected.password_value, actual.password_value);
294     EXPECT_EQ(expected.action, actual.action);
295     EXPECT_EQ(expected.username_element, actual.username_element);
296     EXPECT_EQ(expected.username_value, actual.username_value);
297     EXPECT_EQ(expected.password_element, actual.password_element);
298     EXPECT_EQ(expected.submit_element, actual.submit_element);
299     EXPECT_EQ(expected.signon_realm, actual.signon_realm);
300     EXPECT_EQ(expected.ssl_valid, actual.ssl_valid);
301     EXPECT_EQ(expected.preferred, actual.preferred);
302     // We don't check the date created. It varies due to bug in the
303     // serialization. Integer seconds are saved instead of microseconds.
304     EXPECT_EQ(expected.blacklisted_by_user, actual.blacklisted_by_user);
305     EXPECT_EQ(expected.type, actual.type);
306     EXPECT_EQ(expected.times_used, actual.times_used);
307     EXPECT_EQ(expected.scheme, actual.scheme);
308     EXPECT_EQ(expected.date_synced, actual.date_synced);
309   }
310 }
311
312 void CheckPasswordChangesWithResult(const PasswordStoreChangeList* expected,
313                                     const PasswordStoreChangeList* actual,
314                                     bool result) {
315   EXPECT_TRUE(result);
316   CheckPasswordChanges(*expected, *actual);
317 }
318
319 }  // anonymous namespace
320
321 class NativeBackendGnomeTest : public testing::Test {
322  protected:
323   enum UpdateType {  // Used in CheckPSLUpdate().
324     UPDATE_BY_UPDATELOGIN,
325     UPDATE_BY_ADDLOGIN,
326   };
327   enum RemoveBetweenMethod {  // Used in CheckRemoveLoginsBetween().
328     CREATED,
329     SYNCED,
330   };
331
332   NativeBackendGnomeTest()
333       : ui_thread_(BrowserThread::UI, &message_loop_),
334         db_thread_(BrowserThread::DB) {
335   }
336
337   virtual void SetUp() {
338     ASSERT_TRUE(db_thread_.Start());
339
340     ASSERT_TRUE(MockGnomeKeyringLoader::LoadMockGnomeKeyring());
341
342     form_google_.origin = GURL("http://www.google.com/");
343     form_google_.action = GURL("http://www.google.com/login");
344     form_google_.username_element = UTF8ToUTF16("user");
345     form_google_.username_value = UTF8ToUTF16("joeschmoe");
346     form_google_.password_element = UTF8ToUTF16("pass");
347     form_google_.password_value = UTF8ToUTF16("seekrit");
348     form_google_.submit_element = UTF8ToUTF16("submit");
349     form_google_.signon_realm = "http://www.google.com/";
350     form_google_.type = PasswordForm::TYPE_GENERATED;
351     form_google_.date_created = base::Time::Now();
352     form_google_.date_synced = base::Time::Now();
353
354     form_facebook_.origin = GURL("http://www.facebook.com/");
355     form_facebook_.action = GURL("http://www.facebook.com/login");
356     form_facebook_.username_element = UTF8ToUTF16("user");
357     form_facebook_.username_value = UTF8ToUTF16("a");
358     form_facebook_.password_element = UTF8ToUTF16("password");
359     form_facebook_.password_value = UTF8ToUTF16("b");
360     form_facebook_.submit_element = UTF8ToUTF16("submit");
361     form_facebook_.signon_realm = "http://www.facebook.com/";
362     form_facebook_.date_created = base::Time::Now();
363     form_facebook_.date_synced = base::Time::Now();
364
365     form_isc_.origin = GURL("http://www.isc.org/");
366     form_isc_.action = GURL("http://www.isc.org/auth");
367     form_isc_.username_element = UTF8ToUTF16("id");
368     form_isc_.username_value = UTF8ToUTF16("janedoe");
369     form_isc_.password_element = UTF8ToUTF16("passwd");
370     form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
371     form_isc_.submit_element = UTF8ToUTF16("login");
372     form_isc_.signon_realm = "http://www.isc.org/";
373     form_isc_.date_created = base::Time::Now();
374     form_isc_.date_synced = base::Time::Now();
375
376     other_auth_.origin = GURL("http://www.example.com/");
377     other_auth_.username_value = UTF8ToUTF16("username");
378     other_auth_.password_value = UTF8ToUTF16("pass");
379     other_auth_.signon_realm = "http://www.example.com/Realm";
380     other_auth_.date_created = base::Time::Now();
381     other_auth_.date_synced = base::Time::Now();
382   }
383
384   virtual void TearDown() {
385     base::MessageLoop::current()->PostTask(FROM_HERE,
386                                            base::MessageLoop::QuitClosure());
387     base::MessageLoop::current()->Run();
388     db_thread_.Stop();
389   }
390
391   void RunBothThreads() {
392     // First we post a message to the DB thread that will run after all other
393     // messages that have been posted to the DB thread (we don't expect more
394     // to be posted), which posts a message to the UI thread to quit the loop.
395     // That way we can run both loops and be sure that the UI thread loop will
396     // quit so we can get on with the rest of the test.
397     BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
398         base::Bind(&PostQuitTask, &message_loop_));
399     base::MessageLoop::current()->Run();
400   }
401
402   static void PostQuitTask(base::MessageLoop* loop) {
403     loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
404   }
405
406   void CheckUint32Attribute(const MockKeyringItem* item,
407                             const std::string& attribute,
408                             uint32_t value) {
409     MockKeyringItem::attribute_map::const_iterator it =
410         item->attributes.find(attribute);
411     EXPECT_NE(item->attributes.end(), it);
412     if (it != item->attributes.end()) {
413       EXPECT_EQ(MockKeyringItem::ItemAttribute::UINT32, it->second.type);
414       EXPECT_EQ(value, it->second.value_uint32);
415     }
416   }
417
418   void CheckStringAttribute(const MockKeyringItem* item,
419                             const std::string& attribute,
420                             const std::string& value) {
421     MockKeyringItem::attribute_map::const_iterator it =
422         item->attributes.find(attribute);
423     EXPECT_NE(item->attributes.end(), it);
424     if (it != item->attributes.end()) {
425       EXPECT_EQ(MockKeyringItem::ItemAttribute::STRING, it->second.type);
426       EXPECT_EQ(value, it->second.value_string);
427     }
428   }
429
430   void CheckMockKeyringItem(const MockKeyringItem* item,
431                             const PasswordForm& form,
432                             const std::string& app_string) {
433     // We always add items to the login keyring.
434     EXPECT_EQ("login", item->keyring);
435     EXPECT_EQ(form.origin.spec(), item->display_name);
436     EXPECT_EQ(UTF16ToUTF8(form.password_value), item->password);
437     EXPECT_EQ(16u, item->attributes.size());
438     CheckStringAttribute(item, "origin_url", form.origin.spec());
439     CheckStringAttribute(item, "action_url", form.action.spec());
440     CheckStringAttribute(item, "username_element",
441                          UTF16ToUTF8(form.username_element));
442     CheckStringAttribute(item, "username_value",
443                          UTF16ToUTF8(form.username_value));
444     CheckStringAttribute(item, "password_element",
445                          UTF16ToUTF8(form.password_element));
446     CheckStringAttribute(item, "submit_element",
447                          UTF16ToUTF8(form.submit_element));
448     CheckStringAttribute(item, "signon_realm", form.signon_realm);
449     CheckUint32Attribute(item, "ssl_valid", form.ssl_valid);
450     CheckUint32Attribute(item, "preferred", form.preferred);
451     // We don't check the date created. It varies.
452     CheckUint32Attribute(item, "blacklisted_by_user", form.blacklisted_by_user);
453     CheckUint32Attribute(item, "type", form.type);
454     CheckUint32Attribute(item, "times_used", form.times_used);
455     CheckUint32Attribute(item, "scheme", form.scheme);
456     CheckStringAttribute(item, "application", app_string);
457     CheckStringAttribute(item, "date_synced", base::Int64ToString(
458         form.date_synced.ToInternalValue()));
459   }
460
461   // Saves |credentials| and then gets logins matching |url| and |scheme|.
462   // Returns true when something is found, and in such case copies the result to
463   // |result| when |result| is not NULL. (Note that there can be max. 1 result,
464   // derived from |credentials|.)
465   bool CheckCredentialAvailability(const PasswordForm& credentials,
466                                    const GURL& url,
467                                    const PasswordForm::Scheme& scheme,
468                                    PasswordForm* result) {
469     NativeBackendGnome backend(321);
470     backend.Init();
471
472     BrowserThread::PostTask(
473         BrowserThread::DB,
474         FROM_HERE,
475         base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
476                    base::Unretained(&backend),
477                    credentials));
478
479     PasswordForm target_form;
480     target_form.origin = url;
481     target_form.signon_realm = url.spec();
482     if (scheme != PasswordForm::SCHEME_HTML) {
483       // For non-HTML forms, the realm used for authentication
484       // (http://tools.ietf.org/html/rfc1945#section-10.2) is appended to the
485       // signon_realm. Just use a default value for now.
486       target_form.signon_realm.append("Realm");
487       target_form.scheme = scheme;
488     }
489     std::vector<PasswordForm*> form_list;
490     BrowserThread::PostTask(
491         BrowserThread::DB,
492         FROM_HERE,
493         base::Bind(base::IgnoreResult(&NativeBackendGnome::GetLogins),
494                    base::Unretained(&backend),
495                    target_form,
496                    &form_list));
497
498     RunBothThreads();
499
500     EXPECT_EQ(1u, mock_keyring_items.size());
501     if (mock_keyring_items.size() > 0)
502       CheckMockKeyringItem(&mock_keyring_items[0], credentials, "chrome-321");
503     mock_keyring_items.clear();
504
505     if (form_list.empty())
506       return false;
507     EXPECT_EQ(1u, form_list.size());
508     if (result)
509       *result = *form_list[0];
510     STLDeleteElements(&form_list);
511     return true;
512   }
513
514   // Test that updating does not use PSL matching: Add a www.facebook.com
515   // password, then use PSL matching to get a copy of it for m.facebook.com, and
516   // add that copy as well. Now update the www.facebook.com password -- the
517   // m.facebook.com password should not get updated. Depending on the argument,
518   // the credential update is done via UpdateLogin or AddLogin.
519   void CheckPSLUpdate(UpdateType update_type) {
520     password_manager::PSLMatchingHelper helper;
521     ASSERT_TRUE(helper.IsMatchingEnabled());
522
523     NativeBackendGnome backend(321);
524     backend.Init();
525
526     // Add |form_facebook_| to saved logins.
527     BrowserThread::PostTask(
528         BrowserThread::DB,
529         FROM_HERE,
530         base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
531                    base::Unretained(&backend),
532                    form_facebook_));
533
534     // Get the PSL-matched copy of the saved login for m.facebook.
535     const GURL kMobileURL("http://m.facebook.com/");
536     PasswordForm m_facebook_lookup;
537     m_facebook_lookup.origin = kMobileURL;
538     m_facebook_lookup.signon_realm = kMobileURL.spec();
539     std::vector<PasswordForm*> form_list;
540     BrowserThread::PostTask(
541         BrowserThread::DB,
542         FROM_HERE,
543         base::Bind(base::IgnoreResult(&NativeBackendGnome::GetLogins),
544                    base::Unretained(&backend),
545                    m_facebook_lookup,
546                    &form_list));
547     RunBothThreads();
548     EXPECT_EQ(1u, mock_keyring_items.size());
549     EXPECT_EQ(1u, form_list.size());
550     PasswordForm m_facebook = *form_list[0];
551     STLDeleteElements(&form_list);
552     EXPECT_EQ(kMobileURL, m_facebook.origin);
553     EXPECT_EQ(kMobileURL.spec(), m_facebook.signon_realm);
554
555     // Add the PSL-matched copy to saved logins.
556     BrowserThread::PostTask(
557         BrowserThread::DB,
558         FROM_HERE,
559         base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
560                    base::Unretained(&backend),
561                    m_facebook));
562     RunBothThreads();
563     EXPECT_EQ(2u, mock_keyring_items.size());
564
565     // Update www.facebook.com login.
566     PasswordForm new_facebook(form_facebook_);
567     const base::string16 kOldPassword(form_facebook_.password_value);
568     const base::string16 kNewPassword(UTF8ToUTF16("new_b"));
569     EXPECT_NE(kOldPassword, kNewPassword);
570     new_facebook.password_value = kNewPassword;
571     switch (update_type) {
572       case UPDATE_BY_UPDATELOGIN:
573         BrowserThread::PostTask(
574             BrowserThread::DB,
575             FROM_HERE,
576             base::Bind(base::IgnoreResult(&NativeBackendGnome::UpdateLogin),
577                        base::Unretained(&backend),
578                        new_facebook,
579                        base::Owned(new PasswordStoreChangeList)));
580         break;
581       case UPDATE_BY_ADDLOGIN:
582         BrowserThread::PostTask(
583             BrowserThread::DB,
584             FROM_HERE,
585             base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
586                        base::Unretained(&backend),
587                        new_facebook));
588         break;
589     }
590
591     RunBothThreads();
592     EXPECT_EQ(2u, mock_keyring_items.size());
593
594     // Check that m.facebook.com login was not modified by the update.
595     BrowserThread::PostTask(
596         BrowserThread::DB,
597         FROM_HERE,
598         base::Bind(base::IgnoreResult(&NativeBackendGnome::GetLogins),
599                    base::Unretained(&backend),
600                    m_facebook_lookup,
601                    &form_list));
602     RunBothThreads();
603     // There should be two results -- the exact one, and the PSL-matched one.
604     EXPECT_EQ(2u, form_list.size());
605     size_t index_non_psl = 0;
606     if (!form_list[index_non_psl]->original_signon_realm.empty())
607       index_non_psl = 1;
608     EXPECT_EQ(kMobileURL, form_list[index_non_psl]->origin);
609     EXPECT_EQ(kMobileURL.spec(), form_list[index_non_psl]->signon_realm);
610     EXPECT_EQ(kOldPassword, form_list[index_non_psl]->password_value);
611     STLDeleteElements(&form_list);
612
613     // Check that www.facebook.com login was modified by the update.
614     BrowserThread::PostTask(
615         BrowserThread::DB,
616         FROM_HERE,
617         base::Bind(base::IgnoreResult(&NativeBackendGnome::GetLogins),
618                    base::Unretained(&backend),
619                    form_facebook_,
620                    &form_list));
621     RunBothThreads();
622     // There should be two results -- the exact one, and the PSL-matched one.
623     EXPECT_EQ(2u, form_list.size());
624     index_non_psl = 0;
625     if (!form_list[index_non_psl]->original_signon_realm.empty())
626       index_non_psl = 1;
627     EXPECT_EQ(form_facebook_.origin, form_list[index_non_psl]->origin);
628     EXPECT_EQ(form_facebook_.signon_realm,
629               form_list[index_non_psl]->signon_realm);
630     EXPECT_EQ(kNewPassword, form_list[index_non_psl]->password_value);
631     STLDeleteElements(&form_list);
632   }
633
634   void CheckMatchingWithScheme(const PasswordForm::Scheme& scheme) {
635     other_auth_.scheme = scheme;
636
637     // Don't match a non-HTML form with an HTML form.
638     EXPECT_FALSE(CheckCredentialAvailability(
639         other_auth_, GURL("http://www.example.com"),
640         PasswordForm::SCHEME_HTML, NULL));
641     // Don't match an HTML form with non-HTML auth form.
642     EXPECT_FALSE(CheckCredentialAvailability(
643         form_google_, GURL("http://www.google.com/"), scheme, NULL));
644     // Don't match two different non-HTML auth forms with different origin.
645     EXPECT_FALSE(CheckCredentialAvailability(
646         other_auth_, GURL("http://first.example.com"), scheme, NULL));
647     // Do match non-HTML forms from the same origin.
648     EXPECT_TRUE(CheckCredentialAvailability(
649         other_auth_, GURL("http://www.example.com/"), scheme, NULL));
650   }
651
652   void CheckRemoveLoginsBetween(RemoveBetweenMethod date_to_test) {
653     NativeBackendGnome backend(42);
654     backend.Init();
655
656     form_google_.date_synced = base::Time();
657     form_isc_.date_synced = base::Time();
658     form_google_.date_created = base::Time();
659     form_isc_.date_created = base::Time();
660     base::Time now = base::Time::Now();
661     base::Time next_day = now + base::TimeDelta::FromDays(1);
662     if (date_to_test == CREATED) {
663       // crbug/374132. Remove the next line once it's fixed.
664       next_day = base::Time::FromTimeT(next_day.ToTimeT());
665       form_google_.date_created = now;
666       form_isc_.date_created = next_day;
667     } else {
668       form_google_.date_synced = now;
669       form_isc_.date_synced = next_day;
670     }
671
672     BrowserThread::PostTask(
673         BrowserThread::DB,
674         FROM_HERE,
675         base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
676                    base::Unretained(&backend),
677                    form_google_));
678     BrowserThread::PostTask(
679         BrowserThread::DB,
680         FROM_HERE,
681         base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
682                    base::Unretained(&backend),
683                    form_isc_));
684
685     PasswordStoreChangeList expected_changes;
686     expected_changes.push_back(
687         PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_));
688     PasswordStoreChangeList changes;
689     bool (NativeBackendGnome::*method)(
690         base::Time, base::Time, password_manager::PasswordStoreChangeList*) =
691         date_to_test == CREATED
692             ? &NativeBackendGnome::RemoveLoginsCreatedBetween
693             : &NativeBackendGnome::RemoveLoginsSyncedBetween;
694     BrowserThread::PostTaskAndReplyWithResult(
695         BrowserThread::DB,
696         FROM_HERE,
697         base::Bind(method,
698                    base::Unretained(&backend),
699                    base::Time(),
700                    next_day,
701                    &changes),
702         base::Bind(
703             &CheckPasswordChangesWithResult, &expected_changes, &changes));
704     RunBothThreads();
705
706     EXPECT_EQ(1u, mock_keyring_items.size());
707     if (mock_keyring_items.size() > 0)
708       CheckMockKeyringItem(&mock_keyring_items[0], form_isc_, "chrome-42");
709
710     // Remove form_isc_.
711     expected_changes.clear();
712     expected_changes.push_back(
713         PasswordStoreChange(PasswordStoreChange::REMOVE, form_isc_));
714     BrowserThread::PostTaskAndReplyWithResult(
715         BrowserThread::DB,
716         FROM_HERE,
717         base::Bind(method,
718                    base::Unretained(&backend),
719                    next_day,
720                    base::Time(),
721                    &changes),
722         base::Bind(
723             &CheckPasswordChangesWithResult, &expected_changes, &changes));
724     RunBothThreads();
725
726     EXPECT_EQ(0u, mock_keyring_items.size());
727   }
728
729   base::MessageLoopForUI message_loop_;
730   content::TestBrowserThread ui_thread_;
731   content::TestBrowserThread db_thread_;
732
733   // Provide some test forms to avoid having to set them up in each test.
734   PasswordForm form_google_;
735   PasswordForm form_facebook_;
736   PasswordForm form_isc_;
737   PasswordForm other_auth_;
738 };
739
740 TEST_F(NativeBackendGnomeTest, BasicAddLogin) {
741   NativeBackendGnome backend(42);
742   backend.Init();
743
744   BrowserThread::PostTask(
745       BrowserThread::DB, FROM_HERE,
746       base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
747                  base::Unretained(&backend), form_google_));
748
749   RunBothThreads();
750
751   EXPECT_EQ(1u, mock_keyring_items.size());
752   if (mock_keyring_items.size() > 0)
753     CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
754 }
755
756 TEST_F(NativeBackendGnomeTest, BasicListLogins) {
757   NativeBackendGnome backend(42);
758   backend.Init();
759
760   BrowserThread::PostTask(
761       BrowserThread::DB, FROM_HERE,
762       base::Bind(base::IgnoreResult( &NativeBackendGnome::AddLogin),
763                  base::Unretained(&backend), form_google_));
764
765   std::vector<PasswordForm*> form_list;
766   BrowserThread::PostTask(
767       BrowserThread::DB, FROM_HERE,
768       base::Bind(
769           base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
770           base::Unretained(&backend), &form_list));
771
772   RunBothThreads();
773
774   // Quick check that we got something back.
775   EXPECT_EQ(1u, form_list.size());
776   STLDeleteElements(&form_list);
777
778   EXPECT_EQ(1u, mock_keyring_items.size());
779   if (mock_keyring_items.size() > 0)
780     CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
781 }
782
783 // Save a password for www.facebook.com and see it suggested for m.facebook.com.
784 TEST_F(NativeBackendGnomeTest, PSLMatchingPositive) {
785   PasswordForm result;
786   const GURL kMobileURL("http://m.facebook.com/");
787   password_manager::PSLMatchingHelper helper;
788   ASSERT_TRUE(helper.IsMatchingEnabled());
789   EXPECT_TRUE(CheckCredentialAvailability(
790       form_facebook_, kMobileURL, PasswordForm::SCHEME_HTML, &result));
791   EXPECT_EQ(kMobileURL, result.origin);
792   EXPECT_EQ(kMobileURL.spec(), result.signon_realm);
793 }
794
795 // Save a password for www.facebook.com and see it not suggested for
796 // m-facebook.com.
797 TEST_F(NativeBackendGnomeTest, PSLMatchingNegativeDomainMismatch) {
798   password_manager::PSLMatchingHelper helper;
799   ASSERT_TRUE(helper.IsMatchingEnabled());
800   EXPECT_FALSE(CheckCredentialAvailability(
801       form_facebook_, GURL("http://m-facebook.com/"),
802       PasswordForm::SCHEME_HTML, NULL));
803 }
804
805 // Test PSL matching is off for domains excluded from it.
806 TEST_F(NativeBackendGnomeTest, PSLMatchingDisabledDomains) {
807   password_manager::PSLMatchingHelper helper;
808   ASSERT_TRUE(helper.IsMatchingEnabled());
809   EXPECT_FALSE(CheckCredentialAvailability(
810       form_google_, GURL("http://one.google.com/"),
811       PasswordForm::SCHEME_HTML, NULL));
812 }
813
814 // Make sure PSL matches aren't available for non-HTML forms.
815 TEST_F(NativeBackendGnomeTest, PSLMatchingDisabledForNonHTMLForms) {
816   password_manager::PSLMatchingHelper helper;
817   ASSERT_TRUE(helper.IsMatchingEnabled());
818
819   CheckMatchingWithScheme(PasswordForm::SCHEME_BASIC);
820   CheckMatchingWithScheme(PasswordForm::SCHEME_DIGEST);
821   CheckMatchingWithScheme(PasswordForm::SCHEME_OTHER);
822
823 }
824
825 TEST_F(NativeBackendGnomeTest, PSLUpdatingStrictUpdateLogin) {
826   CheckPSLUpdate(UPDATE_BY_UPDATELOGIN);
827 }
828
829 TEST_F(NativeBackendGnomeTest, PSLUpdatingStrictAddLogin) {
830   // TODO(vabr): if AddLogin becomes no longer valid for existing logins, then
831   // just delete this test.
832   CheckPSLUpdate(UPDATE_BY_ADDLOGIN);
833 }
834
835 TEST_F(NativeBackendGnomeTest, BasicUpdateLogin) {
836   NativeBackendGnome backend(42);
837   backend.Init();
838
839   // First add google login.
840   BrowserThread::PostTask(
841       BrowserThread::DB, FROM_HERE,
842       base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
843                  base::Unretained(&backend), form_google_));
844
845   RunBothThreads();
846
847   PasswordForm new_form_google(form_google_);
848   new_form_google.times_used = 1;
849   new_form_google.action = GURL("http://www.google.com/different/login");
850
851   EXPECT_EQ(1u, mock_keyring_items.size());
852   if (mock_keyring_items.size() > 0)
853     CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
854
855   // Update login
856   PasswordStoreChangeList changes;
857   BrowserThread::PostTask(
858       BrowserThread::DB, FROM_HERE,
859       base::Bind(base::IgnoreResult(&NativeBackendGnome::UpdateLogin),
860                  base::Unretained(&backend),
861                  new_form_google,
862                  base::Unretained(&changes)));
863
864   RunBothThreads();
865
866   ASSERT_EQ(1u, changes.size());
867   EXPECT_EQ(PasswordStoreChange::UPDATE, changes.front().type());
868   EXPECT_EQ(new_form_google, changes.front().form());
869   EXPECT_EQ(1u, mock_keyring_items.size());
870   if (mock_keyring_items.size() > 0)
871     CheckMockKeyringItem(&mock_keyring_items[0], new_form_google, "chrome-42");
872 }
873
874 TEST_F(NativeBackendGnomeTest, BasicRemoveLogin) {
875   NativeBackendGnome backend(42);
876   backend.Init();
877
878   BrowserThread::PostTask(
879       BrowserThread::DB, FROM_HERE,
880       base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
881                  base::Unretained(&backend), form_google_));
882
883   RunBothThreads();
884
885   EXPECT_EQ(1u, mock_keyring_items.size());
886   if (mock_keyring_items.size() > 0)
887     CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
888
889   BrowserThread::PostTask(
890       BrowserThread::DB, FROM_HERE,
891       base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin),
892                  base::Unretained(&backend), form_google_));
893
894   RunBothThreads();
895
896   EXPECT_EQ(0u, mock_keyring_items.size());
897 }
898
899 TEST_F(NativeBackendGnomeTest, RemoveNonexistentLogin) {
900   NativeBackendGnome backend(42);
901   backend.Init();
902
903   // First add an unrelated login.
904   BrowserThread::PostTask(
905       BrowserThread::DB, FROM_HERE,
906       base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
907                  base::Unretained(&backend), form_google_));
908
909   RunBothThreads();
910
911   EXPECT_EQ(1u, mock_keyring_items.size());
912   if (mock_keyring_items.size() > 0)
913     CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
914
915   // Attempt to remove a login that doesn't exist.
916   BrowserThread::PostTask(
917       BrowserThread::DB, FROM_HERE,
918       base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin),
919                  base::Unretained(&backend), form_isc_));
920
921   // Make sure we can still get the first form back.
922   std::vector<PasswordForm*> form_list;
923   BrowserThread::PostTask(
924       BrowserThread::DB, FROM_HERE,
925       base::Bind(
926           base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
927           base::Unretained(&backend), &form_list));
928
929   RunBothThreads();
930
931   // Quick check that we got something back.
932   EXPECT_EQ(1u, form_list.size());
933   STLDeleteElements(&form_list);
934
935   EXPECT_EQ(1u, mock_keyring_items.size());
936   if (mock_keyring_items.size() > 0)
937     CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
938 }
939
940 TEST_F(NativeBackendGnomeTest, UpdateNonexistentLogin) {
941   NativeBackendGnome backend(42);
942   backend.Init();
943
944   // First add an unrelated login.
945   BrowserThread::PostTask(
946       BrowserThread::DB, FROM_HERE,
947       base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
948                  base::Unretained(&backend), form_google_));
949
950   RunBothThreads();
951
952   EXPECT_EQ(1u, mock_keyring_items.size());
953   if (mock_keyring_items.size() > 0)
954     CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
955
956   // Attempt to update a login that doesn't exist.
957   PasswordStoreChangeList changes;
958   BrowserThread::PostTask(
959       BrowserThread::DB, FROM_HERE,
960       base::Bind(base::IgnoreResult(&NativeBackendGnome::UpdateLogin),
961                  base::Unretained(&backend),
962                  form_isc_,
963                  base::Unretained(&changes)));
964
965   RunBothThreads();
966
967   EXPECT_EQ(PasswordStoreChangeList(), changes);
968   EXPECT_EQ(1u, mock_keyring_items.size());
969   if (mock_keyring_items.size() > 0)
970     CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
971 }
972
973 TEST_F(NativeBackendGnomeTest, AddDuplicateLogin) {
974   NativeBackendGnome backend(42);
975   backend.Init();
976
977   PasswordStoreChangeList changes;
978   changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
979                                         form_google_));
980   BrowserThread::PostTaskAndReplyWithResult(
981       BrowserThread::DB, FROM_HERE,
982       base::Bind(&NativeBackendGnome::AddLogin,
983                  base::Unretained(&backend), form_google_),
984       base::Bind(&CheckPasswordChanges, changes));
985
986   changes.clear();
987   changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
988                                         form_google_));
989   form_google_.times_used++;
990   changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
991                                         form_google_));
992
993   BrowserThread::PostTaskAndReplyWithResult(
994       BrowserThread::DB, FROM_HERE,
995       base::Bind(&NativeBackendGnome::AddLogin,
996                  base::Unretained(&backend), form_google_),
997       base::Bind(&CheckPasswordChanges, changes));
998
999   RunBothThreads();
1000
1001   EXPECT_EQ(1u, mock_keyring_items.size());
1002   if (mock_keyring_items.size() > 0)
1003     CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
1004 }
1005
1006 TEST_F(NativeBackendGnomeTest, ListLoginsAppends) {
1007   NativeBackendGnome backend(42);
1008   backend.Init();
1009
1010   BrowserThread::PostTask(
1011       BrowserThread::DB, FROM_HERE,
1012       base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
1013                  base::Unretained(&backend), form_google_));
1014
1015   // Send the same request twice with the same list both times.
1016   std::vector<PasswordForm*> form_list;
1017   BrowserThread::PostTask(
1018       BrowserThread::DB, FROM_HERE,
1019       base::Bind(
1020           base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
1021           base::Unretained(&backend), &form_list));
1022   BrowserThread::PostTask(
1023       BrowserThread::DB, FROM_HERE,
1024       base::Bind(
1025           base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
1026           base::Unretained(&backend), &form_list));
1027
1028   RunBothThreads();
1029
1030   // Quick check that we got two results back.
1031   EXPECT_EQ(2u, form_list.size());
1032   STLDeleteElements(&form_list);
1033
1034   EXPECT_EQ(1u, mock_keyring_items.size());
1035   if (mock_keyring_items.size() > 0)
1036     CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
1037 }
1038
1039 TEST_F(NativeBackendGnomeTest, RemoveLoginsCreatedBetween) {
1040   CheckRemoveLoginsBetween(CREATED);
1041 }
1042
1043 TEST_F(NativeBackendGnomeTest, RemoveLoginsSyncedBetween) {
1044   CheckRemoveLoginsBetween(SYNCED);
1045 }
1046
1047 // TODO(mdm): add more basic tests here at some point.