Update broken references to image assets
[chromium-blink-merge.git] / chrome / browser / password_manager / password_store_x_unittest.cc
blobe09783be1e5f1f9c4f4bd30a9dd4d302628c8717
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,
69 PasswordStoreChangeList* changes) override {
70 return false;
73 bool RemoveLoginsCreatedBetween(
74 base::Time delete_begin,
75 base::Time delete_end,
76 password_manager::PasswordStoreChangeList* changes) override {
77 return false;
80 bool RemoveLoginsSyncedBetween(
81 base::Time delete_begin,
82 base::Time delete_end,
83 password_manager::PasswordStoreChangeList* changes) override {
84 return false;
87 // Use this as a landmine to check whether results of failed Get*Logins calls
88 // get ignored.
89 static ScopedVector<autofill::PasswordForm> CreateTrashForms() {
90 ScopedVector<autofill::PasswordForm> forms;
91 PasswordForm trash;
92 trash.username_element = base::ASCIIToUTF16("trash u. element");
93 trash.username_value = base::ASCIIToUTF16("trash u. value");
94 trash.password_element = base::ASCIIToUTF16("trash p. element");
95 trash.password_value = base::ASCIIToUTF16("trash p. value");
96 for (size_t i = 0; i < 3; ++i) {
97 trash.origin = GURL(base::StringPrintf("http://trash%zu.com", i));
98 forms.push_back(new PasswordForm(trash));
100 return forms.Pass();
103 bool GetLogins(const PasswordForm& form,
104 ScopedVector<autofill::PasswordForm>* forms) override {
105 *forms = CreateTrashForms();
106 return false;
109 bool GetAutofillableLogins(
110 ScopedVector<autofill::PasswordForm>* forms) override {
111 *forms = CreateTrashForms();
112 return false;
114 bool GetBlacklistLogins(
115 ScopedVector<autofill::PasswordForm>* forms) override {
116 *forms = CreateTrashForms();
117 return false;
121 class MockBackend : public PasswordStoreX::NativeBackend {
122 public:
123 bool Init() override { return true; }
125 PasswordStoreChangeList AddLogin(const PasswordForm& form) override {
126 all_forms_.push_back(form);
127 PasswordStoreChange change(PasswordStoreChange::ADD, form);
128 return PasswordStoreChangeList(1, change);
131 bool UpdateLogin(const PasswordForm& form,
132 PasswordStoreChangeList* changes) override {
133 for (size_t i = 0; i < all_forms_.size(); ++i) {
134 if (ArePasswordFormUniqueKeyEqual(all_forms_[i], form)) {
135 all_forms_[i] = form;
136 changes->push_back(PasswordStoreChange(PasswordStoreChange::UPDATE,
137 form));
140 return true;
143 bool RemoveLogin(const PasswordForm& form,
144 PasswordStoreChangeList* changes) override {
145 for (size_t i = 0; i < all_forms_.size(); ++i) {
146 if (ArePasswordFormUniqueKeyEqual(all_forms_[i], form)) {
147 changes->push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
148 form));
149 erase(i--);
152 return true;
155 bool RemoveLoginsCreatedBetween(
156 base::Time delete_begin,
157 base::Time delete_end,
158 password_manager::PasswordStoreChangeList* changes) override {
159 for (size_t i = 0; i < all_forms_.size(); ++i) {
160 if (delete_begin <= all_forms_[i].date_created &&
161 (delete_end.is_null() || all_forms_[i].date_created < delete_end))
162 erase(i--);
164 return true;
167 bool RemoveLoginsSyncedBetween(
168 base::Time delete_begin,
169 base::Time delete_end,
170 password_manager::PasswordStoreChangeList* changes) override {
171 DCHECK(changes);
172 for (size_t i = 0; i < all_forms_.size(); ++i) {
173 if (delete_begin <= all_forms_[i].date_synced &&
174 (delete_end.is_null() || all_forms_[i].date_synced < delete_end)) {
175 changes->push_back(password_manager::PasswordStoreChange(
176 password_manager::PasswordStoreChange::REMOVE, all_forms_[i]));
177 erase(i--);
180 return true;
183 bool GetLogins(const PasswordForm& form,
184 ScopedVector<autofill::PasswordForm>* forms) override {
185 for (size_t i = 0; i < all_forms_.size(); ++i)
186 if (all_forms_[i].signon_realm == form.signon_realm)
187 forms->push_back(new PasswordForm(all_forms_[i]));
188 return true;
191 bool GetAutofillableLogins(
192 ScopedVector<autofill::PasswordForm>* forms) override {
193 for (size_t i = 0; i < all_forms_.size(); ++i)
194 if (!all_forms_[i].blacklisted_by_user)
195 forms->push_back(new PasswordForm(all_forms_[i]));
196 return true;
199 bool GetBlacklistLogins(
200 ScopedVector<autofill::PasswordForm>* forms) override {
201 for (size_t i = 0; i < all_forms_.size(); ++i)
202 if (all_forms_[i].blacklisted_by_user)
203 forms->push_back(new PasswordForm(all_forms_[i]));
204 return true;
207 private:
208 void erase(size_t index) {
209 if (index < all_forms_.size() - 1)
210 all_forms_[index] = all_forms_[all_forms_.size() - 1];
211 all_forms_.pop_back();
214 std::vector<PasswordForm> all_forms_;
217 class MockLoginDatabaseReturn {
218 public:
219 MOCK_METHOD1(OnLoginDatabaseQueryDone,
220 void(const std::vector<PasswordForm*>&));
223 void LoginDatabaseQueryCallback(password_manager::LoginDatabase* login_db,
224 bool autofillable,
225 MockLoginDatabaseReturn* mock_return) {
226 ScopedVector<autofill::PasswordForm> forms;
227 if (autofillable)
228 EXPECT_TRUE(login_db->GetAutofillableLogins(&forms));
229 else
230 EXPECT_TRUE(login_db->GetBlacklistLogins(&forms));
231 mock_return->OnLoginDatabaseQueryDone(forms.get());
234 // Generate |count| expected logins, either auto-fillable or blacklisted.
235 void InitExpectedForms(bool autofillable,
236 size_t count,
237 ScopedVector<autofill::PasswordForm>* forms) {
238 const char* domain = autofillable ? "example" : "blacklisted";
239 for (size_t i = 0; i < count; ++i) {
240 std::string realm = base::StringPrintf("http://%zu.%s.com", i, domain);
241 std::string origin = base::StringPrintf("http://%zu.%s.com/origin",
242 i, domain);
243 std::string action = base::StringPrintf("http://%zu.%s.com/action",
244 i, domain);
245 password_manager::PasswordFormData data = {
246 PasswordForm::SCHEME_HTML,
247 realm.c_str(),
248 origin.c_str(),
249 action.c_str(),
250 L"submit_element",
251 L"username_element",
252 L"password_element",
253 autofillable ? L"username_value" : nullptr,
254 autofillable ? L"password_value" : nullptr,
255 autofillable,
256 false,
257 static_cast<double>(i + 1)};
258 forms->push_back(CreatePasswordFormFromDataForTesting(data).Pass());
262 PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
263 return PasswordStoreChangeList(
264 1, PasswordStoreChange(PasswordStoreChange::ADD, form));
267 } // anonymous namespace
269 enum BackendType {
270 NO_BACKEND,
271 FAILING_BACKEND,
272 WORKING_BACKEND
275 class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
276 protected:
277 void SetUp() override {
278 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
281 void TearDown() override { base::RunLoop().RunUntilIdle(); }
283 base::FilePath test_login_db_file_path() const {
284 return temp_dir_.path().Append(FILE_PATH_LITERAL("login_test"));
287 PasswordStoreX::NativeBackend* GetBackend() {
288 switch (GetParam()) {
289 case FAILING_BACKEND:
290 return new FailingBackend();
291 case WORKING_BACKEND:
292 return new MockBackend();
293 default:
294 return nullptr;
298 content::TestBrowserThreadBundle thread_bundle_;
300 base::ScopedTempDir temp_dir_;
303 ACTION(STLDeleteElements0) {
304 STLDeleteContainerPointers(arg0.begin(), arg0.end());
307 TEST_P(PasswordStoreXTest, Notifications) {
308 scoped_ptr<password_manager::LoginDatabase> login_db(
309 new password_manager::LoginDatabase(test_login_db_file_path()));
310 scoped_refptr<PasswordStoreX> store(new PasswordStoreX(
311 base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
312 login_db.Pass(), GetBackend()));
313 store->Init(syncer::SyncableService::StartSyncFlare());
315 password_manager::PasswordFormData form_data = {
316 PasswordForm::SCHEME_HTML, "http://bar.example.com",
317 "http://bar.example.com/origin", "http://bar.example.com/action",
318 L"submit_element", L"username_element",
319 L"password_element", L"username_value",
320 L"password_value", true,
321 false, 1};
322 scoped_ptr<PasswordForm> form =
323 CreatePasswordFormFromDataForTesting(form_data);
325 MockPasswordStoreObserver observer;
326 store->AddObserver(&observer);
328 const PasswordStoreChange expected_add_changes[] = {
329 PasswordStoreChange(PasswordStoreChange::ADD, *form),
332 EXPECT_CALL(
333 observer,
334 OnLoginsChanged(ElementsAreArray(expected_add_changes)));
336 // Adding a login should trigger a notification.
337 store->AddLogin(*form);
339 // The PasswordStore schedules tasks to run on the DB thread. Wait for them
340 // to complete.
341 base::RunLoop().RunUntilIdle();
343 // Change the password.
344 form->password_value = base::ASCIIToUTF16("a different password");
346 const PasswordStoreChange expected_update_changes[] = {
347 PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
350 EXPECT_CALL(
351 observer,
352 OnLoginsChanged(ElementsAreArray(expected_update_changes)));
354 // Updating the login with the new password should trigger a notification.
355 store->UpdateLogin(*form);
357 // Wait for PasswordStore to send execute.
358 base::RunLoop().RunUntilIdle();
360 const PasswordStoreChange expected_delete_changes[] = {
361 PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
364 EXPECT_CALL(
365 observer,
366 OnLoginsChanged(ElementsAreArray(expected_delete_changes)));
368 // Deleting the login should trigger a notification.
369 store->RemoveLogin(*form);
371 // Wait for PasswordStore to execute.
372 base::RunLoop().RunUntilIdle();
374 store->RemoveObserver(&observer);
376 store->Shutdown();
379 TEST_P(PasswordStoreXTest, NativeMigration) {
380 ScopedVector<autofill::PasswordForm> expected_autofillable;
381 InitExpectedForms(true, 50, &expected_autofillable);
383 ScopedVector<autofill::PasswordForm> expected_blacklisted;
384 InitExpectedForms(false, 50, &expected_blacklisted);
386 const base::FilePath login_db_file = test_login_db_file_path();
387 scoped_ptr<password_manager::LoginDatabase> login_db(
388 new password_manager::LoginDatabase(login_db_file));
389 ASSERT_TRUE(login_db->Init());
391 // Get the initial size of the login DB file, before we populate it.
392 // This will be used later to make sure it gets back to this size.
393 base::File::Info db_file_start_info;
394 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_start_info));
396 // Populate the login DB with logins that should be migrated.
397 for (const autofill::PasswordForm* form : expected_autofillable) {
398 EXPECT_EQ(AddChangeForForm(*form), login_db->AddLogin(*form));
400 for (const autofill::PasswordForm* form : expected_blacklisted) {
401 EXPECT_EQ(AddChangeForForm(*form), login_db->AddLogin(*form));
404 // Get the new size of the login DB file. We expect it to be larger.
405 base::File::Info db_file_full_info;
406 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_full_info));
407 EXPECT_GT(db_file_full_info.size, db_file_start_info.size);
409 // Initializing the PasswordStore shouldn't trigger a native migration (yet).
410 login_db.reset(new password_manager::LoginDatabase(login_db_file));
411 scoped_refptr<PasswordStoreX> store(new PasswordStoreX(
412 base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get(),
413 login_db.Pass(), GetBackend()));
414 store->Init(syncer::SyncableService::StartSyncFlare());
416 MockPasswordStoreConsumer consumer;
418 // The autofillable forms should have been migrated to the native backend.
419 EXPECT_CALL(
420 consumer,
421 OnGetPasswordStoreResultsConstRef(
422 UnorderedPasswordFormElementsAre(expected_autofillable.get())));
424 store->GetAutofillableLogins(&consumer);
425 base::RunLoop().RunUntilIdle();
427 // The blacklisted forms should have been migrated to the native backend.
428 EXPECT_CALL(
429 consumer,
430 OnGetPasswordStoreResultsConstRef(
431 UnorderedPasswordFormElementsAre(expected_blacklisted.get())));
433 store->GetBlacklistLogins(&consumer);
434 base::RunLoop().RunUntilIdle();
436 MockLoginDatabaseReturn ld_return;
438 if (GetParam() == WORKING_BACKEND) {
439 // No autofillable logins should be left in the login DB.
440 EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(IsEmpty()));
441 } else {
442 // The autofillable logins should still be in the login DB.
443 EXPECT_CALL(ld_return,
444 OnLoginDatabaseQueryDone(UnorderedPasswordFormElementsAre(
445 expected_autofillable.get())));
448 LoginDatabaseQueryCallback(store->login_db(), true, &ld_return);
450 // Wait for the login DB methods to execute.
451 base::RunLoop().RunUntilIdle();
453 if (GetParam() == WORKING_BACKEND) {
454 // Likewise, no blacklisted logins should be left in the login DB.
455 EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(IsEmpty()));
456 } else {
457 // The blacklisted logins should still be in the login DB.
458 EXPECT_CALL(ld_return,
459 OnLoginDatabaseQueryDone(UnorderedPasswordFormElementsAre(
460 expected_blacklisted.get())));
463 LoginDatabaseQueryCallback(store->login_db(), false, &ld_return);
465 // Wait for the login DB methods to execute.
466 base::RunLoop().RunUntilIdle();
468 if (GetParam() == WORKING_BACKEND) {
469 // If the migration succeeded, then not only should there be no logins left
470 // in the login DB, but also the file should have been deleted and then
471 // recreated. We approximate checking for this by checking that the file
472 // size is equal to the size before we populated it, even though it was
473 // larger after populating it.
474 base::File::Info db_file_end_info;
475 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_end_info));
476 EXPECT_EQ(db_file_start_info.size, db_file_end_info.size);
479 store->Shutdown();
482 INSTANTIATE_TEST_CASE_P(NoBackend,
483 PasswordStoreXTest,
484 testing::Values(NO_BACKEND));
485 INSTANTIATE_TEST_CASE_P(FailingBackend,
486 PasswordStoreXTest,
487 testing::Values(FAILING_BACKEND));
488 INSTANTIATE_TEST_CASE_P(WorkingBackend,
489 PasswordStoreXTest,
490 testing::Values(WORKING_BACKEND));