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.
10 #include "base/bind.h"
11 #include "base/pickle.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "chrome/browser/password_manager/native_backend_kwallet_x.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "components/autofill/core/common/password_form.h"
19 #include "components/password_manager/core/common/password_manager_pref_names.h"
20 #include "content/public/test/test_browser_thread.h"
21 #include "dbus/message.h"
22 #include "dbus/mock_bus.h"
23 #include "dbus/mock_object_proxy.h"
24 #include "dbus/object_path.h"
25 #include "dbus/object_proxy.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 using autofill::PasswordForm
;
30 using base::UTF8ToUTF16
;
31 using content::BrowserThread
;
32 using password_manager::PasswordStoreChange
;
33 using password_manager::PasswordStoreChangeList
;
35 using testing::Invoke
;
36 using testing::Return
;
40 // This class implements a very simple version of KWallet in memory.
41 // We only provide the parts we actually use; the real version has more.
44 typedef std::basic_string
<uint8_t> Blob
; // std::string is binary-safe.
46 TestKWallet() : reject_local_folders_(false) {}
48 void set_reject_local_folders(bool value
) { reject_local_folders_
= value
; }
50 // NOTE: The method names here are the same as the corresponding DBus
51 // methods, and therefore have names that don't match our style guide.
53 // Check for presence of a given password folder.
54 bool hasFolder(const std::string
& folder
) const {
55 return data_
.find(folder
) != data_
.end();
58 // Check for presence of a given password in a given password folder.
59 bool hasEntry(const std::string
& folder
, const std::string
& key
) const {
60 Data::const_iterator it
= data_
.find(folder
);
61 return it
!= data_
.end() && it
->second
.find(key
) != it
->second
.end();
64 // Get a list of password keys in a given password folder.
65 bool entryList(const std::string
& folder
,
66 std::vector
<std::string
>* entries
) const {
67 Data::const_iterator it
= data_
.find(folder
);
68 if (it
== data_
.end()) return false;
69 for (Folder::const_iterator fit
= it
->second
.begin();
70 fit
!= it
->second
.end(); ++fit
)
71 entries
->push_back(fit
->first
);
75 // Read the password data for a given password in a given password folder.
76 bool readEntry(const std::string
& folder
, const std::string
& key
,
78 Data::const_iterator it
= data_
.find(folder
);
79 if (it
== data_
.end()) return false;
80 Folder::const_iterator fit
= it
->second
.find(key
);
81 if (fit
== it
->second
.end()) return false;
86 // Create the given password folder.
87 bool createFolder(const std::string
& folder
) {
88 if (reject_local_folders_
&& folder
.find('(') != std::string::npos
)
90 return data_
.insert(make_pair(folder
, Folder())).second
;
93 // Remove the given password from the given password folder.
94 bool removeEntry(const std::string
& folder
, const std::string
& key
) {
95 Data::iterator it
= data_
.find(folder
);
96 if (it
== data_
.end()) return false;
97 return it
->second
.erase(key
) > 0;
100 // Write the given password data to the given password folder.
101 bool writeEntry(const std::string
& folder
, const std::string
& key
,
103 Data::iterator it
= data_
.find(folder
);
104 if (it
== data_
.end()) return false;
105 it
->second
[key
] = value
;
110 typedef std::map
<std::string
, Blob
> Folder
;
111 typedef std::map
<std::string
, Folder
> Data
;
114 // "Local" folders are folders containing local profile IDs in their names. We
115 // can reject attempts to create them in order to make it easier to create
116 // legacy shared passwords in these tests, for testing the migration code.
117 bool reject_local_folders_
;
119 // No need to disallow copy and assign. This class is safe to copy and assign.
122 } // anonymous namespace
124 // Obscure magic: we need to declare storage for this constant because we use it
125 // in ways that require its address in this test, but not in the actual code.
126 const int NativeBackendKWallet::kInvalidKWalletHandle
;
128 // Subclass NativeBackendKWallet to promote some members to public for testing.
129 class NativeBackendKWalletStub
: public NativeBackendKWallet
{
131 explicit NativeBackendKWalletStub(LocalProfileId id
)
132 : NativeBackendKWallet(id
) {
134 using NativeBackendKWallet::InitWithBus
;
135 using NativeBackendKWallet::kInvalidKWalletHandle
;
136 using NativeBackendKWallet::DeserializeValue
;
139 // Provide some test forms to avoid having to set them up in each test.
140 class NativeBackendKWalletTestBase
: public testing::Test
{
142 NativeBackendKWalletTestBase() {
143 old_form_google_
.origin
= GURL("http://www.google.com/");
144 old_form_google_
.action
= GURL("http://www.google.com/login");
145 old_form_google_
.username_element
= UTF8ToUTF16("user");
146 old_form_google_
.username_value
= UTF8ToUTF16("joeschmoe");
147 old_form_google_
.password_element
= UTF8ToUTF16("pass");
148 old_form_google_
.password_value
= UTF8ToUTF16("seekrit");
149 old_form_google_
.submit_element
= UTF8ToUTF16("submit");
150 old_form_google_
.signon_realm
= "Google";
152 form_google_
= old_form_google_
;
153 form_google_
.times_used
= 3;
154 form_google_
.type
= PasswordForm::TYPE_GENERATED
;
155 form_google_
.form_data
.name
= UTF8ToUTF16("form_name");
156 form_google_
.form_data
.user_submitted
= true;
157 form_google_
.date_synced
= base::Time::Now();
158 form_google_
.display_name
= UTF8ToUTF16("Joe Schmoe");
159 form_google_
.avatar_url
= GURL("http://www.google.com/avatar");
160 form_google_
.federation_url
= GURL("http://www.google.com/federation_url");
161 form_google_
.is_zero_click
= true;
163 form_isc_
.origin
= GURL("http://www.isc.org/");
164 form_isc_
.action
= GURL("http://www.isc.org/auth");
165 form_isc_
.username_element
= UTF8ToUTF16("id");
166 form_isc_
.username_value
= UTF8ToUTF16("janedoe");
167 form_isc_
.password_element
= UTF8ToUTF16("passwd");
168 form_isc_
.password_value
= UTF8ToUTF16("ihazabukkit");
169 form_isc_
.submit_element
= UTF8ToUTF16("login");
170 form_isc_
.signon_realm
= "ISC";
171 form_isc_
.date_synced
= base::Time::Now();
174 static void CheckPasswordForm(const PasswordForm
& expected
,
175 const PasswordForm
& actual
);
176 static void CheckPasswordChanges(const PasswordStoreChangeList
& expected
,
177 const PasswordStoreChangeList
& actual
);
178 static void CheckPasswordChangesWithResult(
179 const PasswordStoreChangeList
* expected
,
180 const PasswordStoreChangeList
* actual
,
183 PasswordForm old_form_google_
;
184 PasswordForm form_google_
;
185 PasswordForm form_isc_
;
189 void NativeBackendKWalletTestBase::CheckPasswordForm(
190 const PasswordForm
& expected
, const PasswordForm
& actual
) {
191 EXPECT_EQ(expected
.origin
, actual
.origin
);
192 EXPECT_EQ(expected
.password_value
, actual
.password_value
);
193 EXPECT_EQ(expected
.action
, actual
.action
);
194 EXPECT_EQ(expected
.username_element
, actual
.username_element
);
195 EXPECT_EQ(expected
.username_value
, actual
.username_value
);
196 EXPECT_EQ(expected
.password_element
, actual
.password_element
);
197 EXPECT_EQ(expected
.submit_element
, actual
.submit_element
);
198 EXPECT_EQ(expected
.signon_realm
, actual
.signon_realm
);
199 EXPECT_EQ(expected
.ssl_valid
, actual
.ssl_valid
);
200 EXPECT_EQ(expected
.preferred
, actual
.preferred
);
201 // We don't check the date created. It varies.
202 EXPECT_EQ(expected
.blacklisted_by_user
, actual
.blacklisted_by_user
);
203 EXPECT_EQ(expected
.type
, actual
.type
);
204 EXPECT_EQ(expected
.times_used
, actual
.times_used
);
205 EXPECT_EQ(expected
.scheme
, actual
.scheme
);
206 EXPECT_EQ(expected
.date_synced
, actual
.date_synced
);
207 EXPECT_EQ(expected
.display_name
, actual
.display_name
);
208 EXPECT_EQ(expected
.avatar_url
, actual
.avatar_url
);
209 EXPECT_EQ(expected
.federation_url
, actual
.federation_url
);
210 EXPECT_EQ(expected
.is_zero_click
, actual
.is_zero_click
);
214 void NativeBackendKWalletTestBase::CheckPasswordChanges(
215 const PasswordStoreChangeList
& expected
,
216 const PasswordStoreChangeList
& actual
) {
217 ASSERT_EQ(expected
.size(), actual
.size());
218 for (size_t i
= 0; i
< expected
.size(); ++i
) {
219 EXPECT_EQ(expected
[i
].type(), actual
[i
].type());
220 CheckPasswordForm(expected
[i
].form(), actual
[i
].form());
225 void NativeBackendKWalletTestBase::CheckPasswordChangesWithResult(
226 const PasswordStoreChangeList
* expected
,
227 const PasswordStoreChangeList
* actual
,
230 CheckPasswordChanges(*expected
, *actual
);
233 class NativeBackendKWalletTest
: public NativeBackendKWalletTestBase
{
235 NativeBackendKWalletTest()
236 : ui_thread_(BrowserThread::UI
, &message_loop_
),
237 db_thread_(BrowserThread::DB
), klauncher_ret_(0),
238 klauncher_contacted_(false), kwallet_runnable_(true),
239 kwallet_running_(true), kwallet_enabled_(true) {
242 virtual void SetUp();
243 virtual void TearDown();
245 // Let the DB thread run to completion of all current tasks.
247 base::WaitableEvent
event(false, false);
248 BrowserThread::PostTask(BrowserThread::DB
, FROM_HERE
,
249 base::Bind(ThreadDone
, &event
));
251 // Some of the tests may post messages to the UI thread, but we don't need
252 // to run those until after the DB thread is finished. So run it here.
253 message_loop_
.RunUntilIdle();
255 static void ThreadDone(base::WaitableEvent
* event
) {
259 // Utilities to help verify sets of expectations.
261 std::pair
<std::string
,
262 std::vector
<const PasswordForm
*> > > ExpectationArray
;
263 void CheckPasswordForms(const std::string
& folder
,
264 const ExpectationArray
& sorted_expected
);
266 enum RemoveBetweenMethod
{
271 // Tests RemoveLoginsCreatedBetween or RemoveLoginsSyncedBetween.
272 void TestRemoveLoginsBetween(RemoveBetweenMethod date_to_test
);
274 base::MessageLoopForUI message_loop_
;
275 content::TestBrowserThread ui_thread_
;
276 content::TestBrowserThread db_thread_
;
278 scoped_refptr
<dbus::MockBus
> mock_session_bus_
;
279 scoped_refptr
<dbus::MockObjectProxy
> mock_klauncher_proxy_
;
280 scoped_refptr
<dbus::MockObjectProxy
> mock_kwallet_proxy_
;
283 std::string klauncher_error_
;
284 bool klauncher_contacted_
;
286 bool kwallet_runnable_
;
287 bool kwallet_running_
;
288 bool kwallet_enabled_
;
293 dbus::Response
* KLauncherMethodCall(
294 dbus::MethodCall
* method_call
, testing::Unused
);
296 dbus::Response
* KWalletMethodCall(
297 dbus::MethodCall
* method_call
, testing::Unused
);
300 void NativeBackendKWalletTest::SetUp() {
301 ASSERT_TRUE(db_thread_
.Start());
303 dbus::Bus::Options options
;
304 options
.bus_type
= dbus::Bus::SESSION
;
305 mock_session_bus_
= new dbus::MockBus(options
);
307 mock_klauncher_proxy_
=
308 new dbus::MockObjectProxy(mock_session_bus_
.get(),
310 dbus::ObjectPath("/KLauncher"));
311 EXPECT_CALL(*mock_klauncher_proxy_
.get(), MockCallMethodAndBlock(_
, _
))
313 Invoke(this, &NativeBackendKWalletTest::KLauncherMethodCall
));
315 mock_kwallet_proxy_
=
316 new dbus::MockObjectProxy(mock_session_bus_
.get(),
318 dbus::ObjectPath("/modules/kwalletd"));
319 EXPECT_CALL(*mock_kwallet_proxy_
.get(), MockCallMethodAndBlock(_
, _
))
321 Invoke(this, &NativeBackendKWalletTest::KWalletMethodCall
));
324 *mock_session_bus_
.get(),
325 GetObjectProxy("org.kde.klauncher", dbus::ObjectPath("/KLauncher")))
326 .WillRepeatedly(Return(mock_klauncher_proxy_
.get()));
328 *mock_session_bus_
.get(),
329 GetObjectProxy("org.kde.kwalletd", dbus::ObjectPath("/modules/kwalletd")))
330 .WillRepeatedly(Return(mock_kwallet_proxy_
.get()));
332 EXPECT_CALL(*mock_session_bus_
.get(), ShutdownAndBlock()).WillOnce(Return())
333 .WillRepeatedly(Return());
336 void NativeBackendKWalletTest::TearDown() {
337 base::MessageLoop::current()->PostTask(FROM_HERE
,
338 base::MessageLoop::QuitClosure());
339 base::MessageLoop::current()->Run();
343 void NativeBackendKWalletTest::TestRemoveLoginsBetween(
344 RemoveBetweenMethod date_to_test
) {
345 NativeBackendKWalletStub
backend(42);
346 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
348 form_google_
.date_synced
= base::Time();
349 form_isc_
.date_synced
= base::Time();
350 form_google_
.date_created
= base::Time();
351 form_isc_
.date_created
= base::Time();
352 base::Time now
= base::Time::Now();
353 base::Time next_day
= now
+ base::TimeDelta::FromDays(1);
354 if (date_to_test
== CREATED
) {
355 // crbug/374132. Remove the next line once it's fixed.
356 next_day
= base::Time::FromTimeT(next_day
.ToTimeT());
357 form_google_
.date_created
= now
;
358 form_isc_
.date_created
= next_day
;
360 form_google_
.date_synced
= now
;
361 form_isc_
.date_synced
= next_day
;
364 BrowserThread::PostTask(
367 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
368 base::Unretained(&backend
),
370 BrowserThread::PostTask(
373 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
374 base::Unretained(&backend
),
377 PasswordStoreChangeList expected_changes
;
378 expected_changes
.push_back(
379 PasswordStoreChange(PasswordStoreChange::REMOVE
, form_google_
));
380 PasswordStoreChangeList changes
;
381 bool (NativeBackendKWallet::*method
)(
382 base::Time
, base::Time
, password_manager::PasswordStoreChangeList
*) =
383 date_to_test
== CREATED
384 ? &NativeBackendKWalletStub::RemoveLoginsCreatedBetween
385 : &NativeBackendKWalletStub::RemoveLoginsSyncedBetween
;
386 BrowserThread::PostTaskAndReplyWithResult(
390 method
, base::Unretained(&backend
), base::Time(), next_day
, &changes
),
391 base::Bind(&NativeBackendKWalletTest::CheckPasswordChangesWithResult
,
396 std::vector
<const PasswordForm
*> forms
;
397 forms
.push_back(&form_isc_
);
398 ExpectationArray expected
;
399 expected
.push_back(make_pair(std::string(form_isc_
.signon_realm
), forms
));
400 CheckPasswordForms("Chrome Form Data (42)", expected
);
403 expected_changes
.clear();
404 expected_changes
.push_back(
405 PasswordStoreChange(PasswordStoreChange::REMOVE
, form_isc_
));
406 BrowserThread::PostTaskAndReplyWithResult(
410 method
, base::Unretained(&backend
), next_day
, base::Time(), &changes
),
411 base::Bind(&NativeBackendKWalletTest::CheckPasswordChangesWithResult
,
416 CheckPasswordForms("Chrome Form Data (42)", ExpectationArray());
419 dbus::Response
* NativeBackendKWalletTest::KLauncherMethodCall(
420 dbus::MethodCall
* method_call
, testing::Unused
) {
421 EXPECT_EQ("org.kde.KLauncher", method_call
->GetInterface());
422 EXPECT_EQ("start_service_by_desktop_name", method_call
->GetMember());
424 klauncher_contacted_
= true;
426 dbus::MessageReader
reader(method_call
);
427 std::string service_name
;
428 std::vector
<std::string
> urls
;
429 std::vector
<std::string
> envs
;
430 std::string startup_id
;
433 EXPECT_TRUE(reader
.PopString(&service_name
));
434 EXPECT_TRUE(reader
.PopArrayOfStrings(&urls
));
435 EXPECT_TRUE(reader
.PopArrayOfStrings(&envs
));
436 EXPECT_TRUE(reader
.PopString(&startup_id
));
437 EXPECT_TRUE(reader
.PopBool(&blind
));
439 EXPECT_EQ("kwalletd", service_name
);
440 EXPECT_TRUE(urls
.empty());
441 EXPECT_TRUE(envs
.empty());
442 EXPECT_TRUE(startup_id
.empty());
445 if (kwallet_runnable_
)
446 kwallet_running_
= true;
448 scoped_ptr
<dbus::Response
> response(dbus::Response::CreateEmpty());
449 dbus::MessageWriter
writer(response
.get());
450 writer
.AppendInt32(klauncher_ret_
);
451 writer
.AppendString(std::string()); // dbus_name
452 writer
.AppendString(klauncher_error_
);
453 writer
.AppendInt32(1234); // pid
454 return response
.release();
457 dbus::Response
* NativeBackendKWalletTest::KWalletMethodCall(
458 dbus::MethodCall
* method_call
, testing::Unused
) {
459 if (!kwallet_running_
)
461 EXPECT_EQ("org.kde.KWallet", method_call
->GetInterface());
463 scoped_ptr
<dbus::Response
> response
;
464 if (method_call
->GetMember() == "isEnabled") {
465 response
= dbus::Response::CreateEmpty();
466 dbus::MessageWriter
writer(response
.get());
467 writer
.AppendBool(kwallet_enabled_
);
468 } else if (method_call
->GetMember() == "networkWallet") {
469 response
= dbus::Response::CreateEmpty();
470 dbus::MessageWriter
writer(response
.get());
471 writer
.AppendString("test_wallet"); // Should match |open| below.
472 } else if (method_call
->GetMember() == "open") {
473 dbus::MessageReader
reader(method_call
);
474 std::string wallet_name
;
476 std::string app_name
;
477 EXPECT_TRUE(reader
.PopString(&wallet_name
));
478 EXPECT_TRUE(reader
.PopInt64(&wallet_id
));
479 EXPECT_TRUE(reader
.PopString(&app_name
));
480 EXPECT_EQ("test_wallet", wallet_name
); // Should match |networkWallet|.
481 response
= dbus::Response::CreateEmpty();
482 dbus::MessageWriter
writer(response
.get());
483 writer
.AppendInt32(1); // Can be anything but kInvalidKWalletHandle.
484 } else if (method_call
->GetMember() == "hasFolder" ||
485 method_call
->GetMember() == "createFolder") {
486 dbus::MessageReader
reader(method_call
);
487 int handle
= NativeBackendKWalletStub::kInvalidKWalletHandle
;
488 std::string folder_name
;
489 std::string app_name
;
490 EXPECT_TRUE(reader
.PopInt32(&handle
));
491 EXPECT_TRUE(reader
.PopString(&folder_name
));
492 EXPECT_TRUE(reader
.PopString(&app_name
));
493 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle
, handle
);
494 response
= dbus::Response::CreateEmpty();
495 dbus::MessageWriter
writer(response
.get());
496 if (method_call
->GetMember() == "hasFolder")
497 writer
.AppendBool(wallet_
.hasFolder(folder_name
));
499 writer
.AppendBool(wallet_
.createFolder(folder_name
));
500 } else if (method_call
->GetMember() == "hasEntry" ||
501 method_call
->GetMember() == "removeEntry") {
502 dbus::MessageReader
reader(method_call
);
503 int handle
= NativeBackendKWalletStub::kInvalidKWalletHandle
;
504 std::string folder_name
;
506 std::string app_name
;
507 EXPECT_TRUE(reader
.PopInt32(&handle
));
508 EXPECT_TRUE(reader
.PopString(&folder_name
));
509 EXPECT_TRUE(reader
.PopString(&key
));
510 EXPECT_TRUE(reader
.PopString(&app_name
));
511 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle
, handle
);
512 response
= dbus::Response::CreateEmpty();
513 dbus::MessageWriter
writer(response
.get());
514 if (method_call
->GetMember() == "hasEntry")
515 writer
.AppendBool(wallet_
.hasEntry(folder_name
, key
));
517 writer
.AppendInt32(wallet_
.removeEntry(folder_name
, key
) ? 0 : 1);
518 } else if (method_call
->GetMember() == "entryList") {
519 dbus::MessageReader
reader(method_call
);
520 int handle
= NativeBackendKWalletStub::kInvalidKWalletHandle
;
521 std::string folder_name
;
522 std::string app_name
;
523 EXPECT_TRUE(reader
.PopInt32(&handle
));
524 EXPECT_TRUE(reader
.PopString(&folder_name
));
525 EXPECT_TRUE(reader
.PopString(&app_name
));
526 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle
, handle
);
527 std::vector
<std::string
> entries
;
528 if (wallet_
.entryList(folder_name
, &entries
)) {
529 response
= dbus::Response::CreateEmpty();
530 dbus::MessageWriter
writer(response
.get());
531 writer
.AppendArrayOfStrings(entries
);
533 } else if (method_call
->GetMember() == "readEntry") {
534 dbus::MessageReader
reader(method_call
);
535 int handle
= NativeBackendKWalletStub::kInvalidKWalletHandle
;
536 std::string folder_name
;
538 std::string app_name
;
539 EXPECT_TRUE(reader
.PopInt32(&handle
));
540 EXPECT_TRUE(reader
.PopString(&folder_name
));
541 EXPECT_TRUE(reader
.PopString(&key
));
542 EXPECT_TRUE(reader
.PopString(&app_name
));
543 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle
, handle
);
544 TestKWallet::Blob value
;
545 if (wallet_
.readEntry(folder_name
, key
, &value
)) {
546 response
= dbus::Response::CreateEmpty();
547 dbus::MessageWriter
writer(response
.get());
548 writer
.AppendArrayOfBytes(value
.data(), value
.size());
550 } else if (method_call
->GetMember() == "writeEntry") {
551 dbus::MessageReader
reader(method_call
);
552 int handle
= NativeBackendKWalletStub::kInvalidKWalletHandle
;
553 std::string folder_name
;
555 const uint8_t* bytes
= NULL
;
557 std::string app_name
;
558 EXPECT_TRUE(reader
.PopInt32(&handle
));
559 EXPECT_TRUE(reader
.PopString(&folder_name
));
560 EXPECT_TRUE(reader
.PopString(&key
));
561 EXPECT_TRUE(reader
.PopArrayOfBytes(&bytes
, &length
));
562 EXPECT_TRUE(reader
.PopString(&app_name
));
563 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle
, handle
);
564 response
= dbus::Response::CreateEmpty();
565 dbus::MessageWriter
writer(response
.get());
567 wallet_
.writeEntry(folder_name
, key
,
568 TestKWallet::Blob(bytes
, length
)) ? 0 : 1);
571 EXPECT_FALSE(response
.get() == NULL
);
572 return response
.release();
575 void NativeBackendKWalletTest::CheckPasswordForms(
576 const std::string
& folder
, const ExpectationArray
& sorted_expected
) {
577 EXPECT_TRUE(wallet_
.hasFolder(folder
));
578 std::vector
<std::string
> entries
;
579 EXPECT_TRUE(wallet_
.entryList(folder
, &entries
));
580 EXPECT_EQ(sorted_expected
.size(), entries
.size());
581 std::sort(entries
.begin(), entries
.end());
582 for (size_t i
= 0; i
< entries
.size() && i
< sorted_expected
.size(); ++i
) {
583 EXPECT_EQ(sorted_expected
[i
].first
, entries
[i
]);
584 TestKWallet::Blob value
;
585 EXPECT_TRUE(wallet_
.readEntry(folder
, entries
[i
], &value
));
586 Pickle
pickle(reinterpret_cast<const char*>(value
.data()), value
.size());
587 std::vector
<PasswordForm
*> forms
;
588 NativeBackendKWalletStub::DeserializeValue(entries
[i
], pickle
, &forms
);
589 const std::vector
<const PasswordForm
*>& expect
= sorted_expected
[i
].second
;
590 EXPECT_EQ(expect
.size(), forms
.size());
591 for (size_t j
= 0; j
< forms
.size() && j
< expect
.size(); ++j
)
592 CheckPasswordForm(*expect
[j
], *forms
[j
]);
593 STLDeleteElements(&forms
);
597 TEST_F(NativeBackendKWalletTest
, NotEnabled
) {
598 NativeBackendKWalletStub
kwallet(42);
599 kwallet_enabled_
= false;
600 EXPECT_FALSE(kwallet
.InitWithBus(mock_session_bus_
));
601 EXPECT_FALSE(klauncher_contacted_
);
604 TEST_F(NativeBackendKWalletTest
, NotRunnable
) {
605 NativeBackendKWalletStub
kwallet(42);
606 kwallet_runnable_
= false;
607 kwallet_running_
= false;
608 EXPECT_FALSE(kwallet
.InitWithBus(mock_session_bus_
));
609 EXPECT_TRUE(klauncher_contacted_
);
612 TEST_F(NativeBackendKWalletTest
, NotRunningOrEnabled
) {
613 NativeBackendKWalletStub
kwallet(42);
614 kwallet_running_
= false;
615 kwallet_enabled_
= false;
616 EXPECT_FALSE(kwallet
.InitWithBus(mock_session_bus_
));
617 EXPECT_TRUE(klauncher_contacted_
);
620 TEST_F(NativeBackendKWalletTest
, NotRunning
) {
621 NativeBackendKWalletStub
kwallet(42);
622 kwallet_running_
= false;
623 EXPECT_TRUE(kwallet
.InitWithBus(mock_session_bus_
));
624 EXPECT_TRUE(klauncher_contacted_
);
627 TEST_F(NativeBackendKWalletTest
, BasicStartup
) {
628 NativeBackendKWalletStub
kwallet(42);
629 EXPECT_TRUE(kwallet
.InitWithBus(mock_session_bus_
));
630 EXPECT_FALSE(klauncher_contacted_
);
633 TEST_F(NativeBackendKWalletTest
, BasicAddLogin
) {
634 NativeBackendKWalletStub
backend(42);
635 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
637 BrowserThread::PostTask(
638 BrowserThread::DB
, FROM_HERE
,
639 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
640 base::Unretained(&backend
), form_google_
));
644 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
646 std::vector
<const PasswordForm
*> forms
;
647 forms
.push_back(&form_google_
);
648 ExpectationArray expected
;
649 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
650 CheckPasswordForms("Chrome Form Data (42)", expected
);
653 TEST_F(NativeBackendKWalletTest
, BasicUpdateLogin
) {
654 NativeBackendKWalletStub
backend(42);
655 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
657 BrowserThread::PostTask(
658 BrowserThread::DB
, FROM_HERE
,
659 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
660 base::Unretained(&backend
), form_google_
));
664 PasswordForm
new_form_google(form_google_
);
665 new_form_google
.times_used
= 10;
666 new_form_google
.action
= GURL("http://www.google.com/different/login");
669 PasswordStoreChangeList changes
;
670 BrowserThread::PostTask(
671 BrowserThread::DB
, FROM_HERE
,
672 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::UpdateLogin
),
673 base::Unretained(&backend
),
675 base::Unretained(&changes
)));
678 ASSERT_EQ(1u, changes
.size());
679 EXPECT_EQ(PasswordStoreChange::UPDATE
, changes
.front().type());
680 EXPECT_EQ(new_form_google
, changes
.front().form());
682 std::vector
<const PasswordForm
*> forms
;
683 forms
.push_back(&new_form_google
);
684 ExpectationArray expected
;
685 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
686 CheckPasswordForms("Chrome Form Data (42)", expected
);
689 TEST_F(NativeBackendKWalletTest
, BasicListLogins
) {
690 NativeBackendKWalletStub
backend(42);
691 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
693 BrowserThread::PostTask(
694 BrowserThread::DB
, FROM_HERE
,
695 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
696 base::Unretained(&backend
), form_google_
));
698 std::vector
<PasswordForm
*> form_list
;
699 BrowserThread::PostTask(
700 BrowserThread::DB
, FROM_HERE
,
702 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins
),
703 base::Unretained(&backend
), &form_list
));
707 // Quick check that we got something back.
708 EXPECT_EQ(1u, form_list
.size());
709 STLDeleteElements(&form_list
);
711 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
713 std::vector
<const PasswordForm
*> forms
;
714 forms
.push_back(&form_google_
);
715 ExpectationArray expected
;
716 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
717 CheckPasswordForms("Chrome Form Data (42)", expected
);
720 TEST_F(NativeBackendKWalletTest
, BasicRemoveLogin
) {
721 NativeBackendKWalletStub
backend(42);
722 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
724 BrowserThread::PostTask(
725 BrowserThread::DB
, FROM_HERE
,
726 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
727 base::Unretained(&backend
), form_google_
));
731 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
733 std::vector
<const PasswordForm
*> forms
;
734 forms
.push_back(&form_google_
);
735 ExpectationArray expected
;
736 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
737 CheckPasswordForms("Chrome Form Data (42)", expected
);
739 BrowserThread::PostTask(
740 BrowserThread::DB
, FROM_HERE
,
741 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin
),
742 base::Unretained(&backend
), form_google_
));
747 CheckPasswordForms("Chrome Form Data (42)", expected
);
750 TEST_F(NativeBackendKWalletTest
, UpdateNonexistentLogin
) {
751 NativeBackendKWalletStub
backend(42);
752 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
754 // First add an unrelated login.
755 BrowserThread::PostTask(
756 BrowserThread::DB
, FROM_HERE
,
757 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
758 base::Unretained(&backend
), form_google_
));
762 std::vector
<const PasswordForm
*> forms
;
763 forms
.push_back(&form_google_
);
764 ExpectationArray expected
;
765 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
766 CheckPasswordForms("Chrome Form Data (42)", expected
);
768 // Attempt to update a login that doesn't exist.
769 PasswordStoreChangeList changes
;
770 BrowserThread::PostTask(
771 BrowserThread::DB
, FROM_HERE
,
772 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::UpdateLogin
),
773 base::Unretained(&backend
),
775 base::Unretained(&changes
)));
779 EXPECT_EQ(PasswordStoreChangeList(), changes
);
780 CheckPasswordForms("Chrome Form Data (42)", expected
);
783 TEST_F(NativeBackendKWalletTest
, RemoveNonexistentLogin
) {
784 NativeBackendKWalletStub
backend(42);
785 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
787 // First add an unrelated login.
788 BrowserThread::PostTask(
789 BrowserThread::DB
, FROM_HERE
,
790 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
791 base::Unretained(&backend
), form_google_
));
795 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
797 std::vector
<const PasswordForm
*> forms
;
798 forms
.push_back(&form_google_
);
799 ExpectationArray expected
;
800 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
801 CheckPasswordForms("Chrome Form Data (42)", expected
);
803 // Attempt to remove a login that doesn't exist.
804 BrowserThread::PostTask(
805 BrowserThread::DB
, FROM_HERE
,
806 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin
),
807 base::Unretained(&backend
), form_isc_
));
809 // Make sure we can still get the first form back.
810 std::vector
<PasswordForm
*> form_list
;
811 BrowserThread::PostTask(
812 BrowserThread::DB
, FROM_HERE
,
814 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins
),
815 base::Unretained(&backend
), &form_list
));
819 // Quick check that we got something back.
820 EXPECT_EQ(1u, form_list
.size());
821 STLDeleteElements(&form_list
);
823 CheckPasswordForms("Chrome Form Data (42)", expected
);
826 TEST_F(NativeBackendKWalletTest
, AddDuplicateLogin
) {
827 NativeBackendKWalletStub
backend(42);
828 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
830 PasswordStoreChangeList changes
;
831 changes
.push_back(PasswordStoreChange(PasswordStoreChange::ADD
,
833 BrowserThread::PostTaskAndReplyWithResult(
834 BrowserThread::DB
, FROM_HERE
,
835 base::Bind(&NativeBackendKWalletStub::AddLogin
,
836 base::Unretained(&backend
), form_google_
),
837 base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges
,
841 changes
.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE
,
843 form_google_
.times_used
++;
844 changes
.push_back(PasswordStoreChange(PasswordStoreChange::ADD
,
847 BrowserThread::PostTaskAndReplyWithResult(
848 BrowserThread::DB
, FROM_HERE
,
849 base::Bind(&NativeBackendKWalletStub::AddLogin
,
850 base::Unretained(&backend
), form_google_
),
851 base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges
,
856 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
858 std::vector
<const PasswordForm
*> forms
;
859 forms
.push_back(&form_google_
);
860 ExpectationArray expected
;
861 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
862 CheckPasswordForms("Chrome Form Data (42)", expected
);
865 TEST_F(NativeBackendKWalletTest
, ListLoginsAppends
) {
866 NativeBackendKWalletStub
backend(42);
867 EXPECT_TRUE(backend
.InitWithBus(mock_session_bus_
));
869 BrowserThread::PostTask(
870 BrowserThread::DB
, FROM_HERE
,
871 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin
),
872 base::Unretained(&backend
), form_google_
));
874 // Send the same request twice with the same list both times.
875 std::vector
<PasswordForm
*> form_list
;
876 BrowserThread::PostTask(
877 BrowserThread::DB
, FROM_HERE
,
879 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins
),
880 base::Unretained(&backend
), &form_list
));
881 BrowserThread::PostTask(
882 BrowserThread::DB
, FROM_HERE
,
884 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins
),
885 base::Unretained(&backend
), &form_list
));
889 // Quick check that we got two results back.
890 EXPECT_EQ(2u, form_list
.size());
891 STLDeleteElements(&form_list
);
893 EXPECT_FALSE(wallet_
.hasFolder("Chrome Form Data"));
895 std::vector
<const PasswordForm
*> forms
;
896 forms
.push_back(&form_google_
);
897 ExpectationArray expected
;
898 expected
.push_back(make_pair(std::string(form_google_
.signon_realm
), forms
));
899 CheckPasswordForms("Chrome Form Data (42)", expected
);
902 TEST_F(NativeBackendKWalletTest
, RemoveLoginsCreatedBetween
) {
903 TestRemoveLoginsBetween(CREATED
);
906 TEST_F(NativeBackendKWalletTest
, RemoveLoginsSyncedBetween
) {
907 TestRemoveLoginsBetween(SYNCED
);
910 // TODO(mdm): add more basic tests here at some point.
911 // (For example tests for storing >1 password per realm pickle.)
913 class NativeBackendKWalletPickleTest
: public NativeBackendKWalletTestBase
{
915 void CreateVersion3Pickle(const PasswordForm
& form
, Pickle
* pickle
);
916 void CreateVersion2Pickle(const PasswordForm
& form
, Pickle
* pickle
);
917 void CreateVersion1Pickle(const PasswordForm
& form
, Pickle
* pickle
);
918 void CreateVersion0Pickle(bool size_32
,
919 const PasswordForm
& form
,
921 void CheckVersion3Pickle();
922 void CheckVersion2Pickle();
923 void CheckVersion1Pickle();
924 void CheckVersion0Pickle(bool size_32
, PasswordForm::Scheme scheme
);
927 void CreatePickle(bool size_32
, const PasswordForm
& form
, Pickle
* pickle
);
930 void NativeBackendKWalletPickleTest::CreateVersion3Pickle(
931 const PasswordForm
& form
, Pickle
* pickle
) {
933 CreatePickle(false, form
, pickle
);
934 pickle
->WriteInt(form
.type
);
935 pickle
->WriteInt(form
.times_used
);
936 autofill::SerializeFormData(form
.form_data
, pickle
);
937 pickle
->WriteInt64(form
.date_synced
.ToInternalValue());
940 void NativeBackendKWalletPickleTest::CreateVersion2Pickle(
941 const PasswordForm
& form
, Pickle
* pickle
) {
943 CreatePickle(false, form
, pickle
);
944 pickle
->WriteInt(form
.type
);
945 pickle
->WriteInt(form
.times_used
);
946 autofill::SerializeFormData(form
.form_data
, pickle
);
949 void NativeBackendKWalletPickleTest::CreateVersion1Pickle(
950 const PasswordForm
& form
, Pickle
* pickle
) {
952 CreatePickle(false, form
, pickle
);
955 void NativeBackendKWalletPickleTest::CreateVersion0Pickle(
956 bool size_32
, const PasswordForm
& form
, Pickle
* pickle
) {
958 CreatePickle(size_32
, form
, pickle
);
961 void NativeBackendKWalletPickleTest::CreatePickle(
962 bool size_32
, const PasswordForm
& form
, Pickle
* pickle
) {
964 pickle
->WriteUInt32(1); // Size of form list. 32 bits.
966 pickle
->WriteUInt64(1); // Size of form list. 64 bits.
967 pickle
->WriteInt(form
.scheme
);
968 pickle
->WriteString(form
.origin
.spec());
969 pickle
->WriteString(form
.action
.spec());
970 pickle
->WriteString16(form
.username_element
);
971 pickle
->WriteString16(form
.username_value
);
972 pickle
->WriteString16(form
.password_element
);
973 pickle
->WriteString16(form
.password_value
);
974 pickle
->WriteString16(form
.submit_element
);
975 pickle
->WriteBool(form
.ssl_valid
);
976 pickle
->WriteBool(form
.preferred
);
977 pickle
->WriteBool(form
.blacklisted_by_user
);
978 pickle
->WriteInt64(form
.date_created
.ToTimeT());
981 void NativeBackendKWalletPickleTest::CheckVersion3Pickle() {
983 PasswordForm form
= form_google_
;
984 // Remove the fields which were not present in version #3.
985 form
.display_name
.clear();
986 form
.avatar_url
= GURL();
987 form
.federation_url
= GURL();
988 form
.is_zero_click
= false;
989 CreateVersion3Pickle(form
, &pickle
);
991 ScopedVector
<PasswordForm
> form_list
;
992 NativeBackendKWalletStub::DeserializeValue(form
.signon_realm
,
993 pickle
, &form_list
.get());
995 EXPECT_EQ(1u, form_list
.size());
996 if (form_list
.size() > 0)
997 CheckPasswordForm(form
, *form_list
[0]);
1000 void NativeBackendKWalletPickleTest::CheckVersion2Pickle() {
1002 PasswordForm form
= old_form_google_
;
1003 form
.times_used
= form_google_
.times_used
;
1004 form
.type
= form_google_
.type
;
1005 form
.form_data
= form_google_
.form_data
;
1006 CreateVersion2Pickle(form
, &pickle
);
1008 ScopedVector
<PasswordForm
> form_list
;
1009 NativeBackendKWalletStub::DeserializeValue(form
.signon_realm
,
1010 pickle
, &form_list
.get());
1012 EXPECT_EQ(1u, form_list
.size());
1013 if (form_list
.size() > 0)
1014 CheckPasswordForm(form
, *form_list
[0]);
1017 // Make sure that we can still read version 1 pickles.
1018 void NativeBackendKWalletPickleTest::CheckVersion1Pickle() {
1020 PasswordForm form
= form_google_
;
1021 CreateVersion1Pickle(form
, &pickle
);
1023 std::vector
<PasswordForm
*> form_list
;
1024 NativeBackendKWalletStub::DeserializeValue(form
.signon_realm
,
1025 pickle
, &form_list
);
1027 // This will match |old_form_google_| because not all the fields present in
1028 // |form_google_| will be deserialized.
1029 EXPECT_EQ(1u, form_list
.size());
1030 if (form_list
.size() > 0)
1031 CheckPasswordForm(old_form_google_
, *form_list
[0]);
1032 STLDeleteElements(&form_list
);
1035 void NativeBackendKWalletPickleTest::CheckVersion0Pickle(
1036 bool size_32
, PasswordForm::Scheme scheme
) {
1038 PasswordForm form
= old_form_google_
;
1039 form
.scheme
= scheme
;
1040 CreateVersion0Pickle(size_32
, form
, &pickle
);
1041 std::vector
<PasswordForm
*> form_list
;
1042 NativeBackendKWalletStub::DeserializeValue(form
.signon_realm
,
1043 pickle
, &form_list
);
1044 EXPECT_EQ(1u, form_list
.size());
1045 if (form_list
.size() > 0)
1046 CheckPasswordForm(form
, *form_list
[0]);
1047 STLDeleteElements(&form_list
);
1050 // We try both SCHEME_HTML and SCHEME_BASIC since the scheme is stored right
1051 // after the size in the pickle, so it's what gets read as part of the count
1052 // when reading 32-bit pickles on 64-bit systems. SCHEME_HTML is 0 (so we'll
1053 // detect errors later) while SCHEME_BASIC is 1 (so we'll detect it then). We
1054 // try both 32-bit and 64-bit pickles since only one will be the "other" size
1055 // for whatever architecture we're running on, but we want to make sure we can
1056 // read all combinations in any event.
1058 TEST_F(NativeBackendKWalletPickleTest
, ReadsOld32BitHTMLPickles
) {
1059 CheckVersion0Pickle(true, PasswordForm::SCHEME_HTML
);
1062 TEST_F(NativeBackendKWalletPickleTest
, ReadsOld32BitHTTPPickles
) {
1063 CheckVersion0Pickle(true, PasswordForm::SCHEME_BASIC
);
1066 TEST_F(NativeBackendKWalletPickleTest
, ReadsOld64BitHTMLPickles
) {
1067 CheckVersion0Pickle(false, PasswordForm::SCHEME_HTML
);
1070 TEST_F(NativeBackendKWalletPickleTest
, ReadsOld64BitHTTPPickles
) {
1071 CheckVersion0Pickle(false, PasswordForm::SCHEME_BASIC
);
1074 TEST_F(NativeBackendKWalletPickleTest
, CheckVersion1Pickle
) {
1075 CheckVersion1Pickle();
1078 TEST_F(NativeBackendKWalletPickleTest
, CheckVersion2Pickle
) {
1079 CheckVersion2Pickle();
1082 TEST_F(NativeBackendKWalletPickleTest
, CheckVersion3Pickle
) {
1083 CheckVersion3Pickle();