Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / password_manager / password_store_x_unittest.cc
blobed412575371e7b80e5929224bc254517aa1873c3
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/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/platform_file.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/run_loop.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/time/time.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/password_manager/password_form_data.h"
20 #include "chrome/browser/password_manager/password_store_change.h"
21 #include "chrome/browser/password_manager/password_store_consumer.h"
22 #include "chrome/browser/password_manager/password_store_x.h"
23 #include "chrome/common/pref_names.h"
24 #include "chrome/test/base/testing_browser_process.h"
25 #include "chrome/test/base/testing_profile.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/notification_details.h"
28 #include "content/public/browser/notification_registrar.h"
29 #include "content/public/browser/notification_source.h"
30 #include "content/public/test/mock_notification_observer.h"
31 #include "content/public/test/test_browser_thread_bundle.h"
32 #include "testing/gmock/include/gmock/gmock.h"
33 #include "testing/gtest/include/gtest/gtest.h"
35 using autofill::PasswordForm;
36 using content::BrowserThread;
37 using testing::_;
38 using testing::DoAll;
39 using testing::ElementsAreArray;
40 using testing::Pointee;
41 using testing::Property;
42 using testing::WithArg;
44 typedef std::vector<PasswordForm*> VectorOfForms;
46 namespace {
48 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
49 public:
50 MOCK_METHOD2(OnPasswordStoreRequestDone,
51 void(CancelableRequestProvider::Handle,
52 const std::vector<PasswordForm*>&));
53 MOCK_METHOD1(OnGetPasswordStoreResults,
54 void(const std::vector<PasswordForm*>&));
57 // This class will add and remove a mock notification observer from
58 // the DB thread.
59 class DBThreadObserverHelper {
60 public:
61 DBThreadObserverHelper() {}
63 ~DBThreadObserverHelper() {
64 registrar_.RemoveAll();
67 void Init(PasswordStore* password_store) {
68 registrar_.Add(&observer_,
69 chrome::NOTIFICATION_LOGINS_CHANGED,
70 content::Source<PasswordStore>(password_store));
73 content::MockNotificationObserver& observer() {
74 return observer_;
77 private:
78 content::NotificationRegistrar registrar_;
79 content::MockNotificationObserver observer_;
82 class FailingBackend : public PasswordStoreX::NativeBackend {
83 public:
84 virtual bool Init() OVERRIDE { return true; }
86 virtual bool AddLogin(const PasswordForm& form) OVERRIDE { return false; }
87 virtual bool UpdateLogin(const PasswordForm& form) OVERRIDE { return false; }
88 virtual bool RemoveLogin(const PasswordForm& form) OVERRIDE { return false; }
90 virtual bool RemoveLoginsCreatedBetween(
91 const base::Time& delete_begin,
92 const base::Time& delete_end) OVERRIDE {
93 return false;
96 virtual bool GetLogins(const PasswordForm& form,
97 PasswordFormList* forms) OVERRIDE {
98 return false;
101 virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
102 const base::Time& get_end,
103 PasswordFormList* forms) OVERRIDE {
104 return false;
107 virtual bool GetAutofillableLogins(PasswordFormList* forms) OVERRIDE {
108 return false;
110 virtual bool GetBlacklistLogins(PasswordFormList* forms) OVERRIDE {
111 return false;
115 class MockBackend : public PasswordStoreX::NativeBackend {
116 public:
117 virtual bool Init() OVERRIDE { return true; }
119 virtual bool AddLogin(const PasswordForm& form) OVERRIDE {
120 all_forms_.push_back(form);
121 return true;
124 virtual bool UpdateLogin(const PasswordForm& form) OVERRIDE {
125 for (size_t i = 0; i < all_forms_.size(); ++i)
126 if (CompareForms(all_forms_[i], form, true))
127 all_forms_[i] = form;
128 return true;
131 virtual bool RemoveLogin(const PasswordForm& form) OVERRIDE {
132 for (size_t i = 0; i < all_forms_.size(); ++i)
133 if (CompareForms(all_forms_[i], form, false))
134 erase(i--);
135 return true;
138 virtual bool RemoveLoginsCreatedBetween(
139 const base::Time& delete_begin,
140 const base::Time& delete_end) OVERRIDE {
141 for (size_t i = 0; i < all_forms_.size(); ++i) {
142 if (delete_begin <= all_forms_[i].date_created &&
143 (delete_end.is_null() || all_forms_[i].date_created < delete_end))
144 erase(i--);
146 return true;
149 virtual bool GetLogins(const PasswordForm& form,
150 PasswordFormList* forms) OVERRIDE {
151 for (size_t i = 0; i < all_forms_.size(); ++i)
152 if (all_forms_[i].signon_realm == form.signon_realm)
153 forms->push_back(new PasswordForm(all_forms_[i]));
154 return true;
157 virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
158 const base::Time& get_end,
159 PasswordFormList* forms) OVERRIDE {
160 for (size_t i = 0; i < all_forms_.size(); ++i)
161 if (get_begin <= all_forms_[i].date_created &&
162 (get_end.is_null() || all_forms_[i].date_created < get_end))
163 forms->push_back(new PasswordForm(all_forms_[i]));
164 return true;
167 virtual bool GetAutofillableLogins(PasswordFormList* forms) OVERRIDE {
168 for (size_t i = 0; i < all_forms_.size(); ++i)
169 if (!all_forms_[i].blacklisted_by_user)
170 forms->push_back(new PasswordForm(all_forms_[i]));
171 return true;
174 virtual bool GetBlacklistLogins(PasswordFormList* forms) OVERRIDE {
175 for (size_t i = 0; i < all_forms_.size(); ++i)
176 if (all_forms_[i].blacklisted_by_user)
177 forms->push_back(new PasswordForm(all_forms_[i]));
178 return true;
181 private:
182 void erase(size_t index) {
183 if (index < all_forms_.size() - 1)
184 all_forms_[index] = all_forms_[all_forms_.size() - 1];
185 all_forms_.pop_back();
188 bool CompareForms(const PasswordForm& a, const PasswordForm& b, bool update) {
189 // An update check doesn't care about the submit element.
190 if (!update && a.submit_element != b.submit_element)
191 return false;
192 return a.origin == b.origin &&
193 a.password_element == b.password_element &&
194 a.signon_realm == b.signon_realm &&
195 a.username_element == b.username_element &&
196 a.username_value == b.username_value;
199 std::vector<PasswordForm> all_forms_;
202 class MockLoginDatabaseReturn {
203 public:
204 MOCK_METHOD1(OnLoginDatabaseQueryDone,
205 void(const std::vector<PasswordForm*>&));
208 void LoginDatabaseQueryCallback(LoginDatabase* login_db,
209 bool autofillable,
210 MockLoginDatabaseReturn* mock_return) {
211 std::vector<PasswordForm*> forms;
212 if (autofillable)
213 login_db->GetAutofillableLogins(&forms);
214 else
215 login_db->GetBlacklistLogins(&forms);
216 mock_return->OnLoginDatabaseQueryDone(forms);
219 // Generate |count| expected logins, either auto-fillable or blacklisted.
220 void InitExpectedForms(bool autofillable, size_t count, VectorOfForms* forms) {
221 const char* domain = autofillable ? "example" : "blacklisted";
222 for (size_t i = 0; i < count; ++i) {
223 std::string realm = base::StringPrintf("http://%zu.%s.com", i, domain);
224 std::string origin = base::StringPrintf("http://%zu.%s.com/origin",
225 i, domain);
226 std::string action = base::StringPrintf("http://%zu.%s.com/action",
227 i, domain);
228 PasswordFormData data = {
229 PasswordForm::SCHEME_HTML,
230 realm.c_str(),
231 origin.c_str(),
232 action.c_str(),
233 L"submit_element",
234 L"username_element",
235 L"password_element",
236 autofillable ? L"username_value" : NULL,
237 autofillable ? L"password_value" : NULL,
238 autofillable, false, static_cast<double>(i + 1) };
239 forms->push_back(CreatePasswordFormFromData(data));
243 } // anonymous namespace
245 enum BackendType {
246 NO_BACKEND,
247 FAILING_BACKEND,
248 WORKING_BACKEND
251 class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
252 protected:
253 virtual void SetUp() {
254 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
256 profile_.reset(new TestingProfile());
258 login_db_.reset(new LoginDatabase());
259 ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append("login_test")));
262 virtual void TearDown() {
263 base::RunLoop().RunUntilIdle();
266 PasswordStoreX::NativeBackend* GetBackend() {
267 switch (GetParam()) {
268 case FAILING_BACKEND:
269 return new FailingBackend();
270 case WORKING_BACKEND:
271 return new MockBackend();
272 default:
273 return NULL;
277 content::TestBrowserThreadBundle thread_bundle_;
279 scoped_ptr<LoginDatabase> login_db_;
280 scoped_ptr<TestingProfile> profile_;
281 base::ScopedTempDir temp_dir_;
284 ACTION(STLDeleteElements0) {
285 STLDeleteContainerPointers(arg0.begin(), arg0.end());
288 TEST_P(PasswordStoreXTest, Notifications) {
289 scoped_refptr<PasswordStoreX> store(
290 new PasswordStoreX(login_db_.release(),
291 profile_.get(),
292 GetBackend()));
293 store->Init();
295 PasswordFormData form_data =
296 { PasswordForm::SCHEME_HTML,
297 "http://bar.example.com",
298 "http://bar.example.com/origin",
299 "http://bar.example.com/action",
300 L"submit_element",
301 L"username_element",
302 L"password_element",
303 L"username_value",
304 L"password_value",
305 true, false, 1 };
306 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
308 DBThreadObserverHelper helper;
309 helper.Init(store.get());
311 const PasswordStoreChange expected_add_changes[] = {
312 PasswordStoreChange(PasswordStoreChange::ADD, *form),
315 EXPECT_CALL(
316 helper.observer(),
317 Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
318 content::Source<PasswordStore>(store.get()),
319 Property(&content::Details<const PasswordStoreChangeList>::ptr,
320 Pointee(ElementsAreArray(expected_add_changes)))));
322 // Adding a login should trigger a notification.
323 store->AddLogin(*form);
325 // The PasswordStore schedules tasks to run on the DB thread. Wait for them
326 // to complete.
327 base::RunLoop().RunUntilIdle();
329 // Change the password.
330 form->password_value = base::ASCIIToUTF16("a different password");
332 const PasswordStoreChange expected_update_changes[] = {
333 PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
336 EXPECT_CALL(
337 helper.observer(),
338 Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
339 content::Source<PasswordStore>(store.get()),
340 Property(&content::Details<const PasswordStoreChangeList>::ptr,
341 Pointee(ElementsAreArray(expected_update_changes)))));
343 // Updating the login with the new password should trigger a notification.
344 store->UpdateLogin(*form);
346 // Wait for PasswordStore to send execute.
347 base::RunLoop().RunUntilIdle();
349 const PasswordStoreChange expected_delete_changes[] = {
350 PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
353 EXPECT_CALL(
354 helper.observer(),
355 Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
356 content::Source<PasswordStore>(store.get()),
357 Property(&content::Details<const PasswordStoreChangeList>::ptr,
358 Pointee(ElementsAreArray(expected_delete_changes)))));
360 // Deleting the login should trigger a notification.
361 store->RemoveLogin(*form);
363 // Wait for PasswordStore to execute.
364 base::RunLoop().RunUntilIdle();
366 // Public in PasswordStore, protected in PasswordStoreX.
367 static_cast<PasswordStore*>(store.get())->ShutdownOnUIThread();
370 TEST_P(PasswordStoreXTest, NativeMigration) {
371 VectorOfForms expected_autofillable;
372 InitExpectedForms(true, 50, &expected_autofillable);
374 VectorOfForms expected_blacklisted;
375 InitExpectedForms(false, 50, &expected_blacklisted);
377 // Get the initial size of the login DB file, before we populate it.
378 // This will be used later to make sure it gets back to this size.
379 const base::FilePath login_db_file = temp_dir_.path().Append("login_test");
380 base::File::Info db_file_start_info;
381 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_start_info));
383 LoginDatabase* login_db = login_db_.get();
385 // Populate the login DB with logins that should be migrated.
386 for (VectorOfForms::iterator it = expected_autofillable.begin();
387 it != expected_autofillable.end(); ++it) {
388 login_db->AddLogin(**it);
390 for (VectorOfForms::iterator it = expected_blacklisted.begin();
391 it != expected_blacklisted.end(); ++it) {
392 login_db->AddLogin(**it);
395 // Get the new size of the login DB file. We expect it to be larger.
396 base::File::Info db_file_full_info;
397 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_full_info));
398 EXPECT_GT(db_file_full_info.size, db_file_start_info.size);
400 // Initializing the PasswordStore shouldn't trigger a native migration (yet).
401 scoped_refptr<PasswordStoreX> store(
402 new PasswordStoreX(login_db_.release(),
403 profile_.get(),
404 GetBackend()));
405 store->Init();
407 MockPasswordStoreConsumer consumer;
409 // The autofillable forms should have been migrated to the native backend.
410 EXPECT_CALL(consumer,
411 OnPasswordStoreRequestDone(_,
412 ContainsAllPasswordForms(expected_autofillable)))
413 .WillOnce(WithArg<1>(STLDeleteElements0()));
415 store->GetAutofillableLogins(&consumer);
416 base::RunLoop().RunUntilIdle();
418 // The blacklisted forms should have been migrated to the native backend.
419 EXPECT_CALL(consumer,
420 OnPasswordStoreRequestDone(_,
421 ContainsAllPasswordForms(expected_blacklisted)))
422 .WillOnce(WithArg<1>(STLDeleteElements0()));
424 store->GetBlacklistLogins(&consumer);
425 base::RunLoop().RunUntilIdle();
427 VectorOfForms empty;
428 MockLoginDatabaseReturn ld_return;
430 if (GetParam() == WORKING_BACKEND) {
431 // No autofillable logins should be left in the login DB.
432 EXPECT_CALL(ld_return,
433 OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
434 } else {
435 // The autofillable logins should still be in the login DB.
436 EXPECT_CALL(ld_return,
437 OnLoginDatabaseQueryDone(
438 ContainsAllPasswordForms(expected_autofillable)))
439 .WillOnce(WithArg<0>(STLDeleteElements0()));
442 LoginDatabaseQueryCallback(login_db, true, &ld_return);
444 // Wait for the login DB methods to execute.
445 base::RunLoop().RunUntilIdle();
447 if (GetParam() == WORKING_BACKEND) {
448 // Likewise, no blacklisted logins should be left in the login DB.
449 EXPECT_CALL(ld_return,
450 OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
451 } else {
452 // The blacklisted logins should still be in the login DB.
453 EXPECT_CALL(ld_return,
454 OnLoginDatabaseQueryDone(
455 ContainsAllPasswordForms(expected_blacklisted)))
456 .WillOnce(WithArg<0>(STLDeleteElements0()));
459 LoginDatabaseQueryCallback(login_db, false, &ld_return);
461 // Wait for the login DB methods to execute.
462 base::RunLoop().RunUntilIdle();
464 if (GetParam() == WORKING_BACKEND) {
465 // If the migration succeeded, then not only should there be no logins left
466 // in the login DB, but also the file should have been deleted and then
467 // recreated. We approximate checking for this by checking that the file
468 // size is equal to the size before we populated it, even though it was
469 // larger after populating it.
470 base::File::Info db_file_end_info;
471 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_end_info));
472 EXPECT_EQ(db_file_start_info.size, db_file_end_info.size);
475 STLDeleteElements(&expected_autofillable);
476 STLDeleteElements(&expected_blacklisted);
478 // Public in PasswordStore, protected in PasswordStoreX.
479 static_cast<PasswordStore*>(store.get())->ShutdownOnUIThread();
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));