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/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"
29 using autofill::PasswordForm;
30 using base::WaitableEvent;
31 using content::BrowserThread;
34 using testing::ElementsAreArray;
35 using testing::Pointee;
36 using testing::Property;
37 using testing::WithArg;
41 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
43 MOCK_METHOD1(OnGetPasswordStoreResults,
44 void(const std::vector<PasswordForm*>&));
47 // This class will add and remove a mock notification observer from
49 class DBThreadObserverHelper
50 : public base::RefCountedThreadSafe<DBThreadObserverHelper,
51 BrowserThread::DeleteOnDBThread> {
53 DBThreadObserverHelper() : done_event_(true, false) {}
55 void Init(PasswordStore* password_store) {
56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
57 BrowserThread::PostTask(
60 base::Bind(&DBThreadObserverHelper::AddObserverTask,
62 make_scoped_refptr(password_store)));
66 content::MockNotificationObserver& observer() {
71 friend struct BrowserThread::DeleteOnThread<BrowserThread::DB>;
72 friend class base::DeleteHelper<DBThreadObserverHelper>;
74 virtual ~DBThreadObserverHelper() {
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
76 registrar_.RemoveAll();
79 void AddObserverTask(PasswordStore* password_store) {
80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
81 registrar_.Add(&observer_,
82 chrome::NOTIFICATION_LOGINS_CHANGED,
83 content::Source<PasswordStore>(password_store));
87 WaitableEvent done_event_;
88 content::NotificationRegistrar registrar_;
89 content::MockNotificationObserver observer_;
92 } // anonymous namespace
94 class PasswordStoreDefaultTest : public testing::Test {
96 PasswordStoreDefaultTest()
97 : ui_thread_(BrowserThread::UI, &message_loop_),
98 db_thread_(BrowserThread::DB) {
101 virtual void SetUp() {
102 ASSERT_TRUE(db_thread_.Start());
104 profile_.reset(new TestingProfile());
106 login_db_.reset(new LoginDatabase());
107 ASSERT_TRUE(login_db_->Init(profile_->GetPath().Append(
108 FILE_PATH_LITERAL("login_test"))));
111 virtual void TearDown() {
112 base::MessageLoop::current()->PostTask(FROM_HERE,
113 base::MessageLoop::QuitClosure());
114 base::MessageLoop::current()->Run();
118 base::MessageLoopForUI message_loop_;
119 content::TestBrowserThread ui_thread_;
120 // PasswordStore, WDS schedule work on this thread.
121 content::TestBrowserThread db_thread_;
123 scoped_ptr<LoginDatabase> login_db_;
124 scoped_ptr<TestingProfile> profile_;
127 ACTION(STLDeleteElements0) {
128 STLDeleteContainerPointers(arg0.begin(), arg0.end());
131 ACTION(QuitUIMessageLoop) {
132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
133 base::MessageLoop::current()->Quit();
136 TEST_F(PasswordStoreDefaultTest, NonASCIIData) {
137 scoped_refptr<PasswordStoreDefault> store(
138 new PasswordStoreDefault(login_db_.release(), profile_.get()));
141 // Some non-ASCII password form data.
142 static const PasswordFormData form_data[] = {
143 { PasswordForm::SCHEME_HTML,
144 "http://foo.example.com",
145 "http://foo.example.com/origin",
146 "http://foo.example.com/action",
155 // Build the expected forms vector and add the forms to the store.
156 std::vector<PasswordForm*> expected_forms;
157 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(form_data); ++i) {
158 PasswordForm* form = CreatePasswordFormFromData(form_data[i]);
159 expected_forms.push_back(form);
160 store->AddLogin(*form);
163 // The PasswordStore schedules tasks to run on the DB thread so we schedule
164 // yet another task to notify us that it's safe to carry on with the test.
165 // The PasswordStore doesn't really understand that it's "done" once the tasks
166 // we posted above have completed, so there's no formal notification for that.
167 WaitableEvent done(false, false);
168 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
169 base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
172 MockPasswordStoreConsumer consumer;
174 // Make sure we quit the MessageLoop even if the test fails.
175 ON_CALL(consumer, OnGetPasswordStoreResults(_))
176 .WillByDefault(QuitUIMessageLoop());
178 // We expect to get the same data back, even though it's not all ASCII.
179 EXPECT_CALL(consumer,
180 OnGetPasswordStoreResults(ContainsAllPasswordForms(expected_forms)))
181 .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
183 store->GetAutofillableLogins(&consumer);
184 base::MessageLoop::current()->Run();
186 STLDeleteElements(&expected_forms);
189 TEST_F(PasswordStoreDefaultTest, Notifications) {
190 scoped_refptr<PasswordStore> store(
191 new PasswordStoreDefault(login_db_.release(), profile_.get()));
194 PasswordFormData form_data =
195 { PasswordForm::SCHEME_HTML,
196 "http://bar.example.com",
197 "http://bar.example.com/origin",
198 "http://bar.example.com/action",
205 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
207 scoped_refptr<DBThreadObserverHelper> helper = new DBThreadObserverHelper;
208 helper->Init(store.get());
210 const PasswordStoreChange expected_add_changes[] = {
211 PasswordStoreChange(PasswordStoreChange::ADD, *form),
216 Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
217 content::Source<PasswordStore>(store.get()),
218 Property(&content::Details<const PasswordStoreChangeList>::ptr,
219 Pointee(ElementsAreArray(expected_add_changes)))));
221 // Adding a login should trigger a notification.
222 store->AddLogin(*form);
224 // The PasswordStore schedules tasks to run on the DB thread so we schedule
225 // yet another task to notify us that it's safe to carry on with the test.
226 WaitableEvent done(false, false);
227 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
228 base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
231 // Change the password.
232 form->password_value = base::ASCIIToUTF16("a different password");
234 const PasswordStoreChange expected_update_changes[] = {
235 PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
240 Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
241 content::Source<PasswordStore>(store.get()),
242 Property(&content::Details<const PasswordStoreChangeList>::ptr,
243 Pointee(ElementsAreArray(expected_update_changes)))));
245 // Updating the login with the new password should trigger a notification.
246 store->UpdateLogin(*form);
248 // Wait for PasswordStore to send the notification.
249 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
250 base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
253 const PasswordStoreChange expected_delete_changes[] = {
254 PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
259 Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED),
260 content::Source<PasswordStore>(store.get()),
261 Property(&content::Details<const PasswordStoreChangeList>::ptr,
262 Pointee(ElementsAreArray(expected_delete_changes)))));
264 // Deleting the login should trigger a notification.
265 store->RemoveLogin(*form);
267 // Wait for PasswordStore to send the notification.
268 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
269 base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
272 store->ShutdownOnUIThread();