1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/message_loop/message_loop.h"
6 #include "base/run_loop.h"
7 #include "chromeos/geolocation/geoposition.h"
8 #include "chromeos/timezone/timezone_provider.h"
9 #include "chromeos/timezone/timezone_resolver.h"
10 #include "net/http/http_response_headers.h"
11 #include "net/http/http_status_code.h"
12 #include "net/url_request/test_url_fetcher_factory.h"
13 #include "net/url_request/url_fetcher_impl.h"
14 #include "net/url_request/url_request_status.h"
15 #include "testing/gtest/include/gtest/gtest.h"
19 const int kRequestRetryIntervalMilliSeconds
= 200;
21 // This should be different from default to prevent TimeZoneRequest
23 const char kTestTimeZoneProviderUrl
[] =
24 "https://localhost/maps/api/timezone/json?";
26 const char kSimpleResponseBody
[] =
28 " \"dstOffset\" : 0.0,\n"
29 " \"rawOffset\" : -28800.0,\n"
30 " \"status\" : \"OK\",\n"
31 " \"timeZoneId\" : \"America/Los_Angeles\",\n"
32 " \"timeZoneName\" : \"Pacific Standard Time\"\n"
35 struct SimpleRequest
{
37 : url("https://localhost/maps/api/timezone/"
38 "json?location=39.603481,-119.682251×tamp=1331161200&sensor="
40 http_response(kSimpleResponseBody
) {
41 position
.latitude
= 39.6034810;
42 position
.longitude
= -119.6822510;
43 position
.accuracy
= 1;
44 position
.error_code
= 0;
45 position
.timestamp
= base::Time::FromTimeT(1331161200);
46 position
.status
= chromeos::Geoposition::STATUS_NONE
;
48 "latitude=39.603481, longitude=-119.682251, accuracy=1.000000, "
49 "error_code=0, error_message='', status=0 (NONE)",
52 timezone
.dstOffset
= 0;
53 timezone
.rawOffset
= -28800;
54 timezone
.timeZoneId
= "America/Los_Angeles";
55 timezone
.timeZoneName
= "Pacific Standard Time";
56 timezone
.error_message
.erase();
57 timezone
.status
= chromeos::TimeZoneResponseData::OK
;
59 "dstOffset=0.000000, rawOffset=-28800.000000, "
60 "timeZoneId='America/Los_Angeles', timeZoneName='Pacific Standard "
61 "Time', error_message='', status=0 (OK)",
62 timezone
.ToStringForDebug());
66 chromeos::Geoposition position
;
67 std::string http_response
;
68 chromeos::TimeZoneResponseData timezone
;
71 } // anonymous namespace
75 // This is helper class for net::FakeURLFetcherFactory.
76 class TestTimeZoneAPIURLFetcherCallback
{
78 TestTimeZoneAPIURLFetcherCallback(const GURL
& url
,
79 const size_t require_retries
,
80 const std::string
& response
,
81 TimeZoneProvider
* provider
)
83 require_retries_(require_retries
),
87 provider_(provider
) {}
89 scoped_ptr
<net::FakeURLFetcher
> CreateURLFetcher(
91 net::URLFetcherDelegate
* delegate
,
92 const std::string
& response_data
,
93 net::HttpStatusCode response_code
,
94 net::URLRequestStatus::Status status
) {
95 EXPECT_EQ(provider_
->requests_
.size(), 1U);
97 TimeZoneRequest
* timezone_request
= provider_
->requests_
[0];
99 const base::TimeDelta base_retry_interval
=
100 base::TimeDelta::FromMilliseconds(kRequestRetryIntervalMilliSeconds
);
101 timezone_request
->set_retry_sleep_on_server_error_for_testing(
102 base_retry_interval
);
103 timezone_request
->set_retry_sleep_on_bad_response_for_testing(
104 base_retry_interval
);
107 if (attempts_
> require_retries_
) {
108 response_code
= net::HTTP_OK
;
109 status
= net::URLRequestStatus::SUCCESS
;
110 factory_
->SetFakeResponse(url
, response_
, response_code
, status
);
112 scoped_ptr
<net::FakeURLFetcher
> fetcher(new net::FakeURLFetcher(
113 url
, delegate
, response_
, response_code
, status
));
114 scoped_refptr
<net::HttpResponseHeaders
> download_headers
=
115 new net::HttpResponseHeaders(std::string());
116 download_headers
->AddHeader("Content-Type: application/json");
117 fetcher
->set_response_headers(download_headers
);
118 return fetcher
.Pass();
121 void Initialize(net::FakeURLFetcherFactory
* factory
) {
123 factory_
->SetFakeResponse(url_
,
125 net::HTTP_INTERNAL_SERVER_ERROR
,
126 net::URLRequestStatus::FAILED
);
129 size_t attempts() const { return attempts_
; }
133 // Respond with OK on required retry attempt.
134 const size_t require_retries_
;
135 std::string response_
;
136 net::FakeURLFetcherFactory
* factory_
;
138 TimeZoneProvider
* provider_
;
140 DISALLOW_COPY_AND_ASSIGN(TestTimeZoneAPIURLFetcherCallback
);
143 // This implements fake TimeZone API remote endpoint.
144 // Response data is served to TimeZoneProvider via
145 // net::FakeURLFetcher.
146 class TimeZoneAPIFetcherFactory
{
148 TimeZoneAPIFetcherFactory(const GURL
& url
,
149 const std::string
& response
,
150 const size_t require_retries
,
151 TimeZoneProvider
* provider
) {
152 url_callback_
.reset(new TestTimeZoneAPIURLFetcherCallback(
153 url
, require_retries
, response
, provider
));
154 net::URLFetcherImpl::set_factory(NULL
);
155 fetcher_factory_
.reset(new net::FakeURLFetcherFactory(
157 base::Bind(&TestTimeZoneAPIURLFetcherCallback::CreateURLFetcher
,
158 base::Unretained(url_callback_
.get()))));
159 url_callback_
->Initialize(fetcher_factory_
.get());
162 size_t attempts() const { return url_callback_
->attempts(); }
165 scoped_ptr
<TestTimeZoneAPIURLFetcherCallback
> url_callback_
;
166 scoped_ptr
<net::FakeURLFetcherFactory
> fetcher_factory_
;
168 DISALLOW_COPY_AND_ASSIGN(TimeZoneAPIFetcherFactory
);
171 class TimeZoneReceiver
{
173 TimeZoneReceiver() : server_error_(false) {}
175 void OnRequestDone(scoped_ptr
<TimeZoneResponseData
> timezone
,
177 timezone_
= timezone
.Pass();
178 server_error_
= server_error
;
180 message_loop_runner_
->Quit();
183 void WaitUntilRequestDone() {
184 message_loop_runner_
.reset(new base::RunLoop
);
185 message_loop_runner_
->Run();
188 const TimeZoneResponseData
* timezone() const { return timezone_
.get(); }
189 bool server_error() const { return server_error_
; }
192 scoped_ptr
<TimeZoneResponseData
> timezone_
;
194 scoped_ptr
<base::RunLoop
> message_loop_runner_
;
197 class TimeZoneTest
: public testing::Test
{
199 base::MessageLoop message_loop_
;
202 TEST_F(TimeZoneTest
, ResponseOK
) {
203 TimeZoneProvider
provider(NULL
, GURL(kTestTimeZoneProviderUrl
));
204 const SimpleRequest simple_request
;
206 TimeZoneAPIFetcherFactory
url_factory(simple_request
.url
,
207 simple_request
.http_response
,
208 0 /* require_retries */,
211 TimeZoneReceiver receiver
;
213 provider
.RequestTimezone(simple_request
.position
,
214 base::TimeDelta::FromSeconds(1),
215 base::Bind(&TimeZoneReceiver::OnRequestDone
,
216 base::Unretained(&receiver
)));
217 receiver
.WaitUntilRequestDone();
219 EXPECT_EQ(simple_request
.timezone
.ToStringForDebug(),
220 receiver
.timezone()->ToStringForDebug());
221 EXPECT_FALSE(receiver
.server_error());
222 EXPECT_EQ(1U, url_factory
.attempts());
225 TEST_F(TimeZoneTest
, ResponseOKWithRetries
) {
226 TimeZoneProvider
provider(NULL
, GURL(kTestTimeZoneProviderUrl
));
227 const SimpleRequest simple_request
;
229 TimeZoneAPIFetcherFactory
url_factory(simple_request
.url
,
230 simple_request
.http_response
,
231 3 /* require_retries */,
234 TimeZoneReceiver receiver
;
236 provider
.RequestTimezone(simple_request
.position
,
237 base::TimeDelta::FromSeconds(1),
238 base::Bind(&TimeZoneReceiver::OnRequestDone
,
239 base::Unretained(&receiver
)));
240 receiver
.WaitUntilRequestDone();
241 EXPECT_EQ(simple_request
.timezone
.ToStringForDebug(),
242 receiver
.timezone()->ToStringForDebug());
243 EXPECT_FALSE(receiver
.server_error());
244 EXPECT_EQ(4U, url_factory
.attempts());
247 TEST_F(TimeZoneTest
, InvalidResponse
) {
248 TimeZoneProvider
provider(NULL
, GURL(kTestTimeZoneProviderUrl
));
249 const SimpleRequest simple_request
;
251 TimeZoneAPIFetcherFactory
url_factory(simple_request
.url
,
252 "invalid JSON string",
253 0 /* require_retries */,
256 TimeZoneReceiver receiver
;
258 const int timeout_seconds
= 1;
259 size_t expected_retries
= static_cast<size_t>(
260 timeout_seconds
* 1000 / kRequestRetryIntervalMilliSeconds
);
261 ASSERT_GE(expected_retries
, 2U);
263 provider
.RequestTimezone(simple_request
.position
,
264 base::TimeDelta::FromSeconds(timeout_seconds
),
265 base::Bind(&TimeZoneReceiver::OnRequestDone
,
266 base::Unretained(&receiver
)));
267 receiver
.WaitUntilRequestDone();
269 "dstOffset=0.000000, rawOffset=0.000000, timeZoneId='', timeZoneName='', "
270 "error_message='TimeZone provider at 'https://localhost/' : JSONReader "
271 "failed: Line: 1, column: 1, Unexpected token..', status=6 "
273 receiver
.timezone()->ToStringForDebug());
274 EXPECT_FALSE(receiver
.server_error());
275 EXPECT_GE(url_factory
.attempts(), 2U);
276 if (url_factory
.attempts() > expected_retries
+ 1) {
277 LOG(WARNING
) << "TimeZoneTest::InvalidResponse: Too many attempts ("
278 << url_factory
.attempts() << "), no more then "
279 << expected_retries
+ 1 << " expected.";
281 if (url_factory
.attempts() < expected_retries
- 1) {
282 LOG(WARNING
) << "TimeZoneTest::InvalidResponse: Too less attempts ("
283 << url_factory
.attempts() << "), greater then "
284 << expected_retries
- 1 << " expected.";
288 TEST(TimeZoneResolverTest
, CheckIntervals
) {
289 for (int requests_count
= 1; requests_count
< 10; ++requests_count
) {
290 EXPECT_EQ(requests_count
,
291 TimeZoneResolver::MaxRequestsCountForIntervalForTesting(
292 TimeZoneResolver::IntervalForNextRequestForTesting(
297 } // namespace chromeos