Make castv2 performance test work.
[chromium-blink-merge.git] / chrome / browser / password_manager / password_store_x_unittest.cc
blobd74a8e0a726d5da802b41d667142c07bb80d08e8
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/time/time.h"
17 #include "chrome/browser/password_manager/password_store_x.h"
18 #include "chrome/test/base/testing_browser_process.h"
19 #include "components/password_manager/core/browser/password_manager_test_utils.h"
20 #include "components/password_manager/core/browser/password_store_change.h"
21 #include "components/password_manager/core/browser/password_store_consumer.h"
22 #include "components/password_manager/core/common/password_manager_pref_names.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/test/test_browser_thread_bundle.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
28 using autofill::PasswordForm;
29 using password_manager::PasswordStoreChange;
30 using password_manager::PasswordStoreChangeList;
31 using password_manager::UnorderedPasswordFormElementsAre;
32 using testing::ElementsAreArray;
33 using testing::IsEmpty;
35 namespace {
37 class MockPasswordStoreConsumer
38 : public password_manager::PasswordStoreConsumer {
39 public:
40 MOCK_METHOD1(OnGetPasswordStoreResultsConstRef,
41 void(const std::vector<PasswordForm*>&));
43 // GMock cannot mock methods with move-only args.
44 void OnGetPasswordStoreResults(ScopedVector<PasswordForm> results) override {
45 OnGetPasswordStoreResultsConstRef(results.get());
49 class MockPasswordStoreObserver
50 : public password_manager::PasswordStore::Observer {
51 public:
52 MOCK_METHOD1(OnLoginsChanged,
53 void(const password_manager::PasswordStoreChangeList& changes));
56 class FailingBackend : public PasswordStoreX::NativeBackend {
57 public:
58 bool Init() override { return true; }
60 PasswordStoreChangeList AddLogin(const PasswordForm& form) override {
61 return PasswordStoreChangeList();
63 bool UpdateLogin(const PasswordForm& form,
64 PasswordStoreChangeList* changes) override {
65 return false;
67 bool RemoveLogin(const PasswordForm& form) override { return false; }
69 bool RemoveLoginsCreatedBetween(
70 base::Time delete_begin,
71 base::Time delete_end,
72 password_manager::PasswordStoreChangeList* changes) override {
73 return false;
76 bool RemoveLoginsSyncedBetween(
77 base::Time delete_begin,
78 base::Time delete_end,
79 password_manager::PasswordStoreChangeList* changes) override {
80 return false;
83 // Use this as a landmine to check whether results of failed Get*Logins calls
84 // get ignored.
85 static ScopedVector<autofill::PasswordForm> CreateTrashForms() {
86 ScopedVector<autofill::PasswordForm> forms;
87 PasswordForm trash;
88 trash.username_element = base::ASCIIToUTF16("trash u. element");
89 trash.username_value = base::ASCIIToUTF16("trash u. value");
90 trash.password_element = base::ASCIIToUTF16("trash p. element");
91 trash.password_value = base::ASCIIToUTF16("trash p. value");
92 for (size_t i = 0; i < 3; ++i) {
93 trash.origin = GURL(base::StringPrintf("http://trash%zu.com", i));
94 forms.push_back(new PasswordForm(trash));
96 return forms.Pass();
99 bool GetLogins(const PasswordForm& form,
100 ScopedVector<autofill::PasswordForm>* forms) override {
101 *forms = CreateTrashForms();
102 return false;
105 bool GetAutofillableLogins(
106 ScopedVector<autofill::PasswordForm>* forms) override {
107 *forms = CreateTrashForms();
108 return false;
110 bool GetBlacklistLogins(
111 ScopedVector<autofill::PasswordForm>* forms) override {
112 *forms = CreateTrashForms();
113 return false;
117 class MockBackend : public PasswordStoreX::NativeBackend {
118 public:
119 bool Init() override { return true; }
121 PasswordStoreChangeList AddLogin(const PasswordForm& form) override {
122 all_forms_.push_back(form);
123 PasswordStoreChange change(PasswordStoreChange::ADD, form);
124 return PasswordStoreChangeList(1, change);
127 bool UpdateLogin(const PasswordForm& form,
128 PasswordStoreChangeList* changes) override {
129 for (size_t i = 0; i < all_forms_.size(); ++i)
130 if (CompareForms(all_forms_[i], form, true)) {
131 all_forms_[i] = form;
132 changes->push_back(PasswordStoreChange(PasswordStoreChange::UPDATE,
133 form));
135 return true;
138 bool RemoveLogin(const PasswordForm& form) override {
139 for (size_t i = 0; i < all_forms_.size(); ++i)
140 if (CompareForms(all_forms_[i], form, false))
141 erase(i--);
142 return true;
145 bool RemoveLoginsCreatedBetween(
146 base::Time delete_begin,
147 base::Time delete_end,
148 password_manager::PasswordStoreChangeList* changes) override {
149 for (size_t i = 0; i < all_forms_.size(); ++i) {
150 if (delete_begin <= all_forms_[i].date_created &&
151 (delete_end.is_null() || all_forms_[i].date_created < delete_end))
152 erase(i--);
154 return true;
157 bool RemoveLoginsSyncedBetween(
158 base::Time delete_begin,
159 base::Time delete_end,
160 password_manager::PasswordStoreChangeList* changes) override {
161 DCHECK(changes);
162 for (size_t i = 0; i < all_forms_.size(); ++i) {
163 if (delete_begin <= all_forms_[i].date_synced &&
164 (delete_end.is_null() || all_forms_[i].date_synced < delete_end)) {
165 changes->push_back(password_manager::PasswordStoreChange(
166 password_manager::PasswordStoreChange::REMOVE, all_forms_[i]));
167 erase(i--);
170 return true;
173 bool GetLogins(const PasswordForm& form,
174 ScopedVector<autofill::PasswordForm>* forms) override {
175 for (size_t i = 0; i < all_forms_.size(); ++i)
176 if (all_forms_[i].signon_realm == form.signon_realm)
177 forms->push_back(new PasswordForm(all_forms_[i]));
178 return true;
181 bool GetAutofillableLogins(
182 ScopedVector<autofill::PasswordForm>* forms) override {
183 for (size_t i = 0; i < all_forms_.size(); ++i)
184 if (!all_forms_[i].blacklisted_by_user)
185 forms->push_back(new PasswordForm(all_forms_[i]));
186 return true;
189 bool GetBlacklistLogins(
190 ScopedVector<autofill::PasswordForm>* forms) override {
191 for (size_t i = 0; i < all_forms_.size(); ++i)
192 if (all_forms_[i].blacklisted_by_user)
193 forms->push_back(new PasswordForm(all_forms_[i]));
194 return true;
197 private:
198 void erase(size_t index) {
199 if (index < all_forms_.size() - 1)
200 all_forms_[index] = all_forms_[all_forms_.size() - 1];
201 all_forms_.pop_back();
204 bool CompareForms(const PasswordForm& a, const PasswordForm& b, bool update) {
205 // An update check doesn't care about the submit element.
206 if (!update && a.submit_element != b.submit_element)
207 return false;
208 return a.origin == b.origin &&
209 a.password_element == b.password_element &&
210 a.signon_realm == b.signon_realm &&
211 a.username_element == b.username_element &&
212 a.username_value == b.username_value;
215 std::vector<PasswordForm> all_forms_;
218 class MockLoginDatabaseReturn {
219 public:
220 MOCK_METHOD1(OnLoginDatabaseQueryDone,
221 void(const std::vector<PasswordForm*>&));
224 void LoginDatabaseQueryCallback(password_manager::LoginDatabase* login_db,
225 bool autofillable,
226 MockLoginDatabaseReturn* mock_return) {
227 ScopedVector<autofill::PasswordForm> forms;
228 if (autofillable)
229 EXPECT_TRUE(login_db->GetAutofillableLogins(&forms));
230 else
231 EXPECT_TRUE(login_db->GetBlacklistLogins(&forms));
232 mock_return->OnLoginDatabaseQueryDone(forms.get());
235 // Generate |count| expected logins, either auto-fillable or blacklisted.
236 void InitExpectedForms(bool autofillable,
237 size_t count,
238 ScopedVector<autofill::PasswordForm>* forms) {
239 const char* domain = autofillable ? "example" : "blacklisted";
240 for (size_t i = 0; i < count; ++i) {
241 std::string realm = base::StringPrintf("http://%zu.%s.com", i, domain);
242 std::string origin = base::StringPrintf("http://%zu.%s.com/origin",
243 i, domain);
244 std::string action = base::StringPrintf("http://%zu.%s.com/action",
245 i, domain);
246 password_manager::PasswordFormData data = {
247 PasswordForm::SCHEME_HTML,
248 realm.c_str(),
249 origin.c_str(),
250 action.c_str(),
251 L"submit_element",
252 L"username_element",
253 L"password_element",
254 autofillable ? L"username_value" : nullptr,
255 autofillable ? L"password_value" : nullptr,
256 autofillable,
257 false,
258 static_cast<double>(i + 1)};
259 forms->push_back(CreatePasswordFormFromDataForTesting(data).release());
263 PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
264 return PasswordStoreChangeList(
265 1, PasswordStoreChange(PasswordStoreChange::ADD, form));
268 } // anonymous namespace
270 enum BackendType {
271 NO_BACKEND,
272 FAILING_BACKEND,
273 WORKING_BACKEND
276 class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
277 protected:
278 void SetUp() override {
279 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
282 void TearDown() override { base::RunLoop().RunUntilIdle(); }
284 base::FilePath test_login_db_file_path() const {
285 return temp_dir_.path().Append(FILE_PATH_LITERAL("login_test"));
288 PasswordStoreX::NativeBackend* GetBackend() {
289 switch (GetParam()) {
290 case FAILING_BACKEND:
291 return new FailingBackend();
292 case WORKING_BACKEND:
293 return new MockBackend();
294 default:
295 return nullptr;
299 content::TestBrowserThreadBundle thread_bundle_;
301 base::ScopedTempDir temp_dir_;
304 ACTION(STLDeleteElements0) {
305 STLDeleteContainerPointers(arg0.begin(), arg0.end());
308 TEST_P(PasswordStoreXTest, Notifications) {
309 scoped_ptr<password_manager::LoginDatabase> login_db(
310 new password_manager::LoginDatabase(test_login_db_file_path()));
311 scoped_refptr<PasswordStoreX> store(new PasswordStoreX(
312 base::MessageLoopProxy::current(), base::MessageLoopProxy::current(),
313 login_db.Pass(), GetBackend()));
314 store->Init(syncer::SyncableService::StartSyncFlare());
316 password_manager::PasswordFormData form_data = {
317 PasswordForm::SCHEME_HTML, "http://bar.example.com",
318 "http://bar.example.com/origin", "http://bar.example.com/action",
319 L"submit_element", L"username_element",
320 L"password_element", L"username_value",
321 L"password_value", true,
322 false, 1};
323 scoped_ptr<PasswordForm> form =
324 CreatePasswordFormFromDataForTesting(form_data);
326 MockPasswordStoreObserver observer;
327 store->AddObserver(&observer);
329 const PasswordStoreChange expected_add_changes[] = {
330 PasswordStoreChange(PasswordStoreChange::ADD, *form),
333 EXPECT_CALL(
334 observer,
335 OnLoginsChanged(ElementsAreArray(expected_add_changes)));
337 // Adding a login should trigger a notification.
338 store->AddLogin(*form);
340 // The PasswordStore schedules tasks to run on the DB thread. Wait for them
341 // to complete.
342 base::RunLoop().RunUntilIdle();
344 // Change the password.
345 form->password_value = base::ASCIIToUTF16("a different password");
347 const PasswordStoreChange expected_update_changes[] = {
348 PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
351 EXPECT_CALL(
352 observer,
353 OnLoginsChanged(ElementsAreArray(expected_update_changes)));
355 // Updating the login with the new password should trigger a notification.
356 store->UpdateLogin(*form);
358 // Wait for PasswordStore to send execute.
359 base::RunLoop().RunUntilIdle();
361 const PasswordStoreChange expected_delete_changes[] = {
362 PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
365 EXPECT_CALL(
366 observer,
367 OnLoginsChanged(ElementsAreArray(expected_delete_changes)));
369 // Deleting the login should trigger a notification.
370 store->RemoveLogin(*form);
372 // Wait for PasswordStore to execute.
373 base::RunLoop().RunUntilIdle();
375 store->RemoveObserver(&observer);
377 store->Shutdown();
380 TEST_P(PasswordStoreXTest, NativeMigration) {
381 ScopedVector<autofill::PasswordForm> expected_autofillable;
382 InitExpectedForms(true, 50, &expected_autofillable);
384 ScopedVector<autofill::PasswordForm> expected_blacklisted;
385 InitExpectedForms(false, 50, &expected_blacklisted);
387 const base::FilePath login_db_file = test_login_db_file_path();
388 scoped_ptr<password_manager::LoginDatabase> login_db(
389 new password_manager::LoginDatabase(login_db_file));
390 ASSERT_TRUE(login_db->Init());
392 // Get the initial size of the login DB file, before we populate it.
393 // This will be used later to make sure it gets back to this size.
394 base::File::Info db_file_start_info;
395 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_start_info));
397 // Populate the login DB with logins that should be migrated.
398 for (const autofill::PasswordForm* form : expected_autofillable) {
399 EXPECT_EQ(AddChangeForForm(*form), login_db->AddLogin(*form));
401 for (const autofill::PasswordForm* form : expected_blacklisted) {
402 EXPECT_EQ(AddChangeForForm(*form), login_db->AddLogin(*form));
405 // Get the new size of the login DB file. We expect it to be larger.
406 base::File::Info db_file_full_info;
407 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_full_info));
408 EXPECT_GT(db_file_full_info.size, db_file_start_info.size);
410 // Initializing the PasswordStore shouldn't trigger a native migration (yet).
411 login_db.reset(new password_manager::LoginDatabase(login_db_file));
412 scoped_refptr<PasswordStoreX> store(new PasswordStoreX(
413 base::MessageLoopProxy::current(), base::MessageLoopProxy::current(),
414 login_db.Pass(), GetBackend()));
415 store->Init(syncer::SyncableService::StartSyncFlare());
417 MockPasswordStoreConsumer consumer;
419 // The autofillable forms should have been migrated to the native backend.
420 EXPECT_CALL(
421 consumer,
422 OnGetPasswordStoreResultsConstRef(
423 UnorderedPasswordFormElementsAre(expected_autofillable.get())));
425 store->GetAutofillableLogins(&consumer);
426 base::RunLoop().RunUntilIdle();
428 // The blacklisted forms should have been migrated to the native backend.
429 EXPECT_CALL(
430 consumer,
431 OnGetPasswordStoreResultsConstRef(
432 UnorderedPasswordFormElementsAre(expected_blacklisted.get())));
434 store->GetBlacklistLogins(&consumer);
435 base::RunLoop().RunUntilIdle();
437 MockLoginDatabaseReturn ld_return;
439 if (GetParam() == WORKING_BACKEND) {
440 // No autofillable logins should be left in the login DB.
441 EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(IsEmpty()));
442 } else {
443 // The autofillable logins should still be in the login DB.
444 EXPECT_CALL(ld_return,
445 OnLoginDatabaseQueryDone(UnorderedPasswordFormElementsAre(
446 expected_autofillable.get())));
449 LoginDatabaseQueryCallback(store->login_db(), true, &ld_return);
451 // Wait for the login DB methods to execute.
452 base::RunLoop().RunUntilIdle();
454 if (GetParam() == WORKING_BACKEND) {
455 // Likewise, no blacklisted logins should be left in the login DB.
456 EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(IsEmpty()));
457 } else {
458 // The blacklisted logins should still be in the login DB.
459 EXPECT_CALL(ld_return,
460 OnLoginDatabaseQueryDone(UnorderedPasswordFormElementsAre(
461 expected_blacklisted.get())));
464 LoginDatabaseQueryCallback(store->login_db(), false, &ld_return);
466 // Wait for the login DB methods to execute.
467 base::RunLoop().RunUntilIdle();
469 if (GetParam() == WORKING_BACKEND) {
470 // If the migration succeeded, then not only should there be no logins left
471 // in the login DB, but also the file should have been deleted and then
472 // recreated. We approximate checking for this by checking that the file
473 // size is equal to the size before we populated it, even though it was
474 // larger after populating it.
475 base::File::Info db_file_end_info;
476 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_end_info));
477 EXPECT_EQ(db_file_start_info.size, db_file_end_info.size);
480 store->Shutdown();
483 INSTANTIATE_TEST_CASE_P(NoBackend,
484 PasswordStoreXTest,
485 testing::Values(NO_BACKEND));
486 INSTANTIATE_TEST_CASE_P(FailingBackend,
487 PasswordStoreXTest,
488 testing::Values(FAILING_BACKEND));
489 INSTANTIATE_TEST_CASE_P(WorkingBackend,
490 PasswordStoreXTest,
491 testing::Values(WORKING_BACKEND));