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()); |