blob: 1b72bdb74f32dbf75a9e98fed93c3c0814cc674e [file] [log] [blame]
// Copyright (c) 2012 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 <stdarg.h>
#include "base/basictypes.h"
#include "base/prefs/pref_service.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/password_manager/native_backend_gnome_x.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/autofill/core/common/password_form.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
using autofill::PasswordForm;
using content::BrowserThread;
namespace {
// What follows is a very simple implementation of the subset of the GNOME
// Keyring API that we actually use. It gets substituted for the real one by
// MockGnomeKeyringLoader, which hooks into the facility normally used to load
// the GNOME Keyring library at runtime to avoid a static dependency on it.
struct MockKeyringItem {
MockKeyringItem() {}
MockKeyringItem(const char* keyring,
const std::string& display_name,
const std::string& password)
: keyring(keyring ? keyring : "login"),
display_name(display_name),
password(password) {}
struct ItemAttribute {
ItemAttribute() : type(UINT32), value_uint32(0) {}
explicit ItemAttribute(uint32_t value)
: type(UINT32), value_uint32(value) {}
explicit ItemAttribute(const std::string& value)
: type(STRING), value_string(value) {}
bool Equals(const ItemAttribute& x) const {
if (type != x.type) return false;
return (type == STRING) ? value_string == x.value_string
: value_uint32 == x.value_uint32;
}
enum Type { UINT32, STRING } type;
uint32_t value_uint32;
std::string value_string;
};
typedef std::map<std::string, ItemAttribute> attribute_map;
typedef std::vector<std::pair<std::string, ItemAttribute> > attribute_query;
bool Matches(const attribute_query& query) const {
// The real GNOME Keyring doesn't match empty queries.
if (query.empty()) return false;
for (size_t i = 0; i < query.size(); ++i) {
attribute_map::const_iterator match = attributes.find(query[i].first);
if (match == attributes.end()) return false;
if (!match->second.Equals(query[i].second)) return false;
}
return true;
}
std::string keyring;
std::string display_name;
std::string password;
attribute_map attributes;
};
// The list of all keyring items we have stored.
std::vector<MockKeyringItem> mock_keyring_items;
bool mock_keyring_reject_local_ids = false;
bool IsStringAttribute(const GnomeKeyringPasswordSchema* schema,
const std::string& name) {
for (size_t i = 0; schema->attributes[i].name; ++i)
if (name == schema->attributes[i].name)
return schema->attributes[i].type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
NOTREACHED() << "Requested type of nonexistent attribute";
return false;
}
gboolean mock_gnome_keyring_is_available() {
return true;
}
gpointer mock_gnome_keyring_store_password(
const GnomeKeyringPasswordSchema* schema,
const gchar* keyring,
const gchar* display_name,
const gchar* password,
GnomeKeyringOperationDoneCallback callback,
gpointer data,
GDestroyNotify destroy_data,
...) {
mock_keyring_items.push_back(
MockKeyringItem(keyring, display_name, password));
MockKeyringItem* item = &mock_keyring_items.back();
const std::string keyring_desc =
keyring ? base::StringPrintf("keyring %s", keyring)
: std::string("default keyring");
VLOG(1) << "Adding item with origin " << display_name
<< " to " << keyring_desc;
va_list ap;
va_start(ap, destroy_data);
char* name;
while ((name = va_arg(ap, gchar*))) {
if (IsStringAttribute(schema, name)) {
item->attributes[name] =
MockKeyringItem::ItemAttribute(va_arg(ap, gchar*));
VLOG(1) << "Adding item attribute " << name
<< ", value '" << item->attributes[name].value_string << "'";
} else {
item->attributes[name] =
MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t));
VLOG(1) << "Adding item attribute " << name
<< ", value " << item->attributes[name].value_uint32;
}
}
va_end(ap);
// As a hack to ease testing migration, make it possible to reject the new
// format for the app string. This way we can add them easily to migrate.
if (mock_keyring_reject_local_ids) {
MockKeyringItem::attribute_map::iterator it =
item->attributes.find("application");
if (it != item->attributes.end() &&
it->second.type == MockKeyringItem::ItemAttribute::STRING &&
base::StringPiece(it->second.value_string).starts_with("chrome-")) {
mock_keyring_items.pop_back();
// GnomeKeyringResult, data
callback(GNOME_KEYRING_RESULT_IO_ERROR, data);
return NULL;
}
}
// GnomeKeyringResult, data
callback(GNOME_KEYRING_RESULT_OK, data);
return NULL;
}
gpointer mock_gnome_keyring_delete_password(
const GnomeKeyringPasswordSchema* schema,
GnomeKeyringOperationDoneCallback callback,
gpointer data,
GDestroyNotify destroy_data,
...) {
MockKeyringItem::attribute_query query;
va_list ap;
va_start(ap, destroy_data);
char* name;
while ((name = va_arg(ap, gchar*))) {
if (IsStringAttribute(schema, name)) {
query.push_back(make_pair(std::string(name),
MockKeyringItem::ItemAttribute(va_arg(ap, gchar*))));
VLOG(1) << "Querying with item attribute " << name
<< ", value '" << query.back().second.value_string << "'";
} else {
query.push_back(make_pair(std::string(name),
MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t))));
VLOG(1) << "Querying with item attribute " << name
<< ", value " << query.back().second.value_uint32;
}
}
va_end(ap);
bool deleted = false;
for (size_t i = mock_keyring_items.size(); i > 0; --i) {
const MockKeyringItem* item = &mock_keyring_items[i - 1];
if (item->Matches(query)) {
VLOG(1) << "Deleting item with origin " << item->display_name;
mock_keyring_items.erase(mock_keyring_items.begin() + (i - 1));
deleted = true;
}
}
// GnomeKeyringResult, data
callback(deleted ? GNOME_KEYRING_RESULT_OK
: GNOME_KEYRING_RESULT_NO_MATCH, data);
return NULL;
}
gpointer mock_gnome_keyring_find_itemsv(
GnomeKeyringItemType type,
GnomeKeyringOperationGetListCallback callback,
gpointer data,
GDestroyNotify destroy_data,
...) {
MockKeyringItem::attribute_query query;
va_list ap;
va_start(ap, destroy_data);
char* name;
while ((name = va_arg(ap, gchar*))) {
// Really a GnomeKeyringAttributeType, but promoted to int through ...
if (va_arg(ap, int) == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) {
query.push_back(make_pair(std::string(name),
MockKeyringItem::ItemAttribute(va_arg(ap, gchar*))));
VLOG(1) << "Querying with item attribute " << name
<< ", value '" << query.back().second.value_string << "'";
} else {
query.push_back(make_pair(std::string(name),
MockKeyringItem::ItemAttribute(va_arg(ap, uint32_t))));
VLOG(1) << "Querying with item attribute " << name
<< ", value " << query.back().second.value_uint32;
}
}
va_end(ap);
// Find matches and add them to a list of results.
GList* results = NULL;
for (size_t i = 0; i < mock_keyring_items.size(); ++i) {
const MockKeyringItem* item = &mock_keyring_items[i];
if (item->Matches(query)) {
GnomeKeyringFound* found = new GnomeKeyringFound;
found->keyring = strdup(item->keyring.c_str());
found->item_id = i;
found->attributes = gnome_keyring_attribute_list_new();
for (MockKeyringItem::attribute_map::const_iterator it =
item->attributes.begin();
it != item->attributes.end();
++it) {
if (it->second.type == MockKeyringItem::ItemAttribute::STRING) {
gnome_keyring_attribute_list_append_string(
found->attributes, it->first.c_str(),
it->second.value_string.c_str());
} else {
gnome_keyring_attribute_list_append_uint32(
found->attributes, it->first.c_str(),
it->second.value_uint32);
}
}
found->secret = strdup(item->password.c_str());
results = g_list_prepend(results, found);
}
}
// GnomeKeyringResult, GList*, data
callback(results ? GNOME_KEYRING_RESULT_OK
: GNOME_KEYRING_RESULT_NO_MATCH, results, data);
// Now free the list of results.
GList* element = g_list_first(results);
while (element) {
GnomeKeyringFound* found = static_cast<GnomeKeyringFound*>(element->data);
free(found->keyring);
gnome_keyring_attribute_list_free(found->attributes);
free(found->secret);
delete found;
element = g_list_next(element);
}
g_list_free(results);
return NULL;
}
const gchar* mock_gnome_keyring_result_to_message(GnomeKeyringResult res) {
return "mock keyring simulating failure";
}
// Inherit to get access to protected fields.
class MockGnomeKeyringLoader : public GnomeKeyringLoader {
public:
static bool LoadMockGnomeKeyring() {
#define GNOME_KEYRING_ASSIGN_POINTER(name) \
gnome_keyring_##name = &mock_gnome_keyring_##name;
GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_ASSIGN_POINTER)
#undef GNOME_KEYRING_ASSIGN_POINTER
keyring_loaded = true;
// Reset the state of the mock library.
mock_keyring_items.clear();
mock_keyring_reject_local_ids = false;
return true;
}
};
} // anonymous namespace
class NativeBackendGnomeTest : public testing::Test {
protected:
NativeBackendGnomeTest()
: ui_thread_(BrowserThread::UI, &message_loop_),
db_thread_(BrowserThread::DB) {
}
virtual void SetUp() {
ASSERT_TRUE(db_thread_.Start());
MockGnomeKeyringLoader::LoadMockGnomeKeyring();
form_google_.origin = GURL("http://www.google.com/");
form_google_.action = GURL("http://www.google.com/login");
form_google_.username_element = UTF8ToUTF16("user");
form_google_.username_value = UTF8ToUTF16("joeschmoe");
form_google_.password_element = UTF8ToUTF16("pass");
form_google_.password_value = UTF8ToUTF16("seekrit");
form_google_.submit_element = UTF8ToUTF16("submit");
form_google_.signon_realm = "Google";
form_isc_.origin = GURL("http://www.isc.org/");
form_isc_.action = GURL("http://www.isc.org/auth");
form_isc_.username_element = UTF8ToUTF16("id");
form_isc_.username_value = UTF8ToUTF16("janedoe");
form_isc_.password_element = UTF8ToUTF16("passwd");
form_isc_.password_value = UTF8ToUTF16("ihazabukkit");
form_isc_.submit_element = UTF8ToUTF16("login");
form_isc_.signon_realm = "ISC";
}
virtual void TearDown() {
base::MessageLoop::current()->PostTask(FROM_HERE,
base::MessageLoop::QuitClosure());
base::MessageLoop::current()->Run();
db_thread_.Stop();
}
void RunBothThreads() {
// First we post a message to the DB thread that will run after all other
// messages that have been posted to the DB thread (we don't expect more
// to be posted), which posts a message to the UI thread to quit the loop.
// That way we can run both loops and be sure that the UI thread loop will
// quit so we can get on with the rest of the test.
BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
base::Bind(&PostQuitTask, &message_loop_));
base::MessageLoop::current()->Run();
}
static void PostQuitTask(base::MessageLoop* loop) {
loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
}
void CheckUint32Attribute(const MockKeyringItem* item,
const std::string& attribute,
uint32_t value) {
MockKeyringItem::attribute_map::const_iterator it =
item->attributes.find(attribute);
EXPECT_NE(item->attributes.end(), it);
if (it != item->attributes.end()) {
EXPECT_EQ(MockKeyringItem::ItemAttribute::UINT32, it->second.type);
EXPECT_EQ(value, it->second.value_uint32);
}
}
void CheckStringAttribute(const MockKeyringItem* item,
const std::string& attribute,
const std::string& value) {
MockKeyringItem::attribute_map::const_iterator it =
item->attributes.find(attribute);
EXPECT_NE(item->attributes.end(), it);
if (it != item->attributes.end()) {
EXPECT_EQ(MockKeyringItem::ItemAttribute::STRING, it->second.type);
EXPECT_EQ(value, it->second.value_string);
}
}
void CheckMockKeyringItem(const MockKeyringItem* item,
const PasswordForm& form,
const std::string& app_string) {
// We always add items to the login keyring.
EXPECT_EQ("login", item->keyring);
EXPECT_EQ(form.origin.spec(), item->display_name);
EXPECT_EQ(UTF16ToUTF8(form.password_value), item->password);
EXPECT_EQ(13u, item->attributes.size());
CheckStringAttribute(item, "origin_url", form.origin.spec());
CheckStringAttribute(item, "action_url", form.action.spec());
CheckStringAttribute(item, "username_element",
UTF16ToUTF8(form.username_element));
CheckStringAttribute(item, "username_value",
UTF16ToUTF8(form.username_value));
CheckStringAttribute(item, "password_element",
UTF16ToUTF8(form.password_element));
CheckStringAttribute(item, "submit_element",
UTF16ToUTF8(form.submit_element));
CheckStringAttribute(item, "signon_realm", form.signon_realm);
CheckUint32Attribute(item, "ssl_valid", form.ssl_valid);
CheckUint32Attribute(item, "preferred", form.preferred);
// We don't check the date created. It varies.
CheckUint32Attribute(item, "blacklisted_by_user", form.blacklisted_by_user);
CheckUint32Attribute(item, "scheme", form.scheme);
CheckStringAttribute(item, "application", app_string);
}
base::MessageLoopForUI message_loop_;
content::TestBrowserThread ui_thread_;
content::TestBrowserThread db_thread_;
TestingProfile profile_;
// Provide some test forms to avoid having to set them up in each test.
PasswordForm form_google_;
PasswordForm form_isc_;
};
TEST_F(NativeBackendGnomeTest, BasicAddLogin) {
// Pretend that the migration has already taken place.
profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_));
RunBothThreads();
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
}
TEST_F(NativeBackendGnomeTest, BasicListLogins) {
// Pretend that the migration has already taken place.
profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult( &NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_));
std::vector<PasswordForm*> form_list;
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(
base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
base::Unretained(&backend), &form_list));
RunBothThreads();
// Quick check that we got something back.
EXPECT_EQ(1u, form_list.size());
STLDeleteElements(&form_list);
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
}
TEST_F(NativeBackendGnomeTest, BasicRemoveLogin) {
// Pretend that the migration has already taken place.
profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_));
RunBothThreads();
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin),
base::Unretained(&backend), form_google_));
RunBothThreads();
EXPECT_EQ(0u, mock_keyring_items.size());
}
TEST_F(NativeBackendGnomeTest, RemoveNonexistentLogin) {
// Pretend that the migration has already taken place.
profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
// First add an unrelated login.
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_));
RunBothThreads();
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
// Attempt to remove a login that doesn't exist.
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin),
base::Unretained(&backend), form_isc_));
// Make sure we can still get the first form back.
std::vector<PasswordForm*> form_list;
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(
base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
base::Unretained(&backend), &form_list));
RunBothThreads();
// Quick check that we got something back.
EXPECT_EQ(1u, form_list.size());
STLDeleteElements(&form_list);
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
}
TEST_F(NativeBackendGnomeTest, AddDuplicateLogin) {
// Pretend that the migration has already taken place.
profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_));
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_));
RunBothThreads();
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
}
TEST_F(NativeBackendGnomeTest, ListLoginsAppends) {
// Pretend that the migration has already taken place.
profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_));
// Send the same request twice with the same list both times.
std::vector<PasswordForm*> form_list;
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(
base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
base::Unretained(&backend), &form_list));
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(
base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
base::Unretained(&backend), &form_list));
RunBothThreads();
// Quick check that we got two results back.
EXPECT_EQ(2u, form_list.size());
STLDeleteElements(&form_list);
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome-42");
}
// TODO(mdm): add more basic (i.e. non-migration) tests here at some point.
TEST_F(NativeBackendGnomeTest, DISABLED_MigrateOneLogin) {
// Reject attempts to migrate so we can populate the store.
mock_keyring_reject_local_ids = true;
{
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_));
// Make sure we can get the form back even when migration is failing.
std::vector<PasswordForm*> form_list;
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(
base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
base::Unretained(&backend), &form_list));
RunBothThreads();
// Quick check that we got something back.
EXPECT_EQ(1u, form_list.size());
STLDeleteElements(&form_list);
}
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
// Now allow the migration.
mock_keyring_reject_local_ids = false;
{
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
// This should not trigger migration because there will be no results.
std::vector<PasswordForm*> form_list;
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::GetBlacklistLogins),
base::Unretained(&backend), &form_list));
RunBothThreads();
// Check that we got nothing back.
EXPECT_EQ(0u, form_list.size());
STLDeleteElements(&form_list);
}
// Check that the keyring is unmodified.
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
// Check that we haven't set the persistent preference.
EXPECT_FALSE(
profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
{
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
// Trigger the migration by looking something up.
std::vector<PasswordForm*> form_list;
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(
base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
base::Unretained(&backend), &form_list));
RunBothThreads();
// Quick check that we got something back.
EXPECT_EQ(1u, form_list.size());
STLDeleteElements(&form_list);
}
EXPECT_EQ(2u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
if (mock_keyring_items.size() > 1)
CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
// Check that we have set the persistent preference.
EXPECT_TRUE(
profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
}
TEST_F(NativeBackendGnomeTest, DISABLED_MigrateToMultipleProfiles) {
// Reject attempts to migrate so we can populate the store.
mock_keyring_reject_local_ids = true;
{
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_));
RunBothThreads();
}
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
// Now allow the migration.
mock_keyring_reject_local_ids = false;
{
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
// Trigger the migration by looking something up.
std::vector<PasswordForm*> form_list;
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(
base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
base::Unretained(&backend), &form_list));
RunBothThreads();
// Quick check that we got something back.
EXPECT_EQ(1u, form_list.size());
STLDeleteElements(&form_list);
}
EXPECT_EQ(2u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
if (mock_keyring_items.size() > 1)
CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
// Check that we have set the persistent preference.
EXPECT_TRUE(
profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
// Normally we'd actually have a different profile. But in the test just reset
// the profile's persistent pref; we pass in the local profile id anyway.
profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, false);
{
NativeBackendGnome backend(24, profile_.GetPrefs());
backend.Init();
// Trigger the migration by looking something up.
std::vector<PasswordForm*> form_list;
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(
base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
base::Unretained(&backend), &form_list));
RunBothThreads();
// Quick check that we got something back.
EXPECT_EQ(1u, form_list.size());
STLDeleteElements(&form_list);
}
EXPECT_EQ(3u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
if (mock_keyring_items.size() > 1)
CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
if (mock_keyring_items.size() > 2)
CheckMockKeyringItem(&mock_keyring_items[2], form_google_, "chrome-24");
}
TEST_F(NativeBackendGnomeTest, DISABLED_NoMigrationWithPrefSet) {
// Reject attempts to migrate so we can populate the store.
mock_keyring_reject_local_ids = true;
{
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_));
RunBothThreads();
}
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
// Now allow migration, but also pretend that the it has already taken place.
mock_keyring_reject_local_ids = false;
profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
{
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
// Trigger the migration by adding a new login.
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_isc_));
// Look up all logins; we expect only the one we added.
std::vector<PasswordForm*> form_list;
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(
base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
base::Unretained(&backend), &form_list));
RunBothThreads();
// Quick check that we got the right thing back.
EXPECT_EQ(1u, form_list.size());
if (form_list.size() > 0)
EXPECT_EQ(form_isc_.signon_realm, form_list[0]->signon_realm);
STLDeleteElements(&form_list);
}
EXPECT_EQ(2u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
if (mock_keyring_items.size() > 1)
CheckMockKeyringItem(&mock_keyring_items[1], form_isc_, "chrome-42");
}
TEST_F(NativeBackendGnomeTest, DISABLED_DeleteMigratedPasswordIsIsolated) {
// Reject attempts to migrate so we can populate the store.
mock_keyring_reject_local_ids = true;
{
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
base::Unretained(&backend), form_google_));
RunBothThreads();
}
EXPECT_EQ(1u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
// Now allow the migration.
mock_keyring_reject_local_ids = false;
{
NativeBackendGnome backend(42, profile_.GetPrefs());
backend.Init();
// Trigger the migration by looking something up.
std::vector<PasswordForm*> form_list;
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(
base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
base::Unretained(&backend), &form_list));
RunBothThreads();
// Quick check that we got something back.
EXPECT_EQ(1u, form_list.size());
STLDeleteElements(&form_list);
}
EXPECT_EQ(2u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
if (mock_keyring_items.size() > 1)
CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
// Check that we have set the persistent preference.
EXPECT_TRUE(
profile_.GetPrefs()->GetBoolean(prefs::kPasswordsUseLocalProfileId));
// Normally we'd actually have a different profile. But in the test just reset
// the profile's persistent pref; we pass in the local profile id anyway.
profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, false);
{
NativeBackendGnome backend(24, profile_.GetPrefs());
backend.Init();
// Trigger the migration by looking something up.
std::vector<PasswordForm*> form_list;
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(
base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
base::Unretained(&backend), &form_list));
RunBothThreads();
// Quick check that we got something back.
EXPECT_EQ(1u, form_list.size());
STLDeleteElements(&form_list);
// There should be three passwords now.
EXPECT_EQ(3u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
if (mock_keyring_items.size() > 1)
CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
if (mock_keyring_items.size() > 2)
CheckMockKeyringItem(&mock_keyring_items[2], form_google_, "chrome-24");
// Now delete the password from this second profile.
BrowserThread::PostTask(
BrowserThread::DB, FROM_HERE,
base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin),
base::Unretained(&backend), form_google_));
RunBothThreads();
// The other two copies of the password in different profiles should remain.
EXPECT_EQ(2u, mock_keyring_items.size());
if (mock_keyring_items.size() > 0)
CheckMockKeyringItem(&mock_keyring_items[0], form_google_, "chrome");
if (mock_keyring_items.size() > 1)
CheckMockKeyringItem(&mock_keyring_items[1], form_google_, "chrome-42");
}
}