Home | History | Annotate | Download | only in password_manager
      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 
     23 using base::WaitableEvent;
     24 using testing::_;
     25 using testing::DoAll;
     26 using testing::ElementsAreArray;
     27 using testing::Pointee;
     28 using testing::Property;
     29 using testing::WithArg;
     30 using webkit_glue::PasswordForm;
     31 
     32 typedef std::vector<PasswordForm*> VectorOfForms;
     33 
     34 namespace {
     35 
     36 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
     37  public:
     38   MOCK_METHOD2(OnPasswordStoreRequestDone,
     39                void(CancelableRequestProvider::Handle,
     40                     const std::vector<PasswordForm*>&));
     41 };
     42 
     43 class 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.
     51 class 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 
     91 class 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 
    118 class 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 
    203 class MockLoginDatabaseReturn {
    204  public:
    205   MOCK_METHOD1(OnLoginDatabaseQueryDone,
    206                void(const std::vector<PasswordForm*>&));
    207 };
    208 
    209 class 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.
    234 void 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.
    259 template<>
    260 struct RunnableMethodTraits<LoginDatabase> {
    261   void RetainCallee(LoginDatabase*) {}
    262   void ReleaseCallee(LoginDatabase*) {}
    263 };
    264 
    265 enum BackendType {
    266   NO_BACKEND,
    267   FAILING_BACKEND,
    268   WORKING_BACKEND
    269 };
    270 
    271 class 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 
    319 ACTION(STLDeleteElements0) {
    320   STLDeleteContainerPointers(arg0.begin(), arg0.end());
    321 }
    322 
    323 ACTION(QuitUIMessageLoop) {
    324   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    325   MessageLoop::current()->Quit();
    326 }
    327 
    328 MATCHER(EmptyWDResult, "") {
    329   return static_cast<const WDResult<std::vector<PasswordForm*> >*>(
    330       arg)->GetValue().empty();
    331 }
    332 
    333 TEST_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 
    443 TEST_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 
    508 TEST_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 
    602 TEST_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 
    743 INSTANTIATE_TEST_CASE_P(NoBackend,
    744                         PasswordStoreXTest,
    745                         testing::Values(NO_BACKEND));
    746 INSTANTIATE_TEST_CASE_P(FailingBackend,
    747                         PasswordStoreXTest,
    748                         testing::Values(FAILING_BACKEND));
    749 INSTANTIATE_TEST_CASE_P(WorkingBackend,
    750                         PasswordStoreXTest,
    751                         testing::Values(WORKING_BACKEND));
    752