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"
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
;
33 using testing::ElementsAreArray
;
34 using testing::Pointee
;
35 using testing::Property
;
36 using testing::WithArg
;
38 typedef std::vector
<PasswordForm
*> VectorOfForms
;
42 class MockPasswordStoreConsumer
: public PasswordStoreConsumer
{
44 MOCK_METHOD1(OnGetPasswordStoreResults
,
45 void(const std::vector
<PasswordForm
*>&));
48 class MockPasswordStoreObserver
: public PasswordStore::Observer
{
50 MOCK_METHOD1(OnLoginsChanged
,
51 void(const PasswordStoreChangeList
& changes
));
54 class FailingBackend
: public PasswordStoreX::NativeBackend
{
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
{
68 virtual bool GetLogins(const PasswordForm
& form
,
69 PasswordFormList
* forms
) OVERRIDE
{
73 virtual bool GetLoginsCreatedBetween(const base::Time
& get_begin
,
74 const base::Time
& get_end
,
75 PasswordFormList
* forms
) OVERRIDE
{
79 virtual bool GetAutofillableLogins(PasswordFormList
* forms
) OVERRIDE
{
82 virtual bool GetBlacklistLogins(PasswordFormList
* forms
) OVERRIDE
{
87 class MockBackend
: public PasswordStoreX::NativeBackend
{
89 virtual bool Init() OVERRIDE
{ return true; }
91 virtual bool AddLogin(const PasswordForm
& form
) OVERRIDE
{
92 all_forms_
.push_back(form
);
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))
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))
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
))
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
]));
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
]));
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
]));
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
]));
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
)
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
{
176 MOCK_METHOD1(OnLoginDatabaseQueryDone
,
177 void(const std::vector
<PasswordForm
*>&));
180 void LoginDatabaseQueryCallback(LoginDatabase
* login_db
,
182 MockLoginDatabaseReturn
* mock_return
) {
183 std::vector
<PasswordForm
*> forms
;
185 login_db
->GetAutofillableLogins(&forms
);
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",
198 std::string action
= base::StringPrintf("http://%zu.%s.com/action",
200 PasswordFormData data
= {
201 PasswordForm::SCHEME_HTML
,
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
223 class PasswordStoreXTest
: public testing::TestWithParam
<BackendType
> {
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();
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(),
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",
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
),
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
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
),
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
),
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
);
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(),
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();
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
)));
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
)));
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
);
443 INSTANTIATE_TEST_CASE_P(NoBackend
,
445 testing::Values(NO_BACKEND
));
446 INSTANTIATE_TEST_CASE_P(FailingBackend
,
448 testing::Values(FAILING_BACKEND
));
449 INSTANTIATE_TEST_CASE_P(WorkingBackend
,
451 testing::Values(WORKING_BACKEND
));