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. 4 5 #include "base/run_loop.h" 6 #include "chrome/browser/chromeos/geolocation/simple_geolocation_provider.h" 7 #include "content/public/test/test_browser_thread_bundle.h" 8 #include "net/http/http_response_headers.h" 9 #include "net/http/http_status_code.h" 10 #include "net/url_request/test_url_fetcher_factory.h" 11 #include "net/url_request/url_fetcher_impl.h" 12 #include "net/url_request/url_request_status.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 15 namespace { 16 17 const int kRequestRetryIntervalMilliSeconds = 200; 18 19 // This should be different from default to prevent SimpleGeolocationRequest 20 // from modifying it. 21 const char kTestGeolocationProviderUrl[] = 22 "https://localhost/geolocation/v1/geolocate?"; 23 24 const char kSimpleResponseBody[] = 25 "{\n" 26 " \"location\": {\n" 27 " \"lat\": 51.0,\n" 28 " \"lng\": -0.1\n" 29 " },\n" 30 " \"accuracy\": 1200.4\n" 31 "}"; 32 } // anonymous namespace 33 34 namespace chromeos { 35 36 // This is helper class for net::FakeURLFetcherFactory. 37 class TestGeolocationAPIURLFetcherCallback { 38 public: 39 TestGeolocationAPIURLFetcherCallback(const GURL& url, 40 const size_t require_retries, 41 const std::string& response, 42 SimpleGeolocationProvider* provider) 43 : url_(url), 44 require_retries_(require_retries), 45 response_(response), 46 factory_(NULL), 47 attempts_(0), 48 provider_(provider) {} 49 50 scoped_ptr<net::FakeURLFetcher> CreateURLFetcher( 51 const GURL& url, 52 net::URLFetcherDelegate* delegate, 53 const std::string& response_data, 54 net::HttpStatusCode response_code, 55 net::URLRequestStatus::Status status) { 56 EXPECT_EQ(provider_->requests_.size(), 1U); 57 58 SimpleGeolocationRequest* geolocation_request = provider_->requests_[0]; 59 60 const base::TimeDelta base_retry_interval = 61 base::TimeDelta::FromMilliseconds(kRequestRetryIntervalMilliSeconds); 62 geolocation_request->set_retry_sleep_on_server_error_for_testing( 63 base_retry_interval); 64 geolocation_request->set_retry_sleep_on_bad_response_for_testing( 65 base_retry_interval); 66 67 ++attempts_; 68 if (attempts_ > require_retries_) { 69 response_code = net::HTTP_OK; 70 status = net::URLRequestStatus::SUCCESS; 71 factory_->SetFakeResponse(url, response_, response_code, status); 72 } 73 scoped_ptr<net::FakeURLFetcher> fetcher(new net::FakeURLFetcher( 74 url, delegate, response_, response_code, status)); 75 scoped_refptr<net::HttpResponseHeaders> download_headers = 76 new net::HttpResponseHeaders(std::string()); 77 download_headers->AddHeader("Content-Type: application/json"); 78 fetcher->set_response_headers(download_headers); 79 return fetcher.Pass(); 80 } 81 82 void Initialize(net::FakeURLFetcherFactory* factory) { 83 factory_ = factory; 84 factory_->SetFakeResponse(url_, 85 std::string(), 86 net::HTTP_INTERNAL_SERVER_ERROR, 87 net::URLRequestStatus::FAILED); 88 } 89 90 size_t attempts() const { return attempts_; } 91 92 private: 93 const GURL url_; 94 // Respond with OK on required retry attempt. 95 const size_t require_retries_; 96 std::string response_; 97 net::FakeURLFetcherFactory* factory_; 98 size_t attempts_; 99 SimpleGeolocationProvider* provider_; 100 101 DISALLOW_COPY_AND_ASSIGN(TestGeolocationAPIURLFetcherCallback); 102 }; 103 104 // This implements fake Google MAPS Geolocation API remote endpoint. 105 // Response data is served to SimpleGeolocationProvider via 106 // net::FakeURLFetcher. 107 class GeolocationAPIFetcherFactory { 108 public: 109 GeolocationAPIFetcherFactory(const GURL& url, 110 const std::string& response, 111 const size_t require_retries, 112 SimpleGeolocationProvider* provider) { 113 url_callback_.reset(new TestGeolocationAPIURLFetcherCallback( 114 url, require_retries, response, provider)); 115 net::URLFetcherImpl::set_factory(NULL); 116 fetcher_factory_.reset(new net::FakeURLFetcherFactory( 117 NULL, 118 base::Bind(&TestGeolocationAPIURLFetcherCallback::CreateURLFetcher, 119 base::Unretained(url_callback_.get())))); 120 url_callback_->Initialize(fetcher_factory_.get()); 121 } 122 123 size_t attempts() const { return url_callback_->attempts(); } 124 125 private: 126 scoped_ptr<TestGeolocationAPIURLFetcherCallback> url_callback_; 127 scoped_ptr<net::FakeURLFetcherFactory> fetcher_factory_; 128 129 DISALLOW_COPY_AND_ASSIGN(GeolocationAPIFetcherFactory); 130 }; 131 132 class GeolocationReceiver { 133 public: 134 GeolocationReceiver() : server_error_(false) {} 135 136 void OnRequestDone(const Geoposition& position, 137 bool server_error, 138 const base::TimeDelta elapsed) { 139 position_ = position; 140 server_error_ = server_error; 141 elapsed_ = elapsed; 142 143 message_loop_runner_->Quit(); 144 } 145 146 void WaitUntilRequestDone() { 147 message_loop_runner_.reset(new base::RunLoop); 148 message_loop_runner_->Run(); 149 } 150 151 const Geoposition& position() const { return position_; } 152 bool server_error() const { return server_error_; } 153 base::TimeDelta elapsed() const { return elapsed_; } 154 155 private: 156 Geoposition position_; 157 bool server_error_; 158 base::TimeDelta elapsed_; 159 scoped_ptr<base::RunLoop> message_loop_runner_; 160 }; 161 162 class SimpleGeolocationTest : public testing::Test { 163 private: 164 content::TestBrowserThreadBundle thread_bundle_; 165 }; 166 167 TEST_F(SimpleGeolocationTest, ResponseOK) { 168 SimpleGeolocationProvider provider(NULL, GURL(kTestGeolocationProviderUrl)); 169 170 GeolocationAPIFetcherFactory url_factory(GURL(kTestGeolocationProviderUrl), 171 std::string(kSimpleResponseBody), 172 0 /* require_retries */, 173 &provider); 174 175 GeolocationReceiver receiver; 176 provider.RequestGeolocation(base::TimeDelta::FromSeconds(1), 177 base::Bind(&GeolocationReceiver::OnRequestDone, 178 base::Unretained(&receiver))); 179 receiver.WaitUntilRequestDone(); 180 181 EXPECT_EQ( 182 "latitude=51.000000, longitude=-0.100000, accuracy=1200.400000, " 183 "error_code=0, error_message='', status=1 (OK)", 184 receiver.position().ToString()); 185 EXPECT_FALSE(receiver.server_error()); 186 EXPECT_EQ(1U, url_factory.attempts()); 187 } 188 189 TEST_F(SimpleGeolocationTest, ResponseOKWithRetries) { 190 SimpleGeolocationProvider provider(NULL, GURL(kTestGeolocationProviderUrl)); 191 192 GeolocationAPIFetcherFactory url_factory(GURL(kTestGeolocationProviderUrl), 193 std::string(kSimpleResponseBody), 194 3 /* require_retries */, 195 &provider); 196 197 GeolocationReceiver receiver; 198 provider.RequestGeolocation(base::TimeDelta::FromSeconds(1), 199 base::Bind(&GeolocationReceiver::OnRequestDone, 200 base::Unretained(&receiver))); 201 receiver.WaitUntilRequestDone(); 202 EXPECT_EQ( 203 "latitude=51.000000, longitude=-0.100000, accuracy=1200.400000, " 204 "error_code=0, error_message='', status=1 (OK)", 205 receiver.position().ToString()); 206 EXPECT_FALSE(receiver.server_error()); 207 EXPECT_EQ(4U, url_factory.attempts()); 208 } 209 210 TEST_F(SimpleGeolocationTest, InvalidResponse) { 211 SimpleGeolocationProvider provider(NULL, GURL(kTestGeolocationProviderUrl)); 212 213 GeolocationAPIFetcherFactory url_factory(GURL(kTestGeolocationProviderUrl), 214 "invalid JSON string", 215 0 /* require_retries */, 216 &provider); 217 218 GeolocationReceiver receiver; 219 220 const int timeout_seconds = 1; 221 size_t expected_retries = static_cast<size_t>( 222 timeout_seconds * 1000 / kRequestRetryIntervalMilliSeconds); 223 ASSERT_GE(expected_retries, 2U); 224 225 provider.RequestGeolocation(base::TimeDelta::FromSeconds(timeout_seconds), 226 base::Bind(&GeolocationReceiver::OnRequestDone, 227 base::Unretained(&receiver))); 228 receiver.WaitUntilRequestDone(); 229 230 EXPECT_EQ( 231 "latitude=200.000000, longitude=200.000000, accuracy=-1.000000, " 232 "error_code=0, error_message='SimpleGeolocation provider at " 233 "'https://localhost/' : JSONReader failed: Line: 1, column: 1, " 234 "Unexpected token..', status=4 (TIMEOUT)", 235 receiver.position().ToString()); 236 EXPECT_TRUE(receiver.server_error()); 237 EXPECT_GE(url_factory.attempts(), 2U); 238 if (url_factory.attempts() > expected_retries + 1) { 239 LOG(WARNING) 240 << "SimpleGeolocationTest::InvalidResponse: Too many attempts (" 241 << url_factory.attempts() << "), no more then " << expected_retries + 1 242 << " expected."; 243 } 244 if (url_factory.attempts() < expected_retries - 1) { 245 LOG(WARNING) 246 << "SimpleGeolocationTest::InvalidResponse: Too little attempts (" 247 << url_factory.attempts() << "), greater then " << expected_retries - 1 248 << " expected."; 249 } 250 } 251 252 } // namespace chromeos 253