1// Copyright (c) 2011 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 "base/basictypes.h"
6#include "base/memory/scoped_temp_dir.h"
7#include "base/stl_util-inl.h"
8#include "base/string_util.h"
9#include "base/synchronization/waitable_event.h"
10#include "base/time.h"
11#include "chrome/browser/password_manager/password_form_data.h"
12#include "chrome/browser/password_manager/password_store_change.h"
13#include "chrome/browser/password_manager/password_store_x.h"
14#include "chrome/browser/webdata/web_data_service.h"
15#include "chrome/common/pref_names.h"
16#include "chrome/test/signaling_task.h"
17#include "chrome/test/testing_profile.h"
18#include "content/common/notification_observer_mock.h"
19#include "content/common/notification_service.h"
20#include "testing/gmock/include/gmock/gmock.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23using base::WaitableEvent;
24using testing::_;
25using testing::DoAll;
26using testing::ElementsAreArray;
27using testing::Pointee;
28using testing::Property;
29using testing::WithArg;
30using webkit_glue::PasswordForm;
31
32typedef std::vector<PasswordForm*> VectorOfForms;
33
34namespace {
35
36class MockPasswordStoreConsumer : public PasswordStoreConsumer {
37 public:
38  MOCK_METHOD2(OnPasswordStoreRequestDone,
39               void(CancelableRequestProvider::Handle,
40                    const std::vector<PasswordForm*>&));
41};
42
43class MockWebDataServiceConsumer : public WebDataServiceConsumer {
44 public:
45  MOCK_METHOD2(OnWebDataServiceRequestDone, void(WebDataService::Handle,
46                                                 const WDTypedResult*));
47};
48
49// This class will add and remove a mock notification observer from
50// the DB thread.
51class DBThreadObserverHelper
52    : public base::RefCountedThreadSafe<DBThreadObserverHelper,
53                                        BrowserThread::DeleteOnDBThread> {
54 public:
55  DBThreadObserverHelper() : done_event_(true, false) {}
56
57  void Init() {
58    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59    BrowserThread::PostTask(
60        BrowserThread::DB,
61        FROM_HERE,
62        NewRunnableMethod(this, &DBThreadObserverHelper::AddObserverTask));
63    done_event_.Wait();
64  }
65
66  virtual ~DBThreadObserverHelper() {
67    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
68    registrar_.RemoveAll();
69  }
70
71  NotificationObserverMock& observer() {
72    return observer_;
73  }
74
75 protected:
76  friend class base::RefCountedThreadSafe<DBThreadObserverHelper>;
77
78  void AddObserverTask() {
79    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
80    registrar_.Add(&observer_,
81                   NotificationType::LOGINS_CHANGED,
82                   NotificationService::AllSources());
83    done_event_.Signal();
84  }
85
86  WaitableEvent done_event_;
87  NotificationRegistrar registrar_;
88  NotificationObserverMock observer_;
89};
90
91class FailingBackend : public PasswordStoreX::NativeBackend {
92 public:
93  virtual bool Init() { return true; }
94
95  virtual bool AddLogin(const PasswordForm& form) { return false; }
96  virtual bool UpdateLogin(const PasswordForm& form) { return false; }
97  virtual bool RemoveLogin(const PasswordForm& form) { return false; }
98
99  virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
100                                          const base::Time& delete_end) {
101    return false;
102  }
103
104  virtual bool GetLogins(const PasswordForm& form, PasswordFormList* forms) {
105    return false;
106  }
107
108  virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
109                                       const base::Time& get_end,
110                                       PasswordFormList* forms) {
111    return false;
112  }
113
114  virtual bool GetAutofillableLogins(PasswordFormList* forms) { return false; }
115  virtual bool GetBlacklistLogins(PasswordFormList* forms) { return false; }
116};
117
118class MockBackend : public PasswordStoreX::NativeBackend {
119 public:
120  virtual bool Init() { return true; }
121
122  virtual bool AddLogin(const PasswordForm& form) {
123    all_forms_.push_back(form);
124    return true;
125  }
126
127  virtual bool UpdateLogin(const PasswordForm& form) {
128    for (size_t i = 0; i < all_forms_.size(); ++i)
129      if (CompareForms(all_forms_[i], form, true))
130        all_forms_[i] = form;
131    return true;
132  }
133
134  virtual bool RemoveLogin(const PasswordForm& form) {
135    for (size_t i = 0; i < all_forms_.size(); ++i)
136      if (CompareForms(all_forms_[i], form, false))
137        erase(i--);
138    return true;
139  }
140
141  virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
142                                          const base::Time& delete_end) {
143    for (size_t i = 0; i < all_forms_.size(); ++i) {
144      if (delete_begin <= all_forms_[i].date_created &&
145          (delete_end.is_null() || all_forms_[i].date_created < delete_end))
146        erase(i--);
147    }
148    return true;
149  }
150
151  virtual bool GetLogins(const PasswordForm& form, PasswordFormList* forms) {
152    for (size_t i = 0; i < all_forms_.size(); ++i)
153      if (all_forms_[i].signon_realm == form.signon_realm)
154        forms->push_back(new PasswordForm(all_forms_[i]));
155    return true;
156  }
157
158  virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
159                                       const base::Time& get_end,
160                                       PasswordFormList* forms) {
161    for (size_t i = 0; i < all_forms_.size(); ++i)
162      if (get_begin <= all_forms_[i].date_created &&
163          (get_end.is_null() || all_forms_[i].date_created < get_end))
164        forms->push_back(new PasswordForm(all_forms_[i]));
165    return true;
166  }
167
168  virtual bool GetAutofillableLogins(PasswordFormList* forms) {
169    for (size_t i = 0; i < all_forms_.size(); ++i)
170      if (!all_forms_[i].blacklisted_by_user)
171        forms->push_back(new PasswordForm(all_forms_[i]));
172    return true;
173  }
174
175  virtual bool GetBlacklistLogins(PasswordFormList* forms) {
176    for (size_t i = 0; i < all_forms_.size(); ++i)
177      if (all_forms_[i].blacklisted_by_user)
178        forms->push_back(new PasswordForm(all_forms_[i]));
179    return true;
180  }
181
182 private:
183  void erase(size_t index) {
184    if (index < all_forms_.size() - 1)
185      all_forms_[index] = all_forms_[all_forms_.size() - 1];
186    all_forms_.pop_back();
187  }
188
189  bool CompareForms(const PasswordForm& a, const PasswordForm& b, bool update) {
190    // An update check doesn't care about the submit element.
191    if (!update && a.submit_element != b.submit_element)
192      return false;
193    return a.origin           == b.origin &&
194           a.password_element == b.password_element &&
195           a.signon_realm     == b.signon_realm &&
196           a.username_element == b.username_element &&
197           a.username_value   == b.username_value;
198  }
199
200  std::vector<PasswordForm> all_forms_;
201};
202
203class MockLoginDatabaseReturn {
204 public:
205  MOCK_METHOD1(OnLoginDatabaseQueryDone,
206               void(const std::vector<PasswordForm*>&));
207};
208
209class LoginDatabaseQueryTask : public Task {
210 public:
211  LoginDatabaseQueryTask(LoginDatabase* login_db,
212                         bool autofillable,
213                         MockLoginDatabaseReturn* mock_return)
214      : login_db_(login_db), autofillable_(autofillable),
215        mock_return_(mock_return) {
216  }
217
218  virtual void Run() {
219    std::vector<PasswordForm*> forms;
220    if (autofillable_)
221      login_db_->GetAutofillableLogins(&forms);
222    else
223      login_db_->GetBlacklistLogins(&forms);
224    mock_return_->OnLoginDatabaseQueryDone(forms);
225  }
226
227 private:
228  LoginDatabase* login_db_;
229  bool autofillable_;
230  MockLoginDatabaseReturn* mock_return_;
231};
232
233// Generate |count| expected logins, either autofillable or blacklisted.
234void InitExpectedForms(bool autofillable, size_t count, VectorOfForms* forms) {
235  const char* domain = autofillable ? "example" : "blacklisted";
236  for (size_t i = 0; i < count; ++i) {
237    std::string realm = StringPrintf("http://%zu.%s.com", i, domain);
238    std::string origin = StringPrintf("http://%zu.%s.com/origin", i, domain);
239    std::string action = StringPrintf("http://%zu.%s.com/action", i, domain);
240    PasswordFormData data = {
241      PasswordForm::SCHEME_HTML,
242      realm.c_str(),
243      origin.c_str(),
244      action.c_str(),
245      L"submit_element",
246      L"username_element",
247      L"password_element",
248      autofillable ? L"username_value" : NULL,
249      autofillable ? L"password_value" : NULL,
250      autofillable, false, i + 1 };
251    forms->push_back(CreatePasswordFormFromData(data));
252  }
253}
254
255}  // anonymous namespace
256
257// LoginDatabase isn't reference counted, but in these unit tests that won't be
258// a problem as it always outlives the threads we post tasks to.
259template<>
260struct RunnableMethodTraits<LoginDatabase> {
261  void RetainCallee(LoginDatabase*) {}
262  void ReleaseCallee(LoginDatabase*) {}
263};
264
265enum BackendType {
266  NO_BACKEND,
267  FAILING_BACKEND,
268  WORKING_BACKEND
269};
270
271class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
272 protected:
273  PasswordStoreXTest()
274      : ui_thread_(BrowserThread::UI, &message_loop_),
275        db_thread_(BrowserThread::DB) {
276  }
277
278  virtual void SetUp() {
279    ASSERT_TRUE(db_thread_.Start());
280    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
281
282    profile_.reset(new TestingProfile());
283
284    login_db_.reset(new LoginDatabase());
285    ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append("login_test")));
286
287    wds_ = new WebDataService();
288    ASSERT_TRUE(wds_->Init(temp_dir_.path()));
289  }
290
291  virtual void TearDown() {
292    wds_->Shutdown();
293    MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask);
294    MessageLoop::current()->Run();
295    db_thread_.Stop();
296  }
297
298  PasswordStoreX::NativeBackend* GetBackend() {
299    switch (GetParam()) {
300      case FAILING_BACKEND:
301        return new FailingBackend();
302      case WORKING_BACKEND:
303        return new MockBackend();
304      default:
305        return NULL;
306    }
307  }
308
309  MessageLoopForUI message_loop_;
310  BrowserThread ui_thread_;
311  BrowserThread db_thread_;  // PasswordStore, WDS schedule work on this thread.
312
313  scoped_ptr<LoginDatabase> login_db_;
314  scoped_ptr<TestingProfile> profile_;
315  scoped_refptr<WebDataService> wds_;
316  ScopedTempDir temp_dir_;
317};
318
319ACTION(STLDeleteElements0) {
320  STLDeleteContainerPointers(arg0.begin(), arg0.end());
321}
322
323ACTION(QuitUIMessageLoop) {
324  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
325  MessageLoop::current()->Quit();
326}
327
328MATCHER(EmptyWDResult, "") {
329  return static_cast<const WDResult<std::vector<PasswordForm*> >*>(
330      arg)->GetValue().empty();
331}
332
333TEST_P(PasswordStoreXTest, WDSMigration) {
334  VectorOfForms expected_autofillable;
335  InitExpectedForms(true, 5, &expected_autofillable);
336
337  VectorOfForms expected_blacklisted;
338  InitExpectedForms(false, 5, &expected_blacklisted);
339
340  // Populate the WDS with logins that should be migrated.
341  for (VectorOfForms::iterator it = expected_autofillable.begin();
342       it != expected_autofillable.end(); ++it) {
343    wds_->AddLogin(**it);
344  }
345  for (VectorOfForms::iterator it = expected_blacklisted.begin();
346       it != expected_blacklisted.end(); ++it) {
347    wds_->AddLogin(**it);
348  }
349
350  // The WDS schedules tasks to run on the DB thread so we schedule yet another
351  // task to notify us that it's safe to carry on with the test.
352  WaitableEvent done(false, false);
353  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
354      new SignalingTask(&done));
355  done.Wait();
356
357  // Initializing the PasswordStore should trigger a migration.
358  scoped_refptr<PasswordStoreX> store(
359      new PasswordStoreX(login_db_.release(),
360                           profile_.get(),
361                           wds_.get(),
362                           GetBackend()));
363  store->Init();
364
365  // Check that the migration preference has not been initialized.
366  ASSERT_TRUE(NULL == profile_->GetPrefs()->FindPreference(
367      prefs::kLoginDatabaseMigrated));
368
369  // Again, the WDS schedules tasks to run on the DB thread, so schedule a task
370  // to signal us when it is safe to continue.
371  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
372      new SignalingTask(&done));
373  done.Wait();
374
375  // Let the WDS callbacks proceed so the logins can be migrated.
376  MessageLoop::current()->RunAllPending();
377
378  MockPasswordStoreConsumer consumer;
379
380  // Make sure we quit the MessageLoop even if the test fails.
381  ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
382      .WillByDefault(QuitUIMessageLoop());
383
384  // The autofillable forms should have been migrated from the WDS to the login
385  // database.
386  EXPECT_CALL(consumer,
387      OnPasswordStoreRequestDone(_,
388          ContainsAllPasswordForms(expected_autofillable)))
389      .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
390
391  store->GetAutofillableLogins(&consumer);
392  MessageLoop::current()->Run();
393
394  // The blacklisted forms should have been migrated from the WDS to the login
395  // database.
396  EXPECT_CALL(consumer,
397      OnPasswordStoreRequestDone(_,
398          ContainsAllPasswordForms(expected_blacklisted)))
399      .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
400
401  store->GetBlacklistLogins(&consumer);
402  MessageLoop::current()->Run();
403
404  // Check that the migration updated the migrated preference.
405  ASSERT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kLoginDatabaseMigrated));
406
407  MockWebDataServiceConsumer wds_consumer;
408
409  // No autofillable logins should be left in the WDS.
410  EXPECT_CALL(wds_consumer,
411      OnWebDataServiceRequestDone(_, EmptyWDResult()));
412
413  wds_->GetAutofillableLogins(&wds_consumer);
414
415  // Wait for the WDS methods to execute on the DB thread.
416  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
417      new SignalingTask(&done));
418  done.Wait();
419
420  // Handle the callback from the WDS.
421  MessageLoop::current()->RunAllPending();
422
423  // Likewise, no blacklisted logins should be left in the WDS.
424  EXPECT_CALL(wds_consumer,
425      OnWebDataServiceRequestDone(_, EmptyWDResult()));
426
427  wds_->GetBlacklistLogins(&wds_consumer);
428
429  // Wait for the WDS methods to execute on the DB thread.
430  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
431      new SignalingTask(&done));
432  done.Wait();
433
434  // Handle the callback from the WDS.
435  MessageLoop::current()->RunAllPending();
436
437  STLDeleteElements(&expected_autofillable);
438  STLDeleteElements(&expected_blacklisted);
439
440  store->Shutdown();
441}
442
443TEST_P(PasswordStoreXTest, WDSMigrationAlreadyDone) {
444  PasswordFormData wds_data[] = {
445    { PasswordForm::SCHEME_HTML,
446      "http://bar.example.com",
447      "http://bar.example.com/origin",
448      "http://bar.example.com/action",
449      L"submit_element",
450      L"username_element",
451      L"password_element",
452      L"username_value",
453      L"password_value",
454      true, false, 1 },
455  };
456
457  VectorOfForms unexpected_autofillable;
458  for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(wds_data); ++i) {
459    unexpected_autofillable.push_back(
460        CreatePasswordFormFromData(wds_data[i]));
461  }
462
463  // Populate the WDS with logins that should be migrated.
464  for (VectorOfForms::iterator it = unexpected_autofillable.begin();
465       it != unexpected_autofillable.end(); ++it) {
466    wds_->AddLogin(**it);
467  }
468
469  // The WDS schedules tasks to run on the DB thread so we schedule yet another
470  // task to notify us that it's safe to carry on with the test.
471  WaitableEvent done(false, false);
472  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
473      new SignalingTask(&done));
474  done.Wait();
475
476  // Prentend that the migration has already taken place.
477  profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
478                                            true);
479
480  // Initializing the PasswordStore shouldn't trigger a migration.
481  scoped_refptr<PasswordStoreX> store(
482      new PasswordStoreX(login_db_.release(),
483                           profile_.get(),
484                           wds_.get(),
485                           GetBackend()));
486  store->Init();
487
488  MockPasswordStoreConsumer consumer;
489  // Make sure we quit the MessageLoop even if the test fails.
490  ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
491      .WillByDefault(QuitUIMessageLoop());
492
493  // No forms should be migrated.
494  VectorOfForms empty;
495  EXPECT_CALL(consumer,
496      OnPasswordStoreRequestDone(_,
497          ContainsAllPasswordForms(empty)))
498      .WillOnce(QuitUIMessageLoop());
499
500  store->GetAutofillableLogins(&consumer);
501  MessageLoop::current()->Run();
502
503  STLDeleteElements(&unexpected_autofillable);
504
505  store->Shutdown();
506}
507
508TEST_P(PasswordStoreXTest, Notifications) {
509  // Pretend that the migration has already taken place.
510  profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
511                                            true);
512
513  // Initializing the PasswordStore shouldn't trigger a migration.
514  scoped_refptr<PasswordStoreX> store(
515      new PasswordStoreX(login_db_.release(),
516                           profile_.get(),
517                           wds_.get(),
518                           GetBackend()));
519  store->Init();
520
521  PasswordFormData form_data =
522  { PasswordForm::SCHEME_HTML,
523    "http://bar.example.com",
524    "http://bar.example.com/origin",
525    "http://bar.example.com/action",
526    L"submit_element",
527    L"username_element",
528    L"password_element",
529    L"username_value",
530    L"password_value",
531    true, false, 1 };
532  scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
533
534  scoped_refptr<DBThreadObserverHelper> helper = new DBThreadObserverHelper;
535  helper->Init();
536
537  const PasswordStoreChange expected_add_changes[] = {
538    PasswordStoreChange(PasswordStoreChange::ADD, *form),
539  };
540
541  EXPECT_CALL(helper->observer(),
542              Observe(NotificationType(NotificationType::LOGINS_CHANGED),
543                      NotificationService::AllSources(),
544                      Property(&Details<const PasswordStoreChangeList>::ptr,
545                               Pointee(ElementsAreArray(
546                                   expected_add_changes)))));
547
548  // Adding a login should trigger a notification.
549  store->AddLogin(*form);
550
551  // The PasswordStore schedules tasks to run on the DB thread so we schedule
552  // yet another task to notify us that it's safe to carry on with the test.
553  WaitableEvent done(false, false);
554  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
555      new SignalingTask(&done));
556  done.Wait();
557
558  // Change the password.
559  form->password_value = WideToUTF16(L"a different password");
560
561  const PasswordStoreChange expected_update_changes[] = {
562    PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
563  };
564
565  EXPECT_CALL(helper->observer(),
566              Observe(NotificationType(NotificationType::LOGINS_CHANGED),
567                      NotificationService::AllSources(),
568                      Property(&Details<const PasswordStoreChangeList>::ptr,
569                               Pointee(ElementsAreArray(
570                                   expected_update_changes)))));
571
572  // Updating the login with the new password should trigger a notification.
573  store->UpdateLogin(*form);
574
575  // Wait for PasswordStore to send the notification.
576  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
577      new SignalingTask(&done));
578  done.Wait();
579
580  const PasswordStoreChange expected_delete_changes[] = {
581    PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
582  };
583
584  EXPECT_CALL(helper->observer(),
585              Observe(NotificationType(NotificationType::LOGINS_CHANGED),
586                      NotificationService::AllSources(),
587                      Property(&Details<const PasswordStoreChangeList>::ptr,
588                               Pointee(ElementsAreArray(
589                                   expected_delete_changes)))));
590
591  // Deleting the login should trigger a notification.
592  store->RemoveLogin(*form);
593
594  // Wait for PasswordStore to send the notification.
595  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
596      new SignalingTask(&done));
597  done.Wait();
598
599  store->Shutdown();
600}
601
602TEST_P(PasswordStoreXTest, NativeMigration) {
603  VectorOfForms expected_autofillable;
604  InitExpectedForms(true, 50, &expected_autofillable);
605
606  VectorOfForms expected_blacklisted;
607  InitExpectedForms(false, 50, &expected_blacklisted);
608
609  // Get the initial size of the login DB file, before we populate it.
610  // This will be used later to make sure it gets back to this size.
611  const FilePath login_db_file = temp_dir_.path().Append("login_test");
612  base::PlatformFileInfo db_file_start_info;
613  ASSERT_TRUE(file_util::GetFileInfo(login_db_file, &db_file_start_info));
614
615  LoginDatabase* login_db = login_db_.get();
616
617  // Populate the login DB with logins that should be migrated.
618  for (VectorOfForms::iterator it = expected_autofillable.begin();
619       it != expected_autofillable.end(); ++it) {
620    BrowserThread::PostTask(BrowserThread::DB,
621                           FROM_HERE,
622                           NewRunnableMethod(login_db,
623                                             &LoginDatabase::AddLogin,
624                                             **it));
625  }
626  for (VectorOfForms::iterator it = expected_blacklisted.begin();
627       it != expected_blacklisted.end(); ++it) {
628    BrowserThread::PostTask(BrowserThread::DB,
629                           FROM_HERE,
630                           NewRunnableMethod(login_db,
631                                             &LoginDatabase::AddLogin,
632                                             **it));
633  }
634
635  // Schedule another task on the DB thread to notify us that it's safe to
636  // carry on with the test.
637  WaitableEvent done(false, false);
638  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
639      new SignalingTask(&done));
640  done.Wait();
641
642  // Get the new size of the login DB file. We expect it to be larger.
643  base::PlatformFileInfo db_file_full_info;
644  ASSERT_TRUE(file_util::GetFileInfo(login_db_file, &db_file_full_info));
645  EXPECT_GT(db_file_full_info.size, db_file_start_info.size);
646
647  // Pretend that the WDS migration has already taken place.
648  profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
649                                            true);
650
651  // Initializing the PasswordStore shouldn't trigger a native migration (yet).
652  scoped_refptr<PasswordStoreX> store(
653      new PasswordStoreX(login_db_.release(),
654                           profile_.get(),
655                           wds_.get(),
656                           GetBackend()));
657  store->Init();
658
659  MockPasswordStoreConsumer consumer;
660
661  // Make sure we quit the MessageLoop even if the test fails.
662  ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
663      .WillByDefault(QuitUIMessageLoop());
664
665  // The autofillable forms should have been migrated to the native backend.
666  EXPECT_CALL(consumer,
667      OnPasswordStoreRequestDone(_,
668          ContainsAllPasswordForms(expected_autofillable)))
669      .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
670
671  store->GetAutofillableLogins(&consumer);
672  MessageLoop::current()->Run();
673
674  // The blacklisted forms should have been migrated to the native backend.
675  EXPECT_CALL(consumer,
676      OnPasswordStoreRequestDone(_,
677          ContainsAllPasswordForms(expected_blacklisted)))
678      .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
679
680  store->GetBlacklistLogins(&consumer);
681  MessageLoop::current()->Run();
682
683  VectorOfForms empty;
684  MockLoginDatabaseReturn ld_return;
685
686  if (GetParam() == WORKING_BACKEND) {
687    // No autofillable logins should be left in the login DB.
688    EXPECT_CALL(ld_return,
689                OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
690  } else {
691    // The autofillable logins should still be in the login DB.
692    EXPECT_CALL(ld_return,
693                OnLoginDatabaseQueryDone(
694                    ContainsAllPasswordForms(expected_autofillable)))
695        .WillOnce(WithArg<0>(STLDeleteElements0()));
696  }
697
698  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
699      new LoginDatabaseQueryTask(login_db, true, &ld_return));
700
701  // Wait for the login DB methods to execute on the DB thread.
702  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
703      new SignalingTask(&done));
704  done.Wait();
705
706  if (GetParam() == WORKING_BACKEND) {
707    // Likewise, no blacklisted logins should be left in the login DB.
708    EXPECT_CALL(ld_return,
709                OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
710  } else {
711    // The blacklisted logins should still be in the login DB.
712    EXPECT_CALL(ld_return,
713                OnLoginDatabaseQueryDone(
714                    ContainsAllPasswordForms(expected_blacklisted)))
715        .WillOnce(WithArg<0>(STLDeleteElements0()));
716  }
717
718  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
719      new LoginDatabaseQueryTask(login_db, false, &ld_return));
720
721  // Wait for the login DB methods to execute on the DB thread.
722  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
723      new SignalingTask(&done));
724  done.Wait();
725
726  if (GetParam() == WORKING_BACKEND) {
727    // If the migration succeeded, then not only should there be no logins left
728    // in the login DB, but also the file should have been deleted and then
729    // recreated. We approximate checking for this by checking that the file
730    // size is equal to the size before we populated it, even though it was
731    // larger after populating it.
732    base::PlatformFileInfo db_file_end_info;
733    ASSERT_TRUE(file_util::GetFileInfo(login_db_file, &db_file_end_info));
734    EXPECT_EQ(db_file_start_info.size, db_file_end_info.size);
735  }
736
737  STLDeleteElements(&expected_autofillable);
738  STLDeleteElements(&expected_blacklisted);
739
740  store->Shutdown();
741}
742
743INSTANTIATE_TEST_CASE_P(NoBackend,
744                        PasswordStoreXTest,
745                        testing::Values(NO_BACKEND));
746INSTANTIATE_TEST_CASE_P(FailingBackend,
747                        PasswordStoreXTest,
748                        testing::Values(FAILING_BACKEND));
749INSTANTIATE_TEST_CASE_P(WorkingBackend,
750                        PasswordStoreXTest,
751                        testing::Values(WORKING_BACKEND));
752