- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / password_manager / password_store_mac_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 "testing/gmock/include/gmock/gmock.h"
6 #include "testing/gtest/include/gtest/gtest.h"
7
8 #include "base/basictypes.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/path_service.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/password_manager/password_store_consumer.h"
15 #include "chrome/browser/password_manager/password_store_mac.h"
16 #include "chrome/browser/password_manager/password_store_mac_internal.h"
17 #include "chrome/common/chrome_paths.h"
18 #include "content/public/test/test_browser_thread.h"
19 #include "crypto/mock_apple_keychain.h"
20
21 using autofill::PasswordForm;
22 using content::BrowserThread;
23 using crypto::MockAppleKeychain;
24 using testing::_;
25 using testing::DoAll;
26 using testing::WithArg;
27
28 namespace {
29
30 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
31  public:
32   MOCK_METHOD2(OnPasswordStoreRequestDone,
33                void(CancelableRequestProvider::Handle,
34                     const std::vector<autofill::PasswordForm*>&));
35   MOCK_METHOD1(OnGetPasswordStoreResults,
36                void(const std::vector<autofill::PasswordForm*>&));
37 };
38
39 ACTION(STLDeleteElements0) {
40   STLDeleteContainerPointers(arg0.begin(), arg0.end());
41 }
42
43 ACTION(QuitUIMessageLoop) {
44   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
45   base::MessageLoop::current()->Quit();
46 }
47
48 }  // namespace
49
50 #pragma mark -
51
52 class PasswordStoreMacInternalsTest : public testing::Test {
53  public:
54   virtual void SetUp() {
55     MockAppleKeychain::KeychainTestData test_data[] = {
56       // Basic HTML form.
57       { kSecAuthenticationTypeHTMLForm, "some.domain.com",
58         kSecProtocolTypeHTTP, NULL, 0, NULL, "20020601171500Z",
59         "joe_user", "sekrit", false },
60       // HTML form with path.
61       { kSecAuthenticationTypeHTMLForm, "some.domain.com",
62         kSecProtocolTypeHTTP, "/insecure.html", 0, NULL, "19991231235959Z",
63         "joe_user", "sekrit", false },
64       // Secure HTML form with path.
65       { kSecAuthenticationTypeHTMLForm, "some.domain.com",
66         kSecProtocolTypeHTTPS, "/secure.html", 0, NULL, "20100908070605Z",
67         "secure_user", "password", false },
68       // True negative item.
69       { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
70         kSecProtocolTypeHTTP, NULL, 0, NULL, "20000101000000Z",
71         "", "", true },
72       // De-facto negative item, type one.
73       { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
74         kSecProtocolTypeHTTP, NULL, 0, NULL, "20000101000000Z",
75         "Password Not Stored", "", false },
76       // De-facto negative item, type two.
77       { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
78         kSecProtocolTypeHTTPS, NULL, 0, NULL, "20000101000000Z",
79         "Password Not Stored", " ", false },
80       // HTTP auth basic, with port and path.
81       { kSecAuthenticationTypeHTTPBasic, "some.domain.com",
82         kSecProtocolTypeHTTP, "/insecure.html", 4567, "low_security",
83         "19980330100000Z",
84         "basic_auth_user", "basic", false },
85       // HTTP auth digest, secure.
86       { kSecAuthenticationTypeHTTPDigest, "some.domain.com",
87         kSecProtocolTypeHTTPS, NULL, 0, "high_security", "19980330100000Z",
88         "digest_auth_user", "digest", false },
89       // An FTP password with an invalid date, for edge-case testing.
90       { kSecAuthenticationTypeDefault, "a.server.com",
91         kSecProtocolTypeFTP, NULL, 0, NULL, "20010203040",
92         "abc", "123", false },
93     };
94
95     keychain_ = new MockAppleKeychain();
96
97     for (unsigned int i = 0; i < arraysize(test_data); ++i) {
98       keychain_->AddTestItem(test_data[i]);
99     }
100   }
101
102   virtual void TearDown() {
103     ExpectCreatesAndFreesBalanced();
104     ExpectCreatorCodesSet();
105     delete keychain_;
106   }
107
108  protected:
109   // Causes a test failure unless everything returned from keychain_'s
110   // ItemCopyAttributesAndData, SearchCreateFromAttributes, and SearchCopyNext
111   // was correctly freed.
112   void ExpectCreatesAndFreesBalanced() {
113     EXPECT_EQ(0, keychain_->UnfreedSearchCount());
114     EXPECT_EQ(0, keychain_->UnfreedKeychainItemCount());
115     EXPECT_EQ(0, keychain_->UnfreedAttributeDataCount());
116   }
117
118   // Causes a test failure unless any Keychain items added during the test have
119   // their creator code set.
120   void ExpectCreatorCodesSet() {
121     EXPECT_TRUE(keychain_->CreatorCodesSetForAddedItems());
122   }
123
124   MockAppleKeychain* keychain_;
125 };
126
127 #pragma mark -
128
129 // Struct used for creation of PasswordForms from static arrays of data.
130 struct PasswordFormData {
131   const PasswordForm::Scheme scheme;
132   const char* signon_realm;
133   const char* origin;
134   const char* action;
135   const wchar_t* submit_element;
136   const wchar_t* username_element;
137   const wchar_t* password_element;
138   const wchar_t* username_value;  // Set to NULL for a blacklist entry.
139   const wchar_t* password_value;
140   const bool preferred;
141   const bool ssl_valid;
142   const double creation_time;
143 };
144
145 // Creates and returns a new PasswordForm built from form_data. Caller is
146 // responsible for deleting the object when finished with it.
147 static PasswordForm* CreatePasswordFormFromData(
148     const PasswordFormData& form_data) {
149   PasswordForm* form = new PasswordForm();
150   form->scheme = form_data.scheme;
151   form->preferred = form_data.preferred;
152   form->ssl_valid = form_data.ssl_valid;
153   form->date_created = base::Time::FromDoubleT(form_data.creation_time);
154   if (form_data.signon_realm)
155     form->signon_realm = std::string(form_data.signon_realm);
156   if (form_data.origin)
157     form->origin = GURL(form_data.origin);
158   if (form_data.action)
159     form->action = GURL(form_data.action);
160   if (form_data.submit_element)
161     form->submit_element = WideToUTF16(form_data.submit_element);
162   if (form_data.username_element)
163     form->username_element = WideToUTF16(form_data.username_element);
164   if (form_data.password_element)
165     form->password_element = WideToUTF16(form_data.password_element);
166   if (form_data.username_value) {
167     form->username_value = WideToUTF16(form_data.username_value);
168     if (form_data.password_value)
169       form->password_value = WideToUTF16(form_data.password_value);
170   } else {
171     form->blacklisted_by_user = true;
172   }
173   return form;
174 }
175
176 // Macro to simplify calling CheckFormsAgainstExpectations with a useful label.
177 #define CHECK_FORMS(forms, expectations, i) \
178     CheckFormsAgainstExpectations(forms, expectations, #forms, i)
179
180 // Ensures that the data in |forms| match |expectations|, causing test failures
181 // for any discrepencies.
182 // TODO(stuartmorgan): This is current order-dependent; ideally it shouldn't
183 // matter if |forms| and |expectations| are scrambled.
184 static void CheckFormsAgainstExpectations(
185     const std::vector<PasswordForm*>& forms,
186     const std::vector<PasswordFormData*>& expectations,
187     const char* forms_label, unsigned int test_number) {
188   const unsigned int kBufferSize = 128;
189   char test_label[kBufferSize];
190   snprintf(test_label, kBufferSize, "%s in test %u", forms_label, test_number);
191
192   EXPECT_EQ(expectations.size(), forms.size()) << test_label;
193   if (expectations.size() != forms.size())
194     return;
195
196   for (unsigned int i = 0; i < expectations.size(); ++i) {
197     snprintf(test_label, kBufferSize, "%s in test %u, item %u",
198              forms_label, test_number, i);
199     PasswordForm* form = forms[i];
200     PasswordFormData* expectation = expectations[i];
201     EXPECT_EQ(expectation->scheme, form->scheme) << test_label;
202     EXPECT_EQ(std::string(expectation->signon_realm), form->signon_realm)
203         << test_label;
204     EXPECT_EQ(GURL(expectation->origin), form->origin) << test_label;
205     EXPECT_EQ(GURL(expectation->action), form->action) << test_label;
206     EXPECT_EQ(WideToUTF16(expectation->submit_element), form->submit_element)
207         << test_label;
208     EXPECT_EQ(WideToUTF16(expectation->username_element),
209               form->username_element) << test_label;
210     EXPECT_EQ(WideToUTF16(expectation->password_element),
211               form->password_element) << test_label;
212     if (expectation->username_value) {
213       EXPECT_EQ(WideToUTF16(expectation->username_value),
214                 form->username_value) << test_label;
215       EXPECT_EQ(WideToUTF16(expectation->password_value),
216                 form->password_value) << test_label;
217     } else {
218       EXPECT_TRUE(form->blacklisted_by_user) << test_label;
219     }
220     EXPECT_EQ(expectation->preferred, form->preferred)  << test_label;
221     EXPECT_EQ(expectation->ssl_valid, form->ssl_valid) << test_label;
222     EXPECT_DOUBLE_EQ(expectation->creation_time,
223                      form->date_created.ToDoubleT()) << test_label;
224   }
225 }
226
227 #pragma mark -
228
229 TEST_F(PasswordStoreMacInternalsTest, TestKeychainToFormTranslation) {
230   typedef struct {
231     const PasswordForm::Scheme scheme;
232     const char* signon_realm;
233     const char* origin;
234     const wchar_t* username;  // Set to NULL to check for a blacklist entry.
235     const wchar_t* password;
236     const bool ssl_valid;
237     const int creation_year;
238     const int creation_month;
239     const int creation_day;
240     const int creation_hour;
241     const int creation_minute;
242     const int creation_second;
243   } TestExpectations;
244
245   TestExpectations expected[] = {
246     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
247       "http://some.domain.com/", L"joe_user", L"sekrit", false,
248       2002,  6,  1, 17, 15,  0 },
249     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
250       "http://some.domain.com/insecure.html", L"joe_user", L"sekrit", false,
251       1999, 12, 31, 23, 59, 59 },
252     { PasswordForm::SCHEME_HTML, "https://some.domain.com/",
253       "https://some.domain.com/secure.html", L"secure_user", L"password", true,
254       2010,  9,  8,  7,  6,  5 },
255     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
256       "http://dont.remember.com/", NULL, NULL, false,
257       2000,  1,  1,  0,  0,  0 },
258     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
259       "http://dont.remember.com/", NULL, NULL, false,
260       2000,  1,  1,  0,  0,  0 },
261     { PasswordForm::SCHEME_HTML, "https://dont.remember.com/",
262       "https://dont.remember.com/", NULL, NULL, true,
263       2000,  1,  1,  0,  0,  0 },
264     { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
265       "http://some.domain.com:4567/insecure.html", L"basic_auth_user", L"basic",
266       false, 1998, 03, 30, 10, 00, 00 },
267     { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
268       "https://some.domain.com/", L"digest_auth_user", L"digest", true,
269       1998,  3, 30, 10,  0,  0 },
270     // This one gives us an invalid date, which we will treat as a "NULL" date
271     // which is 1601.
272     { PasswordForm::SCHEME_OTHER, "http://a.server.com/",
273       "http://a.server.com/", L"abc", L"123", false,
274       1601,  1,  1,  0,  0,  0 },
275   };
276
277   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(expected); ++i) {
278     // Create our fake KeychainItemRef; see MockAppleKeychain docs.
279     SecKeychainItemRef keychain_item =
280         reinterpret_cast<SecKeychainItemRef>(i + 1);
281     PasswordForm form;
282     bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
283         *keychain_, keychain_item, &form, true);
284
285     EXPECT_TRUE(parsed) << "In iteration " << i;
286
287     EXPECT_EQ(expected[i].scheme, form.scheme) << "In iteration " << i;
288     EXPECT_EQ(GURL(expected[i].origin), form.origin) << "In iteration " << i;
289     EXPECT_EQ(expected[i].ssl_valid, form.ssl_valid) << "In iteration " << i;
290     EXPECT_EQ(std::string(expected[i].signon_realm), form.signon_realm)
291         << "In iteration " << i;
292     if (expected[i].username) {
293       EXPECT_EQ(WideToUTF16(expected[i].username), form.username_value)
294           << "In iteration " << i;
295       EXPECT_EQ(WideToUTF16(expected[i].password), form.password_value)
296           << "In iteration " << i;
297       EXPECT_FALSE(form.blacklisted_by_user) << "In iteration " << i;
298     } else {
299       EXPECT_TRUE(form.blacklisted_by_user) << "In iteration " << i;
300     }
301     base::Time::Exploded exploded_time;
302     form.date_created.UTCExplode(&exploded_time);
303     EXPECT_EQ(expected[i].creation_year, exploded_time.year)
304          << "In iteration " << i;
305     EXPECT_EQ(expected[i].creation_month, exploded_time.month)
306         << "In iteration " << i;
307     EXPECT_EQ(expected[i].creation_day, exploded_time.day_of_month)
308         << "In iteration " << i;
309     EXPECT_EQ(expected[i].creation_hour, exploded_time.hour)
310         << "In iteration " << i;
311     EXPECT_EQ(expected[i].creation_minute, exploded_time.minute)
312         << "In iteration " << i;
313     EXPECT_EQ(expected[i].creation_second, exploded_time.second)
314         << "In iteration " << i;
315   }
316
317   {
318     // Use an invalid ref, to make sure errors are reported.
319     SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(99);
320     PasswordForm form;
321     bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
322         *keychain_, keychain_item, &form, true);
323     EXPECT_FALSE(parsed);
324   }
325 }
326
327 TEST_F(PasswordStoreMacInternalsTest, TestKeychainSearch) {
328   struct TestDataAndExpectation {
329     const PasswordFormData data;
330     const size_t expected_fill_matches;
331     const size_t expected_merge_matches;
332   };
333   // Most fields are left blank because we don't care about them for searching.
334   TestDataAndExpectation test_data[] = {
335     // An HTML form we've seen.
336     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
337         NULL, NULL, NULL, NULL, NULL, L"joe_user", NULL, false, false, 0 },
338       2, 2 },
339     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
340         NULL, NULL, NULL, NULL, NULL, L"wrong_user", NULL, false, false, 0 },
341       2, 0 },
342     // An HTML form we haven't seen
343     { { PasswordForm::SCHEME_HTML, "http://www.unseendomain.com/",
344         NULL, NULL, NULL, NULL, NULL, L"joe_user", NULL, false, false, 0 },
345       0, 0 },
346     // Basic auth that should match.
347     { { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
348         NULL, NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, false, false,
349         0 },
350       1, 1 },
351     // Basic auth with the wrong port.
352     { { PasswordForm::SCHEME_BASIC, "http://some.domain.com:1111/low_security",
353         NULL, NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, false, false,
354         0 },
355       0, 0 },
356     // Digest auth we've saved under https, visited with http.
357     { { PasswordForm::SCHEME_DIGEST, "http://some.domain.com/high_security",
358         NULL, NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, false, false,
359         0 },
360       0, 0 },
361     // Digest auth that should match.
362     { { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
363         NULL, NULL, NULL, NULL, NULL, L"wrong_user", NULL, false, true, 0 },
364       1, 0 },
365     // Digest auth with the wrong domain.
366     { { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/other_domain",
367         NULL, NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, false, true,
368         0 },
369       0, 0 },
370     // Garbage forms should have no matches.
371     { { PasswordForm::SCHEME_HTML, "foo/bar/baz",
372         NULL, NULL, NULL, NULL, NULL, NULL, NULL, false, false, 0 }, 0, 0 },
373   };
374
375   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
376   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
377   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
378   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
379     scoped_ptr<PasswordForm> query_form(
380         CreatePasswordFormFromData(test_data[i].data));
381
382     // Check matches treating the form as a fill target.
383     std::vector<PasswordForm*> matching_items =
384         keychain_adapter.PasswordsFillingForm(*query_form);
385     EXPECT_EQ(test_data[i].expected_fill_matches, matching_items.size());
386     STLDeleteElements(&matching_items);
387
388     // Check matches treating the form as a merging target.
389     EXPECT_EQ(test_data[i].expected_merge_matches > 0,
390               keychain_adapter.HasPasswordsMergeableWithForm(*query_form));
391     std::vector<SecKeychainItemRef> keychain_items;
392     std::vector<internal_keychain_helpers::ItemFormPair> item_form_pairs =
393         internal_keychain_helpers::
394             ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
395                                                               *keychain_);
396     matching_items =
397         internal_keychain_helpers::ExtractPasswordsMergeableWithForm(
398             *keychain_, item_form_pairs, *query_form);
399     EXPECT_EQ(test_data[i].expected_merge_matches, matching_items.size());
400     STLDeleteContainerPairSecondPointers(item_form_pairs.begin(),
401                                          item_form_pairs.end());
402     for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
403          i != keychain_items.end(); ++i) {
404       keychain_->Free(*i);
405     }
406     STLDeleteElements(&matching_items);
407
408     // None of the pre-seeded items are owned by us, so none should match an
409     // owned-passwords-only search.
410     matching_items = owned_keychain_adapter.PasswordsFillingForm(*query_form);
411     EXPECT_EQ(0U, matching_items.size());
412     STLDeleteElements(&matching_items);
413   }
414 }
415
416 // Changes just the origin path of |form|.
417 static void SetPasswordFormPath(PasswordForm* form, const char* path) {
418   GURL::Replacements replacement;
419   std::string new_value(path);
420   replacement.SetPathStr(new_value);
421   form->origin = form->origin.ReplaceComponents(replacement);
422 }
423
424 // Changes just the signon_realm port of |form|.
425 static void SetPasswordFormPort(PasswordForm* form, const char* port) {
426   GURL::Replacements replacement;
427   std::string new_value(port);
428   replacement.SetPortStr(new_value);
429   GURL signon_gurl = GURL(form->signon_realm);
430   form->signon_realm = signon_gurl.ReplaceComponents(replacement).spec();
431 }
432
433 // Changes just the signon_ream auth realm of |form|.
434 static void SetPasswordFormRealm(PasswordForm* form, const char* realm) {
435   GURL::Replacements replacement;
436   std::string new_value(realm);
437   replacement.SetPathStr(new_value);
438   GURL signon_gurl = GURL(form->signon_realm);
439   form->signon_realm = signon_gurl.ReplaceComponents(replacement).spec();
440 }
441
442 TEST_F(PasswordStoreMacInternalsTest, TestKeychainExactSearch) {
443   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
444
445   PasswordFormData base_form_data[] = {
446     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
447       "http://some.domain.com/insecure.html",
448       NULL, NULL, NULL, NULL, L"joe_user", NULL, true, false, 0 },
449     { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
450       "http://some.domain.com:4567/insecure.html",
451       NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, true, false, 0 },
452     { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
453       "https://some.domain.com",
454       NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, true, true, 0 },
455   };
456
457   for (unsigned int i = 0; i < arraysize(base_form_data); ++i) {
458     // Create a base form and make sure we find a match.
459     scoped_ptr<PasswordForm> base_form(CreatePasswordFormFromData(
460         base_form_data[i]));
461     EXPECT_TRUE(keychain_adapter.HasPasswordsMergeableWithForm(*base_form));
462     PasswordForm* match =
463         keychain_adapter.PasswordExactlyMatchingForm(*base_form);
464     EXPECT_TRUE(match != NULL);
465     if (match) {
466       EXPECT_EQ(base_form->scheme, match->scheme);
467       EXPECT_EQ(base_form->origin, match->origin);
468       EXPECT_EQ(base_form->username_value, match->username_value);
469       delete match;
470     }
471
472     // Make sure that the matching isn't looser than it should be by checking
473     // that slightly altered forms don't match.
474     std::vector<PasswordForm*> modified_forms;
475
476     modified_forms.push_back(new PasswordForm(*base_form));
477     modified_forms.back()->username_value = ASCIIToUTF16("wrong_user");
478
479     modified_forms.push_back(new PasswordForm(*base_form));
480     SetPasswordFormPath(modified_forms.back(), "elsewhere.html");
481
482     modified_forms.push_back(new PasswordForm(*base_form));
483     modified_forms.back()->scheme = PasswordForm::SCHEME_OTHER;
484
485     modified_forms.push_back(new PasswordForm(*base_form));
486     SetPasswordFormPort(modified_forms.back(), "1234");
487
488     modified_forms.push_back(new PasswordForm(*base_form));
489     modified_forms.back()->blacklisted_by_user = true;
490
491     if (base_form->scheme == PasswordForm::SCHEME_BASIC ||
492         base_form->scheme == PasswordForm::SCHEME_DIGEST) {
493       modified_forms.push_back(new PasswordForm(*base_form));
494       SetPasswordFormRealm(modified_forms.back(), "incorrect");
495     }
496
497     for (unsigned int j = 0; j < modified_forms.size(); ++j) {
498       PasswordForm* match =
499           keychain_adapter.PasswordExactlyMatchingForm(*modified_forms[j]);
500       EXPECT_EQ(NULL, match) << "In modified version " << j << " of base form "
501                              << i;
502     }
503     STLDeleteElements(&modified_forms);
504   }
505 }
506
507 TEST_F(PasswordStoreMacInternalsTest, TestKeychainAdd) {
508   struct TestDataAndExpectation {
509     PasswordFormData data;
510     bool should_succeed;
511   };
512   TestDataAndExpectation test_data[] = {
513     // Test a variety of scheme/port/protocol/path variations.
514     { { PasswordForm::SCHEME_HTML, "http://web.site.com/",
515         "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
516         L"anonymous", L"knock-knock", false, false, 0 }, true },
517     { { PasswordForm::SCHEME_HTML, "https://web.site.com/",
518         "https://web.site.com/", NULL, NULL, NULL, NULL,
519         L"admin", L"p4ssw0rd", false, false, 0 }, true },
520     { { PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm",
521         "http://a.site.com:2222/", NULL, NULL, NULL, NULL,
522         L"username", L"password", false, false, 0 }, true },
523     { { PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm",
524         "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL,
525         L"testname", L"testpass", false, false, 0 }, true },
526     // Make sure that garbage forms are rejected.
527     { { PasswordForm::SCHEME_HTML, "gobbledygook",
528         "gobbledygook", NULL, NULL, NULL, NULL,
529         L"anonymous", L"knock-knock", false, false, 0 }, false },
530     // Test that failing to update a duplicate (forced using the magic failure
531     // password; see MockAppleKeychain::ItemModifyAttributesAndData) is
532     // reported.
533     { { PasswordForm::SCHEME_HTML, "http://some.domain.com",
534         "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL,
535         L"joe_user", L"fail_me", false, false, 0 }, false },
536   };
537
538   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
539   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
540
541   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
542     scoped_ptr<PasswordForm> in_form(
543         CreatePasswordFormFromData(test_data[i].data));
544     bool add_succeeded = owned_keychain_adapter.AddPassword(*in_form);
545     EXPECT_EQ(test_data[i].should_succeed, add_succeeded);
546     if (add_succeeded) {
547       EXPECT_TRUE(owned_keychain_adapter.HasPasswordsMergeableWithForm(
548           *in_form));
549       scoped_ptr<PasswordForm> out_form(
550           owned_keychain_adapter.PasswordExactlyMatchingForm(*in_form));
551       EXPECT_TRUE(out_form.get() != NULL);
552       EXPECT_EQ(out_form->scheme, in_form->scheme);
553       EXPECT_EQ(out_form->signon_realm, in_form->signon_realm);
554       EXPECT_EQ(out_form->origin, in_form->origin);
555       EXPECT_EQ(out_form->username_value, in_form->username_value);
556       EXPECT_EQ(out_form->password_value, in_form->password_value);
557     }
558   }
559
560   // Test that adding duplicate item updates the existing item.
561   {
562     PasswordFormData data = {
563       PasswordForm::SCHEME_HTML, "http://some.domain.com",
564       "http://some.domain.com/insecure.html", NULL,
565       NULL, NULL, NULL, L"joe_user", L"updated_password", false, false, 0
566     };
567     scoped_ptr<PasswordForm> update_form(CreatePasswordFormFromData(data));
568     MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
569     EXPECT_TRUE(keychain_adapter.AddPassword(*update_form));
570     SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(2);
571     PasswordForm stored_form;
572     internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
573                                                                 keychain_item,
574                                                                 &stored_form,
575                                                                 true);
576     EXPECT_EQ(update_form->password_value, stored_form.password_value);
577   }
578 }
579
580 TEST_F(PasswordStoreMacInternalsTest, TestKeychainRemove) {
581   struct TestDataAndExpectation {
582     PasswordFormData data;
583     bool should_succeed;
584   };
585   TestDataAndExpectation test_data[] = {
586     // Test deletion of an item that we add.
587     { { PasswordForm::SCHEME_HTML, "http://web.site.com/",
588         "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
589         L"anonymous", L"knock-knock", false, false, 0 }, true },
590     // Make sure we don't delete items we don't own.
591     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
592         "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL,
593         L"joe_user", NULL, true, false, 0 }, false },
594   };
595
596   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
597   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
598
599   // Add our test item so that we can delete it.
600   PasswordForm* add_form = CreatePasswordFormFromData(test_data[0].data);
601   EXPECT_TRUE(owned_keychain_adapter.AddPassword(*add_form));
602   delete add_form;
603
604   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
605     scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
606         test_data[i].data));
607     EXPECT_EQ(test_data[i].should_succeed,
608               owned_keychain_adapter.RemovePassword(*form));
609
610     MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
611     PasswordForm* match = keychain_adapter.PasswordExactlyMatchingForm(*form);
612     EXPECT_EQ(test_data[i].should_succeed, match == NULL);
613     if (match) {
614       delete match;
615     }
616   }
617 }
618
619 TEST_F(PasswordStoreMacInternalsTest, TestFormMatch) {
620   PasswordForm base_form;
621   base_form.signon_realm = std::string("http://some.domain.com/");
622   base_form.origin = GURL("http://some.domain.com/page.html");
623   base_form.username_value = ASCIIToUTF16("joe_user");
624
625   {
626     // Check that everything unimportant can be changed.
627     PasswordForm different_form(base_form);
628     different_form.username_element = ASCIIToUTF16("username");
629     different_form.submit_element = ASCIIToUTF16("submit");
630     different_form.username_element = ASCIIToUTF16("password");
631     different_form.password_value = ASCIIToUTF16("sekrit");
632     different_form.action = GURL("http://some.domain.com/action.cgi");
633     different_form.ssl_valid = true;
634     different_form.preferred = true;
635     different_form.date_created = base::Time::Now();
636     EXPECT_TRUE(internal_keychain_helpers::FormsMatchForMerge(base_form,
637                                                               different_form));
638
639     // Check that path differences don't prevent a match.
640     base_form.origin = GURL("http://some.domain.com/other_page.html");
641     EXPECT_TRUE(internal_keychain_helpers::FormsMatchForMerge(base_form,
642                                                               different_form));
643   }
644
645   // Check that any one primary key changing is enough to prevent matching.
646   {
647     PasswordForm different_form(base_form);
648     different_form.scheme = PasswordForm::SCHEME_DIGEST;
649     EXPECT_FALSE(internal_keychain_helpers::FormsMatchForMerge(base_form,
650                                                                different_form));
651   }
652   {
653     PasswordForm different_form(base_form);
654     different_form.signon_realm = std::string("http://some.domain.com:8080/");
655     EXPECT_FALSE(internal_keychain_helpers::FormsMatchForMerge(base_form,
656                                                                different_form));
657   }
658   {
659     PasswordForm different_form(base_form);
660     different_form.username_value = ASCIIToUTF16("john.doe");
661     EXPECT_FALSE(internal_keychain_helpers::FormsMatchForMerge(base_form,
662                                                                different_form));
663   }
664   {
665     PasswordForm different_form(base_form);
666     different_form.blacklisted_by_user = true;
667     EXPECT_FALSE(internal_keychain_helpers::FormsMatchForMerge(base_form,
668                                                                different_form));
669   }
670
671   // Blacklist forms should *never* match for merging, even when identical
672   // (and certainly not when only one is a blacklist entry).
673   {
674     PasswordForm form_a(base_form);
675     form_a.blacklisted_by_user = true;
676     PasswordForm form_b(form_a);
677     EXPECT_FALSE(internal_keychain_helpers::FormsMatchForMerge(form_a, form_b));
678   }
679 }
680
681 TEST_F(PasswordStoreMacInternalsTest, TestFormMerge) {
682   // Set up a bunch of test data to use in varying combinations.
683   PasswordFormData keychain_user_1 =
684       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
685         "http://some.domain.com/", "", L"", L"", L"", L"joe_user", L"sekrit",
686         false, false, 1010101010 };
687   PasswordFormData keychain_user_1_with_path =
688       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
689         "http://some.domain.com/page.html",
690         "", L"", L"", L"", L"joe_user", L"otherpassword",
691         false, false, 1010101010 };
692   PasswordFormData keychain_user_2 =
693       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
694         "http://some.domain.com/", "", L"", L"", L"", L"john.doe", L"sesame",
695         false, false, 958739876 };
696   PasswordFormData keychain_blacklist =
697       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
698         "http://some.domain.com/", "", L"", L"", L"", NULL, NULL,
699         false, false, 1010101010 };
700
701   PasswordFormData db_user_1 =
702       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
703         "http://some.domain.com/", "http://some.domain.com/action.cgi",
704         L"submit", L"username", L"password", L"joe_user", L"",
705         true, false, 1212121212 };
706   PasswordFormData db_user_1_with_path =
707       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
708         "http://some.domain.com/page.html",
709         "http://some.domain.com/handlepage.cgi",
710         L"submit", L"username", L"password", L"joe_user", L"",
711         true, false, 1234567890 };
712   PasswordFormData db_user_3_with_path =
713       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
714         "http://some.domain.com/page.html",
715         "http://some.domain.com/handlepage.cgi",
716         L"submit", L"username", L"password", L"second-account", L"",
717         true, false, 1240000000 };
718   PasswordFormData database_blacklist_with_path =
719       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
720         "http://some.domain.com/path.html", "http://some.domain.com/action.cgi",
721         L"submit", L"username", L"password", NULL, NULL,
722         true, false, 1212121212 };
723
724   PasswordFormData merged_user_1 =
725       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
726         "http://some.domain.com/", "http://some.domain.com/action.cgi",
727         L"submit", L"username", L"password", L"joe_user", L"sekrit",
728         true, false, 1212121212 };
729   PasswordFormData merged_user_1_with_db_path =
730       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
731         "http://some.domain.com/page.html",
732         "http://some.domain.com/handlepage.cgi",
733         L"submit", L"username", L"password", L"joe_user", L"sekrit",
734         true, false, 1234567890 };
735   PasswordFormData merged_user_1_with_both_paths =
736       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
737         "http://some.domain.com/page.html",
738         "http://some.domain.com/handlepage.cgi",
739         L"submit", L"username", L"password", L"joe_user", L"otherpassword",
740         true, false, 1234567890 };
741
742   // Build up the big multi-dimensional array of data sets that will actually
743   // drive the test. Use vectors rather than arrays so that initialization is
744   // simple.
745   enum {
746     KEYCHAIN_INPUT = 0,
747     DATABASE_INPUT,
748     MERGE_OUTPUT,
749     KEYCHAIN_OUTPUT,
750     DATABASE_OUTPUT,
751     MERGE_IO_ARRAY_COUNT  // termination marker
752   };
753   const unsigned int kTestCount = 4;
754   std::vector< std::vector< std::vector<PasswordFormData*> > > test_data(
755       MERGE_IO_ARRAY_COUNT, std::vector< std::vector<PasswordFormData*> >(
756           kTestCount, std::vector<PasswordFormData*>()));
757   unsigned int current_test = 0;
758
759   // Test a merge with a few accounts in both systems, with partial overlap.
760   CHECK(current_test < kTestCount);
761   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
762   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_2);
763   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
764   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1_with_path);
765   test_data[DATABASE_INPUT][current_test].push_back(&db_user_3_with_path);
766   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
767   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1_with_db_path);
768   test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_user_2);
769   test_data[DATABASE_OUTPUT][current_test].push_back(&db_user_3_with_path);
770
771   // Test a merge where Chrome has a blacklist entry, and the keychain has
772   // a stored account.
773   ++current_test;
774   CHECK(current_test < kTestCount);
775   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
776   test_data[DATABASE_INPUT][current_test].push_back(
777       &database_blacklist_with_path);
778   // We expect both to be present because a blacklist could be specific to a
779   // subpath, and we want access to the password on other paths.
780   test_data[MERGE_OUTPUT][current_test].push_back(
781       &database_blacklist_with_path);
782   test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_user_1);
783
784   // Test a merge where Chrome has an account, and Keychain has a blacklist
785   // (from another browser) and the Chrome password data.
786   ++current_test;
787   CHECK(current_test < kTestCount);
788   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_blacklist);
789   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
790   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
791   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
792   test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_blacklist);
793
794   // Test that matches are done using exact path when possible.
795   ++current_test;
796   CHECK(current_test < kTestCount);
797   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
798   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1_with_path);
799   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
800   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1_with_path);
801   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
802   test_data[MERGE_OUTPUT][current_test].push_back(
803       &merged_user_1_with_both_paths);
804
805   for (unsigned int test_case = 0; test_case <= current_test; ++test_case) {
806     std::vector<PasswordForm*> keychain_forms;
807     for (std::vector<PasswordFormData*>::iterator i =
808              test_data[KEYCHAIN_INPUT][test_case].begin();
809          i != test_data[KEYCHAIN_INPUT][test_case].end(); ++i) {
810       keychain_forms.push_back(CreatePasswordFormFromData(*(*i)));
811     }
812     std::vector<PasswordForm*> database_forms;
813     for (std::vector<PasswordFormData*>::iterator i =
814              test_data[DATABASE_INPUT][test_case].begin();
815          i != test_data[DATABASE_INPUT][test_case].end(); ++i) {
816       database_forms.push_back(CreatePasswordFormFromData(*(*i)));
817     }
818
819     std::vector<PasswordForm*> merged_forms;
820     internal_keychain_helpers::MergePasswordForms(&keychain_forms,
821                                                   &database_forms,
822                                                   &merged_forms);
823
824     CHECK_FORMS(keychain_forms, test_data[KEYCHAIN_OUTPUT][test_case],
825                 test_case);
826     CHECK_FORMS(database_forms, test_data[DATABASE_OUTPUT][test_case],
827                 test_case);
828     CHECK_FORMS(merged_forms, test_data[MERGE_OUTPUT][test_case], test_case);
829
830     STLDeleteElements(&keychain_forms);
831     STLDeleteElements(&database_forms);
832     STLDeleteElements(&merged_forms);
833   }
834 }
835
836 TEST_F(PasswordStoreMacInternalsTest, TestPasswordBulkLookup) {
837   PasswordFormData db_data[] = {
838     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
839       "http://some.domain.com/", "http://some.domain.com/action.cgi",
840       L"submit", L"username", L"password", L"joe_user", L"",
841       true, false, 1212121212 },
842     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
843       "http://some.domain.com/page.html",
844       "http://some.domain.com/handlepage.cgi",
845       L"submit", L"username", L"password", L"joe_user", L"",
846       true, false, 1234567890 },
847     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
848       "http://some.domain.com/page.html",
849       "http://some.domain.com/handlepage.cgi",
850       L"submit", L"username", L"password", L"second-account", L"",
851       true, false, 1240000000 },
852     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
853       "http://dont.remember.com/",
854       "http://dont.remember.com/handlepage.cgi",
855       L"submit", L"username", L"password", L"joe_user", L"",
856       true, false, 1240000000 },
857     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
858       "http://some.domain.com/path.html", "http://some.domain.com/action.cgi",
859       L"submit", L"username", L"password", NULL, NULL,
860       true, false, 1212121212 },
861   };
862   std::vector<PasswordForm*> database_forms;
863   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(db_data); ++i) {
864     database_forms.push_back(CreatePasswordFormFromData(db_data[i]));
865   }
866   std::vector<PasswordForm*> merged_forms =
867       internal_keychain_helpers::GetPasswordsForForms(*keychain_,
868                                                       &database_forms);
869   EXPECT_EQ(2U, database_forms.size());
870   ASSERT_EQ(3U, merged_forms.size());
871   EXPECT_EQ(ASCIIToUTF16("sekrit"), merged_forms[0]->password_value);
872   EXPECT_EQ(ASCIIToUTF16("sekrit"), merged_forms[1]->password_value);
873   EXPECT_TRUE(merged_forms[2]->blacklisted_by_user);
874
875   STLDeleteElements(&database_forms);
876   STLDeleteElements(&merged_forms);
877 }
878
879 TEST_F(PasswordStoreMacInternalsTest, TestBlacklistedFiltering) {
880   PasswordFormData db_data[] = {
881     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
882       "http://dont.remember.com/",
883       "http://dont.remember.com/handlepage.cgi",
884       L"submit", L"username", L"password", L"joe_user", L"non_empty_password",
885       true, false, 1240000000 },
886     { PasswordForm::SCHEME_HTML, "https://dont.remember.com/",
887       "https://dont.remember.com/",
888       "https://dont.remember.com/handlepage_secure.cgi",
889       L"submit", L"username", L"password", L"joe_user", L"non_empty_password",
890       true, false, 1240000000 },
891   };
892   std::vector<PasswordForm*> database_forms;
893   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(db_data); ++i) {
894     database_forms.push_back(CreatePasswordFormFromData(db_data[i]));
895   }
896   std::vector<PasswordForm*> merged_forms =
897       internal_keychain_helpers::GetPasswordsForForms(*keychain_,
898                                                       &database_forms);
899   EXPECT_EQ(2U, database_forms.size());
900   ASSERT_EQ(0U, merged_forms.size());
901
902   STLDeleteElements(&database_forms);
903   STLDeleteElements(&merged_forms);
904 }
905
906 TEST_F(PasswordStoreMacInternalsTest, TestFillPasswordFormFromKeychainItem) {
907   // When |extract_password_data| is false, the password field must be empty,
908   // and |blacklisted_by_user| must be false.
909   SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(1);
910   PasswordForm form_without_extracted_password;
911   bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
912       *keychain_,
913       keychain_item,
914       &form_without_extracted_password,
915       false);  // Do not extract password.
916   EXPECT_TRUE(parsed);
917   ASSERT_TRUE(form_without_extracted_password.password_value.empty());
918   ASSERT_FALSE(form_without_extracted_password.blacklisted_by_user);
919
920   // When |extract_password_data| is true and the keychain entry has a non-empty
921   // password, the password field must be non-empty, and the value of
922   // |blacklisted_by_user| must be false.
923   keychain_item = reinterpret_cast<SecKeychainItemRef>(1);
924   PasswordForm form_with_extracted_password;
925   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
926       *keychain_,
927       keychain_item,
928       &form_with_extracted_password,
929       true);  // Extract password.
930   EXPECT_TRUE(parsed);
931   ASSERT_EQ(ASCIIToUTF16("sekrit"),
932             form_with_extracted_password.password_value);
933   ASSERT_FALSE(form_with_extracted_password.blacklisted_by_user);
934
935   // When |extract_password_data| is true and the keychain entry has an empty
936   // username and password (""), the password field must be empty, and the value
937   // of |blacklisted_by_user| must be true.
938   keychain_item = reinterpret_cast<SecKeychainItemRef>(4);
939   PasswordForm negative_form;
940   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
941       *keychain_,
942       keychain_item,
943       &negative_form,
944       true);  // Extract password.
945   EXPECT_TRUE(parsed);
946   ASSERT_TRUE(negative_form.username_value.empty());
947   ASSERT_TRUE(negative_form.password_value.empty());
948   ASSERT_TRUE(negative_form.blacklisted_by_user);
949
950   // When |extract_password_data| is true and the keychain entry has an empty
951   // password (""), the password field must be empty (""), and the value of
952   // |blacklisted_by_user| must be true.
953   keychain_item = reinterpret_cast<SecKeychainItemRef>(5);
954   PasswordForm form_with_empty_password_a;
955   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
956       *keychain_,
957       keychain_item,
958       &form_with_empty_password_a,
959       true);  // Extract password.
960   EXPECT_TRUE(parsed);
961   ASSERT_TRUE(form_with_empty_password_a.password_value.empty());
962   ASSERT_TRUE(form_with_empty_password_a.blacklisted_by_user);
963
964   // When |extract_password_data| is true and the keychain entry has a single
965   // space password (" "), the password field must be a single space (" "), and
966   // the value of |blacklisted_by_user| must be true.
967   keychain_item = reinterpret_cast<SecKeychainItemRef>(6);
968   PasswordForm form_with_empty_password_b;
969   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
970       *keychain_,
971       keychain_item,
972       &form_with_empty_password_b,
973       true);  // Extract password.
974   EXPECT_TRUE(parsed);
975   ASSERT_EQ(ASCIIToUTF16(" "),
976             form_with_empty_password_b.password_value);
977   ASSERT_TRUE(form_with_empty_password_b.blacklisted_by_user);
978 }
979
980 TEST_F(PasswordStoreMacInternalsTest, TestPasswordGetAll) {
981   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
982   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
983   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
984
985   // Add a few passwords of various types so that we own some.
986   PasswordFormData owned_password_data[] = {
987     { PasswordForm::SCHEME_HTML, "http://web.site.com/",
988       "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
989       L"anonymous", L"knock-knock", false, false, 0 },
990     { PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm",
991       "http://a.site.com:2222/", NULL, NULL, NULL, NULL,
992       L"username", L"password", false, false, 0 },
993     { PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm",
994       "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL,
995       L"testname", L"testpass", false, false, 0 },
996   };
997   for (unsigned int i = 0; i < arraysize(owned_password_data); ++i) {
998     scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
999         owned_password_data[i]));
1000     owned_keychain_adapter.AddPassword(*form);
1001   }
1002
1003   std::vector<PasswordForm*> all_passwords =
1004       keychain_adapter.GetAllPasswordFormPasswords();
1005   EXPECT_EQ(8 + arraysize(owned_password_data), all_passwords.size());
1006   STLDeleteElements(&all_passwords);
1007
1008   std::vector<PasswordForm*> owned_passwords =
1009       owned_keychain_adapter.GetAllPasswordFormPasswords();
1010   EXPECT_EQ(arraysize(owned_password_data), owned_passwords.size());
1011   STLDeleteElements(&owned_passwords);
1012 }
1013
1014 #pragma mark -
1015
1016 class PasswordStoreMacTest : public testing::Test {
1017  public:
1018   PasswordStoreMacTest() : ui_thread_(BrowserThread::UI, &message_loop_) {}
1019
1020   virtual void SetUp() {
1021     login_db_ = new LoginDatabase();
1022     ASSERT_TRUE(db_dir_.CreateUniqueTempDir());
1023     base::FilePath db_file = db_dir_.path().AppendASCII("login.db");
1024     ASSERT_TRUE(login_db_->Init(db_file));
1025
1026     keychain_ = new MockAppleKeychain();
1027
1028     store_ = new PasswordStoreMac(keychain_, login_db_);
1029     ASSERT_TRUE(store_->Init());
1030   }
1031
1032   virtual void TearDown() {
1033     store_->ShutdownOnUIThread();
1034     base::MessageLoop::current()->PostTask(FROM_HERE,
1035                                            base::MessageLoop::QuitClosure());
1036     base::MessageLoop::current()->Run();
1037   }
1038
1039  protected:
1040   base::MessageLoopForUI message_loop_;
1041   content::TestBrowserThread ui_thread_;
1042
1043   MockAppleKeychain* keychain_;  // Owned by store_.
1044   LoginDatabase* login_db_;  // Owned by store_.
1045   scoped_refptr<PasswordStoreMac> store_;
1046   base::ScopedTempDir db_dir_;
1047 };
1048
1049 TEST_F(PasswordStoreMacTest, TestStoreUpdate) {
1050   // Insert a password into both the database and the keychain.
1051   // This is done manually, rather than through store_->AddLogin, because the
1052   // Mock Keychain isn't smart enough to be able to support update generically,
1053   // so some.domain.com triggers special handling to test it that make inserting
1054   // fail.
1055   PasswordFormData joint_data = {
1056     PasswordForm::SCHEME_HTML, "http://some.domain.com/",
1057     "http://some.domain.com/insecure.html", "login.cgi",
1058     L"username", L"password", L"submit", L"joe_user", L"sekrit", true, false, 1
1059   };
1060   scoped_ptr<PasswordForm> joint_form(CreatePasswordFormFromData(joint_data));
1061   login_db_->AddLogin(*joint_form);
1062   MockAppleKeychain::KeychainTestData joint_keychain_data = {
1063     kSecAuthenticationTypeHTMLForm, "some.domain.com",
1064     kSecProtocolTypeHTTP, "/insecure.html", 0, NULL, "20020601171500Z",
1065     "joe_user", "sekrit", false };
1066   keychain_->AddTestItem(joint_keychain_data);
1067
1068   // Insert a password into the keychain only.
1069   MockAppleKeychain::KeychainTestData keychain_only_data = {
1070     kSecAuthenticationTypeHTMLForm, "keychain.only.com",
1071     kSecProtocolTypeHTTP, NULL, 0, NULL, "20020601171500Z",
1072     "keychain", "only", false
1073   };
1074   keychain_->AddTestItem(keychain_only_data);
1075
1076   struct UpdateData {
1077     PasswordFormData form_data;
1078     const char* password;  // NULL indicates no entry should be present.
1079   };
1080
1081   // Make a series of update calls.
1082   UpdateData updates[] = {
1083     // Update the keychain+db passwords (the normal password update case).
1084     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
1085         "http://some.domain.com/insecure.html", "login.cgi",
1086         L"username", L"password", L"submit", L"joe_user", L"53krit",
1087         true, false, 2 },
1088       "53krit",
1089     },
1090     // Update the keychain-only password; this simulates the initial use of a
1091     // password stored by another browsers.
1092     { { PasswordForm::SCHEME_HTML, "http://keychain.only.com/",
1093         "http://keychain.only.com/login.html", "login.cgi",
1094         L"username", L"password", L"submit", L"keychain", L"only",
1095         true, false, 2 },
1096       "only",
1097     },
1098     // Update a password that doesn't exist in either location. This tests the
1099     // case where a form is filled, then the stored login is removed, then the
1100     // form is submitted.
1101     { { PasswordForm::SCHEME_HTML, "http://different.com/",
1102         "http://different.com/index.html", "login.cgi",
1103         L"username", L"password", L"submit", L"abc", L"123",
1104         true, false, 2 },
1105       NULL,
1106     },
1107   };
1108   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(updates); ++i) {
1109     scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
1110         updates[i].form_data));
1111     store_->UpdateLogin(*form);
1112   }
1113
1114   // Do a store-level query to wait for all the operations above to be done.
1115   MockPasswordStoreConsumer consumer;
1116   ON_CALL(consumer, OnGetPasswordStoreResults(_)).WillByDefault(
1117       QuitUIMessageLoop());
1118   EXPECT_CALL(consumer, OnGetPasswordStoreResults(_)).WillOnce(
1119       DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
1120   store_->GetLogins(*joint_form, &consumer);
1121   base::MessageLoop::current()->Run();
1122
1123   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
1124   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(updates); ++i) {
1125     scoped_ptr<PasswordForm> query_form(
1126         CreatePasswordFormFromData(updates[i].form_data));
1127
1128     std::vector<PasswordForm*> matching_items =
1129         keychain_adapter.PasswordsFillingForm(*query_form);
1130     if (updates[i].password) {
1131       EXPECT_GT(matching_items.size(), 0U) << "iteration " << i;
1132       if (matching_items.size() >= 1)
1133         EXPECT_EQ(ASCIIToUTF16(updates[i].password),
1134                   matching_items[0]->password_value) << "iteration " << i;
1135     } else {
1136       EXPECT_EQ(0U, matching_items.size()) << "iteration " << i;
1137     }
1138     STLDeleteElements(&matching_items);
1139
1140     login_db_->GetLogins(*query_form, &matching_items);
1141     EXPECT_EQ(updates[i].password ? 1U : 0U, matching_items.size())
1142         << "iteration " << i;
1143     STLDeleteElements(&matching_items);
1144   }
1145 }