Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / password_manager / password_store_x_unittest.cc
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.
4
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"
34
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;
43
44 typedef std::vector<PasswordForm*> VectorOfForms;
45
46 namespace {
47
48 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
49  public:
50   MOCK_METHOD1(OnGetPasswordStoreResults,
51                void(const std::vector<PasswordForm*>&));
52 };
53
54 // This class will add and remove a mock notification observer from
55 // the DB thread.
56 class DBThreadObserverHelper {
57  public:
58   DBThreadObserverHelper() {}
59
60   ~DBThreadObserverHelper() {
61     registrar_.RemoveAll();
62   }
63
64   void Init(PasswordStore* password_store) {
65     registrar_.Add(&observer_,
66                    chrome::NOTIFICATION_LOGINS_CHANGED,
67                    content::Source<PasswordStore>(password_store));
68   }
69
70   content::MockNotificationObserver& observer() {
71     return observer_;
72   }
73
74  private:
75   content::NotificationRegistrar registrar_;
76   content::MockNotificationObserver observer_;
77 };
78
79 class FailingBackend : public PasswordStoreX::NativeBackend {
80  public:
81   virtual bool Init() OVERRIDE { return true; }
82
83   virtual bool AddLogin(const PasswordForm& form) OVERRIDE { return false; }
84   virtual bool UpdateLogin(const PasswordForm& form) OVERRIDE { return false; }
85   virtual bool RemoveLogin(const PasswordForm& form) OVERRIDE { return false; }
86
87   virtual bool RemoveLoginsCreatedBetween(
88       const base::Time& delete_begin,
89       const base::Time& delete_end) OVERRIDE {
90     return false;
91   }
92
93   virtual bool GetLogins(const PasswordForm& form,
94                          PasswordFormList* forms) OVERRIDE {
95     return false;
96   }
97
98   virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
99                                        const base::Time& get_end,
100                                        PasswordFormList* forms) OVERRIDE {
101     return false;
102   }
103
104   virtual bool GetAutofillableLogins(PasswordFormList* forms) OVERRIDE {
105     return false;
106   }
107   virtual bool GetBlacklistLogins(PasswordFormList* forms) OVERRIDE {
108     return false;
109   }
110 };
111
112 class MockBackend : public PasswordStoreX::NativeBackend {
113  public:
114   virtual bool Init() OVERRIDE { return true; }
115
116   virtual bool AddLogin(const PasswordForm& form) OVERRIDE {
117     all_forms_.push_back(form);
118     return true;
119   }
120
121   virtual bool UpdateLogin(const PasswordForm& form) OVERRIDE {
122     for (size_t i = 0; i < all_forms_.size(); ++i)
123       if (CompareForms(all_forms_[i], form, true))
124         all_forms_[i] = form;
125     return true;
126   }
127
128   virtual bool RemoveLogin(const PasswordForm& form) OVERRIDE {
129     for (size_t i = 0; i < all_forms_.size(); ++i)
130       if (CompareForms(all_forms_[i], form, false))
131         erase(i--);
132     return true;
133   }
134
135   virtual bool RemoveLoginsCreatedBetween(
136       const base::Time& delete_begin,
137       const base::Time& delete_end) OVERRIDE {
138     for (size_t i = 0; i < all_forms_.size(); ++i) {
139       if (delete_begin <= all_forms_[i].date_created &&
140           (delete_end.is_null() || all_forms_[i].date_created < delete_end))
141         erase(i--);
142     }
143     return true;
144   }
145
146   virtual bool GetLogins(const PasswordForm& form,
147                          PasswordFormList* forms) OVERRIDE {
148     for (size_t i = 0; i < all_forms_.size(); ++i)
149       if (all_forms_[i].signon_realm == form.signon_realm)
150         forms->push_back(new PasswordForm(all_forms_[i]));
151     return true;
152   }
153
154   virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
155                                        const base::Time& get_end,
156                                        PasswordFormList* forms) OVERRIDE {
157     for (size_t i = 0; i < all_forms_.size(); ++i)
158       if (get_begin <= all_forms_[i].date_created &&
159           (get_end.is_null() || all_forms_[i].date_created < get_end))
160         forms->push_back(new PasswordForm(all_forms_[i]));
161     return true;
162   }
163
164   virtual bool GetAutofillableLogins(PasswordFormList* forms) OVERRIDE {
165     for (size_t i = 0; i < all_forms_.size(); ++i)
166       if (!all_forms_[i].blacklisted_by_user)
167         forms->push_back(new PasswordForm(all_forms_[i]));
168     return true;
169   }
170
171   virtual bool GetBlacklistLogins(PasswordFormList* 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;
176   }
177
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();
183   }
184
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;
194   }
195
196   std::vector<PasswordForm> all_forms_;
197 };
198
199 class MockLoginDatabaseReturn {
200  public:
201   MOCK_METHOD1(OnLoginDatabaseQueryDone,
202                void(const std::vector<PasswordForm*>&));
203 };
204
205 void LoginDatabaseQueryCallback(LoginDatabase* login_db,
206                                 bool autofillable,
207                                 MockLoginDatabaseReturn* mock_return) {
208   std::vector<PasswordForm*> forms;
209   if (autofillable)
210     login_db->GetAutofillableLogins(&forms);
211   else
212     login_db->GetBlacklistLogins(&forms);
213   mock_return->OnLoginDatabaseQueryDone(forms);
214 }
215
216 // Generate |count| expected logins, either auto-fillable or blacklisted.
217 void InitExpectedForms(bool autofillable, size_t count, VectorOfForms* forms) {
218   const char* domain = autofillable ? "example" : "blacklisted";
219   for (size_t i = 0; i < count; ++i) {
220     std::string realm = base::StringPrintf("http://%zu.%s.com", i, domain);
221     std::string origin = base::StringPrintf("http://%zu.%s.com/origin",
222                                             i, domain);
223     std::string action = base::StringPrintf("http://%zu.%s.com/action",
224                                             i, domain);
225     PasswordFormData data = {
226       PasswordForm::SCHEME_HTML,
227       realm.c_str(),
228       origin.c_str(),
229       action.c_str(),
230       L"submit_element",
231       L"username_element",
232       L"password_element",
233       autofillable ? L"username_value" : NULL,
234       autofillable ? L"password_value" : NULL,
235       autofillable, false, static_cast<double>(i + 1) };
236     forms->push_back(CreatePasswordFormFromData(data));
237   }
238 }
239
240 }  // anonymous namespace
241
242 enum BackendType {
243   NO_BACKEND,
244   FAILING_BACKEND,
245   WORKING_BACKEND
246 };
247
248 class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
249  protected:
250   virtual void SetUp() {
251     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
252
253     profile_.reset(new TestingProfile());
254
255     login_db_.reset(new LoginDatabase());
256     ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append("login_test")));
257   }
258
259   virtual void TearDown() {
260     base::RunLoop().RunUntilIdle();
261   }
262
263   PasswordStoreX::NativeBackend* GetBackend() {
264     switch (GetParam()) {
265       case FAILING_BACKEND:
266         return new FailingBackend();
267       case WORKING_BACKEND:
268         return new MockBackend();
269       default:
270         return NULL;
271     }
272   }
273
274   content::TestBrowserThreadBundle thread_bundle_;
275
276   scoped_ptr<LoginDatabase> login_db_;
277   scoped_ptr<TestingProfile> profile_;
278   base::ScopedTempDir temp_dir_;
279 };
280
281 ACTION(STLDeleteElements0) {
282   STLDeleteContainerPointers(arg0.begin(), arg0.end());
283 }
284
285 TEST_P(PasswordStoreXTest, Notifications) {
286   scoped_refptr<PasswordStoreX> store(
287       new PasswordStoreX(login_db_.release(),
288                          profile_.get(),
289                          GetBackend()));
290   store->Init();
291
292   PasswordFormData form_data =
293   { PasswordForm::SCHEME_HTML,
294     "http://bar.example.com",
295     "http://bar.example.com/origin",
296     "http://bar.example.com/action",
297     L"submit_element",
298     L"username_element",
299     L"password_element",
300     L"username_value",
301     L"password_value",
302     true, false, 1 };
303   scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
304
305   DBThreadObserverHelper helper;
306   helper.Init(store.get());
307
308   const PasswordStoreChange expected_add_changes[] = {
309     PasswordStoreChange(PasswordStoreChange::ADD, *form),
310   };
311
312   EXPECT_CALL(
313       helper.observer(),
314       Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
315               content::Source<PasswordStore>(store.get()),
316               Property(&content::Details<const PasswordStoreChangeList>::ptr,
317                        Pointee(ElementsAreArray(expected_add_changes)))));
318
319   // Adding a login should trigger a notification.
320   store->AddLogin(*form);
321
322   // The PasswordStore schedules tasks to run on the DB thread. Wait for them
323   // to complete.
324   base::RunLoop().RunUntilIdle();
325
326   // Change the password.
327   form->password_value = base::ASCIIToUTF16("a different password");
328
329   const PasswordStoreChange expected_update_changes[] = {
330     PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
331   };
332
333   EXPECT_CALL(
334       helper.observer(),
335       Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
336               content::Source<PasswordStore>(store.get()),
337               Property(&content::Details<const PasswordStoreChangeList>::ptr,
338                        Pointee(ElementsAreArray(expected_update_changes)))));
339
340   // Updating the login with the new password should trigger a notification.
341   store->UpdateLogin(*form);
342
343   // Wait for PasswordStore to send execute.
344   base::RunLoop().RunUntilIdle();
345
346   const PasswordStoreChange expected_delete_changes[] = {
347     PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
348   };
349
350   EXPECT_CALL(
351       helper.observer(),
352       Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
353               content::Source<PasswordStore>(store.get()),
354               Property(&content::Details<const PasswordStoreChangeList>::ptr,
355                        Pointee(ElementsAreArray(expected_delete_changes)))));
356
357   // Deleting the login should trigger a notification.
358   store->RemoveLogin(*form);
359
360   // Wait for PasswordStore to execute.
361   base::RunLoop().RunUntilIdle();
362
363   // Public in PasswordStore, protected in PasswordStoreX.
364   static_cast<PasswordStore*>(store.get())->ShutdownOnUIThread();
365 }
366
367 TEST_P(PasswordStoreXTest, NativeMigration) {
368   VectorOfForms expected_autofillable;
369   InitExpectedForms(true, 50, &expected_autofillable);
370
371   VectorOfForms expected_blacklisted;
372   InitExpectedForms(false, 50, &expected_blacklisted);
373
374   // Get the initial size of the login DB file, before we populate it.
375   // This will be used later to make sure it gets back to this size.
376   const base::FilePath login_db_file = temp_dir_.path().Append("login_test");
377   base::File::Info db_file_start_info;
378   ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_start_info));
379
380   LoginDatabase* login_db = login_db_.get();
381
382   // Populate the login DB with logins that should be migrated.
383   for (VectorOfForms::iterator it = expected_autofillable.begin();
384        it != expected_autofillable.end(); ++it) {
385     login_db->AddLogin(**it);
386   }
387   for (VectorOfForms::iterator it = expected_blacklisted.begin();
388        it != expected_blacklisted.end(); ++it) {
389     login_db->AddLogin(**it);
390   }
391
392   // Get the new size of the login DB file. We expect it to be larger.
393   base::File::Info db_file_full_info;
394   ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_full_info));
395   EXPECT_GT(db_file_full_info.size, db_file_start_info.size);
396
397   // Initializing the PasswordStore shouldn't trigger a native migration (yet).
398   scoped_refptr<PasswordStoreX> store(
399       new PasswordStoreX(login_db_.release(),
400                          profile_.get(),
401                          GetBackend()));
402   store->Init();
403
404   MockPasswordStoreConsumer consumer;
405
406   // The autofillable forms should have been migrated to the native backend.
407   EXPECT_CALL(consumer,
408       OnGetPasswordStoreResults(
409           ContainsAllPasswordForms(expected_autofillable)))
410       .WillOnce(WithArg<0>(STLDeleteElements0()));
411
412   store->GetAutofillableLogins(&consumer);
413   base::RunLoop().RunUntilIdle();
414
415   // The blacklisted forms should have been migrated to the native backend.
416   EXPECT_CALL(consumer,
417       OnGetPasswordStoreResults(ContainsAllPasswordForms(expected_blacklisted)))
418       .WillOnce(WithArg<0>(STLDeleteElements0()));
419
420   store->GetBlacklistLogins(&consumer);
421   base::RunLoop().RunUntilIdle();
422
423   VectorOfForms empty;
424   MockLoginDatabaseReturn ld_return;
425
426   if (GetParam() == WORKING_BACKEND) {
427     // No autofillable logins should be left in the login DB.
428     EXPECT_CALL(ld_return,
429                 OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
430   } else {
431     // The autofillable logins should still be in the login DB.
432     EXPECT_CALL(ld_return,
433                 OnLoginDatabaseQueryDone(
434                     ContainsAllPasswordForms(expected_autofillable)))
435         .WillOnce(WithArg<0>(STLDeleteElements0()));
436   }
437
438   LoginDatabaseQueryCallback(login_db, true, &ld_return);
439
440   // Wait for the login DB methods to execute.
441   base::RunLoop().RunUntilIdle();
442
443   if (GetParam() == WORKING_BACKEND) {
444     // Likewise, no blacklisted logins should be left in the login DB.
445     EXPECT_CALL(ld_return,
446                 OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
447   } else {
448     // The blacklisted logins should still be in the login DB.
449     EXPECT_CALL(ld_return,
450                 OnLoginDatabaseQueryDone(
451                     ContainsAllPasswordForms(expected_blacklisted)))
452         .WillOnce(WithArg<0>(STLDeleteElements0()));
453   }
454
455   LoginDatabaseQueryCallback(login_db, false, &ld_return);
456
457   // Wait for the login DB methods to execute.
458   base::RunLoop().RunUntilIdle();
459
460   if (GetParam() == WORKING_BACKEND) {
461     // If the migration succeeded, then not only should there be no logins left
462     // in the login DB, but also the file should have been deleted and then
463     // recreated. We approximate checking for this by checking that the file
464     // size is equal to the size before we populated it, even though it was
465     // larger after populating it.
466     base::File::Info db_file_end_info;
467     ASSERT_TRUE(base::GetFileInfo(login_db_file, &db_file_end_info));
468     EXPECT_EQ(db_file_start_info.size, db_file_end_info.size);
469   }
470
471   STLDeleteElements(&expected_autofillable);
472   STLDeleteElements(&expected_blacklisted);
473
474   // Public in PasswordStore, protected in PasswordStoreX.
475   static_cast<PasswordStore*>(store.get())->ShutdownOnUIThread();
476 }
477
478 INSTANTIATE_TEST_CASE_P(NoBackend,
479                         PasswordStoreXTest,
480                         testing::Values(NO_BACKEND));
481 INSTANTIATE_TEST_CASE_P(FailingBackend,
482                         PasswordStoreXTest,
483                         testing::Values(FAILING_BACKEND));
484 INSTANTIATE_TEST_CASE_P(WorkingBackend,
485                         PasswordStoreXTest,
486                         testing::Values(WORKING_BACKEND));