| Index: chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
|
| ===================================================================
|
| --- chrome/browser/password_manager/native_backend_gnome_x_unittest.cc (revision 110784)
|
| +++ 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, new MessageLoop::QuitTask);
|
| 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::IgnoreReturn<bool>(base::Bind(
|
| &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,
|
| base::IgnoreReturn<bool>(base::Bind(
|
| @@ -426,7 +508,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -442,14 +524,14 @@
|
| profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
|
|
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| base::IgnoreReturn<bool>(base::Bind(
|
| &NativeBackendGnome::AddLogin,
|
| base::Unretained(&backend), form_google_)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| if (mock_keyring_items.size() > 0)
|
| @@ -460,7 +542,7 @@
|
| &NativeBackendGnome::RemoveLogin,
|
| base::Unretained(&backend), form_google_)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(0u, mock_keyring_items.size());
|
| }
|
| @@ -470,7 +552,7 @@
|
| profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
|
|
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // First add an unrelated login.
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| @@ -478,7 +560,7 @@
|
| &NativeBackendGnome::AddLogin,
|
| base::Unretained(&backend), form_google_)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| if (mock_keyring_items.size() > 0)
|
| @@ -497,7 +579,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -513,7 +595,7 @@
|
| profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
|
|
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| base::IgnoreReturn<bool>(base::Bind(
|
| @@ -524,7 +606,7 @@
|
| &NativeBackendGnome::AddLogin,
|
| base::Unretained(&backend), form_google_)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| if (mock_keyring_items.size() > 0)
|
| @@ -536,7 +618,7 @@
|
| profile_.GetPrefs()->SetBoolean(prefs::kPasswordsUseLocalProfileId, true);
|
|
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| base::IgnoreReturn<bool>(base::Bind(
|
| @@ -554,7 +636,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got two results back.
|
| EXPECT_EQ(2u, form_list.size());
|
| @@ -573,7 +655,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| base::IgnoreReturn<bool>(base::Bind(
|
| @@ -587,7 +669,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -603,7 +685,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;
|
| @@ -612,7 +694,7 @@
|
| &NativeBackendGnome::GetBlacklistLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Check that we got nothing back.
|
| EXPECT_EQ(0u, form_list.size());
|
| @@ -630,7 +712,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -639,7 +721,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -663,14 +745,14 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| base::IgnoreReturn<bool>(base::Bind(
|
| &NativeBackendGnome::AddLogin,
|
| base::Unretained(&backend), form_google_)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
| }
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| @@ -682,7 +764,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -691,7 +773,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -714,7 +796,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(24, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -723,7 +805,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -745,14 +827,14 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| base::IgnoreReturn<bool>(base::Bind(
|
| &NativeBackendGnome::AddLogin,
|
| base::Unretained(&backend), form_google_)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
| }
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| @@ -765,7 +847,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by adding a new login.
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| @@ -780,7 +862,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got the right thing back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -802,14 +884,14 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| base::IgnoreReturn<bool>(base::Bind(
|
| &NativeBackendGnome::AddLogin,
|
| base::Unretained(&backend), form_google_)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
| }
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| @@ -821,7 +903,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -830,7 +912,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -853,7 +935,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(24, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -862,7 +944,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -883,7 +965,7 @@
|
| &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());
|
|
|