Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / password_manager / password_store_x_unittest.cc
blobf60217036b980a0017ee4db0959879ebbaa222a3
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.
5 #include "base/basictypes.h"
6 #include "base/bind.h"
7 #include "base/bind_helpers.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/run_loop.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "base/time/time.h"
18 #include "chrome/browser/password_manager/password_store_x.h"
19 #include "chrome/test/base/testing_browser_process.h"
20 #include "components/password_manager/core/browser/password_manager_test_utils.h"
21 #include "components/password_manager/core/browser/password_store_change.h"
22 #include "components/password_manager/core/browser/password_store_consumer.h"
23 #include "components/password_manager/core/common/password_manager_pref_names.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/test/test_browser_thread_bundle.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 using autofill::PasswordForm;
30 using password_manager::PasswordStoreChange;
31 using password_manager::PasswordStoreChangeList;
32 using password_manager::UnorderedPasswordFormElementsAre;
33 using testing::ElementsAreArray;
34 using testing::IsEmpty;
36 namespace {
38 class MockPasswordStoreConsumer
39 : public password_manager::PasswordStoreConsumer {
40 public:
41 MOCK_METHOD1(OnGetPasswordStoreResultsConstRef,
42 void(const std::vector<PasswordForm*>&));
44 // GMock cannot mock methods with move-only args.
45 void OnGetPasswordStoreResults(ScopedVector<PasswordForm> results) override {
46 OnGetPasswordStoreResultsConstRef(results.get());
50 class MockPasswordStoreObserver
51 : public password_manager::PasswordStore::Observer {
52 public:
53 MOCK_METHOD1(OnLoginsChanged,
54 void(const password_manager::PasswordStoreChangeList& changes));
57 class FailingBackend : public PasswordStoreX::NativeBackend {
58 public:
59 bool Init() override { return true; }
61 PasswordStoreChangeList AddLogin(const PasswordForm& form) override {
62 return PasswordStoreChangeList();
64 bool UpdateLogin(const PasswordForm& form,
65 PasswordStoreChangeList* changes) override {
66 return false;
68 bool RemoveLogin(const PasswordForm& form) override { return false; }
70 bool RemoveLoginsCreatedBetween(
71 base::Time delete_begin,
72 base::Time delete_end,
73 password_manager::PasswordStoreChangeList* changes) override {
74 return false;
77 bool RemoveLoginsSyncedBetween(
78 base::Time delete_begin,
79 base::Time delete_end,
80 password_manager::PasswordStoreChangeList* changes) override {
81 return false;
84 // Use this as a landmine to check whether results of failed Get*Logins calls
85 // get ignored.
86 static ScopedVector<autofill::PasswordForm> CreateTrashForms() {
87 ScopedVector<autofill::PasswordForm> forms;
88 PasswordForm trash;
89 trash.username_element = base::ASCIIToUTF16("trash u. element");
90 trash.username_value = base::ASCIIToUTF16("trash u. value");
91 trash.password_element = base::ASCIIToUTF16("trash p. element");
92 trash.password_value = base::ASCIIToUTF16("trash p. value");
93 for (size_t i = 0; i < 3; ++i) {
94 trash.origin = GURL(base::StringPrintf("http://trash%zu.com", i));
95 forms.push_back(new PasswordForm(trash));
97 return forms.Pass();
100 bool GetLogins(const PasswordForm& form,
101 ScopedVector<autofill::PasswordForm>* forms) override {
102 *forms = CreateTrashForms();
103 return false;
106 bool GetAutofillableLogins(
107 ScopedVector<autofill::PasswordForm>* forms) override {
108 *forms = CreateTrashForms();
109 return false;
111 bool GetBlacklistLogins(
112 ScopedVector<autofill::PasswordForm>* forms) override {
113 *forms = CreateTrashForms();
114 return false;
118 class MockBackend : public PasswordStoreX::NativeBackend {
119 public:
120 bool Init() override { return true; }
122 PasswordStoreChangeList AddLogin(const PasswordForm& form) override {
123 all_forms_.push_back(form);
124 PasswordStoreChange change(PasswordStoreChange::ADD, form);
125 return PasswordStoreChangeList(1, change);
128 bool UpdateLogin(const PasswordForm& form,
129 PasswordStoreChangeList* changes) override {
130 for (size_t i = 0; i < all_forms_.size(); ++i)
131 if (CompareForms(all_forms_[i], form, true)) {
132 all_forms_[i] = form;
133 changes->push_back(PasswordStoreChange(PasswordStoreChange::UPDATE,
134 form));
136 return true;
139 bool RemoveLogin(const PasswordForm& form) override {
140 for (size_t i = 0; i < all_forms_.size(); ++i)
141 if (CompareForms(all_forms_[i], form, false))
142 erase(i--);
143 return true;
146 bool RemoveLoginsCreatedBetween(
147 base::Time delete_begin,
148 base::Time delete_end,
149 password_manager::PasswordStoreChangeList* changes) override {
150 for (size_t i = 0; i < all_forms_.size(); ++i) {
151 if (delete_begin <= all_forms_[i].date_created &&
152 (delete_end.is_null() || all_forms_[i].date_created < delete_end))
153 erase(i--);
155 return true;
158 bool RemoveLoginsSyncedBetween(
159 base::Time delete_begin,
160 base::Time delete_end,
161 password_manager::PasswordStoreChangeList* changes) override {
162 DCHECK(changes);
163 for (size_t i = 0; i < all_forms_.size(); ++i) {
164 if (delete_begin <= all_forms_[i].date_synced &&
165 (delete_end.is_null() || all_forms_[i].date_synced < delete_end)) {
166 changes->push_back(password_manager::PasswordStoreChange(
167 password_manager::PasswordStoreChange::REMOVE, all_forms_[i]));
168 erase(i--);
171 return true;
174 bool GetLogins(const PasswordForm& form,
175 ScopedVector<autofill::PasswordForm>* forms) override {
176 for (size_t i = 0; i < all_forms_.size(); ++i)
177 if (all_forms_[i].signon_realm == form.signon_realm)
178 forms->push_back(new PasswordForm(all_forms_[i]));
179 return true;
182 bool GetAutofillableLogins(
183 ScopedVector<autofill::PasswordForm>* forms) override {
184 for (size_t i = 0; i < all_forms_.size(); ++i)
185 if (!all_forms_[i].blacklisted_by_user)
186 forms->push_back(new PasswordForm(all_forms_[i]));
187 return true;
190 bool GetBlacklistLogins(
191 ScopedVector<autofill::PasswordForm>* forms) override {
192 for (size_t i = 0; i < all_forms_.size(); ++i)
193 if (all_forms_[i].blacklisted_by_user)
194 forms->push_back(new PasswordForm(all_forms_[i]));
195 return true;
198 private:
199 void erase(size_t index) {
200 if (index < all_forms_.size() - 1)
201 all_forms_[index] = all_forms_[all_forms_.size() - 1];
202 all_forms_.pop_back();
205 bool CompareForms(const PasswordForm& a, const PasswordForm& b, bool update) {
206 // An update check doesn't care about the submit element.
207 if (!update && a.submit_element != b.submit_element)
208 return false;
209 return a.origin == b.origin &&
210 a.password_element == b.password_element &&
211 a.signon_realm == b.signon_realm &&
212 a.username_element == b.username_element &&
213 a.username_value == b.username_value;
216 std::vector<PasswordForm> all_forms_;
219 class MockLoginDatabaseReturn {
220 public:
221 MOCK_METHOD1(OnLoginDatabaseQueryDone,
222 void(const std::vector<PasswordForm*>&));
225 void LoginDatabaseQueryCallback(password_manager::LoginDatabase* login_db,
226 bool autofillable,
227 MockLoginDatabaseReturn* mock_return) {
228 ScopedVector<autofill::PasswordForm> forms;
229 if (autofillable)
230 EXPECT_TRUE(login_db->GetAutofillableLogins(&forms));
231 else
232 EXPECT_TRUE(login_db->GetBlacklistLogins(&forms));
233 mock_return->OnLoginDatabaseQueryDone(forms.get());
236 // Generate |count| expected logins, either auto-fillable or blacklisted.
237 void InitExpectedForms(bool autofillable,
238 size_t count,
239 ScopedVector<autofill::PasswordForm>* forms) {
240 const char* domain = autofillable ? "example" : "blacklisted";
241 for (size_t i = 0; i < count; ++i) {
242 std::string realm = base::StringPrintf("http://%zu.%s.com", i, domain);
243 std::string origin = base::StringPrintf("http://%zu.%s.com/origin",
244 i, domain);
245 std::string action = base::StringPrintf("http://%zu.%s.com/action",
246 i, domain);
247 password_manager::PasswordFormData data = {
248 PasswordForm::SCHEME_HTML,
249 realm.c_str(),
250 origin.c_str(),
251 action.c_str(),
252 L"submit_element",
253 L"username_element",
254 L"password_element",
255 autofillable ? L"username_value" : nullptr,
256 autofillable ? L"password_value" : nullptr,
257 autofillable,
258 false,
259 static_cast<double>(i + 1)};
260 forms->push_back(CreatePasswordFormFromDataForTesting(data).Pass());
264 PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
265 return PasswordStoreChangeList(
266 1, PasswordStoreChange(PasswordStoreChange::ADD, form));
269 } // anonymous namespace
271 enum BackendType {
272 NO_BACKEND,
273 FAILING_BACKEND,
274 WORKING_BACKEND
277 class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
278 protected:
279 void SetUp() override {
280 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
283 void TearDown() override { base::RunLoop().RunUntilIdle(); }
285 base::FilePath test_login_db_file_path() const {
286 return temp_dir_.path().Append(FILE_PATH_LITERAL("login_test"));
289 PasswordStoreX::NativeBackend* GetBackend() {
290 switch (GetParam()) {
291 case FAILING_BACKEND:
292 return new FailingBackend();
293 case WORKING_BACKEND:
294 return new MockBackend();
295 default:
296 return nullptr;
300 content::TestBrowserThreadBundle thread_bundle_;
302 base::ScopedTempDir temp_dir_;
305 ACTION(STLDeleteElements0) {
306 STLDeleteContainerPointers(arg0.begin(), arg0.end());
309 TEST_P(PasswordStoreXTest, Notifications) {
310 scoped_ptr<password_manager::LoginDatabase> login_db(
311 new password_manager::LoginDatabase(test_login_db_file_path()));
312 scoped_refptr<PasswordStoreX> store(new PasswordStoreX(
313 base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
314 login_db.Pass(), GetBackend()));
315 store->Init(syncer::SyncableService::StartSyncFlare());
317 password_manager::PasswordFormData form_data = {
318 PasswordForm::SCHEME_HTML, "http://bar.example.com",
319 "http://bar.example.com/origin", "http://bar.example.com/action",
320 L"submit_element", L"username_element",
321 L"password_element", L"username_value",
322 L"password_value", true,
323 false, 1};
324 scoped_ptr<PasswordForm> form =
325 CreatePasswordFormFromDataForTesting(form_data);
327 MockPasswordStoreObserver observer;
328 store->AddObserver(&observer);
330 const PasswordStoreChange expected_add_changes[] = {
331 PasswordStoreChange(PasswordStoreChange::ADD, *form),
334 EXPECT_CALL(
335 observer,
336 OnLoginsChanged(ElementsAreArray(expected_add_changes)));
338 // Adding a login should trigger a notification.
339 store->AddLogin(*form);
341 // The PasswordStore schedules tasks to run on the DB thread. Wait for them
342 // to complete.
343 base::RunLoop().RunUntilIdle();
345 // Change the password.
346 form->password_value = base::ASCIIToUTF16("a different password");
348 const PasswordStoreChange expected_update_changes[] = {
349 PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
352 EXPECT_CALL(
353 observer,
354 OnLoginsChanged(ElementsAreArray(expected_update_changes)));
356 // Updating the login with the new password should trigger a notification.
357 store->UpdateLogin(*form);
359 // Wait for PasswordStore to send execute.
360 base::RunLoop().RunUntilIdle();
362 const PasswordStoreChange expected_delete_changes[] = {
363 PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
366 EXPECT_CALL(
367 observer,
368 OnLoginsChanged(ElementsAreArray(expected_delete_changes)));
370 // Deleting the login should trigger a notification.
371 store->RemoveLogin(*form);
373 // Wait for PasswordStore to execute.
374 base::RunLoop().RunUntilIdle();
376 store->RemoveObserver(&observer);
378 store->Shutdown();
381 TEST_P(PasswordStoreXTest, NativeMigration) {
382 ScopedVector<autofill::PasswordForm> expected_autofillable;
383 InitExpectedForms(true, 50, &expected_autofillable);
385 ScopedVector<autofill::PasswordForm> expected_blacklisted;
386 InitExpectedForms(false, 50, &expected_blacklisted);
388 const base::FilePath login_db_file = test_login_db_file_path();
389 scoped_ptr<password_manager::LoginDatabase> login_db(
390 new password_manager::LoginDatabase(login_db_file));
391 ASSERT_TRUE(login_db->Init());
393 // Get the initial size of the login DB file, before we populate it.
394 // This will be used later to make sure it gets back to this size.
395 base::File::Info db_file_start_info;
396 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_start_info));
398 // Populate the login DB with logins that should be migrated.
399 for (const autofill::PasswordForm* form : expected_autofillable) {
400 EXPECT_EQ(AddChangeForForm(*form), login_db->AddLogin(*form));
402 for (const autofill::PasswordForm* form : expected_blacklisted) {
403 EXPECT_EQ(AddChangeForForm(*form), login_db->AddLogin(*form));
406 // Get the new size of the login DB file. We expect it to be larger.
407 base::File::Info db_file_full_info;
408 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_full_info));
409 EXPECT_GT(db_file_full_info.size, db_file_start_info.size);
411 // Initializing the PasswordStore shouldn't trigger a native migration (yet).
412 login_db.reset(new password_manager::LoginDatabase(login_db_file));
413 scoped_refptr<PasswordStoreX> store(new PasswordStoreX(
414 base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
415 login_db.Pass(), GetBackend()));
416 store->Init(syncer::SyncableService::StartSyncFlare());
418 MockPasswordStoreConsumer consumer;
420 // The autofillable forms should have been migrated to the native backend.
421 EXPECT_CALL(
422 consumer,
423 OnGetPasswordStoreResultsConstRef(
424 UnorderedPasswordFormElementsAre(expected_autofillable.get())));
426 store->GetAutofillableLogins(&consumer);
427 base::RunLoop().RunUntilIdle();
429 // The blacklisted forms should have been migrated to the native backend.
430 EXPECT_CALL(
431 consumer,
432 OnGetPasswordStoreResultsConstRef(
433 UnorderedPasswordFormElementsAre(expected_blacklisted.get())));
435 store->GetBlacklistLogins(&consumer);
436 base::RunLoop().RunUntilIdle();
438 MockLoginDatabaseReturn ld_return;
440 if (GetParam() == WORKING_BACKEND) {
441 // No autofillable logins should be left in the login DB.
442 EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(IsEmpty()));
443 } else {
444 // The autofillable logins should still be in the login DB.
445 EXPECT_CALL(ld_return,
446 OnLoginDatabaseQueryDone(UnorderedPasswordFormElementsAre(
447 expected_autofillable.get())));
450 LoginDatabaseQueryCallback(store->login_db(), true, &ld_return);
452 // Wait for the login DB methods to execute.
453 base::RunLoop().RunUntilIdle();
455 if (GetParam() == WORKING_BACKEND) {
456 // Likewise, no blacklisted logins should be left in the login DB.
457 EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(IsEmpty()));
458 } else {
459 // The blacklisted logins should still be in the login DB.
460 EXPECT_CALL(ld_return,
461 OnLoginDatabaseQueryDone(UnorderedPasswordFormElementsAre(
462 expected_blacklisted.get())));
465 LoginDatabaseQueryCallback(store->login_db(), false, &ld_return);
467 // Wait for the login DB methods to execute.
468 base::RunLoop().RunUntilIdle();
470 if (GetParam() == WORKING_BACKEND) {
471 // If the migration succeeded, then not only should there be no logins left
472 // in the login DB, but also the file should have been deleted and then
473 // recreated. We approximate checking for this by checking that the file
474 // size is equal to the size before we populated it, even though it was
475 // larger after populating it.
476 base::File::Info db_file_end_info;
477 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_end_info));
478 EXPECT_EQ(db_file_start_info.size, db_file_end_info.size);
481 store->Shutdown();
484 INSTANTIATE_TEST_CASE_P(NoBackend,
485 PasswordStoreXTest,
486 testing::Values(NO_BACKEND));
487 INSTANTIATE_TEST_CASE_P(FailingBackend,
488 PasswordStoreXTest,
489 testing::Values(FAILING_BACKEND));
490 INSTANTIATE_TEST_CASE_P(WorkingBackend,
491 PasswordStoreXTest,
492 testing::Values(WORKING_BACKEND));