ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / password_manager / password_store_x_unittest.cc
blob737353c320e838aadbd6c815d944910fc7a3474e
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::ContainsSamePasswordForms;
30 using password_manager::PasswordStoreChange;
31 using password_manager::PasswordStoreChangeList;
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 bool GetLogins(const PasswordForm& form,
84 ScopedVector<autofill::PasswordForm>* forms) override {
85 return false;
88 bool GetAutofillableLogins(
89 ScopedVector<autofill::PasswordForm>* forms) override {
90 return false;
92 bool GetBlacklistLogins(
93 ScopedVector<autofill::PasswordForm>* forms) override {
94 return false;
98 class MockBackend : public PasswordStoreX::NativeBackend {
99 public:
100 bool Init() override { return true; }
102 PasswordStoreChangeList AddLogin(const PasswordForm& form) override {
103 all_forms_.push_back(form);
104 PasswordStoreChange change(PasswordStoreChange::ADD, form);
105 return PasswordStoreChangeList(1, change);
108 bool UpdateLogin(const PasswordForm& form,
109 PasswordStoreChangeList* changes) override {
110 for (size_t i = 0; i < all_forms_.size(); ++i)
111 if (CompareForms(all_forms_[i], form, true)) {
112 all_forms_[i] = form;
113 changes->push_back(PasswordStoreChange(PasswordStoreChange::UPDATE,
114 form));
116 return true;
119 bool RemoveLogin(const PasswordForm& form) override {
120 for (size_t i = 0; i < all_forms_.size(); ++i)
121 if (CompareForms(all_forms_[i], form, false))
122 erase(i--);
123 return true;
126 bool RemoveLoginsCreatedBetween(
127 base::Time delete_begin,
128 base::Time delete_end,
129 password_manager::PasswordStoreChangeList* changes) override {
130 for (size_t i = 0; i < all_forms_.size(); ++i) {
131 if (delete_begin <= all_forms_[i].date_created &&
132 (delete_end.is_null() || all_forms_[i].date_created < delete_end))
133 erase(i--);
135 return true;
138 bool RemoveLoginsSyncedBetween(
139 base::Time delete_begin,
140 base::Time delete_end,
141 password_manager::PasswordStoreChangeList* changes) override {
142 DCHECK(changes);
143 for (size_t i = 0; i < all_forms_.size(); ++i) {
144 if (delete_begin <= all_forms_[i].date_synced &&
145 (delete_end.is_null() || all_forms_[i].date_synced < delete_end)) {
146 changes->push_back(password_manager::PasswordStoreChange(
147 password_manager::PasswordStoreChange::REMOVE, all_forms_[i]));
148 erase(i--);
151 return true;
154 bool GetLogins(const PasswordForm& form,
155 ScopedVector<autofill::PasswordForm>* forms) override {
156 for (size_t i = 0; i < all_forms_.size(); ++i)
157 if (all_forms_[i].signon_realm == form.signon_realm)
158 forms->push_back(new PasswordForm(all_forms_[i]));
159 return true;
162 bool GetAutofillableLogins(
163 ScopedVector<autofill::PasswordForm>* forms) override {
164 for (size_t i = 0; i < all_forms_.size(); ++i)
165 if (!all_forms_[i].blacklisted_by_user)
166 forms->push_back(new PasswordForm(all_forms_[i]));
167 return true;
170 bool GetBlacklistLogins(
171 ScopedVector<autofill::PasswordForm>* forms) override {
172 for (size_t i = 0; i < all_forms_.size(); ++i)
173 if (all_forms_[i].blacklisted_by_user)
174 forms->push_back(new PasswordForm(all_forms_[i]));
175 return true;
178 private:
179 void erase(size_t index) {
180 if (index < all_forms_.size() - 1)
181 all_forms_[index] = all_forms_[all_forms_.size() - 1];
182 all_forms_.pop_back();
185 bool CompareForms(const PasswordForm& a, const PasswordForm& b, bool update) {
186 // An update check doesn't care about the submit element.
187 if (!update && a.submit_element != b.submit_element)
188 return false;
189 return a.origin == b.origin &&
190 a.password_element == b.password_element &&
191 a.signon_realm == b.signon_realm &&
192 a.username_element == b.username_element &&
193 a.username_value == b.username_value;
196 std::vector<PasswordForm> all_forms_;
199 class MockLoginDatabaseReturn {
200 public:
201 MOCK_METHOD1(OnLoginDatabaseQueryDone,
202 void(const std::vector<PasswordForm*>&));
205 void LoginDatabaseQueryCallback(password_manager::LoginDatabase* login_db,
206 bool autofillable,
207 MockLoginDatabaseReturn* mock_return) {
208 ScopedVector<autofill::PasswordForm> forms;
209 if (autofillable)
210 login_db->GetAutofillableLogins(&forms);
211 else
212 login_db->GetBlacklistLogins(&forms);
213 mock_return->OnLoginDatabaseQueryDone(forms.get());
216 // Generate |count| expected logins, either auto-fillable or blacklisted.
217 void InitExpectedForms(bool autofillable,
218 size_t count,
219 ScopedVector<autofill::PasswordForm>* forms) {
220 const char* domain = autofillable ? "example" : "blacklisted";
221 for (size_t i = 0; i < count; ++i) {
222 std::string realm = base::StringPrintf("http://%zu.%s.com", i, domain);
223 std::string origin = base::StringPrintf("http://%zu.%s.com/origin",
224 i, domain);
225 std::string action = base::StringPrintf("http://%zu.%s.com/action",
226 i, domain);
227 password_manager::PasswordFormData data = {
228 PasswordForm::SCHEME_HTML,
229 realm.c_str(),
230 origin.c_str(),
231 action.c_str(),
232 L"submit_element",
233 L"username_element",
234 L"password_element",
235 autofillable ? L"username_value" : nullptr,
236 autofillable ? L"password_value" : nullptr,
237 autofillable,
238 false,
239 static_cast<double>(i + 1)};
240 forms->push_back(CreatePasswordFormFromDataForTesting(data).release());
244 PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) {
245 return PasswordStoreChangeList(
246 1, PasswordStoreChange(PasswordStoreChange::ADD, form));
249 } // anonymous namespace
251 enum BackendType {
252 NO_BACKEND,
253 FAILING_BACKEND,
254 WORKING_BACKEND
257 class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
258 protected:
259 void SetUp() override {
260 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
263 void TearDown() override { base::RunLoop().RunUntilIdle(); }
265 base::FilePath test_login_db_file_path() const {
266 return temp_dir_.path().Append(FILE_PATH_LITERAL("login_test"));
269 PasswordStoreX::NativeBackend* GetBackend() {
270 switch (GetParam()) {
271 case FAILING_BACKEND:
272 return new FailingBackend();
273 case WORKING_BACKEND:
274 return new MockBackend();
275 default:
276 return nullptr;
280 content::TestBrowserThreadBundle thread_bundle_;
282 base::ScopedTempDir temp_dir_;
285 ACTION(STLDeleteElements0) {
286 STLDeleteContainerPointers(arg0.begin(), arg0.end());
289 TEST_P(PasswordStoreXTest, Notifications) {
290 scoped_ptr<password_manager::LoginDatabase> login_db(
291 new password_manager::LoginDatabase(test_login_db_file_path()));
292 scoped_refptr<PasswordStoreX> store(new PasswordStoreX(
293 base::MessageLoopProxy::current(), base::MessageLoopProxy::current(),
294 login_db.Pass(), GetBackend()));
295 store->Init(syncer::SyncableService::StartSyncFlare());
297 password_manager::PasswordFormData form_data = {
298 PasswordForm::SCHEME_HTML, "http://bar.example.com",
299 "http://bar.example.com/origin", "http://bar.example.com/action",
300 L"submit_element", L"username_element",
301 L"password_element", L"username_value",
302 L"password_value", true,
303 false, 1};
304 scoped_ptr<PasswordForm> form =
305 CreatePasswordFormFromDataForTesting(form_data);
307 MockPasswordStoreObserver observer;
308 store->AddObserver(&observer);
310 const PasswordStoreChange expected_add_changes[] = {
311 PasswordStoreChange(PasswordStoreChange::ADD, *form),
314 EXPECT_CALL(
315 observer,
316 OnLoginsChanged(ElementsAreArray(expected_add_changes)));
318 // Adding a login should trigger a notification.
319 store->AddLogin(*form);
321 // The PasswordStore schedules tasks to run on the DB thread. Wait for them
322 // to complete.
323 base::RunLoop().RunUntilIdle();
325 // Change the password.
326 form->password_value = base::ASCIIToUTF16("a different password");
328 const PasswordStoreChange expected_update_changes[] = {
329 PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
332 EXPECT_CALL(
333 observer,
334 OnLoginsChanged(ElementsAreArray(expected_update_changes)));
336 // Updating the login with the new password should trigger a notification.
337 store->UpdateLogin(*form);
339 // Wait for PasswordStore to send execute.
340 base::RunLoop().RunUntilIdle();
342 const PasswordStoreChange expected_delete_changes[] = {
343 PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
346 EXPECT_CALL(
347 observer,
348 OnLoginsChanged(ElementsAreArray(expected_delete_changes)));
350 // Deleting the login should trigger a notification.
351 store->RemoveLogin(*form);
353 // Wait for PasswordStore to execute.
354 base::RunLoop().RunUntilIdle();
356 store->RemoveObserver(&observer);
358 store->Shutdown();
361 TEST_P(PasswordStoreXTest, NativeMigration) {
362 ScopedVector<autofill::PasswordForm> expected_autofillable;
363 InitExpectedForms(true, 50, &expected_autofillable);
365 ScopedVector<autofill::PasswordForm> expected_blacklisted;
366 InitExpectedForms(false, 50, &expected_blacklisted);
368 const base::FilePath login_db_file = test_login_db_file_path();
369 scoped_ptr<password_manager::LoginDatabase> login_db(
370 new password_manager::LoginDatabase(login_db_file));
371 ASSERT_TRUE(login_db->Init());
373 // Get the initial size of the login DB file, before we populate it.
374 // This will be used later to make sure it gets back to this size.
375 base::File::Info db_file_start_info;
376 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_start_info));
378 // Populate the login DB with logins that should be migrated.
379 for (const autofill::PasswordForm* form : expected_autofillable) {
380 EXPECT_EQ(AddChangeForForm(*form), login_db->AddLogin(*form));
382 for (const autofill::PasswordForm* form : expected_blacklisted) {
383 EXPECT_EQ(AddChangeForForm(*form), login_db->AddLogin(*form));
386 // Get the new size of the login DB file. We expect it to be larger.
387 base::File::Info db_file_full_info;
388 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_full_info));
389 EXPECT_GT(db_file_full_info.size, db_file_start_info.size);
391 // Initializing the PasswordStore shouldn't trigger a native migration (yet).
392 login_db.reset(new password_manager::LoginDatabase(login_db_file));
393 scoped_refptr<PasswordStoreX> store(new PasswordStoreX(
394 base::MessageLoopProxy::current(), base::MessageLoopProxy::current(),
395 login_db.Pass(), GetBackend()));
396 store->Init(syncer::SyncableService::StartSyncFlare());
398 MockPasswordStoreConsumer consumer;
400 // The autofillable forms should have been migrated to the native backend.
401 EXPECT_CALL(consumer,
402 OnGetPasswordStoreResultsConstRef(
403 ContainsSamePasswordForms(expected_autofillable.get())));
405 store->GetAutofillableLogins(&consumer);
406 base::RunLoop().RunUntilIdle();
408 // The blacklisted forms should have been migrated to the native backend.
409 EXPECT_CALL(consumer,
410 OnGetPasswordStoreResultsConstRef(
411 ContainsSamePasswordForms(expected_blacklisted.get())));
413 store->GetBlacklistLogins(&consumer);
414 base::RunLoop().RunUntilIdle();
416 MockLoginDatabaseReturn ld_return;
418 if (GetParam() == WORKING_BACKEND) {
419 // No autofillable logins should be left in the login DB.
420 EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(IsEmpty()));
421 } else {
422 // The autofillable logins should still be in the login DB.
423 EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(ContainsSamePasswordForms(
424 expected_autofillable.get())));
427 LoginDatabaseQueryCallback(store->login_db(), true, &ld_return);
429 // Wait for the login DB methods to execute.
430 base::RunLoop().RunUntilIdle();
432 if (GetParam() == WORKING_BACKEND) {
433 // Likewise, no blacklisted logins should be left in the login DB.
434 EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(IsEmpty()));
435 } else {
436 // The blacklisted logins should still be in the login DB.
437 EXPECT_CALL(ld_return, OnLoginDatabaseQueryDone(ContainsSamePasswordForms(
438 expected_blacklisted.get())));
441 LoginDatabaseQueryCallback(store->login_db(), false, &ld_return);
443 // Wait for the login DB methods to execute.
444 base::RunLoop().RunUntilIdle();
446 if (GetParam() == WORKING_BACKEND) {
447 // If the migration succeeded, then not only should there be no logins left
448 // in the login DB, but also the file should have been deleted and then
449 // recreated. We approximate checking for this by checking that the file
450 // size is equal to the size before we populated it, even though it was
451 // larger after populating it.
452 base::File::Info db_file_end_info;
453 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_end_info));
454 EXPECT_EQ(db_file_start_info.size, db_file_end_info.size);
457 store->Shutdown();
460 INSTANTIATE_TEST_CASE_P(NoBackend,
461 PasswordStoreXTest,
462 testing::Values(NO_BACKEND));
463 INSTANTIATE_TEST_CASE_P(FailingBackend,
464 PasswordStoreXTest,
465 testing::Values(FAILING_BACKEND));
466 INSTANTIATE_TEST_CASE_P(WorkingBackend,
467 PasswordStoreXTest,
468 testing::Values(WORKING_BACKEND));