From 56d7755b5f835ef7ee63703f2c2917886f6b2e9c Mon Sep 17 00:00:00 2001 From: "alemate@chromium.org" Date: Thu, 22 May 2014 05:51:51 +0000 Subject: [PATCH] Add TimeZone API unit test. Add unit test for chrome/browser/chromeos/timezone/ . BUG=245075 TEST=none Review URL: https://codereview.chromium.org/299503002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272115 0039d316-1c4b-4281-b951-d872f2087c98 --- .../browser/chromeos/timezone/timezone_provider.h | 2 + .../browser/chromeos/timezone/timezone_request.cc | 11 +- .../browser/chromeos/timezone/timezone_request.h | 14 + .../browser/chromeos/timezone/timezone_unittest.cc | 290 +++++++++++++++++++++ chrome/chrome_tests_unit.gypi | 1 + 5 files changed, 314 insertions(+), 4 deletions(-) create mode 100644 chrome/browser/chromeos/timezone/timezone_unittest.cc diff --git a/chrome/browser/chromeos/timezone/timezone_provider.h b/chrome/browser/chromeos/timezone/timezone_provider.h index 0b5741b0a784..c7bc04bbf899 100644 --- a/chrome/browser/chromeos/timezone/timezone_provider.h +++ b/chrome/browser/chromeos/timezone/timezone_provider.h @@ -40,6 +40,8 @@ class TimeZoneProvider { TimeZoneRequest::TimeZoneResponseCallback callback); private: + friend class TestTimeZoneAPIURLFetcherCallback; + // Deletes request from requests_. void OnTimezoneResponse(TimeZoneRequest* request, TimeZoneRequest::TimeZoneResponseCallback callback, diff --git a/chrome/browser/chromeos/timezone/timezone_request.cc b/chrome/browser/chromeos/timezone/timezone_request.cc index c9d442e5965f..1ceb70bbbf77 100644 --- a/chrome/browser/chromeos/timezone/timezone_request.cc +++ b/chrome/browser/chromeos/timezone/timezone_request.cc @@ -312,6 +312,10 @@ TimeZoneRequest::TimeZoneRequest( geoposition_(geoposition), sensor_(sensor), retry_timeout_abs_(base::Time::Now() + retry_timeout), + retry_sleep_on_server_error_(base::TimeDelta::FromSeconds( + kResolveTimeZoneRetrySleepOnServerErrorSeconds)), + retry_sleep_on_bad_response_(base::TimeDelta::FromSeconds( + kResolveTimeZoneRetrySleepBadResponseSeconds)), retries_(0) { } @@ -350,9 +354,8 @@ void TimeZoneRequest::MakeRequest(TimeZoneResponseCallback callback) { } void TimeZoneRequest::Retry(bool server_error) { - const base::TimeDelta delay = base::TimeDelta::FromSeconds( - server_error ? kResolveTimeZoneRetrySleepOnServerErrorSeconds - : kResolveTimeZoneRetrySleepBadResponseSeconds); + const base::TimeDelta delay(server_error ? retry_sleep_on_server_error_ + : retry_sleep_on_bad_response_); timezone_request_scheduled_.Start( FROM_HERE, delay, this, &TimeZoneRequest::StartRequest); } @@ -423,6 +426,6 @@ std::string TimeZoneResponseData::ToStringForDebug() const { error_message.c_str(), (unsigned)status, (status < arraysize(status2string) ? status2string[status] : "unknown")); -}; +} } // namespace chromeos diff --git a/chrome/browser/chromeos/timezone/timezone_request.h b/chrome/browser/chromeos/timezone/timezone_request.h index 77deb4fad0f2..08174b74e435 100644 --- a/chrome/browser/chromeos/timezone/timezone_request.h +++ b/chrome/browser/chromeos/timezone/timezone_request.h @@ -83,6 +83,16 @@ class TimeZoneRequest : private net::URLFetcherDelegate { // request will be silently cancelled. void MakeRequest(TimeZoneResponseCallback callback); + void set_retry_sleep_on_server_error_for_testing( + const base::TimeDelta value) { + retry_sleep_on_server_error_ = value; + } + + void set_retry_sleep_on_bad_response_for_testing( + const base::TimeDelta value) { + retry_sleep_on_bad_response_ = value; + } + private: // net::URLFetcherDelegate virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; @@ -112,6 +122,10 @@ class TimeZoneRequest : private net::URLFetcherDelegate { // Pending retry. base::OneShotTimer timezone_request_scheduled_; + base::TimeDelta retry_sleep_on_server_error_; + + base::TimeDelta retry_sleep_on_bad_response_; + // Number of retry attempts. unsigned retries_; diff --git a/chrome/browser/chromeos/timezone/timezone_unittest.cc b/chrome/browser/chromeos/timezone/timezone_unittest.cc new file mode 100644 index 000000000000..fcb3e63cb532 --- /dev/null +++ b/chrome/browser/chromeos/timezone/timezone_unittest.cc @@ -0,0 +1,290 @@ +// Copyright 2014 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 "base/run_loop.h" +#include "chrome/browser/chromeos/geolocation/geoposition.h" +#include "chrome/browser/chromeos/timezone/timezone_provider.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_status_code.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "net/url_request/url_fetcher_impl.h" +#include "net/url_request/url_request_status.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const int kRequestRetryIntervalMilliSeconds = 200; + +// This should be different from default to prevent TimeZoneRequest +// from modifying it. +const char kTestTimeZoneProviderUrl[] = + "https://localhost/maps/api/timezone/json?"; + +const char kSimpleResponseBody[] = + "{\n" + " \"dstOffset\" : 0.0,\n" + " \"rawOffset\" : -28800.0,\n" + " \"status\" : \"OK\",\n" + " \"timeZoneId\" : \"America/Los_Angeles\",\n" + " \"timeZoneName\" : \"Pacific Standard Time\"\n" + "}"; + +struct SimpleRequest { + SimpleRequest() + : url("https://localhost/maps/api/timezone/" + "json?location=39.603481,-119.682251×tamp=1331161200&sensor=" + "false"), + http_response(kSimpleResponseBody) { + position.latitude = 39.6034810; + position.longitude = -119.6822510; + position.accuracy = 1; + position.error_code = 0; + position.timestamp = base::Time::FromTimeT(1331161200); + position.status = chromeos::Geoposition::STATUS_NONE; + EXPECT_EQ( + "latitude=39.603481, longitude=-119.682251, accuracy=1.000000, " + "error_code=0, error_message='', status=0 (NONE)", + position.ToString()); + + timezone.dstOffset = 0; + timezone.rawOffset = -28800; + timezone.timeZoneId = "America/Los_Angeles"; + timezone.timeZoneName = "Pacific Standard Time"; + timezone.error_message.erase(); + timezone.status = chromeos::TimeZoneResponseData::OK; + EXPECT_EQ( + "dstOffset=0.000000, rawOffset=-28800.000000, " + "timeZoneId='America/Los_Angeles', timeZoneName='Pacific Standard " + "Time', error_message='', status=0 (OK)", + timezone.ToStringForDebug()); + } + + GURL url; + chromeos::Geoposition position; + std::string http_response; + chromeos::TimeZoneResponseData timezone; +}; + +} // anonymous namespace + +namespace chromeos { + +// This is helper class for net::FakeURLFetcherFactory. +class TestTimeZoneAPIURLFetcherCallback { + public: + TestTimeZoneAPIURLFetcherCallback(const GURL& url, + const size_t require_retries, + const std::string& response, + TimeZoneProvider* provider) + : url_(url), + require_retries_(require_retries), + response_(response), + factory_(NULL), + attempts_(0), + provider_(provider) {} + + scoped_ptr CreateURLFetcher( + const GURL& url, + net::URLFetcherDelegate* delegate, + const std::string& response_data, + net::HttpStatusCode response_code, + net::URLRequestStatus::Status status) { + EXPECT_EQ(provider_->requests_.size(), 1U); + + TimeZoneRequest* timezone_request = provider_->requests_[0]; + + const base::TimeDelta base_retry_interval = + base::TimeDelta::FromMilliseconds(kRequestRetryIntervalMilliSeconds); + timezone_request->set_retry_sleep_on_server_error_for_testing( + base_retry_interval); + timezone_request->set_retry_sleep_on_bad_response_for_testing( + base_retry_interval); + + ++attempts_; + if (attempts_ > require_retries_) { + response_code = net::HTTP_OK; + status = net::URLRequestStatus::SUCCESS; + factory_->SetFakeResponse(url, response_, response_code, status); + } + scoped_ptr fetcher(new net::FakeURLFetcher( + url, delegate, response_, response_code, status)); + scoped_refptr download_headers = + new net::HttpResponseHeaders(std::string()); + download_headers->AddHeader("Content-Type: application/json"); + fetcher->set_response_headers(download_headers); + return fetcher.Pass(); + } + + void Initialize(net::FakeURLFetcherFactory* factory) { + factory_ = factory; + factory_->SetFakeResponse(url_, + std::string(), + net::HTTP_INTERNAL_SERVER_ERROR, + net::URLRequestStatus::FAILED); + } + + size_t attempts() const { return attempts_; } + + private: + const GURL url_; + // Respond with OK on required retry attempt. + const size_t require_retries_; + std::string response_; + net::FakeURLFetcherFactory* factory_; + size_t attempts_; + TimeZoneProvider* provider_; + + DISALLOW_COPY_AND_ASSIGN(TestTimeZoneAPIURLFetcherCallback); +}; + +// This implements fake TimeZone API remote endpoint. +// Response data is served to TimeZoneProvider via +// net::FakeURLFetcher. +class TimeZoneAPIFetcherFactory { + public: + TimeZoneAPIFetcherFactory(const GURL& url, + const std::string& response, + const size_t require_retries, + TimeZoneProvider* provider) { + url_callback_.reset(new TestTimeZoneAPIURLFetcherCallback( + url, require_retries, response, provider)); + net::URLFetcherImpl::set_factory(NULL); + fetcher_factory_.reset(new net::FakeURLFetcherFactory( + NULL, + base::Bind(&TestTimeZoneAPIURLFetcherCallback::CreateURLFetcher, + base::Unretained(url_callback_.get())))); + url_callback_->Initialize(fetcher_factory_.get()); + } + + size_t attempts() const { return url_callback_->attempts(); } + + private: + scoped_ptr url_callback_; + scoped_ptr fetcher_factory_; + + DISALLOW_COPY_AND_ASSIGN(TimeZoneAPIFetcherFactory); +}; + +class TimeZoneReceiver { + public: + TimeZoneReceiver() : server_error_(false) {} + + void OnRequestDone(scoped_ptr timezone, + bool server_error) { + timezone_ = timezone.Pass(); + server_error_ = server_error; + + message_loop_runner_->Quit(); + } + + void WaitUntilRequestDone() { + message_loop_runner_.reset(new base::RunLoop); + message_loop_runner_->Run(); + } + + const TimeZoneResponseData* timezone() const { return timezone_.get(); } + bool server_error() const { return server_error_; } + + private: + scoped_ptr timezone_; + bool server_error_; + scoped_ptr message_loop_runner_; +}; + +class TimeZoneTest : public testing::Test { + private: + content::TestBrowserThreadBundle thread_bundle_; +}; + +TEST_F(TimeZoneTest, ResponseOK) { + TimeZoneProvider provider(NULL, GURL(kTestTimeZoneProviderUrl)); + const SimpleRequest simple_request; + + TimeZoneAPIFetcherFactory url_factory(simple_request.url, + simple_request.http_response, + 0 /* require_retries */, + &provider); + + TimeZoneReceiver receiver; + + provider.RequestTimezone(simple_request.position, + false, + base::TimeDelta::FromSeconds(1), + base::Bind(&TimeZoneReceiver::OnRequestDone, + base::Unretained(&receiver))); + receiver.WaitUntilRequestDone(); + + EXPECT_EQ(simple_request.timezone.ToStringForDebug(), + receiver.timezone()->ToStringForDebug()); + EXPECT_FALSE(receiver.server_error()); + EXPECT_EQ(1U, url_factory.attempts()); +} + +TEST_F(TimeZoneTest, ResponseOKWithRetries) { + TimeZoneProvider provider(NULL, GURL(kTestTimeZoneProviderUrl)); + const SimpleRequest simple_request; + + TimeZoneAPIFetcherFactory url_factory(simple_request.url, + simple_request.http_response, + 3 /* require_retries */, + &provider); + + TimeZoneReceiver receiver; + + provider.RequestTimezone(simple_request.position, + false, + base::TimeDelta::FromSeconds(1), + base::Bind(&TimeZoneReceiver::OnRequestDone, + base::Unretained(&receiver))); + receiver.WaitUntilRequestDone(); + EXPECT_EQ(simple_request.timezone.ToStringForDebug(), + receiver.timezone()->ToStringForDebug()); + EXPECT_FALSE(receiver.server_error()); + EXPECT_EQ(4U, url_factory.attempts()); +} + +TEST_F(TimeZoneTest, InvalidResponse) { + TimeZoneProvider provider(NULL, GURL(kTestTimeZoneProviderUrl)); + const SimpleRequest simple_request; + + TimeZoneAPIFetcherFactory url_factory(simple_request.url, + "invalid JSON string", + 0 /* require_retries */, + &provider); + + TimeZoneReceiver receiver; + + const int timeout_seconds = 1; + size_t expected_retries = static_cast( + timeout_seconds * 1000 / kRequestRetryIntervalMilliSeconds); + ASSERT_GE(expected_retries, 2U); + + provider.RequestTimezone(simple_request.position, + false, + base::TimeDelta::FromSeconds(timeout_seconds), + base::Bind(&TimeZoneReceiver::OnRequestDone, + base::Unretained(&receiver))); + receiver.WaitUntilRequestDone(); + EXPECT_EQ( + "dstOffset=0.000000, rawOffset=0.000000, timeZoneId='', timeZoneName='', " + "error_message='TimeZone provider at 'https://localhost/' : JSONReader " + "failed: Line: 1, column: 1, Unexpected token..', status=6 " + "(REQUEST_ERROR)", + receiver.timezone()->ToStringForDebug()); + EXPECT_FALSE(receiver.server_error()); + EXPECT_GE(url_factory.attempts(), 2U); + if (url_factory.attempts() > expected_retries + 1) { + LOG(WARNING) << "TimeZoneTest::InvalidResponse: Too many attempts (" + << url_factory.attempts() << "), no more then " + << expected_retries + 1 << " expected."; + } + if (url_factory.attempts() < expected_retries - 1) { + LOG(WARNING) << "TimeZoneTest::InvalidResponse: Too less attempts (" + << url_factory.attempts() << "), greater then " + << expected_retries - 1 << " expected."; + } +} + +} // namespace chromeos diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 421ed4c4d24b..525c9797ea06 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -782,6 +782,7 @@ 'browser/chromeos/settings/session_manager_operation_unittest.cc', 'browser/chromeos/settings/stub_cros_settings_provider_unittest.cc', 'browser/chromeos/system/automatic_reboot_manager_unittest.cc', + 'browser/chromeos/timezone/timezone_unittest.cc', 'browser/chromeos/ui/idle_app_name_notification_view_unittest.cc', 'browser/chromeos/version_loader_unittest.cc', 'browser/command_updater_unittest.cc', -- 2.11.4.GIT