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