chromeos: Make dbus::MessageReader memory ownership explicit
[chromium-blink-merge.git] / chrome / browser / password_manager / native_backend_kwallet_x_unittest.cc
blob438663c8034752c530288eed8f33ea25850de77b
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/common/pref_names.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "components/autofill/core/common/password_form.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 testing::_;
33 using testing::Invoke;
34 using testing::Return;
36 namespace {
38 // This class implements a very simple version of KWallet in memory.
39 // We only provide the parts we actually use; the real version has more.
40 class TestKWallet {
41 public:
42 typedef std::basic_string<uint8_t> Blob; // std::string is binary-safe.
44 TestKWallet() : reject_local_folders_(false) {}
46 void set_reject_local_folders(bool value) { reject_local_folders_ = value; }
48 // NOTE: The method names here are the same as the corresponding DBus
49 // methods, and therefore have names that don't match our style guide.
51 // Check for presence of a given password folder.
52 bool hasFolder(const std::string& folder) const {
53 return data_.find(folder) != data_.end();
56 // Check for presence of a given password in a given password folder.
57 bool hasEntry(const std::string& folder, const std::string& key) const {
58 Data::const_iterator it = data_.find(folder);
59 return it != data_.end() && it->second.find(key) != it->second.end();
62 // Get a list of password keys in a given password folder.
63 bool entryList(const std::string& folder,
64 std::vector<std::string>* entries) const {
65 Data::const_iterator it = data_.find(folder);
66 if (it == data_.end()) return false;
67 for (Folder::const_iterator fit = it->second.begin();
68 fit != it->second.end(); ++fit)
69 entries->push_back(fit->first);
70 return true;
73 // Read the password data for a given password in a given password folder.
74 bool readEntry(const std::string& folder, const std::string& key,
75 Blob* value) const {
76 Data::const_iterator it = data_.find(folder);
77 if (it == data_.end()) return false;
78 Folder::const_iterator fit = it->second.find(key);
79 if (fit == it->second.end()) return false;
80 *value = fit->second;
81 return true;
84 // Create the given password folder.
85 bool createFolder(const std::string& folder) {
86 if (reject_local_folders_ && folder.find('(') != std::string::npos)
87 return false;
88 return data_.insert(make_pair(folder, Folder())).second;
91 // Remove the given password from the given password folder.
92 bool removeEntry(const std::string& folder, const std::string& key) {
93 Data::iterator it = data_.find(folder);
94 if (it == data_.end()) return false;
95 return it->second.erase(key) > 0;
98 // Write the given password data to the given password folder.
99 bool writeEntry(const std::string& folder, const std::string& key,
100 const Blob& value) {
101 Data::iterator it = data_.find(folder);
102 if (it == data_.end()) return false;
103 it->second[key] = value;
104 return true;
107 private:
108 typedef std::map<std::string, Blob> Folder;
109 typedef std::map<std::string, Folder> Data;
111 Data data_;
112 // "Local" folders are folders containing local profile IDs in their names. We
113 // can reject attempts to create them in order to make it easier to create
114 // legacy shared passwords in these tests, for testing the migration code.
115 bool reject_local_folders_;
117 // No need to disallow copy and assign. This class is safe to copy and assign.
120 } // anonymous namespace
122 // Obscure magic: we need to declare storage for this constant because we use it
123 // in ways that require its address in this test, but not in the actual code.
124 const int NativeBackendKWallet::kInvalidKWalletHandle;
126 // Subclass NativeBackendKWallet to promote some members to public for testing.
127 class NativeBackendKWalletStub : public NativeBackendKWallet {
128 public:
129 NativeBackendKWalletStub(LocalProfileId id, PrefService* pref_service)
130 : NativeBackendKWallet(id, pref_service) {
132 using NativeBackendKWallet::InitWithBus;
133 using NativeBackendKWallet::kInvalidKWalletHandle;
134 using NativeBackendKWallet::DeserializeValue;
137 // Provide some test forms to avoid having to set them up in each test.
138 class NativeBackendKWalletTestBase : public testing::Test {
139 protected:
140 NativeBackendKWalletTestBase() {
141 form_google_.origin = GURL("http://www.google.com/");
142 form_google_.action = GURL("http://www.google.com/login");
143 form_google_.username_element = UTF8ToUTF16("user");
144 form_google_.username_value = UTF8ToUTF16("joeschmoe");
145 form_google_.password_element = UTF8ToUTF16("pass");
146 form_google_.password_value = UTF8ToUTF16("seekrit");
147 form_google_.submit_element = UTF8ToUTF16("submit");
148 form_google_.signon_realm = "Google";
150 form_isc_.origin = GURL("http://www.isc.org/");
151 form_isc_.action = GURL("http://www.isc.org/auth");
152 form_isc_.username_element = UTF8ToUTF16("id");
153 form_isc_.username_value = UTF8ToUTF16("janedoe");
154 form_isc_.password_element = UTF8ToUTF16("passwd");
155 form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
156 form_isc_.submit_element = UTF8ToUTF16("login");
157 form_isc_.signon_realm = "ISC";
160 void CheckPasswordForm(const PasswordForm& expected,
161 const PasswordForm& actual);
163 PasswordForm form_google_;
164 PasswordForm form_isc_;
167 void NativeBackendKWalletTestBase::CheckPasswordForm(
168 const PasswordForm& expected, const PasswordForm& actual) {
169 EXPECT_EQ(expected.origin, actual.origin);
170 EXPECT_EQ(expected.password_value, actual.password_value);
171 EXPECT_EQ(expected.action, actual.action);
172 EXPECT_EQ(expected.username_element, actual.username_element);
173 EXPECT_EQ(expected.username_value, actual.username_value);
174 EXPECT_EQ(expected.password_element, actual.password_element);
175 EXPECT_EQ(expected.submit_element, actual.submit_element);
176 EXPECT_EQ(expected.signon_realm, actual.signon_realm);
177 EXPECT_EQ(expected.ssl_valid, actual.ssl_valid);
178 EXPECT_EQ(expected.preferred, actual.preferred);
179 // We don't check the date created. It varies.
180 EXPECT_EQ(expected.blacklisted_by_user, actual.blacklisted_by_user);
181 EXPECT_EQ(expected.scheme, actual.scheme);
184 class NativeBackendKWalletTest : public NativeBackendKWalletTestBase {
185 protected:
186 NativeBackendKWalletTest()
187 : ui_thread_(BrowserThread::UI, &message_loop_),
188 db_thread_(BrowserThread::DB), klauncher_ret_(0),
189 klauncher_contacted_(false), kwallet_runnable_(true),
190 kwallet_running_(true), kwallet_enabled_(true) {
193 virtual void SetUp();
194 virtual void TearDown();
196 // Let the DB thread run to completion of all current tasks.
197 void RunDBThread() {
198 base::WaitableEvent event(false, false);
199 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
200 base::Bind(ThreadDone, &event));
201 event.Wait();
202 // Some of the tests may post messages to the UI thread, but we don't need
203 // to run those until after the DB thread is finished. So run it here.
204 message_loop_.RunUntilIdle();
206 static void ThreadDone(base::WaitableEvent* event) {
207 event->Signal();
210 // Utilities to help verify sets of expectations.
211 typedef std::vector<
212 std::pair<std::string,
213 std::vector<const PasswordForm*> > > ExpectationArray;
214 void CheckPasswordForms(const std::string& folder,
215 const ExpectationArray& sorted_expected);
217 base::MessageLoopForUI message_loop_;
218 content::TestBrowserThread ui_thread_;
219 content::TestBrowserThread db_thread_;
220 TestingProfile profile_;
222 scoped_refptr<dbus::MockBus> mock_session_bus_;
223 scoped_refptr<dbus::MockObjectProxy> mock_klauncher_proxy_;
224 scoped_refptr<dbus::MockObjectProxy> mock_kwallet_proxy_;
226 int klauncher_ret_;
227 std::string klauncher_error_;
228 bool klauncher_contacted_;
230 bool kwallet_runnable_;
231 bool kwallet_running_;
232 bool kwallet_enabled_;
234 TestKWallet wallet_;
236 private:
237 dbus::Response* KLauncherMethodCall(
238 dbus::MethodCall* method_call, testing::Unused);
240 dbus::Response* KWalletMethodCall(
241 dbus::MethodCall* method_call, testing::Unused);
244 void NativeBackendKWalletTest::SetUp() {
245 ASSERT_TRUE(db_thread_.Start());
247 dbus::Bus::Options options;
248 options.bus_type = dbus::Bus::SESSION;
249 mock_session_bus_ = new dbus::MockBus(options);
251 mock_klauncher_proxy_ =
252 new dbus::MockObjectProxy(mock_session_bus_.get(),
253 "org.kde.klauncher",
254 dbus::ObjectPath("/KLauncher"));
255 EXPECT_CALL(*mock_klauncher_proxy_.get(), MockCallMethodAndBlock(_, _))
256 .WillRepeatedly(
257 Invoke(this, &NativeBackendKWalletTest::KLauncherMethodCall));
259 mock_kwallet_proxy_ =
260 new dbus::MockObjectProxy(mock_session_bus_.get(),
261 "org.kde.kwalletd",
262 dbus::ObjectPath("/modules/kwalletd"));
263 EXPECT_CALL(*mock_kwallet_proxy_.get(), MockCallMethodAndBlock(_, _))
264 .WillRepeatedly(
265 Invoke(this, &NativeBackendKWalletTest::KWalletMethodCall));
267 EXPECT_CALL(
268 *mock_session_bus_.get(),
269 GetObjectProxy("org.kde.klauncher", dbus::ObjectPath("/KLauncher")))
270 .WillRepeatedly(Return(mock_klauncher_proxy_.get()));
271 EXPECT_CALL(
272 *mock_session_bus_.get(),
273 GetObjectProxy("org.kde.kwalletd", dbus::ObjectPath("/modules/kwalletd")))
274 .WillRepeatedly(Return(mock_kwallet_proxy_.get()));
276 EXPECT_CALL(*mock_session_bus_.get(), ShutdownAndBlock()).WillOnce(Return())
277 .WillRepeatedly(Return());
280 void NativeBackendKWalletTest::TearDown() {
281 base::MessageLoop::current()->PostTask(FROM_HERE,
282 base::MessageLoop::QuitClosure());
283 base::MessageLoop::current()->Run();
284 db_thread_.Stop();
287 dbus::Response* NativeBackendKWalletTest::KLauncherMethodCall(
288 dbus::MethodCall* method_call, testing::Unused) {
289 EXPECT_EQ("org.kde.KLauncher", method_call->GetInterface());
290 EXPECT_EQ("start_service_by_desktop_name", method_call->GetMember());
292 klauncher_contacted_ = true;
294 dbus::MessageReader reader(method_call);
295 std::string service_name;
296 std::vector<std::string> urls;
297 std::vector<std::string> envs;
298 std::string startup_id;
299 bool blind = false;
301 EXPECT_TRUE(reader.PopString(&service_name));
302 EXPECT_TRUE(reader.PopArrayOfStrings(&urls));
303 EXPECT_TRUE(reader.PopArrayOfStrings(&envs));
304 EXPECT_TRUE(reader.PopString(&startup_id));
305 EXPECT_TRUE(reader.PopBool(&blind));
307 EXPECT_EQ("kwalletd", service_name);
308 EXPECT_TRUE(urls.empty());
309 EXPECT_TRUE(envs.empty());
310 EXPECT_TRUE(startup_id.empty());
311 EXPECT_FALSE(blind);
313 if (kwallet_runnable_)
314 kwallet_running_ = true;
316 scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
317 dbus::MessageWriter writer(response.get());
318 writer.AppendInt32(klauncher_ret_);
319 writer.AppendString(std::string()); // dbus_name
320 writer.AppendString(klauncher_error_);
321 writer.AppendInt32(1234); // pid
322 return response.release();
325 dbus::Response* NativeBackendKWalletTest::KWalletMethodCall(
326 dbus::MethodCall* method_call, testing::Unused) {
327 if (!kwallet_running_)
328 return NULL;
329 EXPECT_EQ("org.kde.KWallet", method_call->GetInterface());
331 scoped_ptr<dbus::Response> response;
332 if (method_call->GetMember() == "isEnabled") {
333 response = dbus::Response::CreateEmpty();
334 dbus::MessageWriter writer(response.get());
335 writer.AppendBool(kwallet_enabled_);
336 } else if (method_call->GetMember() == "networkWallet") {
337 response = dbus::Response::CreateEmpty();
338 dbus::MessageWriter writer(response.get());
339 writer.AppendString("test_wallet"); // Should match |open| below.
340 } else if (method_call->GetMember() == "open") {
341 dbus::MessageReader reader(method_call);
342 std::string wallet_name;
343 int64_t wallet_id;
344 std::string app_name;
345 EXPECT_TRUE(reader.PopString(&wallet_name));
346 EXPECT_TRUE(reader.PopInt64(&wallet_id));
347 EXPECT_TRUE(reader.PopString(&app_name));
348 EXPECT_EQ("test_wallet", wallet_name); // Should match |networkWallet|.
349 response = dbus::Response::CreateEmpty();
350 dbus::MessageWriter writer(response.get());
351 writer.AppendInt32(1); // Can be anything but kInvalidKWalletHandle.
352 } else if (method_call->GetMember() == "hasFolder" ||
353 method_call->GetMember() == "createFolder") {
354 dbus::MessageReader reader(method_call);
355 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
356 std::string folder_name;
357 std::string app_name;
358 EXPECT_TRUE(reader.PopInt32(&handle));
359 EXPECT_TRUE(reader.PopString(&folder_name));
360 EXPECT_TRUE(reader.PopString(&app_name));
361 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
362 response = dbus::Response::CreateEmpty();
363 dbus::MessageWriter writer(response.get());
364 if (method_call->GetMember() == "hasFolder")
365 writer.AppendBool(wallet_.hasFolder(folder_name));
366 else
367 writer.AppendBool(wallet_.createFolder(folder_name));
368 } else if (method_call->GetMember() == "hasEntry" ||
369 method_call->GetMember() == "removeEntry") {
370 dbus::MessageReader reader(method_call);
371 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
372 std::string folder_name;
373 std::string key;
374 std::string app_name;
375 EXPECT_TRUE(reader.PopInt32(&handle));
376 EXPECT_TRUE(reader.PopString(&folder_name));
377 EXPECT_TRUE(reader.PopString(&key));
378 EXPECT_TRUE(reader.PopString(&app_name));
379 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
380 response = dbus::Response::CreateEmpty();
381 dbus::MessageWriter writer(response.get());
382 if (method_call->GetMember() == "hasEntry")
383 writer.AppendBool(wallet_.hasEntry(folder_name, key));
384 else
385 writer.AppendInt32(wallet_.removeEntry(folder_name, key) ? 0 : 1);
386 } else if (method_call->GetMember() == "entryList") {
387 dbus::MessageReader reader(method_call);
388 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
389 std::string folder_name;
390 std::string app_name;
391 EXPECT_TRUE(reader.PopInt32(&handle));
392 EXPECT_TRUE(reader.PopString(&folder_name));
393 EXPECT_TRUE(reader.PopString(&app_name));
394 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
395 std::vector<std::string> entries;
396 if (wallet_.entryList(folder_name, &entries)) {
397 response = dbus::Response::CreateEmpty();
398 dbus::MessageWriter writer(response.get());
399 writer.AppendArrayOfStrings(entries);
401 } else if (method_call->GetMember() == "readEntry") {
402 dbus::MessageReader reader(method_call);
403 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
404 std::string folder_name;
405 std::string key;
406 std::string app_name;
407 EXPECT_TRUE(reader.PopInt32(&handle));
408 EXPECT_TRUE(reader.PopString(&folder_name));
409 EXPECT_TRUE(reader.PopString(&key));
410 EXPECT_TRUE(reader.PopString(&app_name));
411 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
412 TestKWallet::Blob value;
413 if (wallet_.readEntry(folder_name, key, &value)) {
414 response = dbus::Response::CreateEmpty();
415 dbus::MessageWriter writer(response.get());
416 writer.AppendArrayOfBytes(value.data(), value.size());
418 } else if (method_call->GetMember() == "writeEntry") {
419 dbus::MessageReader reader(method_call);
420 int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
421 std::string folder_name;
422 std::string key;
423 const uint8_t* bytes = NULL;
424 size_t length = 0;
425 std::string app_name;
426 EXPECT_TRUE(reader.PopInt32(&handle));
427 EXPECT_TRUE(reader.PopString(&folder_name));
428 EXPECT_TRUE(reader.PopString(&key));
429 EXPECT_TRUE(reader.PopArrayOfBytes(&bytes, &length));
430 EXPECT_TRUE(reader.PopString(&app_name));
431 EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
432 response = dbus::Response::CreateEmpty();
433 dbus::MessageWriter writer(response.get());
434 writer.AppendInt32(
435 wallet_.writeEntry(folder_name, key,
436 TestKWallet::Blob(bytes, length)) ? 0 : 1);
439 EXPECT_FALSE(response.get() == NULL);
440 return response.release();
443 void NativeBackendKWalletTest::CheckPasswordForms(
444 const std::string& folder, const ExpectationArray& sorted_expected) {
445 EXPECT_TRUE(wallet_.hasFolder(folder));
446 std::vector<std::string> entries;
447 EXPECT_TRUE(wallet_.entryList(folder, &entries));
448 EXPECT_EQ(sorted_expected.size(), entries.size());
449 std::sort(entries.begin(), entries.end());
450 for (size_t i = 0; i < entries.size() && i < sorted_expected.size(); ++i) {
451 EXPECT_EQ(sorted_expected[i].first, entries[i]);
452 TestKWallet::Blob value;
453 EXPECT_TRUE(wallet_.readEntry(folder, entries[i], &value));
454 Pickle pickle(reinterpret_cast<const char*>(value.data()), value.size());
455 std::vector<PasswordForm*> forms;
456 NativeBackendKWalletStub::DeserializeValue(entries[i], pickle, &forms);
457 const std::vector<const PasswordForm*>& expect = sorted_expected[i].second;
458 EXPECT_EQ(expect.size(), forms.size());
459 for (size_t j = 0; j < forms.size() && j < expect.size(); ++j)
460 CheckPasswordForm(*expect[j], *forms[j]);
461 STLDeleteElements(&forms);
465 TEST_F(NativeBackendKWalletTest, NotEnabled) {
466 NativeBackendKWalletStub kwallet(42, profile_.GetPrefs());
467 kwallet_enabled_ = false;
468 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
469 EXPECT_FALSE(klauncher_contacted_);
472 TEST_F(NativeBackendKWalletTest, NotRunnable) {
473 NativeBackendKWalletStub kwallet(42, profile_.GetPrefs());
474 kwallet_runnable_ = false;
475 kwallet_running_ = false;
476 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
477 EXPECT_TRUE(klauncher_contacted_);
480 TEST_F(NativeBackendKWalletTest, NotRunningOrEnabled) {
481 NativeBackendKWalletStub kwallet(42, profile_.GetPrefs());
482 kwallet_running_ = false;
483 kwallet_enabled_ = false;
484 EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
485 EXPECT_TRUE(klauncher_contacted_);
488 TEST_F(NativeBackendKWalletTest, NotRunning) {
489 NativeBackendKWalletStub kwallet(42, profile_.GetPrefs());
490 kwallet_running_ = false;
491 EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_));
492 EXPECT_TRUE(klauncher_contacted_);
495 TEST_F(NativeBackendKWalletTest, BasicStartup) {
496 NativeBackendKWalletStub kwallet(42, profile_.GetPrefs());
497 EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_));
498 EXPECT_FALSE(klauncher_contacted_);
501 TEST_F(NativeBackendKWalletTest, BasicAddLogin) {
502 // Pretend that the migration has already taken place.
503 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
505 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
506 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
508 BrowserThread::PostTask(
509 BrowserThread::DB, FROM_HERE,
510 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
511 base::Unretained(&backend), form_google_));
513 RunDBThread();
515 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
517 std::vector<const PasswordForm*> forms;
518 forms.push_back(&form_google_);
519 ExpectationArray expected;
520 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
521 CheckPasswordForms("Chrome Form Data (42)", expected);
524 TEST_F(NativeBackendKWalletTest, BasicListLogins) {
525 // Pretend that the migration has already taken place.
526 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
528 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
529 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
531 BrowserThread::PostTask(
532 BrowserThread::DB, FROM_HERE,
533 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
534 base::Unretained(&backend), form_google_));
536 std::vector<PasswordForm*> form_list;
537 BrowserThread::PostTask(
538 BrowserThread::DB, FROM_HERE,
539 base::Bind(
540 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
541 base::Unretained(&backend), &form_list));
543 RunDBThread();
545 // Quick check that we got something back.
546 EXPECT_EQ(1u, form_list.size());
547 STLDeleteElements(&form_list);
549 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
551 std::vector<const PasswordForm*> forms;
552 forms.push_back(&form_google_);
553 ExpectationArray expected;
554 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
555 CheckPasswordForms("Chrome Form Data (42)", expected);
558 TEST_F(NativeBackendKWalletTest, BasicRemoveLogin) {
559 // Pretend that the migration has already taken place.
560 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
562 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
563 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
565 BrowserThread::PostTask(
566 BrowserThread::DB, FROM_HERE,
567 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
568 base::Unretained(&backend), form_google_));
570 RunDBThread();
572 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
574 std::vector<const PasswordForm*> forms;
575 forms.push_back(&form_google_);
576 ExpectationArray expected;
577 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
578 CheckPasswordForms("Chrome Form Data (42)", expected);
580 BrowserThread::PostTask(
581 BrowserThread::DB, FROM_HERE,
582 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin),
583 base::Unretained(&backend), form_google_));
585 RunDBThread();
587 expected.clear();
588 CheckPasswordForms("Chrome Form Data (42)", expected);
591 TEST_F(NativeBackendKWalletTest, RemoveNonexistentLogin) {
592 // Pretend that the migration has already taken place.
593 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
595 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
596 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
598 // First add an unrelated login.
599 BrowserThread::PostTask(
600 BrowserThread::DB, FROM_HERE,
601 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
602 base::Unretained(&backend), form_google_));
604 RunDBThread();
606 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
608 std::vector<const PasswordForm*> forms;
609 forms.push_back(&form_google_);
610 ExpectationArray expected;
611 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
612 CheckPasswordForms("Chrome Form Data (42)", expected);
614 // Attempt to remove a login that doesn't exist.
615 BrowserThread::PostTask(
616 BrowserThread::DB, FROM_HERE,
617 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin),
618 base::Unretained(&backend), form_isc_));
620 // Make sure we can still get the first form back.
621 std::vector<PasswordForm*> form_list;
622 BrowserThread::PostTask(
623 BrowserThread::DB, FROM_HERE,
624 base::Bind(
625 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
626 base::Unretained(&backend), &form_list));
628 RunDBThread();
630 // Quick check that we got something back.
631 EXPECT_EQ(1u, form_list.size());
632 STLDeleteElements(&form_list);
634 CheckPasswordForms("Chrome Form Data (42)", expected);
637 TEST_F(NativeBackendKWalletTest, AddDuplicateLogin) {
638 // Pretend that the migration has already taken place.
639 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
641 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
642 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
644 BrowserThread::PostTask(
645 BrowserThread::DB, FROM_HERE,
646 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
647 base::Unretained(&backend), form_google_));
648 BrowserThread::PostTask(
649 BrowserThread::DB, FROM_HERE,
650 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
651 base::Unretained(&backend), form_google_));
653 RunDBThread();
655 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
657 std::vector<const PasswordForm*> forms;
658 forms.push_back(&form_google_);
659 ExpectationArray expected;
660 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
661 CheckPasswordForms("Chrome Form Data (42)", expected);
664 TEST_F(NativeBackendKWalletTest, ListLoginsAppends) {
665 // Pretend that the migration has already taken place.
666 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
668 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
669 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
671 BrowserThread::PostTask(
672 BrowserThread::DB, FROM_HERE,
673 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
674 base::Unretained(&backend), form_google_));
676 // Send the same request twice with the same list both times.
677 std::vector<PasswordForm*> form_list;
678 BrowserThread::PostTask(
679 BrowserThread::DB, FROM_HERE,
680 base::Bind(
681 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
682 base::Unretained(&backend), &form_list));
683 BrowserThread::PostTask(
684 BrowserThread::DB, FROM_HERE,
685 base::Bind(
686 base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
687 base::Unretained(&backend), &form_list));
689 RunDBThread();
691 // Quick check that we got two results back.
692 EXPECT_EQ(2u, form_list.size());
693 STLDeleteElements(&form_list);
695 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
697 std::vector<const PasswordForm*> forms;
698 forms.push_back(&form_google_);
699 ExpectationArray expected;
700 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
701 CheckPasswordForms("Chrome Form Data (42)", expected);
704 // TODO(mdm): add more basic (i.e. non-migration) tests here at some point.
705 // (For example tests for storing >1 password per realm pickle.)
707 TEST_F(NativeBackendKWalletTest, DISABLED_MigrateOneLogin) {
708 // Reject attempts to migrate so we can populate the store.
709 wallet_.set_reject_local_folders(true);
712 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
713 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
715 BrowserThread::PostTask(
716 BrowserThread::DB, FROM_HERE,
717 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
718 base::Unretained(&backend), form_google_));
720 // Make sure we can get the form back even when migration is failing.
721 std::vector<PasswordForm*> form_list;
722 BrowserThread::PostTask(
723 BrowserThread::DB, FROM_HERE,
724 base::Bind(
725 base::IgnoreResult(
726 &NativeBackendKWalletStub::GetAutofillableLogins),
727 base::Unretained(&backend), &form_list));
729 RunDBThread();
731 // Quick check that we got something back.
732 EXPECT_EQ(1u, form_list.size());
733 STLDeleteElements(&form_list);
736 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)"));
738 std::vector<const PasswordForm*> forms;
739 forms.push_back(&form_google_);
740 ExpectationArray expected;
741 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
742 CheckPasswordForms("Chrome Form Data", expected);
744 // Now allow the migration.
745 wallet_.set_reject_local_folders(false);
748 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
749 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
751 // Trigger the migration by looking something up.
752 std::vector<PasswordForm*> form_list;
753 BrowserThread::PostTask(
754 BrowserThread::DB, FROM_HERE,
755 base::Bind(
756 base::IgnoreResult(
757 &NativeBackendKWalletStub::GetAutofillableLogins),
758 base::Unretained(&backend), &form_list));
760 RunDBThread();
762 // Quick check that we got something back.
763 EXPECT_EQ(1u, form_list.size());
764 STLDeleteElements(&form_list);
767 CheckPasswordForms("Chrome Form Data", expected);
768 CheckPasswordForms("Chrome Form Data (42)", expected);
770 // Check that we have set the persistent preference.
771 EXPECT_TRUE(
772 profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
775 TEST_F(NativeBackendKWalletTest, DISABLED_MigrateToMultipleProfiles) {
776 // Reject attempts to migrate so we can populate the store.
777 wallet_.set_reject_local_folders(true);
780 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
781 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
783 BrowserThread::PostTask(
784 BrowserThread::DB, FROM_HERE,
785 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
786 base::Unretained(&backend), form_google_));
788 RunDBThread();
791 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)"));
793 std::vector<const PasswordForm*> forms;
794 forms.push_back(&form_google_);
795 ExpectationArray expected;
796 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
797 CheckPasswordForms("Chrome Form Data", expected);
799 // Now allow the migration.
800 wallet_.set_reject_local_folders(false);
803 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
804 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
806 // Trigger the migration by looking something up.
807 std::vector<PasswordForm*> form_list;
808 BrowserThread::PostTask(
809 BrowserThread::DB, FROM_HERE,
810 base::Bind(
811 base::IgnoreResult(
812 &NativeBackendKWalletStub::GetAutofillableLogins),
813 base::Unretained(&backend), &form_list));
815 RunDBThread();
817 // Quick check that we got something back.
818 EXPECT_EQ(1u, form_list.size());
819 STLDeleteElements(&form_list);
822 CheckPasswordForms("Chrome Form Data", expected);
823 CheckPasswordForms("Chrome Form Data (42)", expected);
825 // Check that we have set the persistent preference.
826 EXPECT_TRUE(
827 profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
829 // Normally we'd actually have a different profile. But in the test just reset
830 // the profile's persistent pref; we pass in the local profile id anyway.
831 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, false);
834 NativeBackendKWalletStub backend(24, profile_.GetPrefs());
835 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
837 // Trigger the migration by looking something up.
838 std::vector<PasswordForm*> form_list;
839 BrowserThread::PostTask(
840 BrowserThread::DB, FROM_HERE,
841 base::Bind(
842 base::IgnoreResult(
843 &NativeBackendKWalletStub::GetAutofillableLogins),
844 base::Unretained(&backend), &form_list));
846 RunDBThread();
848 // Quick check that we got something back.
849 EXPECT_EQ(1u, form_list.size());
850 STLDeleteElements(&form_list);
853 CheckPasswordForms("Chrome Form Data", expected);
854 CheckPasswordForms("Chrome Form Data (42)", expected);
855 CheckPasswordForms("Chrome Form Data (24)", expected);
858 TEST_F(NativeBackendKWalletTest, DISABLED_NoMigrationWithPrefSet) {
859 // Reject attempts to migrate so we can populate the store.
860 wallet_.set_reject_local_folders(true);
863 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
864 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
866 BrowserThread::PostTask(
867 BrowserThread::DB, FROM_HERE,
868 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
869 base::Unretained(&backend), form_google_));
871 RunDBThread();
874 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)"));
876 std::vector<const PasswordForm*> forms;
877 forms.push_back(&form_google_);
878 ExpectationArray expected;
879 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
880 CheckPasswordForms("Chrome Form Data", expected);
882 // Now allow migration, but also pretend that the it has already taken place.
883 wallet_.set_reject_local_folders(false);
884 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
887 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
888 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
890 // Trigger the migration by adding a new login.
891 BrowserThread::PostTask(
892 BrowserThread::DB, FROM_HERE,
893 base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
894 base::Unretained(&backend), form_isc_));
896 // Look up all logins; we expect only the one we added.
897 std::vector<PasswordForm*> form_list;
898 BrowserThread::PostTask(
899 BrowserThread::DB, FROM_HERE,
900 base::Bind(
901 base::IgnoreResult(
902 &NativeBackendKWalletStub::GetAutofillableLogins),
903 base::Unretained(&backend), &form_list));
905 RunDBThread();
907 // Quick check that we got the right thing back.
908 EXPECT_EQ(1u, form_list.size());
909 if (form_list.size() > 0)
910 EXPECT_EQ(form_isc_.signon_realm, form_list[0]->signon_realm);
911 STLDeleteElements(&form_list);
914 CheckPasswordForms("Chrome Form Data", expected);
916 forms[0] = &form_isc_;
917 expected.clear();
918 expected.push_back(make_pair(std::string(form_isc_.signon_realm), forms));
919 CheckPasswordForms("Chrome Form Data (42)", expected);
922 TEST_F(NativeBackendKWalletTest, DISABLED_DeleteMigratedPasswordIsIsolated) {
923 // Reject attempts to migrate so we can populate the store.
924 wallet_.set_reject_local_folders(true);
927 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
928 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
930 BrowserThread::PostTask(
931 BrowserThread::DB, FROM_HERE,
932 base::Bind(
933 base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
934 base::Unretained(&backend), form_google_));
936 RunDBThread();
939 EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data (42)"));
941 std::vector<const PasswordForm*> forms;
942 forms.push_back(&form_google_);
943 ExpectationArray expected;
944 expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
945 CheckPasswordForms("Chrome Form Data", expected);
947 // Now allow the migration.
948 wallet_.set_reject_local_folders(false);
951 NativeBackendKWalletStub backend(42, profile_.GetPrefs());
952 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
954 // Trigger the migration by looking something up.
955 std::vector<PasswordForm*> form_list;
956 BrowserThread::PostTask(
957 BrowserThread::DB, FROM_HERE,
958 base::Bind(
959 base::IgnoreResult(
960 &NativeBackendKWalletStub::GetAutofillableLogins),
961 base::Unretained(&backend), &form_list));
963 RunDBThread();
965 // Quick check that we got something back.
966 EXPECT_EQ(1u, form_list.size());
967 STLDeleteElements(&form_list);
970 CheckPasswordForms("Chrome Form Data", expected);
971 CheckPasswordForms("Chrome Form Data (42)", expected);
973 // Check that we have set the persistent preference.
974 EXPECT_TRUE(
975 profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
977 // Normally we'd actually have a different profile. But in the test just reset
978 // the profile's persistent pref; we pass in the local profile id anyway.
979 profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, false);
982 NativeBackendKWalletStub backend(24, profile_.GetPrefs());
983 EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
985 // Trigger the migration by looking something up.
986 std::vector<PasswordForm*> form_list;
987 BrowserThread::PostTask(
988 BrowserThread::DB, FROM_HERE,
989 base::Bind(
990 base::IgnoreResult(
991 &NativeBackendKWalletStub::GetAutofillableLogins),
992 base::Unretained(&backend), &form_list));
994 RunDBThread();
996 // Quick check that we got something back.
997 EXPECT_EQ(1u, form_list.size());
998 STLDeleteElements(&form_list);
1000 // There should be three passwords now.
1001 CheckPasswordForms("Chrome Form Data", expected);
1002 CheckPasswordForms("Chrome Form Data (42)", expected);
1003 CheckPasswordForms("Chrome Form Data (24)", expected);
1005 // Now delete the password from this second profile.
1006 BrowserThread::PostTask(
1007 BrowserThread::DB, FROM_HERE,
1008 base::Bind(
1009 base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin),
1010 base::Unretained(&backend), form_google_));
1012 RunDBThread();
1014 // The other two copies of the password in different profiles should remain.
1015 CheckPasswordForms("Chrome Form Data", expected);
1016 CheckPasswordForms("Chrome Form Data (42)", expected);
1017 expected.clear();
1018 CheckPasswordForms("Chrome Form Data (24)", expected);
1022 class NativeBackendKWalletPickleTest : public NativeBackendKWalletTestBase {
1023 protected:
1024 void CreateVersion0Pickle(bool size_32,
1025 const PasswordForm& form,
1026 Pickle* pickle);
1027 void CheckVersion0Pickle(bool size_32, PasswordForm::Scheme scheme);
1030 void NativeBackendKWalletPickleTest::CreateVersion0Pickle(
1031 bool size_32, const PasswordForm& form, Pickle* pickle) {
1032 const int kPickleVersion0 = 0;
1033 pickle->WriteInt(kPickleVersion0);
1034 if (size_32)
1035 pickle->WriteUInt32(1); // Size of form list. 32 bits.
1036 else
1037 pickle->WriteUInt64(1); // Size of form list. 64 bits.
1038 pickle->WriteInt(form.scheme);
1039 pickle->WriteString(form.origin.spec());
1040 pickle->WriteString(form.action.spec());
1041 pickle->WriteString16(form.username_element);
1042 pickle->WriteString16(form.username_value);
1043 pickle->WriteString16(form.password_element);
1044 pickle->WriteString16(form.password_value);
1045 pickle->WriteString16(form.submit_element);
1046 pickle->WriteBool(form.ssl_valid);
1047 pickle->WriteBool(form.preferred);
1048 pickle->WriteBool(form.blacklisted_by_user);
1049 pickle->WriteInt64(form.date_created.ToTimeT());
1052 void NativeBackendKWalletPickleTest::CheckVersion0Pickle(
1053 bool size_32, PasswordForm::Scheme scheme) {
1054 Pickle pickle;
1055 PasswordForm form = form_google_;
1056 form.scheme = scheme;
1057 CreateVersion0Pickle(size_32, form, &pickle);
1058 std::vector<PasswordForm*> form_list;
1059 NativeBackendKWalletStub::DeserializeValue(form.signon_realm,
1060 pickle, &form_list);
1061 EXPECT_EQ(1u, form_list.size());
1062 if (form_list.size() > 0)
1063 CheckPasswordForm(form, *form_list[0]);
1064 STLDeleteElements(&form_list);
1067 // We try both SCHEME_HTML and SCHEME_BASIC since the scheme is stored right
1068 // after the size in the pickle, so it's what gets read as part of the count
1069 // when reading 32-bit pickles on 64-bit systems. SCHEME_HTML is 0 (so we'll
1070 // detect errors later) while SCHEME_BASIC is 1 (so we'll detect it then). We
1071 // try both 32-bit and 64-bit pickles since only one will be the "other" size
1072 // for whatever architecture we're running on, but we want to make sure we can
1073 // read all combinations in any event.
1075 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTMLPickles) {
1076 CheckVersion0Pickle(true, PasswordForm::SCHEME_HTML);
1079 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTTPPickles) {
1080 CheckVersion0Pickle(true, PasswordForm::SCHEME_BASIC);
1083 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTMLPickles) {
1084 CheckVersion0Pickle(false, PasswordForm::SCHEME_HTML);
1087 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTTPPickles) {
1088 CheckVersion0Pickle(false, PasswordForm::SCHEME_BASIC);