summaryrefslogtreecommitdiffstats
path: root/sync
diff options
context:
space:
mode:
authorpvalenzuela@chromium.org <pvalenzuela@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-13 20:02:59 +0000
committerpvalenzuela@chromium.org <pvalenzuela@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-13 20:02:59 +0000
commit32cf80b1fc2c71de91ff9375e793dcf5a9b278fa (patch)
treedf732090564c4178eaefffa231146b0f25628a2c /sync
parentc882426bb7f624280b9984420d023c4041b4c1d3 (diff)
downloadchromium_src-32cf80b1fc2c71de91ff9375e793dcf5a9b278fa.zip
chromium_src-32cf80b1fc2c71de91ff9375e793dcf5a9b278fa.tar.gz
chromium_src-32cf80b1fc2c71de91ff9375e793dcf5a9b278fa.tar.bz2
Initial version of Test Accounts service client (attempt #2)
This version uses URLFetcher instead of curl. Original description (committed/reverted r198019): """ Initial version of Test Accounts service client. This client will allow Chrome Sync tests to utilize the upcoming Test Accounts service that allows short-term, exclusive access to test accounts for the purpose of testing against real servers. """ BUG= Review URL: https://chromiumcodereview.appspot.com/14591008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@199805 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync')
-rw-r--r--sync/sync_tests.gypi38
-rw-r--r--sync/test/accounts_client/DEPS5
-rw-r--r--sync/test/accounts_client/test_accounts_client.cc161
-rw-r--r--sync/test/accounts_client/test_accounts_client.h63
-rw-r--r--sync/test/accounts_client/test_accounts_client_unittest.cc99
-rw-r--r--sync/test/accounts_client/url_request_context_getter.cc55
-rw-r--r--sync/test/accounts_client/url_request_context_getter.h43
7 files changed, 464 insertions, 0 deletions
diff --git a/sync/sync_tests.gypi b/sync/sync_tests.gypi
index f7e2b64..f26ac4a 100644
--- a/sync/sync_tests.gypi
+++ b/sync/sync_tests.gypi
@@ -476,6 +476,44 @@
}],
],
},
+
+ # Test support files for using the Test Accounts service.
+ {
+ 'target_name': 'test_support_accounts_client',
+ 'type': 'static_library',
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../net/net.gyp:net',
+ ],
+ 'sources': [
+ 'test/accounts_client/test_accounts_client.cc',
+ 'test/accounts_client/test_accounts_client.h',
+ 'test/accounts_client/url_request_context_getter.cc',
+ 'test/accounts_client/url_request_context_getter.h',
+ ],
+ },
+
+ # The Sync end-to-end (and associated infrastructure) tests.
+ {
+ 'target_name': 'sync_endtoend_tests',
+ 'type': '<(gtest_target_type)',
+ 'dependencies': [
+ '../base/base.gyp:run_all_unittests',
+ '../build/temp_gyp/googleurl.gyp:googleurl',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+ 'test_support_accounts_client',
+ ],
+ 'sources': [
+ 'test/accounts_client/test_accounts_client_unittest.cc',
+ ],
+ },
+
],
'conditions': [
['OS != "ios"', {
diff --git a/sync/test/accounts_client/DEPS b/sync/test/accounts_client/DEPS
new file mode 100644
index 0000000..db6d6e2
--- /dev/null
+++ b/sync/test/accounts_client/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ # Allow this directory to depend on net since it's test infrastructure and
+ # not test cases.
+ "+net",
+]
diff --git a/sync/test/accounts_client/test_accounts_client.cc b/sync/test/accounts_client/test_accounts_client.cc
new file mode 100644
index 0000000..82e7af2
--- /dev/null
+++ b/sync/test/accounts_client/test_accounts_client.cc
@@ -0,0 +1,161 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sync/test/accounts_client/test_accounts_client.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/message_loop.h"
+#include "base/run_loop.h"
+#include "base/stringprintf.h"
+#include "base/time.h"
+#include "base/values.h"
+#include "net/base/url_util.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "sync/test/accounts_client/url_request_context_getter.h"
+
+using std::string;
+using std::vector;
+
+static const int kMaxSessionLifetimeSeconds = 30 * 60;
+static const string kClaimPath = "claim";
+static const string kReleasePath = "release";
+static const base::TimeDelta kRequestTimeout = base::TimeDelta::FromSeconds(10);
+
+AccountSession::AccountSession() {}
+AccountSession::~AccountSession() {}
+
+class AccountsRequestDelegate : public net::URLFetcherDelegate {
+ public:
+ AccountsRequestDelegate(base::RunLoop* run_loop) : response_(""),
+ success_(false), run_loop_(run_loop) {}
+
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
+ string url = source->GetURL().spec();
+ source->GetResponseAsString(&response_);
+
+ if (!source->GetStatus().is_success()) {
+ int error = source->GetStatus().error();
+ DVLOG(0) << "The request failed with error code " << error << "."
+ << "\nRequested URL: " << url << ".";
+ } else if (source->GetResponseCode() != net::HTTP_OK) {
+ DVLOG(0) << "The request failed with response code "
+ << source->GetResponseCode() << "."
+ << "\nRequested URL: " << url
+ << "\nResponse body: \"" << response_ << "\"";
+ } else {
+ success_ = true;
+ }
+
+ run_loop_->Quit();
+ }
+
+ string response() const { return response_; }
+ bool success() const { return success_; }
+
+ private:
+ string response_;
+ bool success_;
+ base::RunLoop* run_loop_;
+};
+
+TestAccountsClient::TestAccountsClient(const string& server,
+ const string& account_space,
+ const vector<string>& usernames)
+ : server_(server), account_space_(account_space), usernames_(usernames) {
+}
+
+TestAccountsClient::~TestAccountsClient() {}
+
+bool TestAccountsClient::ClaimAccount(AccountSession* session) {
+ GURL url = CreateGURLWithPath(kClaimPath);
+ url = net::AppendQueryParameter(url, "account_space", account_space_);
+ string max_lifetime_seconds = base::StringPrintf("%d",
+ kMaxSessionLifetimeSeconds);
+ url = net::AppendQueryParameter(url, "max_lifetime_seconds",
+ max_lifetime_seconds);
+
+ // TODO(pvalenzuela): Select N random usernames instead of all usernames.
+ for (vector<string>::iterator it = usernames_.begin();
+ it != usernames_.end(); ++it) {
+ url = net::AppendQueryParameter(url, "username", *it);
+ }
+
+ string response;
+ if (!SendRequest(url, &response)) {
+ return false;
+ }
+
+ scoped_ptr<Value> value(base::JSONReader::Read(response));
+ base::DictionaryValue* dict_value;
+ if (value != NULL && value->GetAsDictionary(&dict_value) &&
+ dict_value != NULL) {
+ dict_value->GetString("username", &session->username);
+ dict_value->GetString("account_space", &session->account_space);
+ dict_value->GetString("session_id", &session->session_id);
+ dict_value->GetString("expiration_time", &session->expiration_time);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void TestAccountsClient::ReleaseAccount(const AccountSession& session) {
+ // The expiration_time field is ignored since it isn't passed as part of the
+ // release request.
+ if (session.username.empty() || session.account_space.empty() ||
+ account_space_.compare(session.account_space) != 0 ||
+ session.session_id.empty()) {
+ return;
+ }
+
+ GURL url = CreateGURLWithPath(kReleasePath);
+ url = net::AppendQueryParameter(url, "account_space", session.account_space);
+ url = net::AppendQueryParameter(url, "username", session.username);
+ url = net::AppendQueryParameter(url, "session_id", session.session_id);
+
+ // This operation is best effort, so don't send any errors back to the caller.
+ string response;
+ SendRequest(url, &response);
+}
+
+GURL TestAccountsClient::CreateGURLWithPath(const string& path) {
+ return GURL(base::StringPrintf("%s/%s", server_.c_str(), path.c_str()));
+}
+
+
+bool TestAccountsClient::SendRequest(const GURL& url, string* response) {
+ MessageLoop* loop = MessageLoop::current();
+ scoped_refptr<URLRequestContextGetter> context_getter(
+ new URLRequestContextGetter(loop->message_loop_proxy()));
+
+ base::RunLoop run_loop;
+
+ AccountsRequestDelegate delegate(&run_loop);
+ scoped_ptr<net::URLFetcher> fetcher(net::URLFetcher::Create(
+ url, net::URLFetcher::POST, &delegate));
+ fetcher->SetRequestContext(context_getter.get());
+ fetcher->SetUploadData("application/json", "");
+ fetcher->Start();
+
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ run_loop.QuitClosure(),
+ kRequestTimeout);
+ run_loop.Run();
+
+ if (delegate.success()) {
+ *response = delegate.response();
+ }
+
+ return delegate.success();
+}
diff --git a/sync/test/accounts_client/test_accounts_client.h b/sync/test/accounts_client/test_accounts_client.h
new file mode 100644
index 0000000..cade734
--- /dev/null
+++ b/sync/test/accounts_client/test_accounts_client.h
@@ -0,0 +1,63 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_TEST_ACCOUNTS_CLIENT_TEST_ACCOUNTS_CLIENT_H_
+#define SYNC_TEST_ACCOUNTS_CLIENT_TEST_ACCOUNTS_CLIENT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/message_loop.h"
+#include "googleurl/src/gurl.h"
+
+using std::string;
+using std::vector;
+
+// The data associated with an account session.
+struct AccountSession {
+ AccountSession();
+ ~AccountSession();
+
+ string username;
+ string account_space;
+ string session_id;
+ string expiration_time;
+};
+
+// A test-side client for the Test Accounts service. This service provides
+// short-term, exclusive access to test accounts for the purpose of testing
+// against real Chrome Sync servers.
+class TestAccountsClient {
+ public:
+ // Creates a client associated with the given |server| URL (e.g.,
+ // http://service-runs-here.com), |account_space| (for account segregation),
+ // and |usernames| (the collection of accounts to be chosen from).
+ TestAccountsClient(const string& server,
+ const string& account_space,
+ const vector<string>& usernames);
+
+ virtual ~TestAccountsClient();
+
+ // Attempts to claim an account via the Test Accounts service. If
+ // successful, true is returned and the given |session| has its data set.
+ // If an error occurred, then false is returned.
+ bool ClaimAccount(AccountSession* session);
+
+ // Attempts to release an account via the Test Accounts service. The value
+ // of |session| should be one returned from ClaimAccount(). This function
+ // is best-effort and fails silently.
+ void ReleaseAccount(const AccountSession& session);
+
+ // Sends an HTTP POST request to the Test Accounts service.
+ virtual bool SendRequest(const GURL& url, string* response);
+
+ private:
+ GURL CreateGURLWithPath(const string& path);
+ MessageLoopForIO io_loop_;
+ const string server_;
+ const string account_space_;
+ vector<string> usernames_;
+};
+
+#endif // SYNC_TEST_ACCOUNTS_CLIENT_TEST_ACCOUNTS_CLIENT_H_
diff --git a/sync/test/accounts_client/test_accounts_client_unittest.cc b/sync/test/accounts_client/test_accounts_client_unittest.cc
new file mode 100644
index 0000000..ff405f6
--- /dev/null
+++ b/sync/test/accounts_client/test_accounts_client_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/json/json_writer.h"
+#include "base/values.h"
+#include "googleurl/src/gurl.h"
+#include "sync/test/accounts_client/test_accounts_client.cc"
+#include "sync/test/accounts_client/test_accounts_client.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+using std::vector;
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
+
+namespace {
+static const string kServer = "https://test-account-service";
+static const string kUsername = "foobar@baz.com";
+static const string kAccountSpace = "test_account_space";
+static const string kSessionId = "1234-ABCD";
+static const string kExpirationTime = "12:00";
+} // namespace
+
+static AccountSession CreateValidAccountSession() {
+ AccountSession session;
+ session.username = kUsername;
+ session.account_space = kAccountSpace;
+ session.session_id = kSessionId;
+ session.expiration_time = kExpirationTime;
+ return session;
+}
+
+class NoNetworkTestAccountsClient : public TestAccountsClient {
+ public:
+ NoNetworkTestAccountsClient(const string& server,
+ const string& account_space,
+ vector<string> usernames)
+ : TestAccountsClient(server, account_space, usernames) {}
+ MOCK_METHOD2(SendRequest, bool(const GURL&, string*));
+};
+
+TEST(TestAccountsClientTest, ClaimAccountError) {
+ vector<string> usernames;
+ NoNetworkTestAccountsClient client(kServer, kAccountSpace, usernames);
+ EXPECT_CALL(client, SendRequest(_, _))
+ .WillOnce(Return(false));
+ AccountSession session;
+ EXPECT_FALSE(client.ClaimAccount(&session));
+}
+
+TEST(TestAccountsClientTest, ClaimAccountSuccess) {
+ vector<string> usernames;
+ usernames.push_back("foo0@gmail.com");
+ usernames.push_back("foo1@gmail.com");
+ usernames.push_back("foo2@gmail.com");
+ NoNetworkTestAccountsClient client(kServer, kAccountSpace, usernames);
+
+ DictionaryValue success_dict;
+ success_dict.Set("username", new StringValue(kUsername));
+ success_dict.Set("account_space", new StringValue(kAccountSpace));
+ success_dict.Set("session_id", new StringValue(kSessionId));
+ success_dict.Set("expiration_time", new StringValue(kExpirationTime));
+
+ string success_response;
+ base::JSONWriter::Write(&success_dict, &success_response);
+ EXPECT_CALL(client, SendRequest(_, _))
+ .WillOnce(DoAll(SetArgPointee<1>(success_response), Return(true)));
+
+ AccountSession session;
+ EXPECT_TRUE(client.ClaimAccount(&session));
+ EXPECT_EQ(kUsername, session.username);
+ EXPECT_EQ(kAccountSpace, session.account_space);
+ EXPECT_EQ(kSessionId, session.session_id);
+ EXPECT_EQ(kExpirationTime, session.expiration_time);
+}
+
+TEST(TestAccountsClientTest, ReleaseAccountEmptySession) {
+ vector<string> usernames;
+ NoNetworkTestAccountsClient client(kServer, kAccountSpace, usernames);
+ AccountSession session;
+ // No expectation for SendRequest is made because no network call should be
+ // performed in this scenario.
+ client.ReleaseAccount(session);
+}
+
+TEST(TestAccountsClientTest, ReleaseAccountSuccess) {
+ vector<string> usernames;
+ NoNetworkTestAccountsClient client(kServer, kAccountSpace, usernames);
+ EXPECT_CALL(client, SendRequest(_, _))
+ .WillOnce(Return(true));
+ AccountSession session = CreateValidAccountSession();
+ client.ReleaseAccount(session);
+}
diff --git a/sync/test/accounts_client/url_request_context_getter.cc b/sync/test/accounts_client/url_request_context_getter.cc
new file mode 100644
index 0000000..161b57d
--- /dev/null
+++ b/sync/test/accounts_client/url_request_context_getter.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sync/test/accounts_client/url_request_context_getter.h"
+
+#include <string>
+
+#include "net/proxy/proxy_config_service.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
+
+namespace {
+
+// Config getter that always returns direct settings.
+class ProxyConfigServiceDirect : public net::ProxyConfigService {
+ public:
+ // Overridden from ProxyConfigService:
+ virtual void AddObserver(Observer* observer) OVERRIDE {}
+ virtual void RemoveObserver(Observer* observer) OVERRIDE {}
+ virtual ConfigAvailability GetLatestProxyConfig(
+ net::ProxyConfig* config) OVERRIDE {
+ *config = net::ProxyConfig::CreateDirect();
+ return CONFIG_VALID;
+ }
+};
+
+} // namespace
+
+URLRequestContextGetter::URLRequestContextGetter(
+ scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
+ : network_task_runner_(network_task_runner) {
+}
+
+net::URLRequestContext* URLRequestContextGetter::GetURLRequestContext() {
+ CHECK(network_task_runner_->BelongsToCurrentThread());
+ if (!url_request_context_) {
+ net::URLRequestContextBuilder builder;
+ // net::HttpServer fails to parse headers if user-agent header is blank.
+ builder.set_user_agent("sync-test-accounts-client");
+ builder.DisableHttpCache();
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ builder.set_proxy_config_service(new ProxyConfigServiceDirect());
+#endif
+ url_request_context_.reset(builder.Build());
+ }
+ return url_request_context_.get();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+ URLRequestContextGetter::GetNetworkTaskRunner() const {
+ return network_task_runner_;
+}
+
+URLRequestContextGetter::~URLRequestContextGetter() {}
diff --git a/sync/test/accounts_client/url_request_context_getter.h b/sync/test/accounts_client/url_request_context_getter.h
new file mode 100644
index 0000000..6891072
--- /dev/null
+++ b/sync/test/accounts_client/url_request_context_getter.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SYNC_TEST_ACCOUNTS_CLIENT_URL_REQUEST_CONTEXT_GETTER_H_
+#define SYNC_TEST_ACCOUNTS_CLIENT_URL_REQUEST_CONTEXT_GETTER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace net {
+class URLRequestContext;
+}
+
+class URLRequestContextGetter : public net::URLRequestContextGetter {
+ public:
+ explicit URLRequestContextGetter(
+ scoped_refptr<base::SingleThreadTaskRunner> network_task_runner);
+
+ // Overridden from net::URLRequestContextGetter:
+ virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE;
+ virtual scoped_refptr<base::SingleThreadTaskRunner>
+ GetNetworkTaskRunner() const OVERRIDE;
+
+ private:
+ virtual ~URLRequestContextGetter();
+
+ scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
+
+ // Only accessed on the IO thread.
+ scoped_ptr<net::URLRequestContext> url_request_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(URLRequestContextGetter);
+};
+
+#endif // SYNC_TEST_ACCOUNTS_CLIENT_URL_REQUEST_CONTEXT_GETTER_H_