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