Upstream version 8.37.180.0
[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/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"
28
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;
37
38 namespace {
39
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.
45
46   TestKWallet() : reject_local_folders_(false) {}
47
48   void set_reject_local_folders(bool value) { reject_local_folders_ = value; }
49
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.
52
53   // Check for presence of a given password folder.
54   bool hasFolder(const std::string& folder) const {
55     return data_.find(folder) != data_.end();
56   }
57
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();
62   }
63
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;
73   }
74
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;
84   }
85
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;
91   }
92
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;
98   }
99
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;
107   }
108
109  private:
110   typedef std::map<std::string, Blob> Folder;
111   typedef std::map<std::string, Folder> Data;
112
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_;
118
119   // No need to disallow copy and assign. This class is safe to copy and assign.
120 };
121
122 }  // anonymous namespace
123
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;
127
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) {
133   }
134   using NativeBackendKWallet::InitWithBus;
135   using NativeBackendKWallet::kInvalidKWalletHandle;
136   using NativeBackendKWallet::DeserializeValue;
137 };
138
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";
151
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
159     form_isc_.origin = GURL("http://www.isc.org/");
160     form_isc_.action = GURL("http://www.isc.org/auth");
161     form_isc_.username_element = UTF8ToUTF16("id");
162     form_isc_.username_value = UTF8ToUTF16("janedoe");
163     form_isc_.password_element = UTF8ToUTF16("passwd");
164     form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
165     form_isc_.submit_element = UTF8ToUTF16("login");
166     form_isc_.signon_realm = "ISC";
167     form_isc_.date_synced = base::Time::Now();
168   }
169
170   static void CheckPasswordForm(const PasswordForm& expected,
171                                 const PasswordForm& actual);
172   static void CheckPasswordChanges(const PasswordStoreChangeList& expected,
173                                    const PasswordStoreChangeList& actual);
174   static void CheckPasswordChangesWithResult(
175       const PasswordStoreChangeList* expected,
176       const PasswordStoreChangeList* actual,
177       bool result);
178
179   PasswordForm old_form_google_;
180   PasswordForm form_google_;
181   PasswordForm form_isc_;
182 };
183
184 // static
185 void NativeBackendKWalletTestBase::CheckPasswordForm(
186     const PasswordForm& expected, const PasswordForm& actual) {
187   EXPECT_EQ(expected.origin, actual.origin);
188   EXPECT_EQ(expected.password_value, actual.password_value);
189   EXPECT_EQ(expected.action, actual.action);
190   EXPECT_EQ(expected.username_element, actual.username_element);
191   EXPECT_EQ(expected.username_value, actual.username_value);
192   EXPECT_EQ(expected.password_element, actual.password_element);
193   EXPECT_EQ(expected.submit_element, actual.submit_element);
194   EXPECT_EQ(expected.signon_realm, actual.signon_realm);
195   EXPECT_EQ(expected.ssl_valid, actual.ssl_valid);
196   EXPECT_EQ(expected.preferred, actual.preferred);
197   // We don't check the date created. It varies.
198   EXPECT_EQ(expected.blacklisted_by_user, actual.blacklisted_by_user);
199   EXPECT_EQ(expected.type, actual.type);
200   EXPECT_EQ(expected.times_used, actual.times_used);
201   EXPECT_EQ(expected.scheme, actual.scheme);
202   EXPECT_EQ(expected.date_synced, actual.date_synced);
203 }
204
205 // static
206 void NativeBackendKWalletTestBase::CheckPasswordChanges(
207     const PasswordStoreChangeList& expected,
208     const PasswordStoreChangeList& actual) {
209   ASSERT_EQ(expected.size(), actual.size());
210   for (size_t i = 0; i < expected.size(); ++i) {
211     EXPECT_EQ(expected[i].type(), actual[i].type());
212     CheckPasswordForm(expected[i].form(), actual[i].form());
213   }
214 }
215
216 // static
217 void NativeBackendKWalletTestBase::CheckPasswordChangesWithResult(
218     const PasswordStoreChangeList* expected,
219     const PasswordStoreChangeList* actual,
220     bool result) {
221   EXPECT_TRUE(result);
222   CheckPasswordChanges(*expected, *actual);
223 }
224
225 class NativeBackendKWalletTest : public NativeBackendKWalletTestBase {
226  protected:
227   NativeBackendKWalletTest()
228       : ui_thread_(BrowserThread::UI, &message_loop_),
229         db_thread_(BrowserThread::DB), klauncher_ret_(0),
230         klauncher_contacted_(false), kwallet_runnable_(true),
231         kwallet_running_(true), kwallet_enabled_(true) {
232   }
233
234   virtual void SetUp();
235   virtual void TearDown();
236
237   // Let the DB thread run to completion of all current tasks.
238   void RunDBThread() {
239     base::WaitableEvent event(false, false);
240     BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
241                             base::Bind(ThreadDone, &event));
242     event.Wait();
243     // Some of the tests may post messages to the UI thread, but we don't need
244     // to run those until after the DB thread is finished. So run it here.
245     message_loop_.RunUntilIdle();
246   }
247   static void ThreadDone(base::WaitableEvent* event) {
248     event->Signal();
249   }
250
251   // Utilities to help verify sets of expectations.
252   typedef std::vector<
253               std::pair<std::string,
254                         std::vector<const PasswordForm*> > > ExpectationArray;
255   void CheckPasswordForms(const std::string& folder,
256                           const ExpectationArray& sorted_expected);
257
258   enum RemoveBetweenMethod {
259     CREATED,
260     SYNCED,
261   };
262
263   // Tests RemoveLoginsCreatedBetween or RemoveLoginsSyncedBetween.
264   void TestRemoveLoginsBetween(RemoveBetweenMethod date_to_test);
265
266   base::MessageLoopForUI message_loop_;
267   content::TestBrowserThread ui_thread_;
268   content::TestBrowserThread db_thread_;
269
270   scoped_refptr<dbus::MockBus> mock_session_bus_;
271   scoped_refptr<dbus::MockObjectProxy> mock_klauncher_proxy_;
272   scoped_refptr<dbus::MockObjectProxy> mock_kwallet_proxy_;
273
274   int klauncher_ret_;
275   std::string klauncher_error_;
276   bool klauncher_contacted_;
277
278   bool kwallet_runnable_;
279   bool kwallet_running_;
280   bool kwallet_enabled_;
281
282   TestKWallet wallet_;
283
284  private:
285   dbus::Response* KLauncherMethodCall(
286       dbus::MethodCall* method_call, testing::Unused);
287
288   dbus::Response* KWalletMethodCall(
289       dbus::MethodCall* method_call, testing::Unused);
290 };
291
292 void NativeBackendKWalletTest::SetUp() {
293   ASSERT_TRUE(db_thread_.Start());
294
295   dbus::Bus::Options options;
296   options.bus_type = dbus::Bus::SESSION;
297   mock_session_bus_ = new dbus::MockBus(options);
298
299   mock_klauncher_proxy_ =
300       new dbus::MockObjectProxy(mock_session_bus_.get(),
301                                 "org.kde.klauncher",
302                                 dbus::ObjectPath("/KLauncher"));
303   EXPECT_CALL(*mock_klauncher_proxy_.get(), MockCallMethodAndBlock(_, _))
304       .WillRepeatedly(
305            Invoke(this, &NativeBackendKWalletTest::KLauncherMethodCall));
306
307   mock_kwallet_proxy_ =
308       new dbus::MockObjectProxy(mock_session_bus_.get(),
309                                 "org.kde.kwalletd",
310                                 dbus::ObjectPath("/modules/kwalletd"));
311   EXPECT_CALL(*mock_kwallet_proxy_.get(), MockCallMethodAndBlock(_, _))
312       .WillRepeatedly(
313            Invoke(this, &NativeBackendKWalletTest::KWalletMethodCall));
314
315   EXPECT_CALL(
316       *mock_session_bus_.get(),
317       GetObjectProxy("org.kde.klauncher", dbus::ObjectPath("/KLauncher")))
318       .WillRepeatedly(Return(mock_klauncher_proxy_.get()));
319   EXPECT_CALL(
320       *mock_session_bus_.get(),
321       GetObjectProxy("org.kde.kwalletd", dbus::ObjectPath("/modules/kwalletd")))
322       .WillRepeatedly(Return(mock_kwallet_proxy_.get()));
323
324   EXPECT_CALL(*mock_session_bus_.get(), ShutdownAndBlock()).WillOnce(Return())
325       .WillRepeatedly(Return());
326 }
327
328 void NativeBackendKWalletTest::TearDown() {
329   base::MessageLoop::current()->PostTask(FROM_HERE,
330                                          base::MessageLoop::QuitClosure());
331   base::MessageLoop::current()->Run();
332   db_thread_.Stop();
333 }
334
335 void NativeBackendKWalletTest::TestRemoveLoginsBetween(
336     RemoveBetweenMethod date_to_test) {
337   NativeBackendKWalletStub backend(42);
338   EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
339
340   form_google_.date_synced = base::Time();
341   form_isc_.date_synced = base::Time();
342   form_google_.date_created = base::Time();
343   form_isc_.date_created = base::Time();
344   base::Time now = base::Time::Now();
345   base::Time next_day = now + base::TimeDelta::FromDays(1);
346   if (date_to_test == CREATED) {
347     // crbug/374132. Remove the next line once it's fixed.
348     next_day = base::Time::FromTimeT(next_day.ToTimeT());
349     form_google_.date_created = now;
350     form_isc_.date_created = next_day;
351   } else {
352     form_google_.date_synced = now;
353     form_isc_.date_synced = next_day;
354   }
355
356   BrowserThread::PostTask(
357       BrowserThread::DB,
358       FROM_HERE,
359       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
360                  base::Unretained(&backend),
361                  form_google_));
362   BrowserThread::PostTask(
363       BrowserThread::DB,
364       FROM_HERE,
365       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
366                  base::Unretained(&backend),
367                  form_isc_));
368
369   PasswordStoreChangeList expected_changes;
370   expected_changes.push_back(
371       PasswordStoreChange(PasswordStoreChange::REMOVE, form_google_));
372   PasswordStoreChangeList changes;
373   bool (NativeBackendKWallet::*method)(
374       base::Time, base::Time, password_manager::PasswordStoreChangeList*) =
375       date_to_test == CREATED
376           ? &NativeBackendKWalletStub::RemoveLoginsCreatedBetween
377           : &NativeBackendKWalletStub::RemoveLoginsSyncedBetween;
378   BrowserThread::PostTaskAndReplyWithResult(
379       BrowserThread::DB,
380       FROM_HERE,
381       base::Bind(
382           method, base::Unretained(&backend), base::Time(), next_day, &changes),
383       base::Bind(&NativeBackendKWalletTest::CheckPasswordChangesWithResult,
384                  &expected_changes,
385                  &changes));
386   RunDBThread();
387
388   std::vector<const PasswordForm*> forms;
389   forms.push_back(&form_isc_);
390   ExpectationArray expected;
391   expected.push_back(make_pair(std::string(form_isc_.signon_realm), forms));
392   CheckPasswordForms("Chrome Form Data (42)", expected);
393
394   // Remove form_isc_.
395   expected_changes.clear();
396   expected_changes.push_back(
397       PasswordStoreChange(PasswordStoreChange::REMOVE, form_isc_));
398   BrowserThread::PostTaskAndReplyWithResult(
399       BrowserThread::DB,
400       FROM_HERE,
401       base::Bind(
402           method, base::Unretained(&backend), next_day, base::Time(), &changes),
403       base::Bind(&NativeBackendKWalletTest::CheckPasswordChangesWithResult,
404                  &expected_changes,
405                  &changes));
406   RunDBThread();
407
408   CheckPasswordForms("Chrome Form Data (42)", ExpectationArray());
409 }
410
411 dbus::Response* NativeBackendKWalletTest::KLauncherMethodCall(
412     dbus::MethodCall* method_call, testing::Unused) {
413   EXPECT_EQ("org.kde.KLauncher", method_call->GetInterface());
414   EXPECT_EQ("start_service_by_desktop_name", method_call->GetMember());
415
416   klauncher_contacted_ = true;
417
418   dbus::MessageReader reader(method_call);
419   std::string service_name;
420   std::vector<std::string> urls;
421   std::vector<std::string> envs;
422   std::string startup_id;
423   bool blind = false;
424
425   EXPECT_TRUE(reader.PopString(&service_name));
426   EXPECT_TRUE(reader.PopArrayOfStrings(&urls));
427   EXPECT_TRUE(reader.PopArrayOfStrings(&envs));
428   EXPECT_TRUE(reader.PopString(&startup_id));
429   EXPECT_TRUE(reader.PopBool(&blind));
430
431   EXPECT_EQ("kwalletd", service_name);
432   EXPECT_TRUE(urls.empty());
433   EXPECT_TRUE(envs.empty());
434   EXPECT_TRUE(startup_id.empty());
435   EXPECT_FALSE(blind);
436
437   if (kwallet_runnable_)
438     kwallet_running_ = true;
439
440   scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
441   dbus::MessageWriter writer(response.get());
442   writer.AppendInt32(klauncher_ret_);
443   writer.AppendString(std::string());  // dbus_name
444   writer.AppendString(klauncher_error_);
445   writer.AppendInt32(1234);  // pid
446   return response.release();
447 }
448
449 dbus::Response* NativeBackendKWalletTest::KWalletMethodCall(
450     dbus::MethodCall* method_call, testing::Unused) {
451   if (!kwallet_running_)
452     return NULL;
453   EXPECT_EQ("org.kde.KWallet", method_call->GetInterface());
454
455   scoped_ptr<dbus::Response> response;
456   if (method_call->GetMember() == "isEnabled") {
457     response = dbus::Response::CreateEmpty();
458     dbus::MessageWriter writer(response.get());
459     writer.AppendBool(kwallet_enabled_);
460   } else if (method_call->GetMember() == "networkWallet") {
461     response = dbus::Response::CreateEmpty();
462     dbus::MessageWriter writer(response.get());
463     writer.AppendString("test_wallet");  // Should match |open| below.
464   } else if (method_call->GetMember() == "open") {
465     dbus::MessageReader reader(method_call);
466     std::string wallet_name;
467     int64_t wallet_id;
468     std::string app_name;
469     EXPECT_TRUE(reader.PopString(&wallet_name));
470     EXPECT_TRUE(reader.PopInt64(&wallet_id));
471     EXPECT_TRUE(reader.PopString(&app_name));
472     EXPECT_EQ("test_wallet", wallet_name);  // Should match |networkWallet|.
473     response = dbus::Response::CreateEmpty();
474     dbus::MessageWriter writer(response.get());
475     writer.AppendInt32(1);  // Can be anything but kInvalidKWalletHandle.
476   } else if (method_call->GetMember() == "hasFolder" ||
477              method_call->GetMember() == "createFolder") {
478     dbus::MessageReader reader(method_call);
479     int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
480     std::string folder_name;
481     std::string app_name;
482     EXPECT_TRUE(reader.PopInt32(&handle));
483     EXPECT_TRUE(reader.PopString(&folder_name));
484     EXPECT_TRUE(reader.PopString(&app_name));
485     EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
486     response = dbus::Response::CreateEmpty();
487     dbus::MessageWriter writer(response.get());
488     if (method_call->GetMember() == "hasFolder")
489       writer.AppendBool(wallet_.hasFolder(folder_name));
490     else
491       writer.AppendBool(wallet_.createFolder(folder_name));
492   } else if (method_call->GetMember() == "hasEntry" ||
493              method_call->GetMember() == "removeEntry") {
494     dbus::MessageReader reader(method_call);
495     int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
496     std::string folder_name;
497     std::string key;
498     std::string app_name;
499     EXPECT_TRUE(reader.PopInt32(&handle));
500     EXPECT_TRUE(reader.PopString(&folder_name));
501     EXPECT_TRUE(reader.PopString(&key));
502     EXPECT_TRUE(reader.PopString(&app_name));
503     EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
504     response = dbus::Response::CreateEmpty();
505     dbus::MessageWriter writer(response.get());
506     if (method_call->GetMember() == "hasEntry")
507       writer.AppendBool(wallet_.hasEntry(folder_name, key));
508     else
509       writer.AppendInt32(wallet_.removeEntry(folder_name, key) ? 0 : 1);
510   } else if (method_call->GetMember() == "entryList") {
511     dbus::MessageReader reader(method_call);
512     int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
513     std::string folder_name;
514     std::string app_name;
515     EXPECT_TRUE(reader.PopInt32(&handle));
516     EXPECT_TRUE(reader.PopString(&folder_name));
517     EXPECT_TRUE(reader.PopString(&app_name));
518     EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
519     std::vector<std::string> entries;
520     if (wallet_.entryList(folder_name, &entries)) {
521       response = dbus::Response::CreateEmpty();
522       dbus::MessageWriter writer(response.get());
523       writer.AppendArrayOfStrings(entries);
524     }
525   } else if (method_call->GetMember() == "readEntry") {
526     dbus::MessageReader reader(method_call);
527     int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
528     std::string folder_name;
529     std::string key;
530     std::string app_name;
531     EXPECT_TRUE(reader.PopInt32(&handle));
532     EXPECT_TRUE(reader.PopString(&folder_name));
533     EXPECT_TRUE(reader.PopString(&key));
534     EXPECT_TRUE(reader.PopString(&app_name));
535     EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
536     TestKWallet::Blob value;
537     if (wallet_.readEntry(folder_name, key, &value)) {
538       response = dbus::Response::CreateEmpty();
539       dbus::MessageWriter writer(response.get());
540       writer.AppendArrayOfBytes(value.data(), value.size());
541     }
542   } else if (method_call->GetMember() == "writeEntry") {
543     dbus::MessageReader reader(method_call);
544     int handle = NativeBackendKWalletStub::kInvalidKWalletHandle;
545     std::string folder_name;
546     std::string key;
547     const uint8_t* bytes = NULL;
548     size_t length = 0;
549     std::string app_name;
550     EXPECT_TRUE(reader.PopInt32(&handle));
551     EXPECT_TRUE(reader.PopString(&folder_name));
552     EXPECT_TRUE(reader.PopString(&key));
553     EXPECT_TRUE(reader.PopArrayOfBytes(&bytes, &length));
554     EXPECT_TRUE(reader.PopString(&app_name));
555     EXPECT_NE(NativeBackendKWalletStub::kInvalidKWalletHandle, handle);
556     response = dbus::Response::CreateEmpty();
557     dbus::MessageWriter writer(response.get());
558     writer.AppendInt32(
559         wallet_.writeEntry(folder_name, key,
560                            TestKWallet::Blob(bytes, length)) ? 0 : 1);
561   }
562
563   EXPECT_FALSE(response.get() == NULL);
564   return response.release();
565 }
566
567 void NativeBackendKWalletTest::CheckPasswordForms(
568     const std::string& folder, const ExpectationArray& sorted_expected) {
569   EXPECT_TRUE(wallet_.hasFolder(folder));
570   std::vector<std::string> entries;
571   EXPECT_TRUE(wallet_.entryList(folder, &entries));
572   EXPECT_EQ(sorted_expected.size(), entries.size());
573   std::sort(entries.begin(), entries.end());
574   for (size_t i = 0; i < entries.size() && i < sorted_expected.size(); ++i) {
575     EXPECT_EQ(sorted_expected[i].first, entries[i]);
576     TestKWallet::Blob value;
577     EXPECT_TRUE(wallet_.readEntry(folder, entries[i], &value));
578     Pickle pickle(reinterpret_cast<const char*>(value.data()), value.size());
579     std::vector<PasswordForm*> forms;
580     NativeBackendKWalletStub::DeserializeValue(entries[i], pickle, &forms);
581     const std::vector<const PasswordForm*>& expect = sorted_expected[i].second;
582     EXPECT_EQ(expect.size(), forms.size());
583     for (size_t j = 0; j < forms.size() && j < expect.size(); ++j)
584       CheckPasswordForm(*expect[j], *forms[j]);
585     STLDeleteElements(&forms);
586   }
587 }
588
589 TEST_F(NativeBackendKWalletTest, NotEnabled) {
590   NativeBackendKWalletStub kwallet(42);
591   kwallet_enabled_ = false;
592   EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
593   EXPECT_FALSE(klauncher_contacted_);
594 }
595
596 TEST_F(NativeBackendKWalletTest, NotRunnable) {
597   NativeBackendKWalletStub kwallet(42);
598   kwallet_runnable_ = false;
599   kwallet_running_ = false;
600   EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
601   EXPECT_TRUE(klauncher_contacted_);
602 }
603
604 TEST_F(NativeBackendKWalletTest, NotRunningOrEnabled) {
605   NativeBackendKWalletStub kwallet(42);
606   kwallet_running_ = false;
607   kwallet_enabled_ = false;
608   EXPECT_FALSE(kwallet.InitWithBus(mock_session_bus_));
609   EXPECT_TRUE(klauncher_contacted_);
610 }
611
612 TEST_F(NativeBackendKWalletTest, NotRunning) {
613   NativeBackendKWalletStub kwallet(42);
614   kwallet_running_ = false;
615   EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_));
616   EXPECT_TRUE(klauncher_contacted_);
617 }
618
619 TEST_F(NativeBackendKWalletTest, BasicStartup) {
620   NativeBackendKWalletStub kwallet(42);
621   EXPECT_TRUE(kwallet.InitWithBus(mock_session_bus_));
622   EXPECT_FALSE(klauncher_contacted_);
623 }
624
625 TEST_F(NativeBackendKWalletTest, BasicAddLogin) {
626   NativeBackendKWalletStub backend(42);
627   EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
628
629   BrowserThread::PostTask(
630       BrowserThread::DB, FROM_HERE,
631       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
632                  base::Unretained(&backend), form_google_));
633
634   RunDBThread();
635
636   EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
637
638   std::vector<const PasswordForm*> forms;
639   forms.push_back(&form_google_);
640   ExpectationArray expected;
641   expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
642   CheckPasswordForms("Chrome Form Data (42)", expected);
643 }
644
645 TEST_F(NativeBackendKWalletTest, BasicUpdateLogin) {
646   NativeBackendKWalletStub backend(42);
647   EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
648
649   BrowserThread::PostTask(
650       BrowserThread::DB, FROM_HERE,
651       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
652                  base::Unretained(&backend), form_google_));
653
654   RunDBThread();
655
656   PasswordForm new_form_google(form_google_);
657   new_form_google.times_used = 10;
658   new_form_google.action = GURL("http://www.google.com/different/login");
659
660   // Update login
661   PasswordStoreChangeList changes;
662   BrowserThread::PostTask(
663       BrowserThread::DB, FROM_HERE,
664       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::UpdateLogin),
665                  base::Unretained(&backend),
666                  new_form_google,
667                  base::Unretained(&changes)));
668   RunDBThread();
669
670   ASSERT_EQ(1u, changes.size());
671   EXPECT_EQ(PasswordStoreChange::UPDATE, changes.front().type());
672   EXPECT_EQ(new_form_google, changes.front().form());
673
674   std::vector<const PasswordForm*> forms;
675   forms.push_back(&new_form_google);
676   ExpectationArray expected;
677   expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
678   CheckPasswordForms("Chrome Form Data (42)", expected);
679 }
680
681 TEST_F(NativeBackendKWalletTest, BasicListLogins) {
682   NativeBackendKWalletStub backend(42);
683   EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
684
685   BrowserThread::PostTask(
686       BrowserThread::DB, FROM_HERE,
687       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
688                  base::Unretained(&backend), form_google_));
689
690   std::vector<PasswordForm*> form_list;
691   BrowserThread::PostTask(
692       BrowserThread::DB, FROM_HERE,
693       base::Bind(
694           base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
695           base::Unretained(&backend), &form_list));
696
697   RunDBThread();
698
699   // Quick check that we got something back.
700   EXPECT_EQ(1u, form_list.size());
701   STLDeleteElements(&form_list);
702
703   EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
704
705   std::vector<const PasswordForm*> forms;
706   forms.push_back(&form_google_);
707   ExpectationArray expected;
708   expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
709   CheckPasswordForms("Chrome Form Data (42)", expected);
710 }
711
712 TEST_F(NativeBackendKWalletTest, BasicRemoveLogin) {
713   NativeBackendKWalletStub backend(42);
714   EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
715
716   BrowserThread::PostTask(
717       BrowserThread::DB, FROM_HERE,
718       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
719                  base::Unretained(&backend), form_google_));
720
721   RunDBThread();
722
723   EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
724
725   std::vector<const PasswordForm*> forms;
726   forms.push_back(&form_google_);
727   ExpectationArray expected;
728   expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
729   CheckPasswordForms("Chrome Form Data (42)", expected);
730
731   BrowserThread::PostTask(
732       BrowserThread::DB, FROM_HERE,
733       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin),
734                  base::Unretained(&backend), form_google_));
735
736   RunDBThread();
737
738   expected.clear();
739   CheckPasswordForms("Chrome Form Data (42)", expected);
740 }
741
742 TEST_F(NativeBackendKWalletTest, UpdateNonexistentLogin) {
743   NativeBackendKWalletStub backend(42);
744   EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
745
746   // First add an unrelated login.
747   BrowserThread::PostTask(
748       BrowserThread::DB, FROM_HERE,
749       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
750                  base::Unretained(&backend), form_google_));
751
752   RunDBThread();
753
754   std::vector<const PasswordForm*> forms;
755   forms.push_back(&form_google_);
756   ExpectationArray expected;
757   expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
758   CheckPasswordForms("Chrome Form Data (42)", expected);
759
760   // Attempt to update a login that doesn't exist.
761   PasswordStoreChangeList changes;
762   BrowserThread::PostTask(
763       BrowserThread::DB, FROM_HERE,
764       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::UpdateLogin),
765                  base::Unretained(&backend),
766                  form_isc_,
767                  base::Unretained(&changes)));
768
769   RunDBThread();
770
771   EXPECT_EQ(PasswordStoreChangeList(), changes);
772   CheckPasswordForms("Chrome Form Data (42)", expected);
773 }
774
775 TEST_F(NativeBackendKWalletTest, RemoveNonexistentLogin) {
776   NativeBackendKWalletStub backend(42);
777   EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
778
779   // First add an unrelated login.
780   BrowserThread::PostTask(
781       BrowserThread::DB, FROM_HERE,
782       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
783                  base::Unretained(&backend), form_google_));
784
785   RunDBThread();
786
787   EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
788
789   std::vector<const PasswordForm*> forms;
790   forms.push_back(&form_google_);
791   ExpectationArray expected;
792   expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
793   CheckPasswordForms("Chrome Form Data (42)", expected);
794
795   // Attempt to remove a login that doesn't exist.
796   BrowserThread::PostTask(
797       BrowserThread::DB, FROM_HERE,
798       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::RemoveLogin),
799                  base::Unretained(&backend), form_isc_));
800
801   // Make sure we can still get the first form back.
802   std::vector<PasswordForm*> form_list;
803   BrowserThread::PostTask(
804       BrowserThread::DB, FROM_HERE,
805       base::Bind(
806           base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
807           base::Unretained(&backend), &form_list));
808
809   RunDBThread();
810
811   // Quick check that we got something back.
812   EXPECT_EQ(1u, form_list.size());
813   STLDeleteElements(&form_list);
814
815   CheckPasswordForms("Chrome Form Data (42)", expected);
816 }
817
818 TEST_F(NativeBackendKWalletTest, AddDuplicateLogin) {
819   NativeBackendKWalletStub backend(42);
820   EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
821
822   PasswordStoreChangeList changes;
823   changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
824                                         form_google_));
825   BrowserThread::PostTaskAndReplyWithResult(
826       BrowserThread::DB, FROM_HERE,
827       base::Bind(&NativeBackendKWalletStub::AddLogin,
828                  base::Unretained(&backend), form_google_),
829       base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges,
830                  changes));
831
832   changes.clear();
833   changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
834                                         form_google_));
835   form_google_.times_used++;
836   changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
837                                         form_google_));
838
839   BrowserThread::PostTaskAndReplyWithResult(
840       BrowserThread::DB, FROM_HERE,
841       base::Bind(&NativeBackendKWalletStub::AddLogin,
842                  base::Unretained(&backend), form_google_),
843       base::Bind(&NativeBackendKWalletTest::CheckPasswordChanges,
844                  changes));
845
846   RunDBThread();
847
848   EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
849
850   std::vector<const PasswordForm*> forms;
851   forms.push_back(&form_google_);
852   ExpectationArray expected;
853   expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
854   CheckPasswordForms("Chrome Form Data (42)", expected);
855 }
856
857 TEST_F(NativeBackendKWalletTest, ListLoginsAppends) {
858   NativeBackendKWalletStub backend(42);
859   EXPECT_TRUE(backend.InitWithBus(mock_session_bus_));
860
861   BrowserThread::PostTask(
862       BrowserThread::DB, FROM_HERE,
863       base::Bind(base::IgnoreResult(&NativeBackendKWalletStub::AddLogin),
864                  base::Unretained(&backend), form_google_));
865
866   // Send the same request twice with the same list both times.
867   std::vector<PasswordForm*> form_list;
868   BrowserThread::PostTask(
869       BrowserThread::DB, FROM_HERE,
870       base::Bind(
871           base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
872           base::Unretained(&backend), &form_list));
873   BrowserThread::PostTask(
874       BrowserThread::DB, FROM_HERE,
875       base::Bind(
876           base::IgnoreResult(&NativeBackendKWalletStub::GetAutofillableLogins),
877           base::Unretained(&backend), &form_list));
878
879   RunDBThread();
880
881   // Quick check that we got two results back.
882   EXPECT_EQ(2u, form_list.size());
883   STLDeleteElements(&form_list);
884
885   EXPECT_FALSE(wallet_.hasFolder("Chrome Form Data"));
886
887   std::vector<const PasswordForm*> forms;
888   forms.push_back(&form_google_);
889   ExpectationArray expected;
890   expected.push_back(make_pair(std::string(form_google_.signon_realm), forms));
891   CheckPasswordForms("Chrome Form Data (42)", expected);
892 }
893
894 TEST_F(NativeBackendKWalletTest, RemoveLoginsCreatedBetween) {
895   TestRemoveLoginsBetween(CREATED);
896 }
897
898 TEST_F(NativeBackendKWalletTest, RemoveLoginsSyncedBetween) {
899   TestRemoveLoginsBetween(SYNCED);
900 }
901
902 // TODO(mdm): add more basic tests here at some point.
903 // (For example tests for storing >1 password per realm pickle.)
904
905 class NativeBackendKWalletPickleTest : public NativeBackendKWalletTestBase {
906  protected:
907   void CreateVersion2Pickle(const PasswordForm& form, Pickle* pickle);
908   void CreateVersion1Pickle(const PasswordForm& form, Pickle* pickle);
909   void CreateVersion0Pickle(bool size_32,
910                             const PasswordForm& form,
911                             Pickle* pickle);
912   void CheckVersion2Pickle();
913   void CheckVersion1Pickle();
914   void CheckVersion0Pickle(bool size_32, PasswordForm::Scheme scheme);
915
916  private:
917   void CreatePickle(bool size_32, const PasswordForm& form, Pickle* pickle);
918 };
919
920 void NativeBackendKWalletPickleTest::CreateVersion2Pickle(
921     const PasswordForm& form, Pickle* pickle) {
922   pickle->WriteInt(2);
923   CreatePickle(false, form, pickle);
924   pickle->WriteInt(form.type);
925   pickle->WriteInt(form.times_used);
926   autofill::SerializeFormData(form.form_data, pickle);
927 }
928
929 void NativeBackendKWalletPickleTest::CreateVersion1Pickle(
930     const PasswordForm& form, Pickle* pickle) {
931   pickle->WriteInt(1);
932   CreatePickle(false, form, pickle);
933 }
934
935 void NativeBackendKWalletPickleTest::CreateVersion0Pickle(
936     bool size_32, const PasswordForm& form, Pickle* pickle) {
937   pickle->WriteInt(0);
938   CreatePickle(size_32, form, pickle);
939 }
940
941 void NativeBackendKWalletPickleTest::CreatePickle(
942     bool size_32, const PasswordForm& form, Pickle* pickle) {
943   if (size_32)
944     pickle->WriteUInt32(1);  // Size of form list. 32 bits.
945   else
946     pickle->WriteUInt64(1);  // Size of form list. 64 bits.
947   pickle->WriteInt(form.scheme);
948   pickle->WriteString(form.origin.spec());
949   pickle->WriteString(form.action.spec());
950   pickle->WriteString16(form.username_element);
951   pickle->WriteString16(form.username_value);
952   pickle->WriteString16(form.password_element);
953   pickle->WriteString16(form.password_value);
954   pickle->WriteString16(form.submit_element);
955   pickle->WriteBool(form.ssl_valid);
956   pickle->WriteBool(form.preferred);
957   pickle->WriteBool(form.blacklisted_by_user);
958   pickle->WriteInt64(form.date_created.ToTimeT());
959 }
960
961 void NativeBackendKWalletPickleTest::CheckVersion2Pickle() {
962   Pickle pickle;
963   PasswordForm form = form_google_;
964   form.date_synced = base::Time();
965   CreateVersion2Pickle(form, &pickle);
966
967   std::vector<PasswordForm*> form_list;
968   NativeBackendKWalletStub::DeserializeValue(form.signon_realm,
969                                              pickle, &form_list);
970
971   // This will match |old_form_google_| because not all the fields present in
972   // |form_google_| will be deserialized.
973   EXPECT_EQ(1u, form_list.size());
974   if (form_list.size() > 0)
975     CheckPasswordForm(form, *form_list[0]);
976   STLDeleteElements(&form_list);
977 }
978
979 // Make sure that we can still read version 1 pickles.
980 void NativeBackendKWalletPickleTest::CheckVersion1Pickle() {
981   Pickle pickle;
982   PasswordForm form = form_google_;
983   CreateVersion1Pickle(form, &pickle);
984
985   std::vector<PasswordForm*> form_list;
986   NativeBackendKWalletStub::DeserializeValue(form.signon_realm,
987                                              pickle, &form_list);
988
989   // This will match |old_form_google_| because not all the fields present in
990   // |form_google_| will be deserialized.
991   EXPECT_EQ(1u, form_list.size());
992   if (form_list.size() > 0)
993     CheckPasswordForm(old_form_google_, *form_list[0]);
994   STLDeleteElements(&form_list);
995 }
996
997 void NativeBackendKWalletPickleTest::CheckVersion0Pickle(
998     bool size_32, PasswordForm::Scheme scheme) {
999   Pickle pickle;
1000   PasswordForm form = old_form_google_;
1001   form.scheme = scheme;
1002   CreateVersion0Pickle(size_32, form, &pickle);
1003   std::vector<PasswordForm*> form_list;
1004   NativeBackendKWalletStub::DeserializeValue(form.signon_realm,
1005                                              pickle, &form_list);
1006   EXPECT_EQ(1u, form_list.size());
1007   if (form_list.size() > 0)
1008     CheckPasswordForm(form, *form_list[0]);
1009   STLDeleteElements(&form_list);
1010 }
1011
1012 // We try both SCHEME_HTML and SCHEME_BASIC since the scheme is stored right
1013 // after the size in the pickle, so it's what gets read as part of the count
1014 // when reading 32-bit pickles on 64-bit systems. SCHEME_HTML is 0 (so we'll
1015 // detect errors later) while SCHEME_BASIC is 1 (so we'll detect it then). We
1016 // try both 32-bit and 64-bit pickles since only one will be the "other" size
1017 // for whatever architecture we're running on, but we want to make sure we can
1018 // read all combinations in any event.
1019
1020 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTMLPickles) {
1021   CheckVersion0Pickle(true, PasswordForm::SCHEME_HTML);
1022 }
1023
1024 TEST_F(NativeBackendKWalletPickleTest, ReadsOld32BitHTTPPickles) {
1025   CheckVersion0Pickle(true, PasswordForm::SCHEME_BASIC);
1026 }
1027
1028 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTMLPickles) {
1029   CheckVersion0Pickle(false, PasswordForm::SCHEME_HTML);
1030 }
1031
1032 TEST_F(NativeBackendKWalletPickleTest, ReadsOld64BitHTTPPickles) {
1033   CheckVersion0Pickle(false, PasswordForm::SCHEME_BASIC);
1034 }
1035
1036 TEST_F(NativeBackendKWalletPickleTest, CheckVersion1Pickle) {
1037   CheckVersion1Pickle();
1038 }
1039
1040 TEST_F(NativeBackendKWalletPickleTest, CheckVersion2Pickle) {
1041   CheckVersion2Pickle();
1042 }