diff options
author | stuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-23 18:56:34 +0000 |
---|---|---|
committer | stuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-23 18:56:34 +0000 |
commit | 12eb8e78f183c10e08df3dbb080c09669df94424 (patch) | |
tree | 1bac8d46beafd943662d147145ad3afcd4a98115 /chrome | |
parent | d7e5b443612b102a8d2adf5717bc571136e788dd (diff) | |
download | chromium_src-12eb8e78f183c10e08df3dbb080c09669df94424.zip chromium_src-12eb8e78f183c10e08df3dbb080c09669df94424.tar.gz chromium_src-12eb8e78f183c10e08df3dbb080c09669df94424.tar.bz2 |
Move MockKeychain into its own file.
The unit test file has become very unwieldy, and I no longer believe that the
Keychain mock is temporary code that should be replaced with GMock (since the
dummy implementation makes certain tests much easier).
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/146041
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19041 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/keychain_mock_mac.cc | 408 | ||||
-rw-r--r-- | chrome/browser/keychain_mock_mac.h | 120 | ||||
-rw-r--r-- | chrome/browser/password_manager/password_store_mac_unittest.cc | 682 | ||||
-rw-r--r-- | chrome/chrome.gyp | 2 |
4 files changed, 625 insertions, 587 deletions
diff --git a/chrome/browser/keychain_mock_mac.cc b/chrome/browser/keychain_mock_mac.cc new file mode 100644 index 0000000..858fdb6 --- /dev/null +++ b/chrome/browser/keychain_mock_mac.cc @@ -0,0 +1,408 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/time.h" +#include "chrome/browser/keychain_mock_mac.h" + +MockKeychain::MockKeychain() + : search_copy_count_(0), keychain_item_copy_count_(0), + attribute_data_copy_count_(0) { + UInt32 tags[] = { kSecAccountItemAttr, + kSecServerItemAttr, + kSecPortItemAttr, + kSecPathItemAttr, + kSecProtocolItemAttr, + kSecAuthenticationTypeItemAttr, + kSecSecurityDomainItemAttr, + kSecCreationDateItemAttr, + kSecNegativeItemAttr }; + + // Create the test keychain data to return from ItemCopyAttributesAndData, + // and set up everything that's consistent across all the items. + item_capacity_ = 9; + keychain_attr_list_ = static_cast<SecKeychainAttributeList*>( + calloc(item_capacity_, sizeof(SecKeychainAttributeList))); + keychain_data_ = static_cast<KeychainPasswordData*>( + calloc(item_capacity_, sizeof(KeychainPasswordData))); + for (unsigned int i = 0; i < item_capacity_; ++i) { + keychain_attr_list_[i].count = arraysize(tags); + keychain_attr_list_[i].attr = static_cast<SecKeychainAttribute*>( + calloc(keychain_attr_list_[i].count, sizeof(SecKeychainAttribute))); + for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) { + keychain_attr_list_[i].attr[j].tag = tags[j]; + size_t data_size = 0; + switch (tags[j]) { + case kSecPortItemAttr: + data_size = sizeof(UInt32); + break; + case kSecProtocolItemAttr: + data_size = sizeof(SecProtocolType); + break; + case kSecAuthenticationTypeItemAttr: + data_size = sizeof(SecAuthenticationType); + break; + case kSecNegativeItemAttr: + data_size = sizeof(Boolean); + break; + } + if (data_size > 0) { + keychain_attr_list_[i].attr[j].length = data_size; + keychain_attr_list_[i].attr[j].data = calloc(1, data_size); + } + } + } + + // Save one slot for use by AddInternetPassword. + unsigned int available_slots = item_capacity_ - 1; + item_count_ = 0; + + // Basic HTML form. + CHECK(item_count_ < available_slots); + SetTestDataString(item_count_, kSecAccountItemAttr, "joe_user"); + SetTestDataString(item_count_, kSecServerItemAttr, "some.domain.com"); + SetTestDataProtocol(item_count_, kSecProtocolTypeHTTP); + SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); + SetTestDataString(item_count_, kSecCreationDateItemAttr, "20020601171500Z"); + SetTestDataPasswordString(item_count_, "sekrit"); + ++item_count_; + + // HTML form with path. + CHECK(item_count_ < available_slots); + SetTestDataString(item_count_, kSecAccountItemAttr, "joe_user"); + SetTestDataString(item_count_, kSecServerItemAttr, "some.domain.com"); + SetTestDataString(item_count_, kSecPathItemAttr, "/insecure.html"); + SetTestDataProtocol(item_count_, kSecProtocolTypeHTTP); + SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); + SetTestDataString(item_count_, kSecCreationDateItemAttr, "19991231235959Z"); + SetTestDataPasswordString(item_count_, "sekrit"); + ++item_count_; + + // Secure HTML form with path. + CHECK(item_count_ < available_slots); + SetTestDataString(item_count_, kSecAccountItemAttr, "secure_user"); + SetTestDataString(item_count_, kSecServerItemAttr, "some.domain.com"); + SetTestDataString(item_count_, kSecPathItemAttr, "/secure.html"); + SetTestDataProtocol(item_count_, kSecProtocolTypeHTTPS); + SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); + SetTestDataString(item_count_, kSecCreationDateItemAttr, "20100908070605Z"); + SetTestDataPasswordString(item_count_, "password"); + ++item_count_; + + // True negative item. + CHECK(item_count_ < available_slots); + SetTestDataString(item_count_, kSecServerItemAttr, "dont.remember.com"); + SetTestDataProtocol(item_count_, kSecProtocolTypeHTTP); + SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); + SetTestDataString(item_count_, kSecCreationDateItemAttr, "20000101000000Z"); + SetTestDataNegativeItem(item_count_, true); + ++item_count_; + + // De-facto negative item, type one. + CHECK(item_count_ < available_slots); + SetTestDataString(item_count_, kSecAccountItemAttr, "Password Not Stored"); + SetTestDataString(item_count_, kSecServerItemAttr, "dont.remember.com"); + SetTestDataProtocol(item_count_, kSecProtocolTypeHTTP); + SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); + SetTestDataString(item_count_, kSecCreationDateItemAttr, "20000101000000Z"); + SetTestDataPasswordString(item_count_, ""); + ++item_count_; + + // De-facto negative item, type two. + CHECK(item_count_ < available_slots); + SetTestDataString(item_count_, kSecServerItemAttr, "dont.remember.com"); + SetTestDataProtocol(item_count_, kSecProtocolTypeHTTPS); + SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); + SetTestDataString(item_count_, kSecCreationDateItemAttr, "20000101000000Z"); + SetTestDataPasswordString(item_count_, " "); + ++item_count_; + + // HTTP auth basic, with port and path. + CHECK(item_count_ < available_slots); + SetTestDataString(item_count_, kSecAccountItemAttr, "basic_auth_user"); + SetTestDataString(item_count_, kSecServerItemAttr, "some.domain.com"); + SetTestDataString(item_count_, kSecSecurityDomainItemAttr, "low_security"); + SetTestDataString(item_count_, kSecPathItemAttr, "/insecure.html"); + SetTestDataProtocol(item_count_, kSecProtocolTypeHTTP); + SetTestDataPort(item_count_, 4567); + SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTTPBasic); + SetTestDataString(item_count_, kSecCreationDateItemAttr, "19980330100000Z"); + SetTestDataPasswordString(item_count_, "basic"); + ++item_count_; + + // HTTP auth digest, secure. + CHECK(item_count_ < available_slots); + SetTestDataString(item_count_, kSecAccountItemAttr, "digest_auth_user"); + SetTestDataString(item_count_, kSecServerItemAttr, "some.domain.com"); + SetTestDataString(item_count_, kSecSecurityDomainItemAttr, "high_security"); + SetTestDataProtocol(item_count_, kSecProtocolTypeHTTPS); + SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTTPDigest); + SetTestDataString(item_count_, kSecCreationDateItemAttr, "19980330100000Z"); + SetTestDataPasswordString(item_count_, "digest"); + ++item_count_; +} + +MockKeychain::~MockKeychain() { + for (unsigned int i = 0; i < item_capacity_; ++i) { + for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) { + if (keychain_attr_list_[i].attr[j].data) { + free(keychain_attr_list_[i].attr[j].data); + } + } + free(keychain_attr_list_[i].attr); + if (keychain_data_[i].data) { + free(keychain_data_[i].data); + } + } + free(keychain_attr_list_); + free(keychain_data_); +} + +int MockKeychain::IndexForTag(const SecKeychainAttributeList& attribute_list, + UInt32 tag) { + for (unsigned int i = 0; i < attribute_list.count; ++i) { + if (attribute_list.attr[i].tag == tag) { + return i; + } + } + DCHECK(false); + return -1; +} + +void MockKeychain::SetTestDataBytes(int item, UInt32 tag, const void* data, + size_t length) const { + int attribute_index = IndexForTag(keychain_attr_list_[item], tag); + keychain_attr_list_[item].attr[attribute_index].length = length; + if (length > 0) { + if (keychain_attr_list_[item].attr[attribute_index].data) { + free(keychain_attr_list_[item].attr[attribute_index].data); + } + keychain_attr_list_[item].attr[attribute_index].data = malloc(length); + CHECK(keychain_attr_list_[item].attr[attribute_index].data); + memcpy(keychain_attr_list_[item].attr[attribute_index].data, data, length); + } else { + keychain_attr_list_[item].attr[attribute_index].data = NULL; + } +} + +void MockKeychain::SetTestDataString(int item, UInt32 tag, + const char* value) const { + SetTestDataBytes(item, tag, value, value ? strlen(value) : 0); +} + +void MockKeychain::SetTestDataPort(int item, UInt32 value) const { + int attribute_index = IndexForTag(keychain_attr_list_[item], + kSecPortItemAttr); + void* data = keychain_attr_list_[item].attr[attribute_index].data; + *(static_cast<UInt32*>(data)) = value; +} + +void MockKeychain::SetTestDataProtocol(int item, SecProtocolType value) const { + int attribute_index = IndexForTag(keychain_attr_list_[item], + kSecProtocolItemAttr); + void* data = keychain_attr_list_[item].attr[attribute_index].data; + *(static_cast<SecProtocolType*>(data)) = value; +} + +void MockKeychain::SetTestDataAuthType(int item, + SecAuthenticationType value) const { + int attribute_index = IndexForTag(keychain_attr_list_[item], + kSecAuthenticationTypeItemAttr); + void* data = keychain_attr_list_[item].attr[attribute_index].data; + *(static_cast<SecAuthenticationType*>(data)) = value; +} + +void MockKeychain::SetTestDataNegativeItem(int item, Boolean value) const { + int attribute_index = IndexForTag(keychain_attr_list_[item], + kSecNegativeItemAttr); + void* data = keychain_attr_list_[item].attr[attribute_index].data; + *(static_cast<Boolean*>(data)) = value; +} + +void MockKeychain::SetTestDataPasswordBytes(int item, const void* data, + size_t length) const { + keychain_data_[item].length = length; + if (length > 0) { + if (keychain_data_[item].data) { + free(keychain_data_[item].data); + } + keychain_data_[item].data = malloc(length); + memcpy(keychain_data_[item].data, data, length); + } else { + keychain_data_[item].data = NULL; + } +} + +void MockKeychain::SetTestDataPasswordString(int item, + const char* value) const { + SetTestDataPasswordBytes(item, value, value ? strlen(value) : 0); +} + +OSStatus MockKeychain::ItemCopyAttributesAndData( + SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info, + SecItemClass *itemClass, SecKeychainAttributeList **attrList, + UInt32 *length, void **outData) const { + DCHECK(itemRef); + unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1; + if (item_index >= item_count_) { + return errSecInvalidItemRef; + } + + DCHECK(!itemClass); // itemClass not implemented in the Mock. + if (attrList) { + *attrList = &(keychain_attr_list_[item_index]); + } + if (outData) { + *outData = keychain_data_[item_index].data; + DCHECK(length); + *length = keychain_data_[item_index].length; + } + + ++attribute_data_copy_count_; + return noErr; +} + +OSStatus MockKeychain::ItemModifyAttributesAndData( + SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, + UInt32 length, const void *data) const { + DCHECK(itemRef); + const char* fail_trigger = "fail_me"; + if (length == strlen(fail_trigger) && + memcmp(data, fail_trigger, length) == 0) { + return errSecAuthFailed; + } + + unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1; + if (item_index >= item_count_) { + return errSecInvalidItemRef; + } + + if (attrList) { + NOTIMPLEMENTED(); + } + if (data) { + SetTestDataPasswordBytes(item_index, data, length); + } + return noErr; +} + +OSStatus MockKeychain::ItemFreeAttributesAndData( + SecKeychainAttributeList *attrList, + void *data) const { + --attribute_data_copy_count_; + return noErr; +} + +OSStatus MockKeychain::SearchCreateFromAttributes( + CFTypeRef keychainOrArray, SecItemClass itemClass, + const SecKeychainAttributeList *attrList, + SecKeychainSearchRef *searchRef) const { + // Figure out which of our mock items matches, and set up the array we'll use + // to generate results out of SearchCopyNext. + remaining_search_results_.clear(); + for (unsigned int mock_item = 0; mock_item < item_count_; ++mock_item) { + bool mock_item_matches = true; + for (UInt32 search_attr = 0; search_attr < attrList->count; ++search_attr) { + int mock_attr = IndexForTag(keychain_attr_list_[mock_item], + attrList->attr[search_attr].tag); + SecKeychainAttribute* mock_attribute = + &(keychain_attr_list_[mock_item].attr[mock_attr]); + if (mock_attribute->length != attrList->attr[search_attr].length || + memcmp(mock_attribute->data, attrList->attr[search_attr].data, + attrList->attr[search_attr].length) != 0) { + mock_item_matches = false; + break; + } + } + if (mock_item_matches) { + remaining_search_results_.push_back(mock_item); + } + } + + DCHECK(searchRef); + *searchRef = reinterpret_cast<SecKeychainSearchRef>(kDummySearchRef); + ++search_copy_count_; + return noErr; +} + +OSStatus MockKeychain::AddInternetPassword( + SecKeychainRef keychain, + UInt32 serverNameLength, const char *serverName, + UInt32 securityDomainLength, const char *securityDomain, + UInt32 accountNameLength, const char *accountName, + UInt32 pathLength, const char *path, + UInt16 port, SecProtocolType protocol, + SecAuthenticationType authenticationType, + UInt32 passwordLength, const void *passwordData, + SecKeychainItemRef *itemRef) const { + + // Check for the magic duplicate item trigger. + if (strcmp(serverName, "some.domain.com") == 0) { + return errSecDuplicateItem; + } + + // Use empty slots until they run out, then just keep replacing the last item. + int target_item = (item_count_ == item_capacity_) ? item_capacity_ - 1 + : item_count_++; + + SetTestDataBytes(target_item, kSecServerItemAttr, serverName, + serverNameLength); + SetTestDataBytes(target_item, kSecSecurityDomainItemAttr, securityDomain, + securityDomainLength); + SetTestDataBytes(target_item, kSecAccountItemAttr, accountName, + accountNameLength); + SetTestDataBytes(target_item, kSecPathItemAttr, path, pathLength); + SetTestDataPort(target_item, port); + SetTestDataProtocol(target_item, protocol); + SetTestDataAuthType(target_item, authenticationType); + SetTestDataPasswordBytes(target_item, passwordData, passwordLength); + base::Time::Exploded exploded_time; + base::Time::Now().UTCExplode(&exploded_time); + char time_string[128]; + snprintf(time_string, sizeof(time_string), "%04d%02d%02d%02d%02d%02dZ", + exploded_time.year, exploded_time.month, exploded_time.day_of_month, + exploded_time.hour, exploded_time.minute, exploded_time.second); + SetTestDataString(target_item, kSecCreationDateItemAttr, time_string); + + if (itemRef) { + *itemRef = reinterpret_cast<SecKeychainItemRef>(target_item + 1); + } + return noErr; +} + +OSStatus MockKeychain::SearchCopyNext(SecKeychainSearchRef searchRef, + SecKeychainItemRef *itemRef) const { + if (remaining_search_results_.empty()) { + return errSecItemNotFound; + } + unsigned int index = remaining_search_results_.front(); + remaining_search_results_.erase(remaining_search_results_.begin()); + *itemRef = reinterpret_cast<SecKeychainItemRef>(index + 1); + ++keychain_item_copy_count_; + return noErr; +} + +void MockKeychain::Free(CFTypeRef ref) const { + if (!ref) { + return; + } + + if (reinterpret_cast<int>(ref) == kDummySearchRef) { + --search_copy_count_; + } else { + --keychain_item_copy_count_; + } +} + +int MockKeychain::UnfreedSearchCount() const { + return search_copy_count_; +} + +int MockKeychain::UnfreedKeychainItemCount() const { + return keychain_item_copy_count_; +} + +int MockKeychain::UnfreedAttributeDataCount() const { + return attribute_data_copy_count_; +} diff --git a/chrome/browser/keychain_mock_mac.h b/chrome/browser/keychain_mock_mac.h new file mode 100644 index 0000000..1a98a3a --- /dev/null +++ b/chrome/browser/keychain_mock_mac.h @@ -0,0 +1,120 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_KEYCHAIN_MOCK_MAC_H_ +#define CHROME_BROWSER_KEYCHAIN_MOCK_MAC_H_ + +#include <vector> + +#include "chrome/browser/keychain_mac.h" + +// Mock Keychain wrapper for testing code that interacts with the OS Keychain. +// The basic idea of this mock is that it has a static array of data, and +// SecKeychainItemRef values are just indexes into that array (offset by 1 to +// prevent problems with clients that null-check refs), cast to pointers. +// +// TODO(stuartmorgan): The test data is currently hard-coded; instead it should +// be settable by tests so that there isn't a strong coupling between the mock +// implementation and the tests. +class MockKeychain : public MacKeychain { + public: + MockKeychain(); + virtual ~MockKeychain(); + virtual OSStatus ItemCopyAttributesAndData( + SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info, + SecItemClass *itemClass, SecKeychainAttributeList **attrList, + UInt32 *length, void **outData) const; + // Pass "fail_me" as the data to get errSecAuthFailed. + virtual OSStatus ItemModifyAttributesAndData( + SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, + UInt32 length, const void *data) const; + virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList *attrList, + void *data) const; + virtual OSStatus SearchCreateFromAttributes( + CFTypeRef keychainOrArray, SecItemClass itemClass, + const SecKeychainAttributeList *attrList, + SecKeychainSearchRef *searchRef) const; + virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef, + SecKeychainItemRef *itemRef) const; + // Pass "some.domain.com" as the serverName to get errSecDuplicateItem. + virtual OSStatus AddInternetPassword(SecKeychainRef keychain, + UInt32 serverNameLength, + const char *serverName, + UInt32 securityDomainLength, + const char *securityDomain, + UInt32 accountNameLength, + const char *accountName, + UInt32 pathLength, const char *path, + UInt16 port, SecProtocolType protocol, + SecAuthenticationType authenticationType, + UInt32 passwordLength, + const void *passwordData, + SecKeychainItemRef *itemRef) const; + virtual void Free(CFTypeRef ref) const; + + // Return the counts of objects returned by Create/Copy functions but never + // Free'd as they should have been. + int UnfreedSearchCount() const; + int UnfreedKeychainItemCount() const; + int UnfreedAttributeDataCount() const; + + private: + // Note that "const" is pretty much meaningless for this class; the const-ness + // of MacKeychain doesn't apply to the actual keychain data, so all of our + // data is mutable. These helpers are all const because they are needed by + // some of the const API functions we are mocking. + + // Sets the data and length of |tag| in the item-th test item. + void SetTestDataBytes(int item, UInt32 tag, const void* data, + size_t length) const; + // Sets the data and length of |tag| in the item-th test item based on + // |value|. The null-terminator will not be included; the Keychain Services + // docs don't indicate whether it is or not, so clients should not assume + // that it will be. + void SetTestDataString(int item, UInt32 tag, const char* value) const; + // Sets the data of the corresponding attribute of the item-th test item to + // |value|. Assumes that the space has alread been allocated, and the length + // set. + void SetTestDataPort(int item, UInt32 value) const; + void SetTestDataProtocol(int item, SecProtocolType value) const; + void SetTestDataAuthType(int item, SecAuthenticationType value) const; + void SetTestDataNegativeItem(int item, Boolean value) const; + // Sets the password data and length for the item-th test item. + void SetTestDataPasswordBytes(int item, const void* data, + size_t length) const; + // Sets the password for the item-th test item. As with SetTestDataString, + // the data will not be null-terminated. + void SetTestDataPasswordString(int item, const char* value) const; + + // Returns the index of |tag| in |attribute_list|, or -1 if it's not found. + static int IndexForTag(const SecKeychainAttributeList& attribute_list, + UInt32 tag); + + static const int kDummySearchRef = 1000; + + typedef struct { + void* data; + UInt32 length; + } KeychainPasswordData; + + SecKeychainAttributeList* keychain_attr_list_; + KeychainPasswordData* keychain_data_; + unsigned int item_capacity_; + mutable unsigned int item_count_; + + // Tracks the items that should be returned in subsequent calls to + // SearchCopyNext, based on the last call to SearchCreateFromAttributes. + // We can't handle multiple active searches, since we don't track the search + // ref we return, but we don't need to for our mocking. + mutable std::vector<unsigned int> remaining_search_results_; + + // Track copies and releases to make sure they balance. Really these should + // be maps to track per item, but this should be good enough to catch + // real mistakes. + mutable int search_copy_count_; + mutable int keychain_item_copy_count_; + mutable int attribute_data_copy_count_; +}; + +#endif // CHROME_BROWSER_KEYCHAIN_MOCK_MAC_H_ diff --git a/chrome/browser/password_manager/password_store_mac_unittest.cc b/chrome/browser/password_manager/password_store_mac_unittest.cc index 1fcaff5..3ef66c1 100644 --- a/chrome/browser/password_manager/password_store_mac_unittest.cc +++ b/chrome/browser/password_manager/password_store_mac_unittest.cc @@ -5,518 +5,21 @@ #include "testing/gtest/include/gtest/gtest.h" #include "base/basictypes.h" +#include "chrome/browser/keychain_mock_mac.h" #include "chrome/browser/password_manager/password_store_mac.h" #include "chrome/browser/password_manager/password_store_mac_internal.h" using webkit_glue::PasswordForm; -#pragma mark Mock Keychain - -// TODO(stuartmorgan): Replace this with gMock. You know, once we have it. -// The basic idea of this mock is that it has a static array of data to use -// for ItemCopyAttributesAndData, and SecKeychainItemRef values are just indexes -// into that array (offset by 1 to prevent problems with client null-checking -// refs), cast to pointers. -class MockKeychain : public MacKeychain { - public: - MockKeychain(); - virtual ~MockKeychain(); - virtual OSStatus ItemCopyAttributesAndData( - SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info, - SecItemClass *itemClass, SecKeychainAttributeList **attrList, - UInt32 *length, void **outData) const; - // Pass "fail_me" as the data to get errSecAuthFailed. - virtual OSStatus ItemModifyAttributesAndData( - SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, - UInt32 length, const void *data) const; - virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList *attrList, - void *data) const; - virtual OSStatus SearchCreateFromAttributes( - CFTypeRef keychainOrArray, SecItemClass itemClass, - const SecKeychainAttributeList *attrList, - SecKeychainSearchRef *searchRef) const; - virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef, - SecKeychainItemRef *itemRef) const; - // Pass "some.domain.com" as the serverName to get errSecDuplicateItem. - virtual OSStatus AddInternetPassword(SecKeychainRef keychain, - UInt32 serverNameLength, - const char *serverName, - UInt32 securityDomainLength, - const char *securityDomain, - UInt32 accountNameLength, - const char *accountName, - UInt32 pathLength, const char *path, - UInt16 port, SecProtocolType protocol, - SecAuthenticationType authenticationType, - UInt32 passwordLength, - const void *passwordData, - SecKeychainItemRef *itemRef) const; - virtual void Free(CFTypeRef ref) const; - - // Causes a test failure unless everything returned from - // ItemCopyAttributesAndData, SearchCreateFromAttributes, and SearchCopyNext - // was correctly freed. - void ExpectCreatesAndFreesBalanced(); - - private: - // Note that "const" is pretty much meaningless for this class; the const-ness - // of MacKeychain doesn't apply to the actual keychain data, so all of our - // data is mutable. These helpers are all const because they are needed by - // some of the const API functions we are mocking. - - // Sets the data and length of |tag| in the item-th test item. - void SetTestDataBytes(int item, UInt32 tag, const void* data, - size_t length) const; - // Sets the data and length of |tag| in the item-th test item based on - // |value|. The null-terminator will not be included; the Keychain Services - // docs don't indicate whether it is or not, so clients should not assume - // that it will be. - void SetTestDataString(int item, UInt32 tag, const char* value) const; - // Sets the data of the corresponding attribute of the item-th test item to - // |value|. Assumes that the space has alread been allocated, and the length - // set. - void SetTestDataPort(int item, UInt32 value) const; - void SetTestDataProtocol(int item, SecProtocolType value) const; - void SetTestDataAuthType(int item, SecAuthenticationType value) const; - void SetTestDataNegativeItem(int item, Boolean value) const; - // Sets the password data and length for the item-th test item. - void SetTestDataPasswordBytes(int item, const void* data, - size_t length) const; - // Sets the password for the item-th test item. As with SetTestDataString, - // the data will not be null-terminated. - void SetTestDataPasswordString(int item, const char* value) const; - - // Returns the index of |tag| in |attribute_list|, or -1 if it's not found. - static int IndexForTag(const SecKeychainAttributeList& attribute_list, - UInt32 tag); - - static const int kDummySearchRef = 1000; - - typedef struct { - void* data; - UInt32 length; - } KeychainPasswordData; - - SecKeychainAttributeList* keychain_attr_list_; - KeychainPasswordData* keychain_data_; - unsigned int item_capacity_; - mutable unsigned int item_count_; - - // Tracks the items that should be returned in subsequent calls to - // SearchCopyNext, based on the last call to SearchCreateFromAttributes. - // We can't handle multiple active searches, since we don't track the search - // ref we return, but we don't need to for our mocking. - mutable std::vector<unsigned int> remaining_search_results_; - - // Track copies and releases to make sure they balance. Really these should - // be maps to track per item, but this should be good enough to catch - // real mistakes. - mutable int search_copy_count_; - mutable int keychain_item_copy_count_; - mutable int attribute_data_copy_count_; -}; - -#pragma mark - - -MockKeychain::MockKeychain() - : search_copy_count_(0), keychain_item_copy_count_(0), - attribute_data_copy_count_(0) { - UInt32 tags[] = { kSecAccountItemAttr, - kSecServerItemAttr, - kSecPortItemAttr, - kSecPathItemAttr, - kSecProtocolItemAttr, - kSecAuthenticationTypeItemAttr, - kSecSecurityDomainItemAttr, - kSecCreationDateItemAttr, - kSecNegativeItemAttr }; - - // Create the test keychain data to return from ItemCopyAttributesAndData, - // and set up everything that's consistent across all the items. - item_capacity_ = 9; - keychain_attr_list_ = static_cast<SecKeychainAttributeList*>( - calloc(item_capacity_, sizeof(SecKeychainAttributeList))); - keychain_data_ = static_cast<KeychainPasswordData*>( - calloc(item_capacity_, sizeof(KeychainPasswordData))); - for (unsigned int i = 0; i < item_capacity_; ++i) { - keychain_attr_list_[i].count = arraysize(tags); - keychain_attr_list_[i].attr = static_cast<SecKeychainAttribute*>( - calloc(keychain_attr_list_[i].count, sizeof(SecKeychainAttribute))); - for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) { - keychain_attr_list_[i].attr[j].tag = tags[j]; - size_t data_size = 0; - switch (tags[j]) { - case kSecPortItemAttr: - data_size = sizeof(UInt32); - break; - case kSecProtocolItemAttr: - data_size = sizeof(SecProtocolType); - break; - case kSecAuthenticationTypeItemAttr: - data_size = sizeof(SecAuthenticationType); - break; - case kSecNegativeItemAttr: - data_size = sizeof(Boolean); - break; - } - if (data_size > 0) { - keychain_attr_list_[i].attr[j].length = data_size; - keychain_attr_list_[i].attr[j].data = calloc(1, data_size); - } - } - } - - // Save one slot for use by AddInternetPassword. - unsigned int available_slots = item_capacity_ - 1; - item_count_ = 0; - - // Basic HTML form. - CHECK(item_count_ < available_slots); - SetTestDataString(item_count_, kSecAccountItemAttr, "joe_user"); - SetTestDataString(item_count_, kSecServerItemAttr, "some.domain.com"); - SetTestDataProtocol(item_count_, kSecProtocolTypeHTTP); - SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); - SetTestDataString(item_count_, kSecCreationDateItemAttr, "20020601171500Z"); - SetTestDataPasswordString(item_count_, "sekrit"); - ++item_count_; - - // HTML form with path. - CHECK(item_count_ < available_slots); - SetTestDataString(item_count_, kSecAccountItemAttr, "joe_user"); - SetTestDataString(item_count_, kSecServerItemAttr, "some.domain.com"); - SetTestDataString(item_count_, kSecPathItemAttr, "/insecure.html"); - SetTestDataProtocol(item_count_, kSecProtocolTypeHTTP); - SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); - SetTestDataString(item_count_, kSecCreationDateItemAttr, "19991231235959Z"); - SetTestDataPasswordString(item_count_, "sekrit"); - ++item_count_; - - // Secure HTML form with path. - CHECK(item_count_ < available_slots); - SetTestDataString(item_count_, kSecAccountItemAttr, "secure_user"); - SetTestDataString(item_count_, kSecServerItemAttr, "some.domain.com"); - SetTestDataString(item_count_, kSecPathItemAttr, "/secure.html"); - SetTestDataProtocol(item_count_, kSecProtocolTypeHTTPS); - SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); - SetTestDataString(item_count_, kSecCreationDateItemAttr, "20100908070605Z"); - SetTestDataPasswordString(item_count_, "password"); - ++item_count_; - - // True negative item. - CHECK(item_count_ < available_slots); - SetTestDataString(item_count_, kSecServerItemAttr, "dont.remember.com"); - SetTestDataProtocol(item_count_, kSecProtocolTypeHTTP); - SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); - SetTestDataString(item_count_, kSecCreationDateItemAttr, "20000101000000Z"); - SetTestDataNegativeItem(item_count_, true); - ++item_count_; - - // De-facto negative item, type one. - CHECK(item_count_ < available_slots); - SetTestDataString(item_count_, kSecAccountItemAttr, "Password Not Stored"); - SetTestDataString(item_count_, kSecServerItemAttr, "dont.remember.com"); - SetTestDataProtocol(item_count_, kSecProtocolTypeHTTP); - SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); - SetTestDataString(item_count_, kSecCreationDateItemAttr, "20000101000000Z"); - SetTestDataPasswordString(item_count_, ""); - ++item_count_; - - // De-facto negative item, type two. - CHECK(item_count_ < available_slots); - SetTestDataString(item_count_, kSecServerItemAttr, "dont.remember.com"); - SetTestDataProtocol(item_count_, kSecProtocolTypeHTTPS); - SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTMLForm); - SetTestDataString(item_count_, kSecCreationDateItemAttr, "20000101000000Z"); - SetTestDataPasswordString(item_count_, " "); - ++item_count_; - - // HTTP auth basic, with port and path. - CHECK(item_count_ < available_slots); - SetTestDataString(item_count_, kSecAccountItemAttr, "basic_auth_user"); - SetTestDataString(item_count_, kSecServerItemAttr, "some.domain.com"); - SetTestDataString(item_count_, kSecSecurityDomainItemAttr, "low_security"); - SetTestDataString(item_count_, kSecPathItemAttr, "/insecure.html"); - SetTestDataProtocol(item_count_, kSecProtocolTypeHTTP); - SetTestDataPort(item_count_, 4567); - SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTTPBasic); - SetTestDataString(item_count_, kSecCreationDateItemAttr, "19980330100000Z"); - SetTestDataPasswordString(item_count_, "basic"); - ++item_count_; - - // HTTP auth digest, secure. - CHECK(item_count_ < available_slots); - SetTestDataString(item_count_, kSecAccountItemAttr, "digest_auth_user"); - SetTestDataString(item_count_, kSecServerItemAttr, "some.domain.com"); - SetTestDataString(item_count_, kSecSecurityDomainItemAttr, "high_security"); - SetTestDataProtocol(item_count_, kSecProtocolTypeHTTPS); - SetTestDataAuthType(item_count_, kSecAuthenticationTypeHTTPDigest); - SetTestDataString(item_count_, kSecCreationDateItemAttr, "19980330100000Z"); - SetTestDataPasswordString(item_count_, "digest"); - ++item_count_; -} - -MockKeychain::~MockKeychain() { - for (unsigned int i = 0; i < item_capacity_; ++i) { - for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) { - if (keychain_attr_list_[i].attr[j].data) { - free(keychain_attr_list_[i].attr[j].data); - } - } - free(keychain_attr_list_[i].attr); - if (keychain_data_[i].data) { - free(keychain_data_[i].data); - } - } - free(keychain_attr_list_); - free(keychain_data_); -} - -void MockKeychain::ExpectCreatesAndFreesBalanced() { - EXPECT_EQ(0, search_copy_count_); - EXPECT_EQ(0, keychain_item_copy_count_); - EXPECT_EQ(0, attribute_data_copy_count_); -} - -int MockKeychain::IndexForTag(const SecKeychainAttributeList& attribute_list, - UInt32 tag) { - for (unsigned int i = 0; i < attribute_list.count; ++i) { - if (attribute_list.attr[i].tag == tag) { - return i; - } - } - DCHECK(false); - return -1; -} - -void MockKeychain::SetTestDataBytes(int item, UInt32 tag, const void* data, - size_t length) const { - int attribute_index = IndexForTag(keychain_attr_list_[item], tag); - keychain_attr_list_[item].attr[attribute_index].length = length; - if (length > 0) { - if (keychain_attr_list_[item].attr[attribute_index].data) { - free(keychain_attr_list_[item].attr[attribute_index].data); - } - keychain_attr_list_[item].attr[attribute_index].data = malloc(length); - CHECK(keychain_attr_list_[item].attr[attribute_index].data); - memcpy(keychain_attr_list_[item].attr[attribute_index].data, data, length); - } else { - keychain_attr_list_[item].attr[attribute_index].data = NULL; - } -} - -void MockKeychain::SetTestDataString(int item, UInt32 tag, - const char* value) const { - SetTestDataBytes(item, tag, value, value ? strlen(value) : 0); -} - -void MockKeychain::SetTestDataPort(int item, UInt32 value) const { - int attribute_index = IndexForTag(keychain_attr_list_[item], - kSecPortItemAttr); - void* data = keychain_attr_list_[item].attr[attribute_index].data; - *(static_cast<UInt32*>(data)) = value; -} - -void MockKeychain::SetTestDataProtocol(int item, SecProtocolType value) const { - int attribute_index = IndexForTag(keychain_attr_list_[item], - kSecProtocolItemAttr); - void* data = keychain_attr_list_[item].attr[attribute_index].data; - *(static_cast<SecProtocolType*>(data)) = value; -} - -void MockKeychain::SetTestDataAuthType(int item, - SecAuthenticationType value) const { - int attribute_index = IndexForTag(keychain_attr_list_[item], - kSecAuthenticationTypeItemAttr); - void* data = keychain_attr_list_[item].attr[attribute_index].data; - *(static_cast<SecAuthenticationType*>(data)) = value; -} - -void MockKeychain::SetTestDataNegativeItem(int item, Boolean value) const { - int attribute_index = IndexForTag(keychain_attr_list_[item], - kSecNegativeItemAttr); - void* data = keychain_attr_list_[item].attr[attribute_index].data; - *(static_cast<Boolean*>(data)) = value; -} - -void MockKeychain::SetTestDataPasswordBytes(int item, const void* data, - size_t length) const { - keychain_data_[item].length = length; - if (length > 0) { - if (keychain_data_[item].data) { - free(keychain_data_[item].data); - } - keychain_data_[item].data = malloc(length); - memcpy(keychain_data_[item].data, data, length); - } else { - keychain_data_[item].data = NULL; - } -} - -void MockKeychain::SetTestDataPasswordString(int item, - const char* value) const { - SetTestDataPasswordBytes(item, value, value ? strlen(value) : 0); -} - -OSStatus MockKeychain::ItemCopyAttributesAndData( - SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info, - SecItemClass *itemClass, SecKeychainAttributeList **attrList, - UInt32 *length, void **outData) const { - DCHECK(itemRef); - unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1; - if (item_index >= item_count_) { - return errSecInvalidItemRef; - } - - DCHECK(!itemClass); // itemClass not implemented in the Mock. - if (attrList) { - *attrList = &(keychain_attr_list_[item_index]); - } - if (outData) { - *outData = keychain_data_[item_index].data; - DCHECK(length); - *length = keychain_data_[item_index].length; - } - - ++attribute_data_copy_count_; - return noErr; -} - -OSStatus MockKeychain::ItemModifyAttributesAndData( - SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, - UInt32 length, const void *data) const { - DCHECK(itemRef); - const char* fail_trigger = "fail_me"; - if (length == strlen(fail_trigger) && - memcmp(data, fail_trigger, length) == 0) { - return errSecAuthFailed; - } - - unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1; - if (item_index >= item_count_) { - return errSecInvalidItemRef; - } - - if (attrList) { - NOTIMPLEMENTED(); - } - if (data) { - SetTestDataPasswordBytes(item_index, data, length); - } - return noErr; +// Causes a test failure unless everything returned from +// ItemCopyAttributesAndData, SearchCreateFromAttributes, and SearchCopyNext +// was correctly freed. +static void ExpectCreatesAndFreesBalanced(const MockKeychain& keychain) { + EXPECT_EQ(0, keychain.UnfreedSearchCount()); + EXPECT_EQ(0, keychain.UnfreedKeychainItemCount()); + EXPECT_EQ(0, keychain.UnfreedAttributeDataCount()); } -OSStatus MockKeychain::ItemFreeAttributesAndData( - SecKeychainAttributeList *attrList, - void *data) const { - --attribute_data_copy_count_; - return noErr; -} - -OSStatus MockKeychain::SearchCreateFromAttributes( - CFTypeRef keychainOrArray, SecItemClass itemClass, - const SecKeychainAttributeList *attrList, - SecKeychainSearchRef *searchRef) const { - // Figure out which of our mock items matches, and set up the array we'll use - // to generate results out of SearchCopyNext. - remaining_search_results_.clear(); - for (unsigned int mock_item = 0; mock_item < item_count_; ++mock_item) { - bool mock_item_matches = true; - for (UInt32 search_attr = 0; search_attr < attrList->count; ++search_attr) { - int mock_attr = IndexForTag(keychain_attr_list_[mock_item], - attrList->attr[search_attr].tag); - SecKeychainAttribute* mock_attribute = - &(keychain_attr_list_[mock_item].attr[mock_attr]); - if (mock_attribute->length != attrList->attr[search_attr].length || - memcmp(mock_attribute->data, attrList->attr[search_attr].data, - attrList->attr[search_attr].length) != 0) { - mock_item_matches = false; - break; - } - } - if (mock_item_matches) { - remaining_search_results_.push_back(mock_item); - } - } - - DCHECK(searchRef); - *searchRef = reinterpret_cast<SecKeychainSearchRef>(kDummySearchRef); - ++search_copy_count_; - return noErr; -} - -OSStatus MockKeychain::AddInternetPassword( - SecKeychainRef keychain, - UInt32 serverNameLength, const char *serverName, - UInt32 securityDomainLength, const char *securityDomain, - UInt32 accountNameLength, const char *accountName, - UInt32 pathLength, const char *path, - UInt16 port, SecProtocolType protocol, - SecAuthenticationType authenticationType, - UInt32 passwordLength, const void *passwordData, - SecKeychainItemRef *itemRef) const { - - // Check for the magic duplicate item trigger. - if (strcmp(serverName, "some.domain.com") == 0) { - return errSecDuplicateItem; - } - - // Use empty slots until they run out, then just keep replacing the last item. - int target_item = (item_count_ == item_capacity_) ? item_capacity_ - 1 - : item_count_++; - - SetTestDataBytes(target_item, kSecServerItemAttr, serverName, - serverNameLength); - SetTestDataBytes(target_item, kSecSecurityDomainItemAttr, securityDomain, - securityDomainLength); - SetTestDataBytes(target_item, kSecAccountItemAttr, accountName, - accountNameLength); - SetTestDataBytes(target_item, kSecPathItemAttr, path, pathLength); - SetTestDataPort(target_item, port); - SetTestDataProtocol(target_item, protocol); - SetTestDataAuthType(target_item, authenticationType); - SetTestDataPasswordBytes(target_item, passwordData, passwordLength); - base::Time::Exploded exploded_time; - base::Time::Now().UTCExplode(&exploded_time); - char time_string[128]; - snprintf(time_string, sizeof(time_string), "%04d%02d%02d%02d%02d%02dZ", - exploded_time.year, exploded_time.month, exploded_time.day_of_month, - exploded_time.hour, exploded_time.minute, exploded_time.second); - SetTestDataString(target_item, kSecCreationDateItemAttr, time_string); - - if (itemRef) { - *itemRef = reinterpret_cast<SecKeychainItemRef>(target_item + 1); - } - return noErr; -} - -OSStatus MockKeychain::SearchCopyNext(SecKeychainSearchRef searchRef, - SecKeychainItemRef *itemRef) const { - if (remaining_search_results_.empty()) { - return errSecItemNotFound; - } - unsigned int index = remaining_search_results_.front(); - remaining_search_results_.erase(remaining_search_results_.begin()); - *itemRef = reinterpret_cast<SecKeychainItemRef>(index + 1); - ++keychain_item_copy_count_; - return noErr; -} - -void MockKeychain::Free(CFTypeRef ref) const { - if (!ref) { - return; - } - - if (reinterpret_cast<int>(ref) == kDummySearchRef) { - --search_copy_count_; - } else { - --keychain_item_copy_count_; - } -} - -#pragma mark - -#pragma mark Unit Tests -//////////////////////////////////////////////////////////////////////////////// - // Struct used for creation of PasswordForms from static arrays of data. struct PasswordFormData { const PasswordForm::Scheme scheme; @@ -564,6 +67,80 @@ static PasswordForm* CreatePasswordFormFromData( return form; } +// Macro to simplify calling CheckFormsAgainstExpectations with a useful label. +#define CHECK_FORMS(forms, expectations, i) \ + CheckFormsAgainstExpectations(forms, expectations, #forms, i) + +// Ensures that the data in |forms| match |expectations|, causing test failures +// for any discrepencies. +// TODO(stuartmorgan): This is current order-dependent; ideally it shouldn't +// matter if |forms| and |expectations| are scrambled. +static void CheckFormsAgainstExpectations( + const std::vector<PasswordForm*>& forms, + const std::vector<PasswordFormData*>& expectations, + const char* forms_label, unsigned int test_number) { + const unsigned int kBufferSize = 128; + char test_label[kBufferSize]; + snprintf(test_label, kBufferSize, "%s in test %u", forms_label, test_number); + + EXPECT_EQ(expectations.size(), forms.size()) << test_label; + if (expectations.size() != forms.size()) + return; + + for (unsigned int i = 0; i < expectations.size(); ++i) { + snprintf(test_label, kBufferSize, "%s in test %u, item %u", + forms_label, test_number, i); + PasswordForm* form = forms[i]; + PasswordFormData* expectation = expectations[i]; + EXPECT_EQ(expectation->scheme, form->scheme) << test_label; + EXPECT_EQ(std::string(expectation->signon_realm), form->signon_realm) + << test_label; + EXPECT_EQ(GURL(expectation->origin), form->origin) << test_label; + EXPECT_EQ(GURL(expectation->action), form->action) << test_label; + EXPECT_EQ(std::wstring(expectation->submit_element), form->submit_element) + << test_label; + EXPECT_EQ(std::wstring(expectation->username_element), + form->username_element) << test_label; + EXPECT_EQ(std::wstring(expectation->password_element), + form->password_element) << test_label; + if (expectation->username_value) { + EXPECT_EQ(std::wstring(expectation->username_value), + form->username_value) << test_label; + EXPECT_EQ(std::wstring(expectation->password_value), + form->password_value) << test_label; + } else { + EXPECT_TRUE(form->blacklisted_by_user) << test_label; + } + EXPECT_EQ(expectation->preferred, form->preferred) << test_label; + EXPECT_EQ(expectation->ssl_valid, form->ssl_valid) << test_label; + EXPECT_DOUBLE_EQ(expectation->creation_time, + form->date_created.ToDoubleT()) << test_label; + } +} + +// Frees all the Keychain items in |items|, and clears the vector. +static void FreeKeychainItems(const MacKeychain& keychain, + std::vector<SecKeychainItemRef>* items) { + CHECK(items); + for (std::vector<SecKeychainItemRef>::iterator i = items->begin(); + i != items->end(); ++i) { + keychain.Free(*i); + } + items->clear(); +} + +// Deletes all the PasswordForms in |forms|, and clears the vector. +static void DeletePasswordForms(std::vector<PasswordForm*>* forms) { + CHECK(forms); + for (std::vector<PasswordForm*>::iterator i = forms->begin(); + i != forms->end(); ++i) { + delete *i; + } + forms->clear(); +} + +#pragma mark - + TEST(PasswordStoreMacTest, TestSignonRealmParsing) { typedef struct { const char* signon_realm; @@ -760,7 +337,7 @@ TEST(PasswordStoreMacTest, TestKeychainToFormTranslation) { mock_keychain, keychain_item, &form); EXPECT_TRUE(parsed) << "In iteration " << i; - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); EXPECT_EQ(expected[i].scheme, form.scheme) << "In iteration " << i; EXPECT_EQ(GURL(expected[i].origin), form.origin) << "In iteration " << i; @@ -798,20 +375,11 @@ TEST(PasswordStoreMacTest, TestKeychainToFormTranslation) { PasswordForm form; bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem( mock_keychain, keychain_item, &form); - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); EXPECT_FALSE(parsed); } } -static void FreeKeychainItems(const MacKeychain& keychain, - std::vector<SecKeychainItemRef>* items) { - for (std::vector<SecKeychainItemRef>::iterator i = items->begin(); - i != items->end(); ++i) { - keychain.Free(*i); - } - items->clear(); -} - TEST(PasswordStoreMacTest, TestKeychainSearch) { MockKeychain mock_keychain; @@ -822,7 +390,7 @@ TEST(PasswordStoreMacTest, TestKeychainSearch) { PasswordForm::SCHEME_HTML, &matching_items); EXPECT_EQ(static_cast<size_t>(2), matching_items.size()); FreeKeychainItems(mock_keychain, &matching_items); - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); } { // An HTML form we haven't seen @@ -832,7 +400,7 @@ TEST(PasswordStoreMacTest, TestKeychainSearch) { PasswordForm::SCHEME_HTML, &matching_items); EXPECT_EQ(static_cast<size_t>(0), matching_items.size()); FreeKeychainItems(mock_keychain, &matching_items); - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); } { // Basic auth that should match. @@ -842,7 +410,7 @@ TEST(PasswordStoreMacTest, TestKeychainSearch) { PasswordForm::SCHEME_BASIC, &matching_items); EXPECT_EQ(static_cast<size_t>(1), matching_items.size()); FreeKeychainItems(mock_keychain, &matching_items); - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); } { // Basic auth with the wrong port. @@ -852,7 +420,7 @@ TEST(PasswordStoreMacTest, TestKeychainSearch) { PasswordForm::SCHEME_BASIC, &matching_items); EXPECT_EQ(static_cast<size_t>(0), matching_items.size()); FreeKeychainItems(mock_keychain, &matching_items); - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); } { // Digest auth we've saved under https, visited with http. @@ -862,7 +430,7 @@ TEST(PasswordStoreMacTest, TestKeychainSearch) { PasswordForm::SCHEME_DIGEST, &matching_items); EXPECT_EQ(static_cast<size_t>(0), matching_items.size()); FreeKeychainItems(mock_keychain, &matching_items); - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); } { // Digest auth that should match. @@ -872,7 +440,7 @@ TEST(PasswordStoreMacTest, TestKeychainSearch) { PasswordForm::SCHEME_DIGEST, &matching_items); EXPECT_EQ(static_cast<size_t>(1), matching_items.size()); FreeKeychainItems(mock_keychain, &matching_items); - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); } } @@ -930,7 +498,7 @@ TEST(PasswordStoreMacTest, TestKeychainExactSearch) { blacklist); EXPECT_EQ(NULL, match); - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); } // Test an http auth entry (SCHEME_BASIC, but SCHEME_DIGEST works is searched @@ -989,7 +557,7 @@ TEST(PasswordStoreMacTest, TestKeychainExactSearch) { blacklist); EXPECT_EQ(NULL, match); - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); } } @@ -1013,7 +581,7 @@ TEST(PasswordStoreMacTest, TestKeychainModify) { EXPECT_FALSE(internal_keychain_helpers::SetKeychainItemPassword( mock_keychain, keychain_item, std::string("fail_me"))); - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); } TEST(PasswordStoreMacTest, TestKeychainAdd) { @@ -1068,7 +636,7 @@ TEST(PasswordStoreMacTest, TestKeychainAdd) { EXPECT_EQ(out_form.password_value, in_form->password_value); mock_keychain.Free(matching_item); } - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); delete in_form; } @@ -1091,7 +659,7 @@ TEST(PasswordStoreMacTest, TestKeychainAdd) { delete update_form; } - mock_keychain.ExpectCreatesAndFreesBalanced(); + ExpectCreatesAndFreesBalanced(mock_keychain); } TEST(PasswordStoreMacTest, TestFormMatch) { @@ -1166,66 +734,6 @@ TEST(PasswordStoreMacTest, TestFormMatch) { } } -// Macro to simplify calling CheckFormsAgainstExpectations with a useful label. -#define CHECK_FORMS(forms, expectations, i) \ - CheckFormsAgainstExpectations(forms, expectations, #forms, i) - -// Ensures that the data in |forms| match |expectations|, causing test failures -// for any discrepencies. -// TODO(stuartmorgan): This is current order-dependent; ideally it shouldn't -// matter if |forms| and |expectations| are scrambled. -static void CheckFormsAgainstExpectations( - const std::vector<PasswordForm*>& forms, - const std::vector<PasswordFormData*>& expectations, - const char* forms_label, unsigned int test_number) { - - const unsigned int kBufferSize = 128; - char test_label[kBufferSize]; - snprintf(test_label, kBufferSize, "%s in test %u", forms_label, test_number); - - EXPECT_EQ(expectations.size(), forms.size()) << test_label; - if (expectations.size() != forms.size()) - return; - - for (unsigned int i = 0; i < expectations.size(); ++i) { - snprintf(test_label, kBufferSize, "%s in test %u, item %u", - forms_label, test_number, i); - PasswordForm* form = forms[i]; - PasswordFormData* expectation = expectations[i]; - EXPECT_EQ(expectation->scheme, form->scheme) << test_label; - EXPECT_EQ(std::string(expectation->signon_realm), form->signon_realm) - << test_label; - EXPECT_EQ(GURL(expectation->origin), form->origin) << test_label; - EXPECT_EQ(GURL(expectation->action), form->action) << test_label; - EXPECT_EQ(std::wstring(expectation->submit_element), form->submit_element) - << test_label; - EXPECT_EQ(std::wstring(expectation->username_element), - form->username_element) << test_label; - EXPECT_EQ(std::wstring(expectation->password_element), - form->password_element) << test_label; - if (expectation->username_value) { - EXPECT_EQ(std::wstring(expectation->username_value), - form->username_value) << test_label; - EXPECT_EQ(std::wstring(expectation->password_value), - form->password_value) << test_label; - } else { - EXPECT_TRUE(form->blacklisted_by_user) << test_label; - } - EXPECT_EQ(expectation->preferred, form->preferred) << test_label; - EXPECT_EQ(expectation->ssl_valid, form->ssl_valid) << test_label; - EXPECT_DOUBLE_EQ(expectation->creation_time, - form->date_created.ToDoubleT()) << test_label; - } -} - -// Deletes all the PasswordForms in |forms|, and clears the vector. -static void DeletePasswordForms(std::vector<PasswordForm*>* forms) { - for (std::vector<PasswordForm*>::iterator i = forms->begin(); - i != forms->end(); ++i) { - delete *i; - } - forms->clear(); -} TEST(PasswordStoreMacTest, TestFormMerge) { // Set up a bunch of test data to use in varying combinations. PasswordFormData keychain_user_1 = diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index e7e47e9..a78fd07 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -3448,6 +3448,8 @@ 'browser/importer/firefox_importer_unittest.cc', 'browser/importer/importer_unittest.cc', 'browser/importer/toolbar_importer_unittest.cc', + 'browser/keychain_mock_mac.cc', + 'browser/keychain_mock_mac.h', 'browser/login_prompt_unittest.cc', 'browser/metrics/metrics_log_unittest.cc', 'browser/metrics/metrics_response_unittest.cc', |