- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / password_manager / password_store_default_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/prefs/pref_service.h"
9 #include "base/stl_util.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/time/time.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/password_manager/password_form_data.h"
16 #include "chrome/browser/password_manager/password_store_change.h"
17 #include "chrome/browser/password_manager/password_store_consumer.h"
18 #include "chrome/browser/password_manager/password_store_default.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/test/base/testing_profile.h"
21 #include "content/public/browser/notification_details.h"
22 #include "content/public/browser/notification_registrar.h"
23 #include "content/public/browser/notification_source.h"
24 #include "content/public/test/mock_notification_observer.h"
25 #include "content/public/test/test_browser_thread.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 using autofill::PasswordForm;
30 using base::WaitableEvent;
31 using content::BrowserThread;
32 using testing::_;
33 using testing::DoAll;
34 using testing::ElementsAreArray;
35 using testing::Pointee;
36 using testing::Property;
37 using testing::WithArg;
38
39 namespace {
40
41 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
42  public:
43   MOCK_METHOD2(OnPasswordStoreRequestDone,
44                void(CancelableRequestProvider::Handle,
45                     const std::vector<PasswordForm*>&));
46   MOCK_METHOD1(OnGetPasswordStoreResults,
47                void(const std::vector<PasswordForm*>&));
48 };
49
50 // This class will add and remove a mock notification observer from
51 // the DB thread.
52 class DBThreadObserverHelper
53     : public base::RefCountedThreadSafe<DBThreadObserverHelper,
54                                         BrowserThread::DeleteOnDBThread> {
55  public:
56   DBThreadObserverHelper() : done_event_(true, false) {}
57
58   void Init(PasswordStore* password_store) {
59     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
60     BrowserThread::PostTask(
61         BrowserThread::DB,
62         FROM_HERE,
63         base::Bind(&DBThreadObserverHelper::AddObserverTask,
64                    this,
65                    make_scoped_refptr(password_store)));
66     done_event_.Wait();
67   }
68
69   content::MockNotificationObserver& observer() {
70     return observer_;
71   }
72
73  protected:
74   friend struct BrowserThread::DeleteOnThread<BrowserThread::DB>;
75   friend class base::DeleteHelper<DBThreadObserverHelper>;
76
77   virtual ~DBThreadObserverHelper() {
78     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
79     registrar_.RemoveAll();
80   }
81
82   void AddObserverTask(PasswordStore* password_store) {
83     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
84     registrar_.Add(&observer_,
85                    chrome::NOTIFICATION_LOGINS_CHANGED,
86                    content::Source<PasswordStore>(password_store));
87     done_event_.Signal();
88   }
89
90   WaitableEvent done_event_;
91   content::NotificationRegistrar registrar_;
92   content::MockNotificationObserver observer_;
93 };
94
95 }  // anonymous namespace
96
97 class PasswordStoreDefaultTest : public testing::Test {
98  protected:
99   PasswordStoreDefaultTest()
100       : ui_thread_(BrowserThread::UI, &message_loop_),
101         db_thread_(BrowserThread::DB) {
102   }
103
104   virtual void SetUp() {
105     ASSERT_TRUE(db_thread_.Start());
106
107     profile_.reset(new TestingProfile());
108
109     login_db_.reset(new LoginDatabase());
110     ASSERT_TRUE(login_db_->Init(profile_->GetPath().Append(
111         FILE_PATH_LITERAL("login_test"))));
112   }
113
114   virtual void TearDown() {
115     base::MessageLoop::current()->PostTask(FROM_HERE,
116                                            base::MessageLoop::QuitClosure());
117     base::MessageLoop::current()->Run();
118     db_thread_.Stop();
119   }
120
121   base::MessageLoopForUI message_loop_;
122   content::TestBrowserThread ui_thread_;
123   // PasswordStore, WDS schedule work on this thread.
124   content::TestBrowserThread db_thread_;
125
126   scoped_ptr<LoginDatabase> login_db_;
127   scoped_ptr<TestingProfile> profile_;
128 };
129
130 ACTION(STLDeleteElements0) {
131   STLDeleteContainerPointers(arg0.begin(), arg0.end());
132 }
133
134 ACTION(QuitUIMessageLoop) {
135   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
136   base::MessageLoop::current()->Quit();
137 }
138
139 TEST_F(PasswordStoreDefaultTest, NonASCIIData) {
140   scoped_refptr<PasswordStoreDefault> store(
141       new PasswordStoreDefault(login_db_.release(), profile_.get()));
142   store->Init();
143
144   // Some non-ASCII password form data.
145   static const PasswordFormData form_data[] = {
146     { PasswordForm::SCHEME_HTML,
147       "http://foo.example.com",
148       "http://foo.example.com/origin",
149       "http://foo.example.com/action",
150       L"มีสีสัน",
151       L"お元気ですか?",
152       L"盆栽",
153       L"أحب كرة",
154       L"£éä국수çà",
155       true, false, 1 },
156   };
157
158   // Build the expected forms vector and add the forms to the store.
159   std::vector<PasswordForm*> expected_forms;
160   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(form_data); ++i) {
161     PasswordForm* form = CreatePasswordFormFromData(form_data[i]);
162     expected_forms.push_back(form);
163     store->AddLogin(*form);
164   }
165
166   // The PasswordStore schedules tasks to run on the DB thread so we schedule
167   // yet another task to notify us that it's safe to carry on with the test.
168   // The PasswordStore doesn't really understand that it's "done" once the tasks
169   // we posted above have completed, so there's no formal notification for that.
170   WaitableEvent done(false, false);
171   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
172       base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
173   done.Wait();
174
175   MockPasswordStoreConsumer consumer;
176
177   // Make sure we quit the MessageLoop even if the test fails.
178   ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
179       .WillByDefault(QuitUIMessageLoop());
180
181   // We expect to get the same data back, even though it's not all ASCII.
182   EXPECT_CALL(consumer,
183       OnPasswordStoreRequestDone(_,
184           ContainsAllPasswordForms(expected_forms)))
185       .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
186
187   store->GetAutofillableLogins(&consumer);
188   base::MessageLoop::current()->Run();
189
190   STLDeleteElements(&expected_forms);
191 }
192
193 TEST_F(PasswordStoreDefaultTest, Notifications) {
194   scoped_refptr<PasswordStore> store(
195       new PasswordStoreDefault(login_db_.release(), profile_.get()));
196   store->Init();
197
198   PasswordFormData form_data =
199   { PasswordForm::SCHEME_HTML,
200     "http://bar.example.com",
201     "http://bar.example.com/origin",
202     "http://bar.example.com/action",
203     L"submit_element",
204     L"username_element",
205     L"password_element",
206     L"username_value",
207     L"password_value",
208     true, false, 1 };
209   scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
210
211   scoped_refptr<DBThreadObserverHelper> helper = new DBThreadObserverHelper;
212   helper->Init(store.get());
213
214   const PasswordStoreChange expected_add_changes[] = {
215     PasswordStoreChange(PasswordStoreChange::ADD, *form),
216   };
217
218   EXPECT_CALL(
219       helper->observer(),
220       Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
221               content::Source<PasswordStore>(store.get()),
222               Property(&content::Details<const PasswordStoreChangeList>::ptr,
223                        Pointee(ElementsAreArray(expected_add_changes)))));
224
225   // Adding a login should trigger a notification.
226   store->AddLogin(*form);
227
228   // The PasswordStore schedules tasks to run on the DB thread so we schedule
229   // yet another task to notify us that it's safe to carry on with the test.
230   WaitableEvent done(false, false);
231   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
232       base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
233   done.Wait();
234
235   // Change the password.
236   form->password_value = WideToUTF16(L"a different password");
237
238   const PasswordStoreChange expected_update_changes[] = {
239     PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
240   };
241
242   EXPECT_CALL(
243       helper->observer(),
244       Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
245               content::Source<PasswordStore>(store.get()),
246               Property(&content::Details<const PasswordStoreChangeList>::ptr,
247                        Pointee(ElementsAreArray(expected_update_changes)))));
248
249   // Updating the login with the new password should trigger a notification.
250   store->UpdateLogin(*form);
251
252   // Wait for PasswordStore to send the notification.
253   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
254       base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
255   done.Wait();
256
257   const PasswordStoreChange expected_delete_changes[] = {
258     PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
259   };
260
261   EXPECT_CALL(
262       helper->observer(),
263       Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
264               content::Source<PasswordStore>(store.get()),
265               Property(&content::Details<const PasswordStoreChangeList>::ptr,
266                        Pointee(ElementsAreArray(expected_delete_changes)))));
267
268   // Deleting the login should trigger a notification.
269   store->RemoveLogin(*form);
270
271   // Wait for PasswordStore to send the notification.
272   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
273       base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
274   done.Wait();
275
276   store->ShutdownOnUIThread();
277 }