diff options
-rw-r--r-- | build/linux/system.gyp | 35 | ||||
-rw-r--r-- | chrome/browser/password_manager/native_backend_gnome_x.cc | 94 | ||||
-rw-r--r-- | chrome/browser/password_manager/native_backend_gnome_x.h | 46 | ||||
-rw-r--r-- | chrome/browser/password_manager/native_backend_gnome_x_unittest.cc | 767 | ||||
-rw-r--r-- | chrome/browser/password_manager/password_store_default_unittest.cc | 6 | ||||
-rw-r--r-- | chrome/browser/password_manager/password_store_x_unittest.cc | 74 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 44 |
8 files changed, 958 insertions, 110 deletions
diff --git a/build/linux/system.gyp b/build/linux/system.gyp index 6e8a8be..8d82eab 100644 --- a/build/linux/system.gyp +++ b/build/linux/system.gyp @@ -340,7 +340,7 @@ }]] }, { - 'target_name': 'gnome-keyring', + 'target_name': 'gnome_keyring', 'type': 'settings', 'conditions': [ ['use_gnome_keyring==1', { @@ -379,6 +379,39 @@ ], }, { + # The unit tests use a few convenience functions from the GNOME + # Keyring library directly. We ignore linux_link_gnome_keyring and + # link directly in this version of the target to allow this. + # *** Do not use this target in the main binary! *** + 'target_name': 'gnome_keyring_direct', + 'type': 'settings', + 'conditions': [ + ['use_gnome_keyring==1', { + 'direct_dependent_settings': { + 'cflags': [ + '<!@(<(pkg-config) --cflags gnome-keyring-1)', + ], + 'defines': [ + 'USE_GNOME_KEYRING', + ], + 'conditions': [ + ['linux_link_gnome_keyring==0', { + 'defines': ['DLOPEN_GNOME_KEYRING'], + }], + ], + }, + 'link_settings': { + 'ldflags': [ + '<!@(<(pkg-config) --libs-only-L --libs-only-other gnome-keyring-1)', + ], + 'libraries': [ + '<!@(<(pkg-config) --libs-only-l gnome-keyring-1)', + ], + }, + }], + ], + }, + { 'target_name': 'dbus-glib', 'type': 'settings', 'direct_dependent_settings': { diff --git a/chrome/browser/password_manager/native_backend_gnome_x.cc b/chrome/browser/password_manager/native_backend_gnome_x.cc index 80c9c80..2da9b5a 100644 --- a/chrome/browser/password_manager/native_backend_gnome_x.cc +++ b/chrome/browser/password_manager/native_backend_gnome_x.cc @@ -23,66 +23,28 @@ using webkit_glue::PasswordForm; -namespace { - -// Many of the gnome_keyring_* functions use variable arguments, which makes -// them difficult if not impossible to wrap in C. Therefore, we want the -// actual uses below to either call the functions directly (if we are linking -// against libgnome-keyring), or call them via appropriately-typed function -// pointers (if we are dynamically loading libgnome-keyring). +#define GNOME_KEYRING_DEFINE_POINTER(name) \ + typeof(&::gnome_keyring_##name) GnomeKeyringLoader::gnome_keyring_##name; +GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_POINTER) +#undef GNOME_KEYRING_DEFINE_POINTER -// Thus, instead of making a wrapper class with two implementations, we use -// the preprocessor to rename the calls below in the dynamic load case, and -// provide a function to initialize a set of function pointers that have the -// alternate names. We also make sure the types are correct, since otherwise -// dynamic loading like this would leave us vulnerable to signature changes. +bool GnomeKeyringLoader::keyring_loaded = false; #if defined(DLOPEN_GNOME_KEYRING) -// Call a given parameter with the name of each function we use from GNOME -// Keyring. -#define GNOME_KEYRING_FOR_EACH_FUNC(F) \ - F(is_available) \ - F(store_password) \ - F(delete_password) \ - F(find_itemsv) \ - F(result_to_message) - -// Define the actual function pointers that we'll use in application code. -#define GNOME_KEYRING_DEFINE_WRAPPER(name) \ - typeof(&gnome_keyring_##name) wrap_gnome_keyring_##name; -GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_WRAPPER) -#undef GNOME_KEYRING_DEFINE_WRAPPER - -// Make it easy to initialize the function pointers above with a loop below. -#define GNOME_KEYRING_FUNCTION(name) \ - {"gnome_keyring_"#name, reinterpret_cast<void**>(&wrap_gnome_keyring_##name)}, -const struct { - const char* name; - void** pointer; -} gnome_keyring_functions[] = { - GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION) +#define GNOME_KEYRING_FUNCTION_INFO(name) \ + {"gnome_keyring_"#name, reinterpret_cast<void**>(&gnome_keyring_##name)}, +const GnomeKeyringLoader::FunctionInfo GnomeKeyringLoader::functions[] = { + GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION_INFO) {NULL, NULL} }; -#undef GNOME_KEYRING_FUNCTION - -#undef GNOME_KEYRING_FOR_EACH_FUNC - -// Allow application code below to use the normal function names, but actually -// end up using the function pointers above instead. -#define gnome_keyring_is_available \ - wrap_gnome_keyring_is_available -#define gnome_keyring_store_password \ - wrap_gnome_keyring_store_password -#define gnome_keyring_delete_password \ - wrap_gnome_keyring_delete_password -#define gnome_keyring_find_itemsv \ - wrap_gnome_keyring_find_itemsv -#define gnome_keyring_result_to_message \ - wrap_gnome_keyring_result_to_message +#undef GNOME_KEYRING_FUNCTION_INFO /* Load the library and initialize the function pointers. */ -bool LoadGnomeKeyring() { +bool GnomeKeyringLoader::LoadGnomeKeyring() { + if (keyring_loaded) + return true; + void* handle = dlopen("libgnome-keyring.so.0", RTLD_NOW | RTLD_GLOBAL); if (!handle) { // We wanted to use GNOME Keyring, but we couldn't load it. Warn, because @@ -91,30 +53,40 @@ bool LoadGnomeKeyring() { LOG(WARNING) << "Could not load libgnome-keyring.so.0: " << dlerror(); return false; } - for (size_t i = 0; gnome_keyring_functions[i].name; ++i) { + + for (size_t i = 0; functions[i].name; ++i) { dlerror(); - *gnome_keyring_functions[i].pointer = - dlsym(handle, gnome_keyring_functions[i].name); + *functions[i].pointer = dlsym(handle, functions[i].name); const char* error = dlerror(); if (error) { LOG(ERROR) << "Unable to load symbol " - << gnome_keyring_functions[i].name << ": " << error; + << functions[i].name << ": " << error; dlclose(handle); return false; } } + + keyring_loaded = true; // We leak the library handle. That's OK: this function is called only once. return true; } -#else // !defined(DLOPEN_GNOME_KEYRING) +#else // defined(DLOPEN_GNOME_KEYRING) -bool LoadGnomeKeyring() { - // We don't need to do anything here. +bool GnomeKeyringLoader::LoadGnomeKeyring() { + if (keyring_loaded) + return true; +#define GNOME_KEYRING_ASSIGN_POINTER(name) \ + gnome_keyring_##name = &::gnome_keyring_##name; + GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_ASSIGN_POINTER) +#undef GNOME_KEYRING_ASSIGN_POINTER + keyring_loaded = true; return true; } -#endif // !defined(DLOPEN_GNOME_KEYRING) +#endif // defined(DLOPEN_GNOME_KEYRING) + +namespace { const char kGnomeKeyringAppString[] = "chrome"; @@ -221,7 +193,7 @@ const GnomeKeyringPasswordSchema kGnomeSchema = { // a WaitResult() method should be called to wait for the result. Each instance // supports only one outstanding method at a time, though multiple instances may // be used in parallel. -class GKRMethod { +class GKRMethod : public GnomeKeyringLoader { public: typedef NativeBackendGnome::PasswordFormList PasswordFormList; diff --git a/chrome/browser/password_manager/native_backend_gnome_x.h b/chrome/browser/password_manager/native_backend_gnome_x.h index 1740c42..88f4250 100644 --- a/chrome/browser/password_manager/native_backend_gnome_x.h +++ b/chrome/browser/password_manager/native_backend_gnome_x.h @@ -6,6 +6,8 @@ #define CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_GNOME_X_H_ #pragma once +#include <gnome-keyring.h> + #include <string> #include "base/basictypes.h" @@ -19,8 +21,50 @@ namespace webkit_glue { struct PasswordForm; } +// Many of the gnome_keyring_* functions use variable arguments, which makes +// them difficult if not impossible to truly wrap in C. Therefore, we use +// appropriately-typed function pointers and scoping to make the fact that we +// might be dynamically loading the library almost invisible. As a bonus, we +// also get a simple way to mock the library for testing. Classes that inherit +// from GnomeKeyringLoader will use its versions of the gnome_keyring_* +// functions. Note that it has only static fields. +class GnomeKeyringLoader { + protected: + static bool LoadGnomeKeyring(); + +// Call a given parameter with the name of each function we use from GNOME +// Keyring. Make sure to adjust the unit test if you change these. +#define GNOME_KEYRING_FOR_EACH_FUNC(F) \ + F(is_available) \ + F(store_password) \ + F(delete_password) \ + F(find_itemsv) \ + F(result_to_message) + +// Declare the actual function pointers that we'll use in client code. +#define GNOME_KEYRING_DECLARE_POINTER(name) \ + static typeof(&::gnome_keyring_##name) gnome_keyring_##name; + GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DECLARE_POINTER) +#undef GNOME_KEYRING_DECLARE_POINTER + + // Set to true if LoadGnomeKeyring() has already succeeded. + static bool keyring_loaded; + + private: +#if defined(DLOPEN_GNOME_KEYRING) + struct FunctionInfo { + const char* name; + void** pointer; + }; + + // Make it easy to initialize the function pointers in LoadGnomeKeyring(). + static const FunctionInfo functions[]; +#endif // defined(DLOPEN_GNOME_KEYRING) +}; + // NativeBackend implementation using GNOME Keyring. -class NativeBackendGnome : public PasswordStoreX::NativeBackend { +class NativeBackendGnome : public PasswordStoreX::NativeBackend, + public GnomeKeyringLoader { public: NativeBackendGnome(LocalProfileId id, PrefService* prefs); diff --git a/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc b/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc new file mode 100644 index 0000000..232278b --- /dev/null +++ b/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc @@ -0,0 +1,767 @@ +// Copyright (c) 2011 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/stl_util-inl.h" +#include "base/string_util.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/password_manager/native_backend_gnome_x.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/testing_profile.h" +#include "testing/gtest/include/gtest/gtest.h" + +using webkit_glue::PasswordForm; + +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 { 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 ? 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); + 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 + +// NativeBackendGnome isn't reference counted, but in these unit tests that +// won't be a problem as it always outlives the threads we post tasks to. +template<> +struct RunnableMethodTraits<NativeBackendGnome> { + void RetainCallee(NativeBackendGnome*) {} + void ReleaseCallee(NativeBackendGnome*) {} +}; + +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(); + profile_.reset(new TestingProfile()); + + 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() { + MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask); + 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, + NewRunnableFunction(&PostQuitTask, &message_loop_)); + MessageLoop::current()->Run(); + } + + static void PostQuitTask(MessageLoop* loop) { + loop->PostTask(FROM_HERE, new MessageLoop::QuitTask); + } + + 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); + } + + MessageLoopForUI message_loop_; + BrowserThread ui_thread_; + BrowserThread db_thread_; + + scoped_ptr<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, + NewRunnableMethod(&backend, + &NativeBackendGnome::AddLogin, + 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, + NewRunnableMethod(&backend, + &NativeBackendGnome::AddLogin, + form_google_)); + + std::vector<PasswordForm*> form_list; + BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + NewRunnableMethod(&backend, + &NativeBackendGnome::GetAutofillableLogins, + &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, + NewRunnableMethod(&backend, + &NativeBackendGnome::AddLogin, + 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, + NewRunnableMethod(&backend, + &NativeBackendGnome::RemoveLogin, + 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, + NewRunnableMethod(&backend, + &NativeBackendGnome::AddLogin, + 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, + NewRunnableMethod(&backend, + &NativeBackendGnome::RemoveLogin, + form_isc_)); + + // Make sure we can still get the first form back. + std::vector<PasswordForm*> form_list; + BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + NewRunnableMethod(&backend, + &NativeBackendGnome::GetAutofillableLogins, + &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, + NewRunnableMethod(&backend, + &NativeBackendGnome::AddLogin, + form_google_)); + BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + NewRunnableMethod(&backend, + &NativeBackendGnome::AddLogin, + 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"); +} + +// TODO(mdm): add more basic (i.e. non-migration) tests here at some point. + +TEST_F(NativeBackendGnomeTest, 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, + NewRunnableMethod(&backend, + &NativeBackendGnome::AddLogin, + 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, + NewRunnableMethod(&backend, + &NativeBackendGnome::GetAutofillableLogins, + &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, + NewRunnableMethod(&backend, + &NativeBackendGnome::GetBlacklistLogins, + &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, + NewRunnableMethod(&backend, + &NativeBackendGnome::GetAutofillableLogins, + &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, 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, + NewRunnableMethod(&backend, + &NativeBackendGnome::AddLogin, + 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, + NewRunnableMethod(&backend, + &NativeBackendGnome::GetAutofillableLogins, + &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, + NewRunnableMethod(&backend, + &NativeBackendGnome::GetAutofillableLogins, + &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, 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, + NewRunnableMethod(&backend, + &NativeBackendGnome::AddLogin, + 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, + NewRunnableMethod(&backend, + &NativeBackendGnome::AddLogin, + form_isc_)); + + // Look up all logins; we expect only the one we added. + std::vector<PasswordForm*> form_list; + BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, + NewRunnableMethod(&backend, + &NativeBackendGnome::GetAutofillableLogins, + &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"); +} diff --git a/chrome/browser/password_manager/password_store_default_unittest.cc b/chrome/browser/password_manager/password_store_default_unittest.cc index 06ec903..57651c4 100644 --- a/chrome/browser/password_manager/password_store_default_unittest.cc +++ b/chrome/browser/password_manager/password_store_default_unittest.cc @@ -151,7 +151,7 @@ MATCHER(EmptyWDResult, "") { } TEST_F(PasswordStoreDefaultTest, NonASCIIData) { - // Prentend that the migration has already taken place. + // Pretend that the migration has already taken place. profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated, true, PrefService::UNSYNCABLE_PREF); @@ -399,7 +399,7 @@ TEST_F(PasswordStoreDefaultTest, MigrationAlreadyDone) { new SignalingTask(&done)); done.Wait(); - // Prentend that the migration has already taken place. + // Pretend that the migration has already taken place. profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated, true, PrefService::UNSYNCABLE_PREF); @@ -431,7 +431,7 @@ TEST_F(PasswordStoreDefaultTest, MigrationAlreadyDone) { } TEST_F(PasswordStoreDefaultTest, Notifications) { - // Prentend that the migration has already taken place. + // Pretend that the migration has already taken place. profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated, true, PrefService::UNSYNCABLE_PREF); diff --git a/chrome/browser/password_manager/password_store_x_unittest.cc b/chrome/browser/password_manager/password_store_x_unittest.cc index 1a6b9bd..efcec40 100644 --- a/chrome/browser/password_manager/password_store_x_unittest.cc +++ b/chrome/browser/password_manager/password_store_x_unittest.cc @@ -3,20 +3,28 @@ // found in the LICENSE file. #include "base/basictypes.h" +#include "base/file_util.h" +#include "base/platform_file.h" #include "base/scoped_temp_dir.h" #include "base/stl_util-inl.h" #include "base/string_util.h" #include "base/synchronization/waitable_event.h" #include "base/time.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/password_manager/password_form_data.h" #include "chrome/browser/password_manager/password_store_change.h" +#include "chrome/browser/password_manager/password_store_consumer.h" #include "chrome/browser/password_manager/password_store_x.h" +#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/webdata/web_data_service.h" +#include "chrome/common/chrome_notification_types.h" #include "chrome/common/pref_names.h" #include "chrome/test/signaling_task.h" #include "chrome/test/testing_profile.h" +#include "content/common/notification_details.h" #include "content/common/notification_observer_mock.h" -#include "content/common/notification_service.h" +#include "content/common/notification_registrar.h" +#include "content/common/notification_source.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -54,12 +62,14 @@ class DBThreadObserverHelper public: DBThreadObserverHelper() : done_event_(true, false) {} - void Init() { + void Init(PasswordStore* password_store) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); BrowserThread::PostTask( BrowserThread::DB, FROM_HERE, - NewRunnableMethod(this, &DBThreadObserverHelper::AddObserverTask)); + NewRunnableMethod(this, + &DBThreadObserverHelper::AddObserverTask, + make_scoped_refptr(password_store))); done_event_.Wait(); } @@ -75,11 +85,11 @@ class DBThreadObserverHelper protected: friend class base::RefCountedThreadSafe<DBThreadObserverHelper>; - void AddObserverTask() { + void AddObserverTask(PasswordStore* password_store) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); registrar_.Add(&observer_, - chrome::LOGINS_CHANGED, - NotificationService::AllSources()); + chrome::NOTIFICATION_LOGINS_CHANGED, + Source<PasswordStore>(password_store)); done_event_.Signal(); } @@ -357,9 +367,9 @@ TEST_P(PasswordStoreXTest, WDSMigration) { // Initializing the PasswordStore should trigger a migration. scoped_refptr<PasswordStoreX> store( new PasswordStoreX(login_db_.release(), - profile_.get(), - wds_.get(), - GetBackend())); + profile_.get(), + wds_.get(), + GetBackend())); store->Init(); // Check that the migration preference has not been initialized. @@ -437,7 +447,8 @@ TEST_P(PasswordStoreXTest, WDSMigration) { STLDeleteElements(&expected_autofillable); STLDeleteElements(&expected_blacklisted); - store->Shutdown(); + // Public in PasswordStore, protected in PasswordStoreX. + static_cast<PasswordStore*>(store)->Shutdown(); } TEST_P(PasswordStoreXTest, WDSMigrationAlreadyDone) { @@ -473,7 +484,7 @@ TEST_P(PasswordStoreXTest, WDSMigrationAlreadyDone) { new SignalingTask(&done)); done.Wait(); - // Prentend that the migration has already taken place. + // Pretend that the migration has already taken place. profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated, true, PrefService::UNSYNCABLE_PREF); @@ -481,9 +492,9 @@ TEST_P(PasswordStoreXTest, WDSMigrationAlreadyDone) { // Initializing the PasswordStore shouldn't trigger a migration. scoped_refptr<PasswordStoreX> store( new PasswordStoreX(login_db_.release(), - profile_.get(), - wds_.get(), - GetBackend())); + profile_.get(), + wds_.get(), + GetBackend())); store->Init(); MockPasswordStoreConsumer consumer; @@ -503,7 +514,8 @@ TEST_P(PasswordStoreXTest, WDSMigrationAlreadyDone) { STLDeleteElements(&unexpected_autofillable); - store->Shutdown(); + // Public in PasswordStore, protected in PasswordStoreX. + static_cast<PasswordStore*>(store)->Shutdown(); } TEST_P(PasswordStoreXTest, Notifications) { @@ -515,9 +527,9 @@ TEST_P(PasswordStoreXTest, Notifications) { // Initializing the PasswordStore shouldn't trigger a migration. scoped_refptr<PasswordStoreX> store( new PasswordStoreX(login_db_.release(), - profile_.get(), - wds_.get(), - GetBackend())); + profile_.get(), + wds_.get(), + GetBackend())); store->Init(); PasswordFormData form_data = @@ -534,15 +546,15 @@ TEST_P(PasswordStoreXTest, Notifications) { scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data)); scoped_refptr<DBThreadObserverHelper> helper = new DBThreadObserverHelper; - helper->Init(); + helper->Init(store); const PasswordStoreChange expected_add_changes[] = { PasswordStoreChange(PasswordStoreChange::ADD, *form), }; EXPECT_CALL(helper->observer(), - Observe(int(chrome::LOGINS_CHANGED), - NotificationService::AllSources(), + Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED), + Source<PasswordStore>(store), Property(&Details<const PasswordStoreChangeList>::ptr, Pointee(ElementsAreArray( expected_add_changes))))); @@ -565,8 +577,8 @@ TEST_P(PasswordStoreXTest, Notifications) { }; EXPECT_CALL(helper->observer(), - Observe(int(chrome::LOGINS_CHANGED), - NotificationService::AllSources(), + Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED), + Source<PasswordStore>(store), Property(&Details<const PasswordStoreChangeList>::ptr, Pointee(ElementsAreArray( expected_update_changes))))); @@ -584,8 +596,8 @@ TEST_P(PasswordStoreXTest, Notifications) { }; EXPECT_CALL(helper->observer(), - Observe(int(chrome::LOGINS_CHANGED), - NotificationService::AllSources(), + Observe(int(chrome::NOTIFICATION_LOGINS_CHANGED), + Source<PasswordStore>(store), Property(&Details<const PasswordStoreChangeList>::ptr, Pointee(ElementsAreArray( expected_delete_changes))))); @@ -598,7 +610,8 @@ TEST_P(PasswordStoreXTest, Notifications) { new SignalingTask(&done)); done.Wait(); - store->Shutdown(); + // Public in PasswordStore, protected in PasswordStoreX. + static_cast<PasswordStore*>(store)->Shutdown(); } TEST_P(PasswordStoreXTest, NativeMigration) { @@ -654,9 +667,9 @@ TEST_P(PasswordStoreXTest, NativeMigration) { // Initializing the PasswordStore shouldn't trigger a native migration (yet). scoped_refptr<PasswordStoreX> store( new PasswordStoreX(login_db_.release(), - profile_.get(), - wds_.get(), - GetBackend())); + profile_.get(), + wds_.get(), + GetBackend())); store->Init(); MockPasswordStoreConsumer consumer; @@ -740,7 +753,8 @@ TEST_P(PasswordStoreXTest, NativeMigration) { STLDeleteElements(&expected_autofillable); STLDeleteElements(&expected_blacklisted); - store->Shutdown(); + // Public in PasswordStore, protected in PasswordStoreX. + static_cast<PasswordStore*>(store)->Shutdown(); } INSTANTIATE_TEST_CASE_P(NoBackend, diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 7a45515..c2981fd 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -3857,7 +3857,7 @@ 'conditions': [ ['use_gnome_keyring==1', { 'dependencies': [ - '../build/linux/system.gyp:gnome-keyring', + '../build/linux/system.gyp:gnome_keyring', ], }], ['linux_breakpad==1', { diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index dbf61c0..a8ab510 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1490,12 +1490,14 @@ 'browser/password_manager/encryptor_password_mac_unittest.cc', 'browser/password_manager/encryptor_unittest.cc', 'browser/password_manager/login_database_unittest.cc', + 'browser/password_manager/native_backend_gnome_x_unittest.cc', 'browser/password_manager/password_form_data.cc', 'browser/password_manager/password_form_manager_unittest.cc', 'browser/password_manager/password_manager_unittest.cc', 'browser/password_manager/password_store_default_unittest.cc', 'browser/password_manager/password_store_mac_unittest.cc', 'browser/password_manager/password_store_win_unittest.cc', + 'browser/password_manager/password_store_x_unittest.cc', 'browser/plugin_exceptions_table_model_unittest.cc', 'browser/policy/asynchronous_policy_loader_unittest.cc', 'browser/policy/asynchronous_policy_provider_unittest.cc', @@ -1827,7 +1829,7 @@ 'browser/ui/content_settings/content_setting_bubble_model_unittest.cc', 'browser/ui/content_settings/content_setting_image_model_unittest.cc', 'browser/ui/find_bar/find_backend_unittest.cc', - 'browser/ui/gtk/accelerators_gtk_unittest.cc', + 'browser/ui/gtk/accelerators_gtk_unittest.cc', 'browser/ui/gtk/bookmarks/bookmark_bar_gtk_unittest.cc', 'browser/ui/gtk/bookmarks/bookmark_editor_gtk_unittest.cc', 'browser/ui/gtk/bookmarks/bookmark_utils_gtk_unittest.cc', @@ -2087,11 +2089,12 @@ }], ['chromeos==1', { 'sources/': [ + ['exclude', '^browser/notifications/desktop_notifications_unittest.cc'], + ['exclude', '^browser/password_manager/native_backend_gnome_x_unittest.cc'], + ['exclude', '^browser/renderer_host/gtk_key_bindings_handler_unittest.cc'], # TODO(thestig) Enable PrintPreviewUI tests on CrOS when # print preview is enabled on CrOS. - ['exclude', 'browser/notifications/desktop_notifications_unittest.cc'], - ['exclude', 'browser/renderer_host/gtk_key_bindings_handler_unittest.cc'], - ['exclude', 'browser/ui/webui/print_preview_ui_unittest.cc'], + ['exclude', '^browser/ui/webui/print_preview_ui_unittest.cc'], ], }, { # else: chromeos == 0 'sources/': [ @@ -2111,15 +2114,30 @@ }], ['toolkit_views==1', { 'sources!': [ - 'browser/ui/gtk/accelerators_gtk_unittest.cc', - 'browser/ui/gtk/bookmarks/bookmark_bar_gtk_unittest.cc', - 'browser/ui/gtk/bookmarks/bookmark_editor_gtk_unittest.cc', - 'browser/ui/gtk/gtk_chrome_shrinkable_hbox_unittest.cc', - 'browser/ui/gtk/gtk_expanded_container_unittest.cc', - 'browser/ui/gtk/gtk_theme_service_unittest.cc', - 'browser/ui/gtk/omnibox/omnibox_popup_view_gtk_unittest.cc', - 'browser/ui/gtk/reload_button_gtk_unittest.cc', - 'browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc', + 'browser/ui/gtk/accelerators_gtk_unittest.cc', + 'browser/ui/gtk/bookmarks/bookmark_bar_gtk_unittest.cc', + 'browser/ui/gtk/bookmarks/bookmark_editor_gtk_unittest.cc', + 'browser/ui/gtk/gtk_chrome_shrinkable_hbox_unittest.cc', + 'browser/ui/gtk/gtk_expanded_container_unittest.cc', + 'browser/ui/gtk/gtk_theme_service_unittest.cc', + 'browser/ui/gtk/omnibox/omnibox_popup_view_gtk_unittest.cc', + 'browser/ui/gtk/reload_button_gtk_unittest.cc', + 'browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc', + ], + }], + ['chromeos==0', { + 'conditions': [ + ['use_gnome_keyring==1', { + # We use a few library functions directly, so link directly. + 'dependencies': [ + '../build/linux/system.gyp:gnome_keyring_direct', + ], + }, { + # Disable the GNOME Keyring tests if we are not using it. + 'sources!': [ + 'browser/password_manager/native_backend_gnome_x_unittest.cc', + ], + }], ], }], ], |