Home | History | Annotate | Download | only in geolocation
      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