Introduce new PasswordForm attributes for Credential Management API.
[chromium-blink-merge.git] / chrome / browser / password_manager / native_backend_kwallet_x_unittest.cc
blob2da71e07ffd8e4a248ede819674081500a836ac1
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 <algorithm>
6 #include <map>
7 #include <string>
8 #include <vector>
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;
34 using testing::_;
35 using testing::Invoke;
36 using testing::Return;
38 namespace {
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.
42 class TestKWallet {
43 public:
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);
72 return true;
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,
77 Blob* value) const {
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;
82 *value = fit->second;
83 return true;
86 // Create the given password folder.
87 bool createFolder(const std::string& folder) {
88 if (reject_local_folders_ && folder.find('(') != std::string::npos)
89 return false;
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,
102 const Blob& value) {
103 Data::iterator it = data_.find(folder);
104 if (it == data_.end()) return false;
105 it->second[key] = value;
106 return true;
109 private:
110 typedef std::map<std::string, Blob> Folder;
111 typedef std::map<std::string, Folder> Data;
113 Data 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 {
130 public:
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 {
141 protected:
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,
181 bool result);
183 PasswordForm old_form_google_;
184 PasswordForm form_google_;
185 PasswordForm form_isc_;
188 // static
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);
213 // static
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());
224 // static
225 void NativeBackendKWalletTestBase::CheckPasswordChangesWithResult(
226 const PasswordStoreChangeList* expected,
227 const PasswordStoreChangeList* actual,
228 bool result) {
229 EXPECT_TRUE(result);
230 CheckPasswordChanges(*expected, *actual);
233 class NativeBackendKWalletTest : public NativeBackendKWalletTestBase {
234 protected:
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.
246 void RunDBThread() {
247 base::WaitableEvent event(false, false);
248 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
249 base::Bind(ThreadDone, &event));
250 event.Wait();
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) {
256 event->Signal();
259 // Utilities to help verify sets of expectations.
260 typedef std::vector<
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 {
267 CREATED,
268 SYNCED,
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_;
282 int klauncher_ret_;
283 std::string klauncher_error_;
284 bool klauncher_contacted_;
286 bool kwallet_runnable_;
287 bool kwallet_running_;
288 bool kwallet_enabled_;
290 TestKWallet wallet_;
292 private:
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(),
309 "org.kde.klauncher",
310 dbus::ObjectPath("/KLauncher"));
311 EXPECT_CALL(*mock_klauncher_proxy_.get(), MockCallMethodAndBlock(_, _))
312 .WillRepeatedly(
313 Invoke(this, &NativeBackendKWalletTest::KLauncherMethodCall));
315 mock_kwallet_proxy_ =
316 new dbus::MockObjectProxy(mock_session_bus_.get(),
317 "org.kde.kwalletd",
318 dbus::ObjectPath("/modules/kwalletd"));
319 EXPECT_CALL(*mock_kwallet_proxy_.get(), MockCallMethodAndBlock(_, _))
320 .WillRepeatedly(
321 Invoke(this, &NativeBackendKWalletTest::KWalletMethodCall));
323 EXPECT_CALL(
324 *mock_session_bus_.get(),
325 GetObjectProxy("org.kde.klauncher", dbus::ObjectPath("/KLauncher")))
326 .WillRepeatedly(Return(mock_klauncher_proxy_.get()));
327 EXPECT_CALL(
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();
340 db_thread_.Stop();
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;
359 } else {
360 form_google_.date_synced = now;
361 form_isc_.date_synced = next_day;
364 BrowserThread::PostTask(
365 BrowserThread::DB,
366 FROM_HERE,
367 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
368 base::Unretained(&backend),
369 form_google_));
370 BrowserThread::PostTask(
371 BrowserThread::DB,
372 FROM_HERE,
373 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
374 base::Unretained(&backend),
375 form_isc_));
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(
387 BrowserThread::DB,
388 FROM_HERE,
389 base::Bind(
390 method, base::Unretained(&backend), base::Time(), next_day, &changes),
391 base::Bind(&NativeBackendKWalletTest::CheckPasswordChangesWithResult,
392 &expected_changes,
393 &changes));
394 RunDBThread();
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);
402 // Remove form_isc_.
403 expected_changes.clear();
404 expected_changes.push_back(
405 PasswordStoreChange(PasswordStoreChange::REMOVE, form_isc_));
406 BrowserThread::PostTaskAndReplyWithResult(
407 BrowserThread::DB,
408 FROM_HERE,
409 base::Bind(
410 method, base::Unretained(&backend), next_day, base::Time(), &changes),
411 base::Bind(&NativeBackendKWalletTest::CheckPasswordChangesWithResult,
412 &expected_changes,
413 &changes));
414 RunDBThread();
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;
431 bool blind = false;
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());
443 EXPECT_FALSE(blind);
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_)
460 return NULL;
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;
475 int64_t wallet_id;
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));
498 else
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;
505 std::string key;
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));
516 else
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;
537 std::string key;
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;
554 std::string key;
555 const uint8_t* bytes = NULL;
556 size_t length = 0;
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());
566 writer.AppendInt32(
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_));
642 RunDBThread();
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_));
662 RunDBThread();
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");
668 // Update login
669 PasswordStoreChangeList changes;
670 BrowserThread::PostTask(
671 BrowserThread::DB, FROM_HERE,
672 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::UpdateLogin),
673 base::Unretained(&backend),
674 new_form_google,
675 base::Unretained(&changes)));
676 RunDBThread();
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,
701 base::Bind(
702 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
703 base::Unretained(&backend), &form_list));
705 RunDBThread();
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_));
729 RunDBThread();
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_));
744 RunDBThread();
746 expected.clear();
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_));
760 RunDBThread();
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),
774 form_isc_,
775 base::Unretained(&changes)));
777 RunDBThread();
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_));
793 RunDBThread();
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,
813 base::Bind(
814 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
815 base::Unretained(&backend), &form_list));
817 RunDBThread();
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,
832 form_google_));
833 BrowserThread::PostTaskAndReplyWithResult(
834 BrowserThread::DB, FROM_HERE,
835 base::Bind(&NativeBackendKWalletStub::AddLogin,
836 base::Unretained(&backend), form_google_),
837 base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges,
838 changes));
840 changes.clear();
841 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
842 form_google_));
843 form_google_.times_used++;
844 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
845 form_google_));
847 BrowserThread::PostTaskAndReplyWithResult(
848 BrowserThread::DB, FROM_HERE,
849 base::Bind(&NativeBackendKWalletStub::AddLogin,
850 base::Unretained(&backend), form_google_),
851 base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges,
852 changes));
854 RunDBThread();
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,
878 base::Bind(
879 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
880 base::Unretained(&backend), &form_list));
881 BrowserThread::PostTask(
882 BrowserThread::DB, FROM_HERE,
883 base::Bind(
884 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
885 base::Unretained(&backend), &form_list));
887 RunDBThread();
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 {
914 protected:
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,
920 Pickle* pickle);
921 void CheckVersion3Pickle();
922 void CheckVersion2Pickle();
923 void CheckVersion1Pickle();
924 void CheckVersion0Pickle(bool size_32, PasswordForm::Scheme scheme);
926 private:
927 void CreatePickle(bool size_32, const PasswordForm& form, Pickle* pickle);
930 void NativeBackendKWalletPickleTest::CreateVersion3Pickle(
931 const PasswordForm& form, Pickle* pickle) {
932 pickle->WriteInt(3);
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) {
942 pickle->WriteInt(2);
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) {
951 pickle->WriteInt(1);
952 CreatePickle(false, form, pickle);
955 void NativeBackendKWalletPickleTest::CreateVersion0Pickle(
956 bool size_32, const PasswordForm& form, Pickle* pickle) {
957 pickle->WriteInt(0);
958 CreatePickle(size_32, form, pickle);
961 void NativeBackendKWalletPickleTest::CreatePickle(
962 bool size_32, const PasswordForm& form, Pickle* pickle) {
963 if (size_32)
964 pickle->WriteUInt32(1); // Size of form list. 32 bits.
965 else
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() {
982 Pickle pickle;
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() {
1001 Pickle pickle;
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() {
1019 Pickle pickle;
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) {
1037 Pickle pickle;
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();