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