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/files/scoped_temp_dir.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/time/time.h"
15 #include "components/password_manager/core/browser/password_form_data.h"
16 #include "components/password_manager/core/browser/password_store_change.h"
17 #include "components/password_manager/core/browser/password_store_consumer.h"
18 #include "components/password_manager/core/browser/password_store_default.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 using autofill::PasswordForm;
23 using base::WaitableEvent;
24 using testing::_;
25 using testing::DoAll;
26 using testing::ElementsAreArray;
27 using testing::Pointee;
28 using testing::Property;
29 using testing::WithArg;
30
31 namespace password_manager {
32
33 namespace {
34
35 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
36 public:
37 MOCK_METHOD1(OnGetPasswordStoreResults,
38 void(const std::vector<PasswordForm*>&));
39 };
40
41 class MockPasswordStoreObserver : public PasswordStore::Observer {
42 public:
43 MOCK_METHOD1(OnLoginsChanged,
44 void(const PasswordStoreChangeList& changes));
45 };
46
47 } // anonymous namespace
48
49 class PasswordStoreDefaultTest : public testing::Test {
50 protected:
SetUp()51 virtual void SetUp() OVERRIDE {
52 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
53 login_db_.reset(new LoginDatabase());
54 ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append(
55 FILE_PATH_LITERAL("login_test"))));
56 }
57
TearDown()58 virtual void TearDown() OVERRIDE {
59 ASSERT_TRUE(temp_dir_.Delete());
60 }
61
62 base::MessageLoopForUI message_loop_;
63 scoped_ptr<LoginDatabase> login_db_;
64 base::ScopedTempDir temp_dir_;
65 };
66
ACTION(STLDeleteElements0)67 ACTION(STLDeleteElements0) {
68 STLDeleteContainerPointers(arg0.begin(), arg0.end());
69 }
70
TEST_F(PasswordStoreDefaultTest,NonASCIIData)71 TEST_F(PasswordStoreDefaultTest, NonASCIIData) {
72 scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
73 base::MessageLoopProxy::current(),
74 base::MessageLoopProxy::current(),
75 login_db_.release()));
76 store->Init(syncer::SyncableService::StartSyncFlare());
77
78 // Some non-ASCII password form data.
79 static const PasswordFormData form_data[] = {
80 { PasswordForm::SCHEME_HTML,
81 "http://foo.example.com",
82 "http://foo.example.com/origin",
83 "http://foo.example.com/action",
84 L"มีสีสัน",
85 L"お元気ですか?",
86 L"盆栽",
87 L"أحب كرة",
88 L"£éä국수çà",
89 true, false, 1 },
90 };
91
92 // Build the expected forms vector and add the forms to the store.
93 std::vector<PasswordForm*> expected_forms;
94 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(form_data); ++i) {
95 PasswordForm* form = CreatePasswordFormFromData(form_data[i]);
96 expected_forms.push_back(form);
97 store->AddLogin(*form);
98 }
99
100 base::MessageLoop::current()->RunUntilIdle();
101
102 MockPasswordStoreConsumer consumer;
103
104 // We expect to get the same data back, even though it's not all ASCII.
105 EXPECT_CALL(consumer,
106 OnGetPasswordStoreResults(ContainsAllPasswordForms(expected_forms)))
107 .WillOnce(WithArg<0>(STLDeleteElements0()));
108 store->GetAutofillableLogins(&consumer);
109
110 base::MessageLoop::current()->RunUntilIdle();
111
112 STLDeleteElements(&expected_forms);
113 store->Shutdown();
114 base::MessageLoop::current()->RunUntilIdle();
115 }
116
TEST_F(PasswordStoreDefaultTest,Notifications)117 TEST_F(PasswordStoreDefaultTest, Notifications) {
118 scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
119 base::MessageLoopProxy::current(),
120 base::MessageLoopProxy::current(),
121 login_db_.release()));
122 store->Init(syncer::SyncableService::StartSyncFlare());
123
124 PasswordFormData form_data =
125 { PasswordForm::SCHEME_HTML,
126 "http://bar.example.com",
127 "http://bar.example.com/origin",
128 "http://bar.example.com/action",
129 L"submit_element",
130 L"username_element",
131 L"password_element",
132 L"username_value",
133 L"password_value",
134 true, false, 1 };
135 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
136
137 MockPasswordStoreObserver observer;
138 store->AddObserver(&observer);
139
140 const PasswordStoreChange expected_add_changes[] = {
141 PasswordStoreChange(PasswordStoreChange::ADD, *form),
142 };
143
144 EXPECT_CALL(
145 observer,
146 OnLoginsChanged(ElementsAreArray(expected_add_changes)));
147
148 // Adding a login should trigger a notification.
149 store->AddLogin(*form);
150 base::MessageLoop::current()->RunUntilIdle();
151
152 // Change the password.
153 form->password_value = base::ASCIIToUTF16("a different password");
154
155 const PasswordStoreChange expected_update_changes[] = {
156 PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
157 };
158
159 EXPECT_CALL(
160 observer,
161 OnLoginsChanged(ElementsAreArray(expected_update_changes)));
162
163 // Updating the login with the new password should trigger a notification.
164 store->UpdateLogin(*form);
165 base::MessageLoop::current()->RunUntilIdle();
166
167 const PasswordStoreChange expected_delete_changes[] = {
168 PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
169 };
170
171 EXPECT_CALL(
172 observer,
173 OnLoginsChanged(ElementsAreArray(expected_delete_changes)));
174
175 // Deleting the login should trigger a notification.
176 store->RemoveLogin(*form);
177 base::MessageLoop::current()->RunUntilIdle();
178
179 store->RemoveObserver(&observer);
180 store->Shutdown();
181 base::MessageLoop::current()->RunUntilIdle();
182 }
183
184 } // namespace password_manager
185