NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / password_manager / password_store_x_unittest.cc
blob0a063604b03fe2944c66380d2b4c40e0bf55c4b5
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/password_manager/password_store_x.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/test/base/testing_browser_process.h"
21 #include "components/password_manager/core/browser/password_form_data.h"
22 #include "components/password_manager/core/browser/password_store_change.h"
23 #include "components/password_manager/core/browser/password_store_consumer.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 content::BrowserThread;
31 using testing::_;
32 using testing::DoAll;
33 using testing::ElementsAreArray;
34 using testing::Pointee;
35 using testing::Property;
36 using testing::WithArg;
38 typedef std::vector<PasswordForm*> VectorOfForms;
40 namespace {
42 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
43 public:
44 MOCK_METHOD1(OnGetPasswordStoreResults,
45 void(const std::vector<PasswordForm*>&));
48 class MockPasswordStoreObserver : public PasswordStore::Observer {
49 public:
50 MOCK_METHOD1(OnLoginsChanged,
51 void(const PasswordStoreChangeList& changes));
54 class FailingBackend : public PasswordStoreX::NativeBackend {
55 public:
56 virtual bool Init() OVERRIDE { return true; }
58 virtual bool AddLogin(const PasswordForm& form) OVERRIDE { return false; }
59 virtual bool UpdateLogin(const PasswordForm& form) OVERRIDE { return false; }
60 virtual bool RemoveLogin(const PasswordForm& form) OVERRIDE { return false; }
62 virtual bool RemoveLoginsCreatedBetween(
63 const base::Time& delete_begin,
64 const base::Time& delete_end) OVERRIDE {
65 return false;
68 virtual bool GetLogins(const PasswordForm& form,
69 PasswordFormList* forms) OVERRIDE {
70 return false;
73 virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
74 const base::Time& get_end,
75 PasswordFormList* forms) OVERRIDE {
76 return false;
79 virtual bool GetAutofillableLogins(PasswordFormList* forms) OVERRIDE {
80 return false;
82 virtual bool GetBlacklistLogins(PasswordFormList* forms) OVERRIDE {
83 return false;
87 class MockBackend : public PasswordStoreX::NativeBackend {
88 public:
89 virtual bool Init() OVERRIDE { return true; }
91 virtual bool AddLogin(const PasswordForm& form) OVERRIDE {
92 all_forms_.push_back(form);
93 return true;
96 virtual bool UpdateLogin(const PasswordForm& form) OVERRIDE {
97 for (size_t i = 0; i < all_forms_.size(); ++i)
98 if (CompareForms(all_forms_[i], form, true))
99 all_forms_[i] = form;
100 return true;
103 virtual bool RemoveLogin(const PasswordForm& form) OVERRIDE {
104 for (size_t i = 0; i < all_forms_.size(); ++i)
105 if (CompareForms(all_forms_[i], form, false))
106 erase(i--);
107 return true;
110 virtual bool RemoveLoginsCreatedBetween(
111 const base::Time& delete_begin,
112 const base::Time& delete_end) OVERRIDE {
113 for (size_t i = 0; i < all_forms_.size(); ++i) {
114 if (delete_begin <= all_forms_[i].date_created &&
115 (delete_end.is_null() || all_forms_[i].date_created < delete_end))
116 erase(i--);
118 return true;
121 virtual bool GetLogins(const PasswordForm& form,
122 PasswordFormList* forms) OVERRIDE {
123 for (size_t i = 0; i < all_forms_.size(); ++i)
124 if (all_forms_[i].signon_realm == form.signon_realm)
125 forms->push_back(new PasswordForm(all_forms_[i]));
126 return true;
129 virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
130 const base::Time& get_end,
131 PasswordFormList* forms) OVERRIDE {
132 for (size_t i = 0; i < all_forms_.size(); ++i)
133 if (get_begin <= all_forms_[i].date_created &&
134 (get_end.is_null() || all_forms_[i].date_created < get_end))
135 forms->push_back(new PasswordForm(all_forms_[i]));
136 return true;
139 virtual bool GetAutofillableLogins(PasswordFormList* forms) OVERRIDE {
140 for (size_t i = 0; i < all_forms_.size(); ++i)
141 if (!all_forms_[i].blacklisted_by_user)
142 forms->push_back(new PasswordForm(all_forms_[i]));
143 return true;
146 virtual bool GetBlacklistLogins(PasswordFormList* forms) OVERRIDE {
147 for (size_t i = 0; i < all_forms_.size(); ++i)
148 if (all_forms_[i].blacklisted_by_user)
149 forms->push_back(new PasswordForm(all_forms_[i]));
150 return true;
153 private:
154 void erase(size_t index) {
155 if (index < all_forms_.size() - 1)
156 all_forms_[index] = all_forms_[all_forms_.size() - 1];
157 all_forms_.pop_back();
160 bool CompareForms(const PasswordForm& a, const PasswordForm& b, bool update) {
161 // An update check doesn't care about the submit element.
162 if (!update && a.submit_element != b.submit_element)
163 return false;
164 return a.origin == b.origin &&
165 a.password_element == b.password_element &&
166 a.signon_realm == b.signon_realm &&
167 a.username_element == b.username_element &&
168 a.username_value == b.username_value;
171 std::vector<PasswordForm> all_forms_;
174 class MockLoginDatabaseReturn {
175 public:
176 MOCK_METHOD1(OnLoginDatabaseQueryDone,
177 void(const std::vector<PasswordForm*>&));
180 void LoginDatabaseQueryCallback(LoginDatabase* login_db,
181 bool autofillable,
182 MockLoginDatabaseReturn* mock_return) {
183 std::vector<PasswordForm*> forms;
184 if (autofillable)
185 login_db->GetAutofillableLogins(&forms);
186 else
187 login_db->GetBlacklistLogins(&forms);
188 mock_return->OnLoginDatabaseQueryDone(forms);
191 // Generate |count| expected logins, either auto-fillable or blacklisted.
192 void InitExpectedForms(bool autofillable, size_t count, VectorOfForms* forms) {
193 const char* domain = autofillable ? "example" : "blacklisted";
194 for (size_t i = 0; i < count; ++i) {
195 std::string realm = base::StringPrintf("http://%zu.%s.com", i, domain);
196 std::string origin = base::StringPrintf("http://%zu.%s.com/origin",
197 i, domain);
198 std::string action = base::StringPrintf("http://%zu.%s.com/action",
199 i, domain);
200 PasswordFormData data = {
201 PasswordForm::SCHEME_HTML,
202 realm.c_str(),
203 origin.c_str(),
204 action.c_str(),
205 L"submit_element",
206 L"username_element",
207 L"password_element",
208 autofillable ? L"username_value" : NULL,
209 autofillable ? L"password_value" : NULL,
210 autofillable, false, static_cast<double>(i + 1) };
211 forms->push_back(CreatePasswordFormFromData(data));
215 } // anonymous namespace
217 enum BackendType {
218 NO_BACKEND,
219 FAILING_BACKEND,
220 WORKING_BACKEND
223 class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
224 protected:
225 virtual void SetUp() {
226 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
228 login_db_.reset(new LoginDatabase());
229 ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append("login_test")));
232 virtual void TearDown() {
233 base::RunLoop().RunUntilIdle();
236 PasswordStoreX::NativeBackend* GetBackend() {
237 switch (GetParam()) {
238 case FAILING_BACKEND:
239 return new FailingBackend();
240 case WORKING_BACKEND:
241 return new MockBackend();
242 default:
243 return NULL;
247 content::TestBrowserThreadBundle thread_bundle_;
249 scoped_ptr<LoginDatabase> login_db_;
250 base::ScopedTempDir temp_dir_;
253 ACTION(STLDeleteElements0) {
254 STLDeleteContainerPointers(arg0.begin(), arg0.end());
257 TEST_P(PasswordStoreXTest, Notifications) {
258 scoped_refptr<PasswordStoreX> store(
259 new PasswordStoreX(base::MessageLoopProxy::current(),
260 base::MessageLoopProxy::current(),
261 login_db_.release(),
262 GetBackend()));
263 store->Init();
265 PasswordFormData form_data =
266 { PasswordForm::SCHEME_HTML,
267 "http://bar.example.com",
268 "http://bar.example.com/origin",
269 "http://bar.example.com/action",
270 L"submit_element",
271 L"username_element",
272 L"password_element",
273 L"username_value",
274 L"password_value",
275 true, false, 1 };
276 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
278 MockPasswordStoreObserver observer;
279 store->AddObserver(&observer);
281 const PasswordStoreChange expected_add_changes[] = {
282 PasswordStoreChange(PasswordStoreChange::ADD, *form),
285 EXPECT_CALL(
286 observer,
287 OnLoginsChanged(ElementsAreArray(expected_add_changes)));
289 // Adding a login should trigger a notification.
290 store->AddLogin(*form);
292 // The PasswordStore schedules tasks to run on the DB thread. Wait for them
293 // to complete.
294 base::RunLoop().RunUntilIdle();
296 // Change the password.
297 form->password_value = base::ASCIIToUTF16("a different password");
299 const PasswordStoreChange expected_update_changes[] = {
300 PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
303 EXPECT_CALL(
304 observer,
305 OnLoginsChanged(ElementsAreArray(expected_update_changes)));
307 // Updating the login with the new password should trigger a notification.
308 store->UpdateLogin(*form);
310 // Wait for PasswordStore to send execute.
311 base::RunLoop().RunUntilIdle();
313 const PasswordStoreChange expected_delete_changes[] = {
314 PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
317 EXPECT_CALL(
318 observer,
319 OnLoginsChanged(ElementsAreArray(expected_delete_changes)));
321 // Deleting the login should trigger a notification.
322 store->RemoveLogin(*form);
324 // Wait for PasswordStore to execute.
325 base::RunLoop().RunUntilIdle();
327 store->RemoveObserver(&observer);
329 store->Shutdown();
332 TEST_P(PasswordStoreXTest, NativeMigration) {
333 VectorOfForms expected_autofillable;
334 InitExpectedForms(true, 50, &expected_autofillable);
336 VectorOfForms expected_blacklisted;
337 InitExpectedForms(false, 50, &expected_blacklisted);
339 // Get the initial size of the login DB file, before we populate it.
340 // This will be used later to make sure it gets back to this size.
341 const base::FilePath login_db_file = temp_dir_.path().Append("login_test");
342 base::File::Info db_file_start_info;
343 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_start_info));
345 LoginDatabase* login_db = login_db_.get();
347 // Populate the login DB with logins that should be migrated.
348 for (VectorOfForms::iterator it = expected_autofillable.begin();
349 it != expected_autofillable.end(); ++it) {
350 login_db->AddLogin(**it);
352 for (VectorOfForms::iterator it = expected_blacklisted.begin();
353 it != expected_blacklisted.end(); ++it) {
354 login_db->AddLogin(**it);
357 // Get the new size of the login DB file. We expect it to be larger.
358 base::File::Info db_file_full_info;
359 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_full_info));
360 EXPECT_GT(db_file_full_info.size, db_file_start_info.size);
362 // Initializing the PasswordStore shouldn't trigger a native migration (yet).
363 scoped_refptr<PasswordStoreX> store(
364 new PasswordStoreX(base::MessageLoopProxy::current(),
365 base::MessageLoopProxy::current(),
366 login_db_.release(),
367 GetBackend()));
368 store->Init();
370 MockPasswordStoreConsumer consumer;
372 // The autofillable forms should have been migrated to the native backend.
373 EXPECT_CALL(consumer,
374 OnGetPasswordStoreResults(
375 ContainsAllPasswordForms(expected_autofillable)))
376 .WillOnce(WithArg<0>(STLDeleteElements0()));
378 store->GetAutofillableLogins(&consumer);
379 base::RunLoop().RunUntilIdle();
381 // The blacklisted forms should have been migrated to the native backend.
382 EXPECT_CALL(consumer,
383 OnGetPasswordStoreResults(ContainsAllPasswordForms(expected_blacklisted)))
384 .WillOnce(WithArg<0>(STLDeleteElements0()));
386 store->GetBlacklistLogins(&consumer);
387 base::RunLoop().RunUntilIdle();
389 VectorOfForms empty;
390 MockLoginDatabaseReturn ld_return;
392 if (GetParam() == WORKING_BACKEND) {
393 // No autofillable logins should be left in the login DB.
394 EXPECT_CALL(ld_return,
395 OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
396 } else {
397 // The autofillable logins should still be in the login DB.
398 EXPECT_CALL(ld_return,
399 OnLoginDatabaseQueryDone(
400 ContainsAllPasswordForms(expected_autofillable)))
401 .WillOnce(WithArg<0>(STLDeleteElements0()));
404 LoginDatabaseQueryCallback(login_db, true, &ld_return);
406 // Wait for the login DB methods to execute.
407 base::RunLoop().RunUntilIdle();
409 if (GetParam() == WORKING_BACKEND) {
410 // Likewise, no blacklisted logins should be left in the login DB.
411 EXPECT_CALL(ld_return,
412 OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
413 } else {
414 // The blacklisted logins should still be in the login DB.
415 EXPECT_CALL(ld_return,
416 OnLoginDatabaseQueryDone(
417 ContainsAllPasswordForms(expected_blacklisted)))
418 .WillOnce(WithArg<0>(STLDeleteElements0()));
421 LoginDatabaseQueryCallback(login_db, false, &ld_return);
423 // Wait for the login DB methods to execute.
424 base::RunLoop().RunUntilIdle();
426 if (GetParam() == WORKING_BACKEND) {
427 // If the migration succeeded, then not only should there be no logins left
428 // in the login DB, but also the file should have been deleted and then
429 // recreated. We approximate checking for this by checking that the file
430 // size is equal to the size before we populated it, even though it was
431 // larger after populating it.
432 base::File::Info db_file_end_info;
433 ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_end_info));
434 EXPECT_EQ(db_file_start_info.size, db_file_end_info.size);
437 STLDeleteElements(&expected_autofillable);
438 STLDeleteElements(&expected_blacklisted);
440 store->Shutdown();
443 INSTANTIATE_TEST_CASE_P(NoBackend,
444 PasswordStoreXTest,
445 testing::Values(NO_BACKEND));
446 INSTANTIATE_TEST_CASE_P(FailingBackend,
447 PasswordStoreXTest,
448 testing::Values(FAILING_BACKEND));
449 INSTANTIATE_TEST_CASE_P(WorkingBackend,
450 PasswordStoreXTest,
451 testing::Values(WORKING_BACKEND));