| Index: chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
|
| ===================================================================
|
| --- chrome/browser/password_manager/native_backend_gnome_x_unittest.cc (revision 108068)
|
| +++ 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"
|
| @@ -11,6 +12,9 @@
|
| #include "base/time.h"
|
| #include "base/utf_string_conversions.h"
|
| #include "chrome/browser/password_manager/native_backend_gnome_x.h"
|
| +#include "chrome/browser/password_manager/proxy/chrome_keyring_proxy.h"
|
| +#include "chrome/browser/password_manager/proxy/chrome_keyring_proxy_client.h"
|
| +#include "chrome/browser/password_manager/proxy/keyring_loader.h"
|
| #include "chrome/browser/prefs/pref_service.h"
|
| #include "chrome/common/pref_names.h"
|
| #include "chrome/test/base/testing_profile.h"
|
| @@ -25,6 +29,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() {}
|
| @@ -272,20 +279,69 @@
|
| }
|
| };
|
|
|
| +// 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 TestChromeKeyringProxy : public ChromeKeyringProxy {
|
| + public:
|
| + explicit TestChromeKeyringProxy(int fd)
|
| + : ChromeKeyringProxy(fd, CreateOutput(fd)) {}
|
| + ~TestChromeKeyringProxy() { fclose(output_); }
|
| +
|
| + private:
|
| + // This is a bit of a hack. We need to do this before calling the constructor
|
| + // for ChromeKeyringProxy, 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 TestChromeKeyringProxyClient : public ChromeKeyringProxyClient {
|
| + public:
|
| + using ChromeKeyringProxyClient::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());
|
| + base::Thread::Options options(MessageLoop::TYPE_IO, 0);
|
| + ASSERT_TRUE(file_thread_.StartWithOptions(options));
|
|
|
| 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 TestChromeKeyringProxy(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");
|
| @@ -308,10 +364,25 @@
|
| 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();
|
| }
|
|
|
| - void RunBothThreads() {
|
| + void InitNativeBackend(NativeBackendGnome* backend) {
|
| + int fd = dup(proxy_fd_);
|
| + ASSERT_GE(fd, 0);
|
| + TestChromeKeyringProxyClient* client = new TestChromeKeyringProxyClient;
|
| + 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.
|
| @@ -380,9 +451,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<TestChromeKeyringProxy> proxy_;
|
| + // This is the file descriptor connected to the proxy, suitable for dup()ing
|
| + // and using to construct TestChromeKeyringProxyClients.
|
| + int proxy_fd_;
|
| +
|
| // Provide some test forms to avoid having to set them up in each test.
|
| PasswordForm form_google_;
|
| PasswordForm form_isc_;
|
| @@ -393,14 +472,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)
|
| @@ -412,7 +491,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(
|
| @@ -425,7 +504,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -441,14 +520,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)
|
| @@ -459,7 +538,7 @@
|
| &NativeBackendGnome::RemoveLogin,
|
| base::Unretained(&backend), form_google_)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(0u, mock_keyring_items.size());
|
| }
|
| @@ -469,7 +548,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,
|
| @@ -477,7 +556,7 @@
|
| &NativeBackendGnome::AddLogin,
|
| base::Unretained(&backend), form_google_)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| if (mock_keyring_items.size() > 0)
|
| @@ -496,7 +575,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -512,7 +591,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(
|
| @@ -523,7 +602,7 @@
|
| &NativeBackendGnome::AddLogin,
|
| base::Unretained(&backend), form_google_)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| EXPECT_EQ(1u, mock_keyring_items.size());
|
| if (mock_keyring_items.size() > 0)
|
| @@ -535,7 +614,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(
|
| @@ -553,7 +632,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got two results back.
|
| EXPECT_EQ(2u, form_list.size());
|
| @@ -572,7 +651,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| base::IgnoreReturn<bool>(base::Bind(
|
| @@ -586,7 +665,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -602,7 +681,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;
|
| @@ -611,7 +690,7 @@
|
| &NativeBackendGnome::GetBlacklistLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Check that we got nothing back.
|
| EXPECT_EQ(0u, form_list.size());
|
| @@ -629,7 +708,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -638,7 +717,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -662,14 +741,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());
|
| @@ -681,7 +760,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -690,7 +769,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -713,7 +792,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(24, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -722,7 +801,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -744,14 +823,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());
|
| @@ -764,7 +843,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by adding a new login.
|
| BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| @@ -779,7 +858,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());
|
| @@ -801,14 +880,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());
|
| @@ -820,7 +899,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(42, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -829,7 +908,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -852,7 +931,7 @@
|
|
|
| {
|
| NativeBackendGnome backend(24, profile_.GetPrefs());
|
| - backend.Init();
|
| + InitNativeBackend(&backend);
|
|
|
| // Trigger the migration by looking something up.
|
| std::vector<PasswordForm*> form_list;
|
| @@ -861,7 +940,7 @@
|
| &NativeBackendGnome::GetAutofillableLogins,
|
| base::Unretained(&backend), &form_list)));
|
|
|
| - RunBothThreads();
|
| + RunAllThreads();
|
|
|
| // Quick check that we got something back.
|
| EXPECT_EQ(1u, form_list.size());
|
| @@ -882,7 +961,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());
|
|
|