diff options
author | pvalenzuela@chromium.org <pvalenzuela@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-13 20:02:59 +0000 |
---|---|---|
committer | pvalenzuela@chromium.org <pvalenzuela@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-13 20:02:59 +0000 |
commit | 32cf80b1fc2c71de91ff9375e793dcf5a9b278fa (patch) | |
tree | df732090564c4178eaefffa231146b0f25628a2c /sync | |
parent | c882426bb7f624280b9984420d023c4041b4c1d3 (diff) | |
download | chromium_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.gypi | 38 | ||||
-rw-r--r-- | sync/test/accounts_client/DEPS | 5 | ||||
-rw-r--r-- | sync/test/accounts_client/test_accounts_client.cc | 161 | ||||
-rw-r--r-- | sync/test/accounts_client/test_accounts_client.h | 63 | ||||
-rw-r--r-- | sync/test/accounts_client/test_accounts_client_unittest.cc | 99 | ||||
-rw-r--r-- | sync/test/accounts_client/url_request_context_getter.cc | 55 | ||||
-rw-r--r-- | sync/test/accounts_client/url_request_context_getter.h | 43 |
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_ |