| Index: chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
|
| ===================================================================
|
| --- chrome/browser/password_manager/native_backend_gnome_x_unittest.cc (revision 119287)
|
| +++ chrome/browser/password_manager/native_backend_gnome_x_unittest.cc (working copy)
|
| @@ -3,6 +3,7 @@
|
| // found in the LICENSE file.
|
|
|
| #include <stdarg.h>
|
| +#include <sys/socket.h>
|
|
|
| #include "base/basictypes.h"
|
| #include "base/stl_util.h"
|
| @@ -10,6 +11,9 @@
|
| #include "base/stringprintf.h"
|
| #include "base/time.h"
|
| #include "base/utf_string_conversions.h"
|
| +#include "chrome/browser/password_manager/keyring_proxy/gnome_keyring_loader.h"
|
| +#include "chrome/browser/password_manager/keyring_proxy/keyring_proxy.h"
|
| +#include "chrome/browser/password_manager/keyring_proxy/keyring_proxy_client.h"
|
| #include "chrome/browser/password_manager/native_backend_gnome_x.h"
|
| #include "chrome/browser/prefs/pref_service.h"
|
| #include "chrome/common/pref_names.h"
|
| @@ -26,6 +30,9 @@
|
| // 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.
|
| +// We also use some special glue code to link directly with the main keyring
|
| +// proxy code and thus call into the mock implementation in this process,
|
| +// rather than in a separate proxy process. (See below.)
|
|
|
| struct MockKeyringItem {
|
| MockKeyringItem() {}
|
| @@ -258,7 +265,7 @@
|
| }
|
|
|
| // Inherit to get access to protected fields.
|
| -class MockGnomeKeyringLoader : public GnomeKeyringLoader {
|
| +class MockGnomeKeyringLoader : public keyring_proxy::GnomeKeyringLoader {
|
| public:
|
| static bool LoadMockGnomeKeyring() {
|
| #define GNOME_KEYRING_ASSIGN_POINTER(name) \
|
| @@ -271,22 +278,71 @@
|
| mock_keyring_reject_local_ids = false;
|
| return true;
|
| }
|
| +
|
| + static void ResetGnomeKeyring() { keyring_loaded = false; }
|
| };
|
|
|
| +// Now that we can inject a mock GNOME Keyring API, we need to glue the keyring
|
| +// proxy client directly to the proxy code, which is linked into the unit test
|
| +// directly. We do this by creating a socket and giving one end to each, but we
|
| +// need some slightly adapted proxy and proxy client classes to make that work.
|
| +
|
| +class TestKeyringProxy : public keyring_proxy::KeyringProxy {
|
| + public:
|
| + explicit TestKeyringProxy(int fd) : KeyringProxy(fd, CreateOutput(fd)) {}
|
| + ~TestKeyringProxy() { fclose(output_); }
|
| +
|
| + private:
|
| + // This is a bit of a hack. We need to do this before calling the constructor
|
| + // for KeyringProxy, since it needs a FILE*. So we have a member method that
|
| + // fills in output_ for us before the body of our constructor even runs. It's
|
| + // safe since there's nothing virtual involved.
|
| + FILE* CreateOutput(int fd) {
|
| + int dup_fd = dup(fd);
|
| + CHECK_GE(dup_fd, 0);
|
| + output_ = fdopen(dup_fd, "r+");
|
| + CHECK(output_);
|
| + return output_;
|
| + }
|
| +
|
| + FILE* output_;
|
| +};
|
| +
|
| +class TestKeyringProxyClient : public keyring_proxy::KeyringProxyClient {
|
| + public:
|
| + using KeyringProxyClient::ConnectForTesting;
|
| +};
|
| +
|
| +class TestNativeBackendGnome : public NativeBackendGnome {
|
| + public:
|
| + using NativeBackendGnome::InitForTesting;
|
| +};
|
| +
|
| } // anonymous namespace
|
|
|
| class NativeBackendGnomeTest : public testing::Test {
|
| protected:
|
| NativeBackendGnomeTest()
|
| : ui_thread_(BrowserThread::UI, &message_loop_),
|
| - db_thread_(BrowserThread::DB) {
|
| + db_thread_(BrowserThread::DB),
|
| + file_thread_(BrowserThread::FILE) {
|
| }
|
|
|
| virtual void SetUp() {
|
| ASSERT_TRUE(db_thread_.Start());
|
| + ASSERT_TRUE(file_thread_.StartIOThread());
|
|
|
| MockGnomeKeyringLoader::LoadMockGnomeKeyring();
|
|
|
| + int fds[2];
|
| + ASSERT_TRUE(socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) >= 0);
|
| + // Give one side to the proxy itself, which is linked into the unit test.
|
| + // We won't call its Run() method, but its events will run from the UI
|
| + // thread's GMainContext automatically. Its Stop() method won't work.
|
| + proxy_.reset(new TestKeyringProxy(fds[0]));
|
| + // Save the other side for test proxy clients.
|
| + proxy_fd_ = fds[1];
|
| +
|
| form_google_.origin = GURL("http://www.google.com/");
|
| form_google_.action = GURL("http://www.google.com/login");
|
| form_google_.username_element = UTF8ToUTF16("user");
|
| @@ -309,10 +365,28 @@
|
| virtual void TearDown() {
|
| MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
|
| MessageLoop::current()->Run();
|
| + // It is important to close this file descriptor only after finishing
|
| + // running the UI thread's message loop, or else it will get into an
|
| + // infinite loop due to the proxy's Stop() method not working with the
|
| + // way we've linked it directly with the unit test.
|
| + close(proxy_fd_);
|
| + file_thread_.Stop();
|
| db_thread_.Stop();
|
| + // Reset keyring_loaded to false in case other tests use it. We don't want
|
| + // them to use our mock library accidentally.
|
| + MockGnomeKeyringLoader::ResetGnomeKeyring();
|
| }
|
|
|
| - void RunBothThreads() {
|
| + void InitNativeBackend(NativeBackendGnome* backend) {
|
| + int fd = dup(proxy_fd_);
|
| + ASSERT_GE(fd, 0);
|
| + TestKeyringProxyClient* client = new TestKeyringProxyClient;
|
| + client->ConnectForTesting(fd, true);
|
| + // The cast is safe because all it does is change protection.
|
| + static_cast<TestNativeBackendGnome*>(backend)->InitForTesting(client);
|
| + }
|
| +
|
| + void RunAllThreads() {
|
| // 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.
|
| @@ -381,9 +455,17 @@
|
| MessageLoopForUI message_loop_;
|
| content::TestBrowserThread ui_thread_;
|
| content::TestBrowserThread db_thread_;
|
| + content::TestBrowserThread file_thread_;
|
|
|
| TestingProfile profile_;
|
|
|
| + // By its very existence, the test proxy integrates with the UI thread's
|
| + // message loop (via GMainContext). We don't need to access it explicitly.
|
| + scoped_ptr<TestKeyringProxy> proxy_;
|
| + // This is the file descriptor connected to the proxy, suitable for dup()ing
|
| + // and using to construct TestKeyringProxyClients.
|
| + int proxy_fd_;
|
| +
|
| // Provide some test forms to avoid having to set them up in each test.
|
| PasswordForm form_google_;
|
| PasswordForm form_isc_;
|
| @@ -394,14 +476,14 @@
|
| profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
|
|
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(
|
| BrowserThread::DB, FROM_HERE,
|
| base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
|
| base::Unretained(&backend), form_google_));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| if (mock_keyring_items.size() > 0)
|
| @@ -413,7 +495,7 @@
|
| profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
|
|
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(
|
| BrowserThread::DB, FROM_HERE,
|
| @@ -427,7 +509,7 @@
|
| base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
|
| base::Unretained(&backend), &form_list));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -443,14 +525,14 @@
|
| profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
|
|
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(
|
| BrowserThread::DB, FROM_HERE,
|
| base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
|
| base::Unretained(&backend), form_google_));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| if (mock_keyring_items.size() > 0)
|
| @@ -461,7 +543,7 @@
|
| base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin),
|
| base::Unretained(&backend), form_google_));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(0u, mock_keyring_items.size());
|
| }
|
| @@ -471,7 +553,7 @@
|
| profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
|
|
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // First add an unrelated login.
|
| BrowserThread::PostTask(
|
| @@ -479,7 +561,7 @@
|
| base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
|
| base::Unretained(&backend), form_google_));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| if (mock_keyring_items.size() > 0)
|
| @@ -499,7 +581,7 @@
|
| base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
|
| base::Unretained(&backend), &form_list));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -515,7 +597,7 @@
|
| profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
|
|
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(
|
| BrowserThread::DB, FROM_HERE,
|
| @@ -526,7 +608,7 @@
|
| base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
|
| base::Unretained(&backend), form_google_));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| if (mock_keyring_items.size() > 0)
|
| @@ -538,7 +620,7 @@
|
| profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
|
|
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(
|
| BrowserThread::DB, FROM_HERE,
|
| @@ -558,7 +640,7 @@
|
| base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
|
| base::Unretained(&backend), &form_list));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got two results back.
|
| EXPECT_EQ(2u, form_list.size());
|
| @@ -577,7 +659,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
|
| @@ -591,7 +673,7 @@
|
| base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
|
| base::Unretained(&backend), &form_list));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -607,7 +689,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // This should not trigger migration because there will be no results.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -616,7 +698,7 @@
|
| base::Bind(base::IgnoreResult(&NativeBackendGnome::GetBlacklistLogins),
|
| base::Unretained(&backend), &form_list));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Check that we got nothing back.
|
| EXPECT_EQ(0u, form_list.size());
|
| @@ -634,7 +716,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -644,7 +726,7 @@
|
| base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
|
| base::Unretained(&backend), &form_list));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -668,14 +750,14 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(
|
| BrowserThread::DB, FROM_HERE,
|
| base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
|
| base::Unretained(&backend), form_google_));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
| }
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| @@ -687,7 +769,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -697,7 +779,7 @@
|
| base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
|
| base::Unretained(&backend), &form_list));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -720,7 +802,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(24, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -730,7 +812,7 @@
|
| base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
|
| base::Unretained(&backend), &form_list));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -752,14 +834,14 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(
|
| BrowserThread::DB, FROM_HERE,
|
| base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
|
| base::Unretained(&backend), form_google_));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
| }
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| @@ -772,7 +854,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by adding a new login.
|
| BrowserThread::PostTask(
|
| @@ -788,7 +870,7 @@
|
| base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
|
| base::Unretained(&backend), &form_list));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got the right thing back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -810,14 +892,14 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(
|
| BrowserThread::DB, FROM_HERE,
|
| base::Bind(base::IgnoreResult(&NativeBackendGnome::AddLogin),
|
| base::Unretained(&backend), form_google_));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
| }
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| @@ -829,7 +911,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -839,7 +921,7 @@
|
| base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
|
| base::Unretained(&backend), &form_list));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -862,7 +944,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(24, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -872,7 +954,7 @@
|
| base::IgnoreResult(&NativeBackendGnome::GetAutofillableLogins),
|
| base::Unretained(&backend), &form_list));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -893,7 +975,7 @@
|
| base::Bind(base::IgnoreResult(&NativeBackendGnome::RemoveLogin),
|
| base::Unretained(&backend), form_google_));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // The other two copies of the password in different profiles should remain.
|
| EXPECT_EQ(2u, mock_keyring_items.size());
|
|
|