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();
159 form_isc_.origin = GURL("http://www.isc.org/");
160 form_isc_.action = GURL("http://www.isc.org/auth");
161 form_isc_.username_element = UTF8ToUTF16("id");
162 form_isc_.username_value = UTF8ToUTF16("janedoe");
163 form_isc_.password_element = UTF8ToUTF16("passwd");
164 form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
165 form_isc_.submit_element = UTF8ToUTF16("login");
166 form_isc_.signon_realm = "ISC";
167 form_isc_.date_synced = base::Time::Now();
170 static void CheckPasswordForm(const PasswordForm& expected,
171 const PasswordForm& actual);
172 static void CheckPasswordChanges(const PasswordStoreChangeList& expected,
173 const PasswordStoreChangeList& actual);
174 static void CheckPasswordChangesWithResult(
175 const PasswordStoreChangeList* expected,
176 const PasswordStoreChangeList* actual,
179 PasswordForm old_form_google_;
180 PasswordForm form_google_;
181 PasswordForm form_isc_;
185 void NativeBackendKWalletTestBase::CheckPasswordForm(
186 const PasswordForm& expected, const PasswordForm& actual) {
187 EXPECT_EQ(expected.origin, actual.origin);
188 EXPECT_EQ(expected.password_value, actual.password_value);
189 EXPECT_EQ(expected.action, actual.action);
190 EXPECT_EQ(expected.username_element, actual.username_element);
191 EXPECT_EQ(expected.username_value, actual.username_value);
192 EXPECT_EQ(expected.password_element, actual.password_element);
193 EXPECT_EQ(expected.submit_element, actual.submit_element);
194 EXPECT_EQ(expected.signon_realm, actual.signon_realm);
195 EXPECT_EQ(expected.ssl_valid, actual.ssl_valid);
196 EXPECT_EQ(expected.preferred, actual.preferred);
197 // We don't check the date created. It varies.
198 EXPECT_EQ(expected.blacklisted_by_user, actual.blacklisted_by_user);
199 EXPECT_EQ(expected.type, actual.type);
200 EXPECT_EQ(expected.times_used, actual.times_used);
201 EXPECT_EQ(expected.scheme, actual.scheme);
202 EXPECT_EQ(expected.date_synced, actual.date_synced);
206 void NativeBackendKWalletTestBase::CheckPasswordChanges(
207 const PasswordStoreChangeList& expected,
208 const PasswordStoreChangeList& actual) {
209 ASSERT_EQ(expected.size(), actual.size());
210 for (size_t i = 0; i < expected.size(); ++i) {
211 EXPECT_EQ(expected[i].type(), actual[i].type());
212 CheckPasswordForm(expected[i].form(), actual[i].form());
217 void NativeBackendKWalletTestBase::CheckPasswordChangesWithResult(
218 const PasswordStoreChangeList* expected,
219 const PasswordStoreChangeList* actual,
222 CheckPasswordChanges(*expected, *actual);
225 class NativeBackendKWalletTest : public NativeBackendKWalletTestBase {
227 NativeBackendKWalletTest()
228 : ui_thread_(BrowserThread::UI, &message_loop_),
229 db_thread_(BrowserThread::DB), klauncher_ret_(0),
230 klauncher_contacted_(false), kwallet_runnable_(true),
231 kwallet_running_(true), kwallet_enabled_(true) {
234 virtual void SetUp();
235 virtual void TearDown();
237 // Let the DB thread run to completion of all current tasks.
239 base::WaitableEvent event(false, false);
240 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
241 base::Bind(ThreadDone, &event));
243 // Some of the tests may post messages to the UI thread, but we don't need
244 // to run those until after the DB thread is finished. So run it here.
245 message_loop_.RunUntilIdle();
247 static void ThreadDone(base::WaitableEvent* event) {
251 // Utilities to help verify sets of expectations.
253 std::pair<std::string,
254 std::vector<const PasswordForm*> > > ExpectationArray;
255 void CheckPasswordForms(const std::string& folder,
256 const ExpectationArray& sorted_expected);
258 enum RemoveBetweenMethod {
263 // Tests RemoveLoginsCreatedBetween or RemoveLoginsSyncedBetween.
264 void TestRemoveLoginsBetween(RemoveBetweenMethod date_to_test);
266 base::MessageLoopForUI message_loop_;
267 content::TestBrowserThread ui_thread_;
268 content::TestBrowserThread db_thread_;
270 scoped_refptr<dbus::MockBus> mock_session_bus_;
271 scoped_refptr<dbus::MockObjectProxy> mock_klauncher_proxy_;
272 scoped_refptr<dbus::MockObjectProxy> mock_kwallet_proxy_;
275 std::string klauncher_error_;
276 bool klauncher_contacted_;
278 bool kwallet_runnable_;
279 bool kwallet_running_;
280 bool kwallet_enabled_;
285 dbus::Response* KLauncherMethodCall(
286 dbus::MethodCall* method_call, testing::Unused);
288 dbus::Response* KWalletMethodCall(
289 dbus::MethodCall* method_call, testing::Unused);
292 void NativeBackendKWalletTest::SetUp() {
293 ASSERT_TRUE(db_thread_.Start());
295 dbus::Bus::Options options;
296 options.bus_type = dbus::Bus::SESSION;
297 mock_session_bus_ = new dbus::MockBus(options);
299 mock_klauncher_proxy_ =
300 new dbus::MockObjectProxy(mock_session_bus_.get(),
302 dbus::ObjectPath("/KLauncher"));
303 EXPECT_CALL(*mock_klauncher_proxy_.get(), MockCallMethodAndBlock(_, _))
305 Invoke(this, &NativeBackendKWalletTest::KLauncherMethodCall));
307 mock_kwallet_proxy_ =
308 new dbus::MockObjectProxy(mock_session_bus_.get(),
310 dbus::ObjectPath("/modules/kwalletd"));
311 EXPECT_CALL(*mock_kwallet_proxy_.get(), MockCallMethodAndBlock(_, _))
313 Invoke(this, &NativeBackendKWalletTest::KWalletMethodCall));
316 *mock_session_bus_.get(),
317 GetObjectProxy("org.kde.klauncher", dbus::ObjectPath("/KLauncher")))
318 .WillRepeatedly(Return(mock_klauncher_proxy_.get()));
320 *mock_session_bus_.get(),
321 GetObjectProxy("org.kde.kwalletd", dbus::ObjectPath("/modules/kwalletd")))
322 .WillRepeatedly(Return(mock_kwallet_proxy_.get()));
324 EXPECT_CALL(*mock_session_bus_.get(), ShutdownAndBlock()).WillOnce(Return())
325 .WillRepeatedly(Return());
328 void NativeBackendKWalletTest::TearDown() {
329 base::MessageLoop::current()->PostTask(FROM_HERE,
330 base::MessageLoop::QuitClosure());
331 base::MessageLoop::current()->Run();
335 void NativeBackendKWalletTest::TestRemoveLoginsBetween(
336 RemoveBetweenMethod date_to_test) {
337 NativeBackendKWalletStub backend(42);
338 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
340 form_google_.date_synced = base::Time();
341 form_isc_.date_synced = base::Time();
342 form_google_.date_created = base::Time();
343 form_isc_.date_created = base::Time();
344 base::Time now = base::Time::Now();
345 base::Time next_day = now + base::TimeDelta::FromDays(1);
346 if (date_to_test == CREATED) {
347 // crbug/374132. Remove the next line once it's fixed.
348 next_day = base::Time::FromTimeT(next_day.ToTimeT());
349 form_google_.date_created = now;
350 form_isc_.date_created = next_day;
352 form_google_.date_synced = now;
353 form_isc_.date_synced = next_day;
356 BrowserThread::PostTask(
359 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
360 base::Unretained(&backend),
362 BrowserThread::PostTask(
365 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
366 base::Unretained(&backend),
369 PasswordStoreChangeList expected_changes;
370 expected_changes.push_back(
371 PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_));
372 PasswordStoreChangeList changes;
373 bool (NativeBackendKWallet::*method)(
374 base::Time, base::Time, password_manager::PasswordStoreChangeList*) =
375 date_to_test == CREATED
376 ? &NativeBackendKWalletStub::RemoveLoginsCreatedBetween
377 : &NativeBackendKWalletStub::RemoveLoginsSyncedBetween;
378 BrowserThread::PostTaskAndReplyWithResult(
382 method, base::Unretained(&backend), base::Time(), next_day, &changes),
383 base::Bind(&NativeBackendKWalletTest::CheckPasswordChangesWithResult,
388 std::vector<const PasswordForm*> forms;
389 forms.push_back(&form_isc_);
390 ExpectationArray expected;
391 expected.push_back(make_pair(std::string(form_isc_.signon_realm), forms));
392 CheckPasswordForms("Chrome Form Data (42)", expected);
395 expected_changes.clear();
396 expected_changes.push_back(
397 PasswordStoreChange(PasswordStoreChange::REMOVE, form_isc_));
398 BrowserThread::PostTaskAndReplyWithResult(
402 method, base::Unretained(&backend), next_day, base::Time(), &changes),
403 base::Bind(&NativeBackendKWalletTest::CheckPasswordChangesWithResult,
408 CheckPasswordForms("Chrome Form Data (42)", ExpectationArray());
411 dbus::Response* NativeBackendKWalletTest::KLauncherMethodCall(
412 dbus::MethodCall* method_call, testing::Unused) {
413 EXPECT_EQ("org.kde.KLauncher", method_call->GetInterface());
414 EXPECT_EQ("start_service_by_desktop_name", method_call->GetMember());
416 klauncher_contacted_ = true;
418 dbus::MessageReader reader(method_call);
419 std::string service_name;
420 std::vector<std::string> urls;
421 std::vector<std::string> envs;
422 std::string startup_id;
425 EXPECT_TRUE(reader.PopString(&service_name));
426 EXPECT_TRUE(reader.PopArrayOfStrings(&urls));
427 EXPECT_TRUE(reader.PopArrayOfStrings(&envs));
428 EXPECT_TRUE(reader.PopString(&startup_id));
429 EXPECT_TRUE(reader.PopBool(&blind));
431 EXPECT_EQ("kwalletd", service_name);
432 EXPECT_TRUE(urls.empty());
433 EXPECT_TRUE(envs.empty());
434 EXPECT_TRUE(startup_id.empty());
437 if (kwallet_runnable_)
438 kwallet_running_ = true;
440 scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
441 dbus::MessageWriter writer(response.get());
442 writer.AppendInt32(klauncher_ret_);
443 writer.AppendString(std::string()); // dbus_name
444 writer.AppendString(klauncher_error_);
445 writer.AppendInt32(1234); // pid
446 return response.release();
449 dbus::Response* NativeBackendKWalletTest::KWalletMethodCall(
450 dbus::MethodCall* method_call, testing::Unused) {
451 if (!kwallet_running_)
453 EXPECT_EQ("org.kde.KWallet", method_call->GetInterface());
455 scoped_ptr<dbus::Response> response;
456 if (method_call->GetMember() == "isEnabled") {
457 response = dbus::Response::CreateEmpty();
458 dbus::MessageWriter writer(response.get());
459 writer.AppendBool(kwallet_enabled_);
460 } else if (method_call->GetMember() == "networkWallet") {
461 response = dbus::Response::CreateEmpty();
462 dbus::MessageWriter writer(response.get());
463 writer.AppendString("test_wallet"); // Should match |open| below.
464 } else if (method_call->GetMember() == "open") {
465 dbus::MessageReader reader(method_call);
466 std::string wallet_name;
468 std::string app_name;
469 EXPECT_TRUE(reader.PopString(&wallet_name));
470 EXPECT_TRUE(reader.PopInt64(&wallet_id));
471 EXPECT_TRUE(reader.PopString(&app_name));
472 EXPECT_EQ("test_wallet", wallet_name); // Should match |networkWallet|.
473 response = dbus::Response::CreateEmpty();
474 dbus::MessageWriter writer(response.get());
475 writer.AppendInt32(1); // Can be anything but kInvalidKWalletHandle.
476 } else if (method_call->GetMember() == "hasFolder" ||
477 method_call->GetMember() == "createFolder") {
478 dbus::MessageReader reader(method_call);
479 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
480 std::string folder_name;
481 std::string app_name;
482 EXPECT_TRUE(reader.PopInt32(&handle));
483 EXPECT_TRUE(reader.PopString(&folder_name));
484 EXPECT_TRUE(reader.PopString(&app_name));
485 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
486 response = dbus::Response::CreateEmpty();
487 dbus::MessageWriter writer(response.get());
488 if (method_call->GetMember() == "hasFolder")
489 writer.AppendBool(wallet_.hasFolder(folder_name));
491 writer.AppendBool(wallet_.createFolder(folder_name));
492 } else if (method_call->GetMember() == "hasEntry" ||
493 method_call->GetMember() == "removeEntry") {
494 dbus::MessageReader reader(method_call);
495 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
496 std::string folder_name;
498 std::string app_name;
499 EXPECT_TRUE(reader.PopInt32(&handle));
500 EXPECT_TRUE(reader.PopString(&folder_name));
501 EXPECT_TRUE(reader.PopString(&key));
502 EXPECT_TRUE(reader.PopString(&app_name));
503 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
504 response = dbus::Response::CreateEmpty();
505 dbus::MessageWriter writer(response.get());
506 if (method_call->GetMember() == "hasEntry")
507 writer.AppendBool(wallet_.hasEntry(folder_name, key));
509 writer.AppendInt32(wallet_.removeEntry(folder_name, key) ? 0 : 1);
510 } else if (method_call->GetMember() == "entryList") {
511 dbus::MessageReader reader(method_call);
512 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
513 std::string folder_name;
514 std::string app_name;
515 EXPECT_TRUE(reader.PopInt32(&handle));
516 EXPECT_TRUE(reader.PopString(&folder_name));
517 EXPECT_TRUE(reader.PopString(&app_name));
518 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
519 std::vector<std::string> entries;
520 if (wallet_.entryList(folder_name, &entries)) {
521 response = dbus::Response::CreateEmpty();
522 dbus::MessageWriter writer(response.get());
523 writer.AppendArrayOfStrings(entries);
525 } else if (method_call->GetMember() == "readEntry") {
526 dbus::MessageReader reader(method_call);
527 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
528 std::string folder_name;
530 std::string app_name;
531 EXPECT_TRUE(reader.PopInt32(&handle));
532 EXPECT_TRUE(reader.PopString(&folder_name));
533 EXPECT_TRUE(reader.PopString(&key));
534 EXPECT_TRUE(reader.PopString(&app_name));
535 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
536 TestKWallet::Blob value;
537 if (wallet_.readEntry(folder_name, key, &value)) {
538 response = dbus::Response::CreateEmpty();
539 dbus::MessageWriter writer(response.get());
540 writer.AppendArrayOfBytes(value.data(), value.size());
542 } else if (method_call->GetMember() == "writeEntry") {
543 dbus::MessageReader reader(method_call);
544 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
545 std::string folder_name;
547 const uint8_t* bytes = NULL;
549 std::string app_name;
550 EXPECT_TRUE(reader.PopInt32(&handle));
551 EXPECT_TRUE(reader.PopString(&folder_name));
552 EXPECT_TRUE(reader.PopString(&key));
553 EXPECT_TRUE(reader.PopArrayOfBytes(&bytes, &length));
554 EXPECT_TRUE(reader.PopString(&app_name));
555 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
556 response = dbus::Response::CreateEmpty();
557 dbus::MessageWriter writer(response.get());
559 wallet_.writeEntry(folder_name, key,
560 TestKWallet::Blob(bytes, length)) ? 0 : 1);
563 EXPECT_FALSE(response.get() == NULL);
564 return response.release();
567 void NativeBackendKWalletTest::CheckPasswordForms(
568 const std::string& folder, const ExpectationArray& sorted_expected) {
569 EXPECT_TRUE(wallet_.hasFolder(folder));
570 std::vector<std::string> entries;
571 EXPECT_TRUE(wallet_.entryList(folder, &entries));
572 EXPECT_EQ(sorted_expected.size(), entries.size());
573 std::sort(entries.begin(), entries.end());
574 for (size_t i = 0; i < entries.size() && i < sorted_expected.size(); ++i) {
575 EXPECT_EQ(sorted_expected[i].first, entries[i]);
576 TestKWallet::Blob value;
577 EXPECT_TRUE(wallet_.readEntry(folder, entries[i], &value));
578 Pickle pickle(reinterpret_cast<const char*>(value.data()), value.size());
579 std::vector<PasswordForm*> forms;
580 NativeBackendKWalletStub::DeserializeValue(entries[i], pickle, &forms);
581 const std::vector<const PasswordForm*>& expect = sorted_expected[i].second;
582 EXPECT_EQ(expect.size(), forms.size());
583 for (size_t j = 0; j < forms.size() && j < expect.size(); ++j)
584 CheckPasswordForm(*expect[j], *forms[j]);
585 STLDeleteElements(&forms);
589 TEST_F(NativeBackendKWalletTest, NotEnabled) {
590 NativeBackendKWalletStub kwallet(42);
591 kwallet_enabled_ = false;
592 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
593 EXPECT_FALSE(klauncher_contacted_);
596 TEST_F(NativeBackendKWalletTest, NotRunnable) {
597 NativeBackendKWalletStub kwallet(42);
598 kwallet_runnable_ = false;
599 kwallet_running_ = false;
600 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
601 EXPECT_TRUE(klauncher_contacted_);
604 TEST_F(NativeBackendKWalletTest, NotRunningOrEnabled) {
605 NativeBackendKWalletStub kwallet(42);
606 kwallet_running_ = false;
607 kwallet_enabled_ = false;
608 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
609 EXPECT_TRUE(klauncher_contacted_);
612 TEST_F(NativeBackendKWalletTest, NotRunning) {
613 NativeBackendKWalletStub kwallet(42);
614 kwallet_running_ = false;
615 EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_));
616 EXPECT_TRUE(klauncher_contacted_);
619 TEST_F(NativeBackendKWalletTest, BasicStartup) {
620 NativeBackendKWalletStub kwallet(42);
621 EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_));
622 EXPECT_FALSE(klauncher_contacted_);
625 TEST_F(NativeBackendKWalletTest, BasicAddLogin) {
626 NativeBackendKWalletStub backend(42);
627 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
629 BrowserThread::PostTask(
630 BrowserThread::DB, FROM_HERE,
631 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
632 base::Unretained(&backend), form_google_));
636 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
638 std::vector<const PasswordForm*> forms;
639 forms.push_back(&form_google_);
640 ExpectationArray expected;
641 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
642 CheckPasswordForms("Chrome Form Data (42)", expected);
645 TEST_F(NativeBackendKWalletTest, BasicUpdateLogin) {
646 NativeBackendKWalletStub backend(42);
647 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
649 BrowserThread::PostTask(
650 BrowserThread::DB, FROM_HERE,
651 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
652 base::Unretained(&backend), form_google_));
656 PasswordForm new_form_google(form_google_);
657 new_form_google.times_used = 10;
658 new_form_google.action = GURL("http://www.google.com/different/login");
661 PasswordStoreChangeList changes;
662 BrowserThread::PostTask(
663 BrowserThread::DB, FROM_HERE,
664 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::UpdateLogin),
665 base::Unretained(&backend),
667 base::Unretained(&changes)));
670 ASSERT_EQ(1u, changes.size());
671 EXPECT_EQ(PasswordStoreChange::UPDATE, changes.front().type());
672 EXPECT_EQ(new_form_google, changes.front().form());
674 std::vector<const PasswordForm*> forms;
675 forms.push_back(&new_form_google);
676 ExpectationArray expected;
677 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
678 CheckPasswordForms("Chrome Form Data (42)", expected);
681 TEST_F(NativeBackendKWalletTest, BasicListLogins) {
682 NativeBackendKWalletStub backend(42);
683 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
685 BrowserThread::PostTask(
686 BrowserThread::DB, FROM_HERE,
687 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
688 base::Unretained(&backend), form_google_));
690 std::vector<PasswordForm*> form_list;
691 BrowserThread::PostTask(
692 BrowserThread::DB, FROM_HERE,
694 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
695 base::Unretained(&backend), &form_list));
699 // Quick check that we got something back.
700 EXPECT_EQ(1u, form_list.size());
701 STLDeleteElements(&form_list);
703 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
705 std::vector<const PasswordForm*> forms;
706 forms.push_back(&form_google_);
707 ExpectationArray expected;
708 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
709 CheckPasswordForms("Chrome Form Data (42)", expected);
712 TEST_F(NativeBackendKWalletTest, BasicRemoveLogin) {
713 NativeBackendKWalletStub backend(42);
714 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
716 BrowserThread::PostTask(
717 BrowserThread::DB, FROM_HERE,
718 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
719 base::Unretained(&backend), form_google_));
723 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
725 std::vector<const PasswordForm*> forms;
726 forms.push_back(&form_google_);
727 ExpectationArray expected;
728 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
729 CheckPasswordForms("Chrome Form Data (42)", expected);
731 BrowserThread::PostTask(
732 BrowserThread::DB, FROM_HERE,
733 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin),
734 base::Unretained(&backend), form_google_));
739 CheckPasswordForms("Chrome Form Data (42)", expected);
742 TEST_F(NativeBackendKWalletTest, UpdateNonexistentLogin) {
743 NativeBackendKWalletStub backend(42);
744 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
746 // First add an unrelated login.
747 BrowserThread::PostTask(
748 BrowserThread::DB, FROM_HERE,
749 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
750 base::Unretained(&backend), form_google_));
754 std::vector<const PasswordForm*> forms;
755 forms.push_back(&form_google_);
756 ExpectationArray expected;
757 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
758 CheckPasswordForms("Chrome Form Data (42)", expected);
760 // Attempt to update a login that doesn't exist.
761 PasswordStoreChangeList changes;
762 BrowserThread::PostTask(
763 BrowserThread::DB, FROM_HERE,
764 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::UpdateLogin),
765 base::Unretained(&backend),
767 base::Unretained(&changes)));
771 EXPECT_EQ(PasswordStoreChangeList(), changes);
772 CheckPasswordForms("Chrome Form Data (42)", expected);
775 TEST_F(NativeBackendKWalletTest, RemoveNonexistentLogin) {
776 NativeBackendKWalletStub backend(42);
777 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
779 // First add an unrelated login.
780 BrowserThread::PostTask(
781 BrowserThread::DB, FROM_HERE,
782 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
783 base::Unretained(&backend), form_google_));
787 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
789 std::vector<const PasswordForm*> forms;
790 forms.push_back(&form_google_);
791 ExpectationArray expected;
792 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
793 CheckPasswordForms("Chrome Form Data (42)", expected);
795 // Attempt to remove a login that doesn't exist.
796 BrowserThread::PostTask(
797 BrowserThread::DB, FROM_HERE,
798 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin),
799 base::Unretained(&backend), form_isc_));
801 // Make sure we can still get the first form back.
802 std::vector<PasswordForm*> form_list;
803 BrowserThread::PostTask(
804 BrowserThread::DB, FROM_HERE,
806 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
807 base::Unretained(&backend), &form_list));
811 // Quick check that we got something back.
812 EXPECT_EQ(1u, form_list.size());
813 STLDeleteElements(&form_list);
815 CheckPasswordForms("Chrome Form Data (42)", expected);
818 TEST_F(NativeBackendKWalletTest, AddDuplicateLogin) {
819 NativeBackendKWalletStub backend(42);
820 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
822 PasswordStoreChangeList changes;
823 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
825 BrowserThread::PostTaskAndReplyWithResult(
826 BrowserThread::DB, FROM_HERE,
827 base::Bind(&NativeBackendKWalletStub::AddLogin,
828 base::Unretained(&backend), form_google_),
829 base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges,
833 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
835 form_google_.times_used++;
836 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
839 BrowserThread::PostTaskAndReplyWithResult(
840 BrowserThread::DB, FROM_HERE,
841 base::Bind(&NativeBackendKWalletStub::AddLogin,
842 base::Unretained(&backend), form_google_),
843 base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges,
848 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
850 std::vector<const PasswordForm*> forms;
851 forms.push_back(&form_google_);
852 ExpectationArray expected;
853 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
854 CheckPasswordForms("Chrome Form Data (42)", expected);
857 TEST_F(NativeBackendKWalletTest, ListLoginsAppends) {
858 NativeBackendKWalletStub backend(42);
859 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
861 BrowserThread::PostTask(
862 BrowserThread::DB, FROM_HERE,
863 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
864 base::Unretained(&backend), form_google_));
866 // Send the same request twice with the same list both times.
867 std::vector<PasswordForm*> form_list;
868 BrowserThread::PostTask(
869 BrowserThread::DB, FROM_HERE,
871 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
872 base::Unretained(&backend), &form_list));
873 BrowserThread::PostTask(
874 BrowserThread::DB, FROM_HERE,
876 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
877 base::Unretained(&backend), &form_list));
881 // Quick check that we got two results back.
882 EXPECT_EQ(2u, form_list.size());
883 STLDeleteElements(&form_list);
885 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
887 std::vector<const PasswordForm*> forms;
888 forms.push_back(&form_google_);
889 ExpectationArray expected;
890 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
891 CheckPasswordForms("Chrome Form Data (42)", expected);
894 TEST_F(NativeBackendKWalletTest, RemoveLoginsCreatedBetween) {
895 TestRemoveLoginsBetween(CREATED);
898 TEST_F(NativeBackendKWalletTest, RemoveLoginsSyncedBetween) {
899 TestRemoveLoginsBetween(SYNCED);
902 // TODO(mdm): add more basic tests here at some point.
903 // (For example tests for storing >1 password per realm pickle.)
905 class NativeBackendKWalletPickleTest : public NativeBackendKWalletTestBase {
907 void CreateVersion2Pickle(const PasswordForm& form, Pickle* pickle);
908 void CreateVersion1Pickle(const PasswordForm& form, Pickle* pickle);
909 void CreateVersion0Pickle(bool size_32,
910 const PasswordForm& form,
912 void CheckVersion2Pickle();
913 void CheckVersion1Pickle();
914 void CheckVersion0Pickle(bool size_32, PasswordForm::Scheme scheme);
917 void CreatePickle(bool size_32, const PasswordForm& form, Pickle* pickle);
920 void NativeBackendKWalletPickleTest::CreateVersion2Pickle(
921 const PasswordForm& form, Pickle* pickle) {
923 CreatePickle(false, form, pickle);
924 pickle->WriteInt(form.type);
925 pickle->WriteInt(form.times_used);
926 autofill::SerializeFormData(form.form_data, pickle);
929 void NativeBackendKWalletPickleTest::CreateVersion1Pickle(
930 const PasswordForm& form, Pickle* pickle) {
932 CreatePickle(false, form, pickle);
935 void NativeBackendKWalletPickleTest::CreateVersion0Pickle(
936 bool size_32, const PasswordForm& form, Pickle* pickle) {
938 CreatePickle(size_32, form, pickle);
941 void NativeBackendKWalletPickleTest::CreatePickle(
942 bool size_32, const PasswordForm& form, Pickle* pickle) {
944 pickle->WriteUInt32(1); // Size of form list. 32 bits.
946 pickle->WriteUInt64(1); // Size of form list. 64 bits.
947 pickle->WriteInt(form.scheme);
948 pickle->WriteString(form.origin.spec());
949 pickle->WriteString(form.action.spec());
950 pickle->WriteString16(form.username_element);
951 pickle->WriteString16(form.username_value);
952 pickle->WriteString16(form.password_element);
953 pickle->WriteString16(form.password_value);
954 pickle->WriteString16(form.submit_element);
955 pickle->WriteBool(form.ssl_valid);
956 pickle->WriteBool(form.preferred);
957 pickle->WriteBool(form.blacklisted_by_user);
958 pickle->WriteInt64(form.date_created.ToTimeT());
961 void NativeBackendKWalletPickleTest::CheckVersion2Pickle() {
963 PasswordForm form = form_google_;
964 form.date_synced = base::Time();
965 CreateVersion2Pickle(form, &pickle);
967 std::vector<PasswordForm*> form_list;
968 NativeBackendKWalletStub::DeserializeValue(form.signon_realm,
971 // This will match |old_form_google_| because not all the fields present in
972 // |form_google_| will be deserialized.
973 EXPECT_EQ(1u, form_list.size());
974 if (form_list.size() > 0)
975 CheckPasswordForm(form, *form_list[0]);
976 STLDeleteElements(&form_list);
979 // Make sure that we can still read version 1 pickles.
980 void NativeBackendKWalletPickleTest::CheckVersion1Pickle() {
982 PasswordForm form = form_google_;
983 CreateVersion1Pickle(form, &pickle);
985 std::vector<PasswordForm*> form_list;
986 NativeBackendKWalletStub::DeserializeValue(form.signon_realm,
989 // This will match |old_form_google_| because not all the fields present in
990 // |form_google_| will be deserialized.
991 EXPECT_EQ(1u, form_list.size());
992 if (form_list.size() > 0)
993 CheckPasswordForm(old_form_google_, *form_list[0]);
994 STLDeleteElements(&form_list);
997 void NativeBackendKWalletPickleTest::CheckVersion0Pickle(
998 bool size_32, PasswordForm::Scheme scheme) {
1000 PasswordForm form = old_form_google_;
1001 form.scheme = scheme;
1002 CreateVersion0Pickle(size_32, form, &pickle);
1003 std::vector<PasswordForm*> form_list;
1004 NativeBackendKWalletStub::DeserializeValue(form.signon_realm,
1005 pickle, &form_list);
1006 EXPECT_EQ(1u, form_list.size());
1007 if (form_list.size() > 0)
1008 CheckPasswordForm(form, *form_list[0]);
1009 STLDeleteElements(&form_list);
1012 // We try both SCHEME_HTML and SCHEME_BASIC since the scheme is stored right
1013 // after the size in the pickle, so it's what gets read as part of the count
1014 // when reading 32-bit pickles on 64-bit systems. SCHEME_HTML is 0 (so we'll
1015 // detect errors later) while SCHEME_BASIC is 1 (so we'll detect it then). We
1016 // try both 32-bit and 64-bit pickles since only one will be the "other" size
1017 // for whatever architecture we're running on, but we want to make sure we can
1018 // read all combinations in any event.
1020 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTMLPickles) {
1021 CheckVersion0Pickle(true, PasswordForm::SCHEME_HTML);
1024 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTTPPickles) {
1025 CheckVersion0Pickle(true, PasswordForm::SCHEME_BASIC);
1028 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTMLPickles) {
1029 CheckVersion0Pickle(false, PasswordForm::SCHEME_HTML);
1032 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTTPPickles) {
1033 CheckVersion0Pickle(false, PasswordForm::SCHEME_BASIC);
1036 TEST_F(NativeBackendKWalletPickleTest, CheckVersion1Pickle) {
1037 CheckVersion1Pickle();
1040 TEST_F(NativeBackendKWalletPickleTest, CheckVersion2Pickle) {
1041 CheckVersion2Pickle();