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_util.h"
11#include "base/strings/stringprintf.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/time/time.h"
14#include "chrome/browser/password_manager/native_backend_gnome_x.h"
15#include "chrome/common/pref_names.h"
16#include "chrome/test/base/testing_profile.h"
17#include "components/autofill/core/common/password_form.h"
18#include "content/public/test/test_browser_thread.h"
19#include "testing/gtest/include/gtest/gtest.h"
20
21using autofill::PasswordForm;
22using content::BrowserThread;
23
24namespace {
25
26// What follows is a very simple implementation of the subset of the GNOME
27// Keyring API that we actually use. It gets substituted for the real one by
28// MockGnomeKeyringLoader, which hooks into the facility normally used to load
29// the GNOME Keyring library at runtime to avoid a static dependency on it.
30
31struct MockKeyringItem {
32  MockKeyringItem() {}
33  MockKeyringItem(const char* keyring,
34                  const std::string& display_name,
35                  const std::string& password)
36    : keyring(keyring ? keyring : "login"),
37      display_name(display_name),
38      password(password) {}
39
40  struct ItemAttribute {
41    ItemAttribute() : type(UINT32), value_uint32(0) {}
42    explicit ItemAttribute(uint32_t value)
43      : type(UINT32), value_uint32(value) {}
44    explicit ItemAttribute(const std::string& value)
45      : type(STRING), value_string(value) {}
46
47    bool Equals(const ItemAttribute& x) const {
48      if (type != x.type) return false;
49      return (type == STRING) ? value_string == x.value_string
50                              : value_uint32 == x.value_uint32;
51    }
52
53    enum Type { UINT32, STRING } type;
54    uint32_t value_uint32;
55    std::string value_string;
56  };
57
58  typedef std::map<std::string, ItemAttribute> attribute_map;
59  typedef std::vector<std::pair<std::string, ItemAttribute> > attribute_query;
60
61  bool Matches(const attribute_query& query) const {
62    // The real GNOME Keyring doesn't match empty queries.
63    if (query.empty()) return false;
64    for (size_t i = 0; i < query.size(); ++i) {
65      attribute_map::const_iterator match = attributes.find(query[i].first);
66      if (match == attributes.end()) return false;
67      if (!match->second.Equals(query[i].second)) return false;
68    }
69    return true;
70  }
71
72  std::string keyring;
73  std::string display_name;
74  std::string password;
75
76  attribute_map attributes;
77};
78
79// The list of all keyring items we have stored.
80std::vector<MockKeyringItem> mock_keyring_items;
81bool mock_keyring_reject_local_ids = false;
82
83bool IsStringAttribute(const GnomeKeyringPasswordSchema* schema,
84                       const std::string& name) {
85  for (size_t i = 0; schema->attributes[i].name; ++i)
86    if (name == schema->attributes[i].name)
87      return schema->attributes[i].type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
88  NOTREACHED() << "Requested type of nonexistent attribute";
89  return false;
90}
91
92gboolean mock_gnome_keyring_is_available() {
93  return true;
94}
95
96gpointer mock_gnome_keyring_store_password(
97    const GnomeKeyringPasswordSchema* schema,
98    const gchar* keyring,
99    const gchar* display_name,
100    const gchar* password,
101    GnomeKeyringOperationDoneCallback callback,
102    gpointer data,
103    GDestroyNotify destroy_data,
104    ...) {
105  mock_keyring_items.push_back(
106      MockKeyringItem(keyring, display_name, password));
107  MockKeyringItem* item = &mock_keyring_items.back();
108  const std::string keyring_desc =
109      keyring ? base::StringPrintf("keyring %s", keyring)
110              : std::string("default keyring");
111  VLOG(1) << "Adding item with origin " << display_name
112          << " to " << keyring_desc;
113  va_list ap;
114  va_start(ap, destroy_data);
115  char* name;
116  while ((name = va_arg(ap, gchar*))) {
117    if (IsStringAttribute(schema, name)) {
118      item->attributes[name] =
119          MockKeyringItem::ItemAttribute(va_arg(ap, gchar*));
120      VLOG(1) << "Adding item attribute " << name
121              << ", value '" << item->attributes[name].value_string << "'";
122    } else {
123      item->attributes[name] =
124          MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t));
125      VLOG(1) << "Adding item attribute " << name
126              << ", value " << item->attributes[name].value_uint32;
127    }
128  }
129  va_end(ap);
130  // As a hack to ease testing migration, make it possible to reject the new
131  // format for the app string. This way we can add them easily to migrate.
132  if (mock_keyring_reject_local_ids) {
133    MockKeyringItem::attribute_map::iterator it =
134        item->attributes.find("application");
135    if (it != item->attributes.end() &&
136        it->second.type == MockKeyringItem::ItemAttribute::STRING &&
137        base::StringPiece(it->second.value_string).starts_with("chrome-")) {
138      mock_keyring_items.pop_back();
139      // GnomeKeyringResult, data
140      callback(GNOME_KEYRING_RESULT_IO_ERROR, data);
141      return NULL;
142    }
143  }
144  // GnomeKeyringResult, data
145  callback(GNOME_KEYRING_RESULT_OK, data);
146  return NULL;
147}
148
149gpointer mock_gnome_keyring_delete_password(
150    const GnomeKeyringPasswordSchema* schema,
151    GnomeKeyringOperationDoneCallback callback,
152    gpointer data,
153    GDestroyNotify destroy_data,
154    ...) {
155  MockKeyringItem::attribute_query query;
156  va_list ap;
157  va_start(ap, destroy_data);
158  char* name;
159  while ((name = va_arg(ap, gchar*))) {
160    if (IsStringAttribute(schema, name)) {
161      query.push_back(make_pair(std::string(name),
162          MockKeyringItem::ItemAttribute(va_arg(ap, gchar*))));
163      VLOG(1) << "Querying with item attribute " << name
164              << ", value '" << query.back().second.value_string << "'";
165    } else {
166      query.push_back(make_pair(std::string(name),
167          MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t))));
168      VLOG(1) << "Querying with item attribute " << name
169              << ", value " << query.back().second.value_uint32;
170    }
171  }
172  va_end(ap);
173  bool deleted = false;
174  for (size_t i = mock_keyring_items.size(); i > 0; --i) {
175    const MockKeyringItem* item = &mock_keyring_items[i - 1];
176    if (item->Matches(query)) {
177      VLOG(1) << "Deleting item with origin " <<  item->display_name;
178      mock_keyring_items.erase(mock_keyring_items.begin() + (i - 1));
179      deleted = true;
180    }
181  }
182  // GnomeKeyringResult, data
183  callback(deleted ? GNOME_KEYRING_RESULT_OK
184                   : GNOME_KEYRING_RESULT_NO_MATCH, data);
185  return NULL;
186}
187
188gpointer mock_gnome_keyring_find_itemsv(
189    GnomeKeyringItemType type,
190    GnomeKeyringOperationGetListCallback callback,
191    gpointer data,
192    GDestroyNotify destroy_data,
193    ...) {
194  MockKeyringItem::attribute_query query;
195  va_list ap;
196  va_start(ap, destroy_data);
197  char* name;
198  while ((name = va_arg(ap, gchar*))) {
199    // Really a GnomeKeyringAttributeType, but promoted to int through ...
200    if (va_arg(ap, int) == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) {
201      query.push_back(make_pair(std::string(name),
202          MockKeyringItem::ItemAttribute(va_arg(ap, gchar*))));
203      VLOG(1) << "Querying with item attribute " << name
204              << ", value '" << query.back().second.value_string << "'";
205    } else {
206      query.push_back(make_pair(std::string(name),
207          MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t))));
208      VLOG(1) << "Querying with item attribute " << name
209              << ", value " << query.back().second.value_uint32;
210    }
211  }
212  va_end(ap);
213  // Find matches and add them to a list of results.
214  GList* results = NULL;
215  for (size_t i = 0; i < mock_keyring_items.size(); ++i) {
216    const MockKeyringItem* item = &mock_keyring_items[i];
217    if (item->Matches(query)) {
218      GnomeKeyringFound* found = new GnomeKeyringFound;
219      found->keyring = strdup(item->keyring.c_str());
220      found->item_id = i;
221      found->attributes = gnome_keyring_attribute_list_new();
222      for (MockKeyringItem::attribute_map::const_iterator it =
223               item->attributes.begin();
224           it != item->attributes.end();
225           ++it) {
226        if (it->second.type == MockKeyringItem::ItemAttribute::STRING) {
227          gnome_keyring_attribute_list_append_string(
228              found->attributes, it->first.c_str(),
229              it->second.value_string.c_str());
230        } else {
231          gnome_keyring_attribute_list_append_uint32(
232              found->attributes, it->first.c_str(),
233              it->second.value_uint32);
234        }
235      }
236      found->secret = strdup(item->password.c_str());
237      results = g_list_prepend(results, found);
238    }
239  }
240  // GnomeKeyringResult, GList*, data
241  callback(results ? GNOME_KEYRING_RESULT_OK
242                   : GNOME_KEYRING_RESULT_NO_MATCH, results, data);
243  // Now free the list of results.
244  GList* element = g_list_first(results);
245  while (element) {
246    GnomeKeyringFound* found = static_cast<GnomeKeyringFound*>(element->data);
247    free(found->keyring);
248    gnome_keyring_attribute_list_free(found->attributes);
249    free(found->secret);
250    delete found;
251    element = g_list_next(element);
252  }
253  g_list_free(results);
254  return NULL;
255}
256
257const gchar* mock_gnome_keyring_result_to_message(GnomeKeyringResult res) {
258  return "mock keyring simulating failure";
259}
260
261// Inherit to get access to protected fields.
262class MockGnomeKeyringLoader : public GnomeKeyringLoader {
263 public:
264  static bool LoadMockGnomeKeyring() {
265#define GNOME_KEYRING_ASSIGN_POINTER(name) \
266  gnome_keyring_##name = &mock_gnome_keyring_##name;
267    GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_ASSIGN_POINTER)
268#undef GNOME_KEYRING_ASSIGN_POINTER
269    keyring_loaded = true;
270    // Reset the state of the mock library.
271    mock_keyring_items.clear();
272    mock_keyring_reject_local_ids = false;
273    return true;
274  }
275};
276
277}  // anonymous namespace
278
279class NativeBackendGnomeTest : public testing::Test {
280 protected:
281  NativeBackendGnomeTest()
282      : ui_thread_(BrowserThread::UI, &message_loop_),
283        db_thread_(BrowserThread::DB) {
284  }
285
286  virtual void SetUp() {
287    ASSERT_TRUE(db_thread_.Start());
288
289    MockGnomeKeyringLoader::LoadMockGnomeKeyring();
290
291    form_google_.origin = GURL("http://www.google.com/");
292    form_google_.action = GURL("http://www.google.com/login");
293    form_google_.username_element = UTF8ToUTF16("user");
294    form_google_.username_value = UTF8ToUTF16("joeschmoe");
295    form_google_.password_element = UTF8ToUTF16("pass");
296    form_google_.password_value = UTF8ToUTF16("seekrit");
297    form_google_.submit_element = UTF8ToUTF16("submit");
298    form_google_.signon_realm = "Google";
299
300    form_isc_.origin = GURL("http://www.isc.org/");
301    form_isc_.action = GURL("http://www.isc.org/auth");
302    form_isc_.username_element = UTF8ToUTF16("id");
303    form_isc_.username_value = UTF8ToUTF16("janedoe");
304    form_isc_.password_element = UTF8ToUTF16("passwd");
305    form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
306    form_isc_.submit_element = UTF8ToUTF16("login");
307    form_isc_.signon_realm = "ISC";
308  }
309
310  virtual void TearDown() {
311    base::MessageLoop::current()->PostTask(FROM_HERE,
312                                           base::MessageLoop::QuitClosure());
313    base::MessageLoop::current()->Run();
314    db_thread_.Stop();
315  }
316
317  void RunBothThreads() {
318    // First we post a message to the DB thread that will run after all other
319    // messages that have been posted to the DB thread (we don't expect more
320    // to be posted), which posts a message to the UI thread to quit the loop.
321    // That way we can run both loops and be sure that the UI thread loop will
322    // quit so we can get on with the rest of the test.
323    BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
324        base::Bind(&PostQuitTask, &message_loop_));
325    base::MessageLoop::current()->Run();
326  }
327
328  static void PostQuitTask(base::MessageLoop* loop) {
329    loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
330  }
331
332  void CheckUint32Attribute(const MockKeyringItem* item,
333                            const std::string& attribute,
334                            uint32_t value) {
335    MockKeyringItem::attribute_map::const_iterator it =
336        item->attributes.find(attribute);
337    EXPECT_NE(item->attributes.end(), it);
338    if (it != item->attributes.end()) {
339      EXPECT_EQ(MockKeyringItem::ItemAttribute::UINT32, it->second.type);
340      EXPECT_EQ(value, it->second.value_uint32);
341    }
342  }
343
344  void CheckStringAttribute(const MockKeyringItem* item,
345                            const std::string& attribute,
346                            const std::string& value) {
347    MockKeyringItem::attribute_map::const_iterator it =
348        item->attributes.find(attribute);
349    EXPECT_NE(item->attributes.end(), it);
350    if (it != item->attributes.end()) {
351      EXPECT_EQ(MockKeyringItem::ItemAttribute::STRING, it->second.type);
352      EXPECT_EQ(value, it->second.value_string);
353    }
354  }
355
356  void CheckMockKeyringItem(const MockKeyringItem* item,
357                            const PasswordForm& form,
358                            const std::string& app_string) {
359    // We always add items to the login keyring.
360    EXPECT_EQ("login", item->keyring);
361    EXPECT_EQ(form.origin.spec(), item->display_name);
362    EXPECT_EQ(UTF16ToUTF8(form.password_value), item->password);
363    EXPECT_EQ(13u, item->attributes.size());
364    CheckStringAttribute(item, "origin_url", form.origin.spec());
365    CheckStringAttribute(item, "action_url", form.action.spec());
366    CheckStringAttribute(item, "username_element",
367                         UTF16ToUTF8(form.username_element));
368    CheckStringAttribute(item, "username_value",
369                         UTF16ToUTF8(form.username_value));
370    CheckStringAttribute(item, "password_element",
371                         UTF16ToUTF8(form.password_element));
372    CheckStringAttribute(item, "submit_element",
373                         UTF16ToUTF8(form.submit_element));
374    CheckStringAttribute(item, "signon_realm", form.signon_realm);
375    CheckUint32Attribute(item, "ssl_valid", form.ssl_valid);
376    CheckUint32Attribute(item, "preferred", form.preferred);
377    // We don't check the date created. It varies.
378    CheckUint32Attribute(item, "blacklisted_by_user", form.blacklisted_by_user);
379    CheckUint32Attribute(item, "scheme", form.scheme);
380    CheckStringAttribute(item, "application", app_string);
381  }
382
383  base::MessageLoopForUI message_loop_;
384  content::TestBrowserThread ui_thread_;
385  content::TestBrowserThread db_thread_;
386
387  TestingProfile profile_;
388
389  // Provide some test forms to avoid having to set them up in each test.
390  PasswordForm form_google_;
391  PasswordForm form_isc_;
392};
393
394TEST_F(NativeBackendGnomeTest, BasicAddLogin) {
395  // Pretend that the migration has already taken place.
396  profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
397
398  NativeBackendGnome backend(42, profile_.GetPrefs());
399  backend.Init();
400
401  BrowserThread::PostTask(
402      BrowserThread::DB, FROM_HERE,
403      base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
404                 base::Unretained(&backend), form_google_));
405
406  RunBothThreads();
407
408  EXPECT_EQ(1u, mock_keyring_items.size());
409  if (mock_keyring_items.size() > 0)
410    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
411}
412
413TEST_F(NativeBackendGnomeTest, BasicListLogins) {
414  // Pretend that the migration has already taken place.
415  profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
416
417  NativeBackendGnome backend(42, profile_.GetPrefs());
418  backend.Init();
419
420  BrowserThread::PostTask(
421      BrowserThread::DB, FROM_HERE,
422      base::Bind(base::IgnoreResult( &NativeBackendGnome::AddLogin),
423                 base::Unretained(&backend), form_google_));
424
425  std::vector<PasswordForm*> form_list;
426  BrowserThread::PostTask(
427      BrowserThread::DB, FROM_HERE,
428      base::Bind(
429          base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
430          base::Unretained(&backend), &form_list));
431
432  RunBothThreads();
433
434  // Quick check that we got something back.
435  EXPECT_EQ(1u, form_list.size());
436  STLDeleteElements(&form_list);
437
438  EXPECT_EQ(1u, mock_keyring_items.size());
439  if (mock_keyring_items.size() > 0)
440    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
441}
442
443TEST_F(NativeBackendGnomeTest, BasicRemoveLogin) {
444  // Pretend that the migration has already taken place.
445  profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
446
447  NativeBackendGnome backend(42, profile_.GetPrefs());
448  backend.Init();
449
450  BrowserThread::PostTask(
451      BrowserThread::DB, FROM_HERE,
452      base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
453                 base::Unretained(&backend), form_google_));
454
455  RunBothThreads();
456
457  EXPECT_EQ(1u, mock_keyring_items.size());
458  if (mock_keyring_items.size() > 0)
459    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
460
461  BrowserThread::PostTask(
462      BrowserThread::DB, FROM_HERE,
463      base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin),
464                 base::Unretained(&backend), form_google_));
465
466  RunBothThreads();
467
468  EXPECT_EQ(0u, mock_keyring_items.size());
469}
470
471TEST_F(NativeBackendGnomeTest, RemoveNonexistentLogin) {
472  // Pretend that the migration has already taken place.
473  profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
474
475  NativeBackendGnome backend(42, profile_.GetPrefs());
476  backend.Init();
477
478  // First add an unrelated login.
479  BrowserThread::PostTask(
480      BrowserThread::DB, FROM_HERE,
481      base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
482                 base::Unretained(&backend), form_google_));
483
484  RunBothThreads();
485
486  EXPECT_EQ(1u, mock_keyring_items.size());
487  if (mock_keyring_items.size() > 0)
488    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
489
490  // Attempt to remove a login that doesn't exist.
491  BrowserThread::PostTask(
492      BrowserThread::DB, FROM_HERE,
493      base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin),
494                 base::Unretained(&backend), form_isc_));
495
496  // Make sure we can still get the first form back.
497  std::vector<PasswordForm*> form_list;
498  BrowserThread::PostTask(
499      BrowserThread::DB, FROM_HERE,
500      base::Bind(
501          base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
502          base::Unretained(&backend), &form_list));
503
504  RunBothThreads();
505
506  // Quick check that we got something back.
507  EXPECT_EQ(1u, form_list.size());
508  STLDeleteElements(&form_list);
509
510  EXPECT_EQ(1u, mock_keyring_items.size());
511  if (mock_keyring_items.size() > 0)
512    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
513}
514
515TEST_F(NativeBackendGnomeTest, AddDuplicateLogin) {
516  // Pretend that the migration has already taken place.
517  profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
518
519  NativeBackendGnome backend(42, profile_.GetPrefs());
520  backend.Init();
521
522  BrowserThread::PostTask(
523      BrowserThread::DB, FROM_HERE,
524      base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
525                 base::Unretained(&backend), form_google_));
526  BrowserThread::PostTask(
527      BrowserThread::DB, FROM_HERE,
528      base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
529                 base::Unretained(&backend), form_google_));
530
531  RunBothThreads();
532
533  EXPECT_EQ(1u, mock_keyring_items.size());
534  if (mock_keyring_items.size() > 0)
535    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
536}
537
538TEST_F(NativeBackendGnomeTest, ListLoginsAppends) {
539  // Pretend that the migration has already taken place.
540  profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
541
542  NativeBackendGnome backend(42, profile_.GetPrefs());
543  backend.Init();
544
545  BrowserThread::PostTask(
546      BrowserThread::DB, FROM_HERE,
547      base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
548                 base::Unretained(&backend), form_google_));
549
550  // Send the same request twice with the same list both times.
551  std::vector<PasswordForm*> form_list;
552  BrowserThread::PostTask(
553      BrowserThread::DB, FROM_HERE,
554      base::Bind(
555          base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
556          base::Unretained(&backend), &form_list));
557  BrowserThread::PostTask(
558      BrowserThread::DB, FROM_HERE,
559      base::Bind(
560          base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
561          base::Unretained(&backend), &form_list));
562
563  RunBothThreads();
564
565  // Quick check that we got two results back.
566  EXPECT_EQ(2u, form_list.size());
567  STLDeleteElements(&form_list);
568
569  EXPECT_EQ(1u, mock_keyring_items.size());
570  if (mock_keyring_items.size() > 0)
571    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
572}
573
574// TODO(mdm): add more basic (i.e. non-migration) tests here at some point.
575
576TEST_F(NativeBackendGnomeTest, DISABLED_MigrateOneLogin) {
577  // Reject attempts to migrate so we can populate the store.
578  mock_keyring_reject_local_ids = true;
579
580  {
581    NativeBackendGnome backend(42, profile_.GetPrefs());
582    backend.Init();
583
584    BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
585        base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
586                   base::Unretained(&backend), form_google_));
587
588    // Make sure we can get the form back even when migration is failing.
589    std::vector<PasswordForm*> form_list;
590    BrowserThread::PostTask(
591        BrowserThread::DB, FROM_HERE,
592        base::Bind(
593            base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
594            base::Unretained(&backend), &form_list));
595
596    RunBothThreads();
597
598    // Quick check that we got something back.
599    EXPECT_EQ(1u, form_list.size());
600    STLDeleteElements(&form_list);
601  }
602
603  EXPECT_EQ(1u, mock_keyring_items.size());
604  if (mock_keyring_items.size() > 0)
605    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
606
607  // Now allow the migration.
608  mock_keyring_reject_local_ids = false;
609
610  {
611    NativeBackendGnome backend(42, profile_.GetPrefs());
612    backend.Init();
613
614    // This should not trigger migration because there will be no results.
615    std::vector<PasswordForm*> form_list;
616    BrowserThread::PostTask(
617        BrowserThread::DB, FROM_HERE,
618        base::Bind(base::IgnoreResult(&NativeBackendGnome::GetBlacklistLogins),
619                   base::Unretained(&backend), &form_list));
620
621    RunBothThreads();
622
623    // Check that we got nothing back.
624    EXPECT_EQ(0u, form_list.size());
625    STLDeleteElements(&form_list);
626  }
627
628  // Check that the keyring is unmodified.
629  EXPECT_EQ(1u, mock_keyring_items.size());
630  if (mock_keyring_items.size() > 0)
631    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
632
633  // Check that we haven't set the persistent preference.
634  EXPECT_FALSE(
635      profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
636
637  {
638    NativeBackendGnome backend(42, profile_.GetPrefs());
639    backend.Init();
640
641    // Trigger the migration by looking something up.
642    std::vector<PasswordForm*> form_list;
643    BrowserThread::PostTask(
644        BrowserThread::DB, FROM_HERE,
645        base::Bind(
646            base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
647            base::Unretained(&backend), &form_list));
648
649    RunBothThreads();
650
651    // Quick check that we got something back.
652    EXPECT_EQ(1u, form_list.size());
653    STLDeleteElements(&form_list);
654  }
655
656  EXPECT_EQ(2u, mock_keyring_items.size());
657  if (mock_keyring_items.size() > 0)
658    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
659  if (mock_keyring_items.size() > 1)
660    CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
661
662  // Check that we have set the persistent preference.
663  EXPECT_TRUE(
664      profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
665}
666
667TEST_F(NativeBackendGnomeTest, DISABLED_MigrateToMultipleProfiles) {
668  // Reject attempts to migrate so we can populate the store.
669  mock_keyring_reject_local_ids = true;
670
671  {
672    NativeBackendGnome backend(42, profile_.GetPrefs());
673    backend.Init();
674
675    BrowserThread::PostTask(
676        BrowserThread::DB, FROM_HERE,
677        base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
678                   base::Unretained(&backend), form_google_));
679
680    RunBothThreads();
681  }
682
683  EXPECT_EQ(1u, mock_keyring_items.size());
684  if (mock_keyring_items.size() > 0)
685    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
686
687  // Now allow the migration.
688  mock_keyring_reject_local_ids = false;
689
690  {
691    NativeBackendGnome backend(42, profile_.GetPrefs());
692    backend.Init();
693
694    // Trigger the migration by looking something up.
695    std::vector<PasswordForm*> form_list;
696    BrowserThread::PostTask(
697        BrowserThread::DB, FROM_HERE,
698        base::Bind(
699            base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
700            base::Unretained(&backend), &form_list));
701
702    RunBothThreads();
703
704    // Quick check that we got something back.
705    EXPECT_EQ(1u, form_list.size());
706    STLDeleteElements(&form_list);
707  }
708
709  EXPECT_EQ(2u, mock_keyring_items.size());
710  if (mock_keyring_items.size() > 0)
711    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
712  if (mock_keyring_items.size() > 1)
713    CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
714
715  // Check that we have set the persistent preference.
716  EXPECT_TRUE(
717      profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
718
719  // Normally we'd actually have a different profile. But in the test just reset
720  // the profile's persistent pref; we pass in the local profile id anyway.
721  profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, false);
722
723  {
724    NativeBackendGnome backend(24, profile_.GetPrefs());
725    backend.Init();
726
727    // Trigger the migration by looking something up.
728    std::vector<PasswordForm*> form_list;
729    BrowserThread::PostTask(
730        BrowserThread::DB, FROM_HERE,
731        base::Bind(
732            base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
733            base::Unretained(&backend), &form_list));
734
735    RunBothThreads();
736
737    // Quick check that we got something back.
738    EXPECT_EQ(1u, form_list.size());
739    STLDeleteElements(&form_list);
740  }
741
742  EXPECT_EQ(3u, mock_keyring_items.size());
743  if (mock_keyring_items.size() > 0)
744    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
745  if (mock_keyring_items.size() > 1)
746    CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
747  if (mock_keyring_items.size() > 2)
748    CheckMockKeyringItem(&mock_keyring_items[2], form_google_, "chrome-24");
749}
750
751TEST_F(NativeBackendGnomeTest, DISABLED_NoMigrationWithPrefSet) {
752  // Reject attempts to migrate so we can populate the store.
753  mock_keyring_reject_local_ids = true;
754
755  {
756    NativeBackendGnome backend(42, profile_.GetPrefs());
757    backend.Init();
758
759    BrowserThread::PostTask(
760        BrowserThread::DB, FROM_HERE,
761        base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
762                   base::Unretained(&backend), form_google_));
763
764    RunBothThreads();
765  }
766
767  EXPECT_EQ(1u, mock_keyring_items.size());
768  if (mock_keyring_items.size() > 0)
769    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
770
771  // Now allow migration, but also pretend that the it has already taken place.
772  mock_keyring_reject_local_ids = false;
773  profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
774
775  {
776    NativeBackendGnome backend(42, profile_.GetPrefs());
777    backend.Init();
778
779    // Trigger the migration by adding a new login.
780    BrowserThread::PostTask(
781        BrowserThread::DB, FROM_HERE,
782        base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
783                   base::Unretained(&backend), form_isc_));
784
785    // Look up all logins; we expect only the one we added.
786    std::vector<PasswordForm*> form_list;
787    BrowserThread::PostTask(
788        BrowserThread::DB, FROM_HERE,
789        base::Bind(
790            base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
791            base::Unretained(&backend), &form_list));
792
793    RunBothThreads();
794
795    // Quick check that we got the right thing back.
796    EXPECT_EQ(1u, form_list.size());
797    if (form_list.size() > 0)
798      EXPECT_EQ(form_isc_.signon_realm, form_list[0]->signon_realm);
799    STLDeleteElements(&form_list);
800  }
801
802  EXPECT_EQ(2u, mock_keyring_items.size());
803  if (mock_keyring_items.size() > 0)
804    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
805  if (mock_keyring_items.size() > 1)
806    CheckMockKeyringItem(&mock_keyring_items[1], form_isc_, "chrome-42");
807}
808
809TEST_F(NativeBackendGnomeTest, DISABLED_DeleteMigratedPasswordIsIsolated) {
810  // Reject attempts to migrate so we can populate the store.
811  mock_keyring_reject_local_ids = true;
812
813  {
814    NativeBackendGnome backend(42, profile_.GetPrefs());
815    backend.Init();
816
817    BrowserThread::PostTask(
818        BrowserThread::DB, FROM_HERE,
819        base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
820                   base::Unretained(&backend), form_google_));
821
822    RunBothThreads();
823  }
824
825  EXPECT_EQ(1u, mock_keyring_items.size());
826  if (mock_keyring_items.size() > 0)
827    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
828
829  // Now allow the migration.
830  mock_keyring_reject_local_ids = false;
831
832  {
833    NativeBackendGnome backend(42, profile_.GetPrefs());
834    backend.Init();
835
836    // Trigger the migration by looking something up.
837    std::vector<PasswordForm*> form_list;
838    BrowserThread::PostTask(
839        BrowserThread::DB, FROM_HERE,
840        base::Bind(
841            base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
842            base::Unretained(&backend), &form_list));
843
844    RunBothThreads();
845
846    // Quick check that we got something back.
847    EXPECT_EQ(1u, form_list.size());
848    STLDeleteElements(&form_list);
849  }
850
851  EXPECT_EQ(2u, mock_keyring_items.size());
852  if (mock_keyring_items.size() > 0)
853    CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
854  if (mock_keyring_items.size() > 1)
855    CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
856
857  // Check that we have set the persistent preference.
858  EXPECT_TRUE(
859      profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
860
861  // Normally we'd actually have a different profile. But in the test just reset
862  // the profile's persistent pref; we pass in the local profile id anyway.
863  profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, false);
864
865  {
866    NativeBackendGnome backend(24, profile_.GetPrefs());
867    backend.Init();
868
869    // Trigger the migration by looking something up.
870    std::vector<PasswordForm*> form_list;
871    BrowserThread::PostTask(
872        BrowserThread::DB, FROM_HERE,
873        base::Bind(
874            base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
875            base::Unretained(&backend), &form_list));
876
877    RunBothThreads();
878
879    // Quick check that we got something back.
880    EXPECT_EQ(1u, form_list.size());
881    STLDeleteElements(&form_list);
882
883    // There should be three passwords now.
884    EXPECT_EQ(3u, mock_keyring_items.size());
885    if (mock_keyring_items.size() > 0)
886      CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
887    if (mock_keyring_items.size() > 1)
888      CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
889    if (mock_keyring_items.size() > 2)
890      CheckMockKeyringItem(&mock_keyring_items[2], form_google_, "chrome-24");
891
892    // Now delete the password from this second profile.
893    BrowserThread::PostTask(
894        BrowserThread::DB, FROM_HERE,
895        base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin),
896                   base::Unretained(&backend), form_google_));
897
898    RunBothThreads();
899
900    // The other two copies of the password in different profiles should remain.
901    EXPECT_EQ(2u, mock_keyring_items.size());
902    if (mock_keyring_items.size() > 0)
903      CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
904    if (mock_keyring_items.size() > 1)
905      CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
906  }
907}
908