| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2010 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 "chrome/browser/geolocation/network_location_provider.h" |
| 6 |
| 7 #include <map> |
| 8 |
| 9 #include "base/json/json_reader.h" |
| 10 #include "base/scoped_ptr.h" |
| 11 #include "base/string_util.h" |
| 12 #include "base/values.h" |
| 13 #include "chrome/browser/net/test_url_fetcher_factory.h" |
| 14 #include "net/url_request/url_request_status.h" |
| 15 #include "testing/gtest/include/gtest/gtest.h" |
| 16 |
| 17 namespace { |
| 18 const char kTestServerUrl[] = "https://www.geolocation.test/service"; |
| 19 const char kTestHost[] = "myclienthost.test"; |
| 20 } // namespace |
| 21 |
| 22 // Stops the specified (nested) message loop when the listener is called back. |
| 23 class MessageLoopQuitListener |
| 24 : public LocationProviderBase::ListenerInterface { |
| 25 public: |
| 26 MessageLoopQuitListener() |
| 27 : client_message_loop_(MessageLoop::current()), |
| 28 updated_provider_(NULL), |
| 29 movement_provider_(NULL) { |
| 30 DCHECK(client_message_loop_); |
| 31 } |
| 32 // ListenerInterface |
| 33 virtual void LocationUpdateAvailable(LocationProviderBase* provider) { |
| 34 EXPECT_EQ(client_message_loop_, MessageLoop::current()); |
| 35 updated_provider_ = provider; |
| 36 client_message_loop_->Quit(); |
| 37 } |
| 38 virtual void MovementDetected(LocationProviderBase* provider) { |
| 39 EXPECT_EQ(client_message_loop_, MessageLoop::current()); |
| 40 movement_provider_ = provider; |
| 41 client_message_loop_->Quit(); |
| 42 } |
| 43 MessageLoop* client_message_loop_; |
| 44 LocationProviderBase* updated_provider_; |
| 45 LocationProviderBase* movement_provider_; |
| 46 }; |
| 47 |
| 48 class FakeAccessTokenStore : public LocationProviderBase::AccessTokenStore { |
| 49 public: |
| 50 FakeAccessTokenStore() : allow_set_(true) {} |
| 51 |
| 52 virtual bool SetAccessToken(const GURL& url, |
| 53 const string16& access_token) { |
| 54 if (!allow_set_) |
| 55 return false; |
| 56 token_map_[url] = access_token; |
| 57 return true; |
| 58 } |
| 59 virtual bool GetAccessToken(const GURL& url, string16* access_token) { |
| 60 std::map<GURL, string16>::iterator item = token_map_.find(url); |
| 61 if (item == token_map_.end()) |
| 62 return false; |
| 63 *access_token = item->second; |
| 64 return true; |
| 65 } |
| 66 bool allow_set_; |
| 67 std::map<GURL, string16> token_map_; |
| 68 }; |
| 69 |
| 70 |
| 71 // A mock implementation of DeviceDataProviderImplBase for testing. Adapted from |
| 72 // http://gears.googlecode.com/svn/trunk/gears/geolocation/geolocation_test.cc |
| 73 template<typename DataType> |
| 74 class MockDeviceDataProviderImpl |
| 75 : public DeviceDataProviderImplBase<DataType> { |
| 76 public: |
| 77 // Factory method for use with DeviceDataProvider::SetFactory. |
| 78 static DeviceDataProviderImplBase<DataType>* Create() { |
| 79 return new MockDeviceDataProviderImpl<DataType>(); |
| 80 } |
| 81 static MockDeviceDataProviderImpl<DataType>* instance() { |
| 82 CHECK(instance_ != NULL); |
| 83 return instance_; |
| 84 } |
| 85 |
| 86 MockDeviceDataProviderImpl() { |
| 87 CHECK(instance_ == NULL); |
| 88 instance_ = this; |
| 89 } |
| 90 virtual ~MockDeviceDataProviderImpl() { |
| 91 CHECK(this == instance_); |
| 92 instance_ = NULL; |
| 93 } |
| 94 |
| 95 // DeviceDataProviderImplBase implementation. |
| 96 virtual bool StartDataProvider() { |
| 97 return true; |
| 98 } |
| 99 virtual bool GetData(DataType* data_out) { |
| 100 CHECK(data_out); |
| 101 AutoLock lock(data_mutex_); |
| 102 *data_out = data_; |
| 103 // We always have all the data we can get, so return true. |
| 104 return true; |
| 105 } |
| 106 |
| 107 void SetData(const DataType& new_data) { |
| 108 data_mutex_.Acquire(); |
| 109 const bool differs = data_.DiffersSignificantly(new_data); |
| 110 data_ = new_data; |
| 111 data_mutex_.Release(); |
| 112 if (differs) |
| 113 this->NotifyListeners(); |
| 114 } |
| 115 |
| 116 private: |
| 117 static MockDeviceDataProviderImpl<DataType>* instance_; |
| 118 |
| 119 DataType data_; |
| 120 Lock data_mutex_; |
| 121 |
| 122 DISALLOW_COPY_AND_ASSIGN(MockDeviceDataProviderImpl); |
| 123 }; |
| 124 |
| 125 template<typename DataType> |
| 126 MockDeviceDataProviderImpl<DataType>* |
| 127 MockDeviceDataProviderImpl<DataType>::instance_ = NULL; |
| 128 |
| 129 // Main test fixture |
| 130 class NetworkLocationProviderTest : public testing::Test { |
| 131 public: |
| 132 virtual void SetUp() { |
| 133 URLFetcher::set_factory(&url_fetcher_factory_); |
| 134 } |
| 135 |
| 136 virtual void TearDown() { |
| 137 WifiDataProvider::ResetFactory(); |
| 138 RadioDataProvider::ResetFactory(); |
| 139 URLFetcher::set_factory(NULL); |
| 140 base::LeakTracker<URLFetcher>::CheckForLeaks(); |
| 141 } |
| 142 |
| 143 LocationProviderBase* CreateProvider() { |
| 144 return NewNetworkLocationProvider( |
| 145 &access_token_store_, |
| 146 NULL, // No URLContextGetter needed, as using test urlfecther factory. |
| 147 test_server_url_, |
| 148 ASCIIToUTF16(kTestHost)); |
| 149 } |
| 150 |
| 151 protected: |
| 152 NetworkLocationProviderTest() : test_server_url_(kTestServerUrl) { |
| 153 // TODO(joth): Really these should be in SetUp, not here, but they take no |
| 154 // effect on Mac OS Release builds if done there. I kid not. Figure out why. |
| 155 RadioDataProvider::SetFactory( |
| 156 MockDeviceDataProviderImpl<RadioData>::Create); |
| 157 WifiDataProvider::SetFactory( |
| 158 MockDeviceDataProviderImpl<WifiData>::Create); |
| 159 } |
| 160 |
| 161 static int IndexToChannal(int index) { return index + 4; } |
| 162 static int IndexToAge(int index) { return (index * 3) + 100; } |
| 163 |
| 164 // Creates wifi data containing the specified number of access points, with |
| 165 // some differentiating charactistics in each. |
| 166 static WifiData CreateReferenceWifiScanData(int ap_count) { |
| 167 WifiData data; |
| 168 for (int i = 0; i < ap_count; ++i) { |
| 169 AccessPointData ap; |
| 170 ap.mac_address = ASCIIToUTF16(StringPrintf("%02d-34-56-78-54-32", i)); |
| 171 ap.radio_signal_strength = i; |
| 172 ap.age = IndexToAge(i); |
| 173 ap.channel = IndexToChannal(i); |
| 174 ap.signal_to_noise = i + 42; |
| 175 ap.ssid = ASCIIToUTF16("Some nice network"); |
| 176 data.access_point_data.insert(ap); |
| 177 } |
| 178 return data; |
| 179 } |
| 180 |
| 181 static void ParseRequest(const std::string& request_data, |
| 182 WifiData* wifi_data_out, |
| 183 std::string* access_token_out) { |
| 184 CHECK(wifi_data_out && access_token_out); |
| 185 scoped_ptr<Value> value(base::JSONReader::Read(request_data, false)); |
| 186 EXPECT_TRUE(value != NULL); |
| 187 EXPECT_EQ(Value::TYPE_DICTIONARY, value->GetType()); |
| 188 DictionaryValue* dictionary = static_cast<DictionaryValue*>(value.get()); |
| 189 std::string attr_value; |
| 190 EXPECT_TRUE(dictionary->GetString(L"version", &attr_value)); |
| 191 EXPECT_EQ(attr_value, "1.1.0"); |
| 192 EXPECT_TRUE(dictionary->GetString(L"host", &attr_value)); |
| 193 EXPECT_EQ(attr_value, kTestHost); |
| 194 // Everything else is optional. |
| 195 ListValue* wifi_aps; |
| 196 if (dictionary->GetList(L"wifi_towers", &wifi_aps)) { |
| 197 int i = 0; |
| 198 for (ListValue::const_iterator it = wifi_aps->begin(); |
| 199 it < wifi_aps->end(); ++it, ++i) { |
| 200 EXPECT_EQ(Value::TYPE_DICTIONARY, (*it)->GetType()); |
| 201 DictionaryValue* ap = static_cast<DictionaryValue*>(*it); |
| 202 AccessPointData data; |
| 203 ap->GetStringAsUTF16(L"mac_address", &data.mac_address); |
| 204 ap->GetInteger(L"signal_strength", &data.radio_signal_strength); |
| 205 ap->GetInteger(L"age", &data.age); |
| 206 ap->GetInteger(L"channel", &data.channel); |
| 207 ap->GetInteger(L"signal_to_noise", &data.signal_to_noise); |
| 208 ap->GetStringAsUTF16(L"ssid", &data.ssid); |
| 209 wifi_data_out->access_point_data.insert(data); |
| 210 } |
| 211 } else { |
| 212 wifi_data_out->access_point_data.clear(); |
| 213 } |
| 214 if (!dictionary->GetString(L"access_token", access_token_out)) |
| 215 access_token_out->clear(); |
| 216 } |
| 217 |
| 218 static void CheckEmptyRequestIsValid(const std::string& request_data) { |
| 219 WifiData wifi_aps; |
| 220 std::string access_token; |
| 221 ParseRequest(request_data, &wifi_aps, &access_token); |
| 222 EXPECT_EQ(0, static_cast<int>(wifi_aps.access_point_data.size())); |
| 223 EXPECT_TRUE(access_token.empty()); |
| 224 } |
| 225 |
| 226 static void CheckRequestIsValid(const std::string& request_data, |
| 227 int expected_wifi_aps, |
| 228 const std::string& expected_access_token) { |
| 229 WifiData wifi_aps; |
| 230 std::string access_token; |
| 231 ParseRequest(request_data, &wifi_aps, &access_token); |
| 232 EXPECT_EQ(expected_wifi_aps, |
| 233 static_cast<int>(wifi_aps.access_point_data.size())); |
| 234 WifiData expected_data = CreateReferenceWifiScanData(expected_wifi_aps); |
| 235 WifiData::AccessPointDataSet::const_iterator expected = |
| 236 expected_data.access_point_data.begin(); |
| 237 WifiData::AccessPointDataSet::const_iterator actual = |
| 238 wifi_aps.access_point_data.begin(); |
| 239 for (int i = 0; i < expected_wifi_aps; ++i) { |
| 240 EXPECT_EQ(expected->mac_address, actual->mac_address) << i; |
| 241 EXPECT_EQ(expected->radio_signal_strength, actual->radio_signal_strength) |
| 242 << i; |
| 243 EXPECT_EQ(expected->age, actual->age) << i; |
| 244 EXPECT_EQ(expected->channel, actual->channel) << i; |
| 245 EXPECT_EQ(expected->signal_to_noise, actual->signal_to_noise) << i; |
| 246 EXPECT_EQ(expected->ssid, actual->ssid) << i; |
| 247 ++expected; |
| 248 ++actual; |
| 249 } |
| 250 EXPECT_EQ(expected_access_token, access_token); |
| 251 } |
| 252 |
| 253 const GURL test_server_url_; |
| 254 MessageLoop main_message_loop_; |
| 255 FakeAccessTokenStore access_token_store_; |
| 256 TestURLFetcherFactory url_fetcher_factory_; |
| 257 }; |
| 258 |
| 259 |
| 260 TEST_F(NetworkLocationProviderTest, CreateDestroy) { |
| 261 // Test fixture members were SetUp correctly. |
| 262 EXPECT_EQ(&main_message_loop_, MessageLoop::current()); |
| 263 scoped_refptr<LocationProviderBase> provider(CreateProvider()); |
| 264 EXPECT_TRUE(NULL != provider.get()); |
| 265 provider = NULL; |
| 266 SUCCEED(); |
| 267 } |
| 268 |
| 269 TEST_F(NetworkLocationProviderTest, StartProvider) { |
| 270 scoped_refptr<LocationProviderBase> provider(CreateProvider()); |
| 271 EXPECT_TRUE(provider->StartProvider()); |
| 272 TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); |
| 273 ASSERT_TRUE(fetcher != NULL); |
| 274 |
| 275 EXPECT_EQ(test_server_url_, fetcher->original_url()); |
| 276 |
| 277 // No wifi data so expect an empty request. |
| 278 CheckEmptyRequestIsValid(fetcher->upload_data()); |
| 279 } |
| 280 |
| 281 TEST_F(NetworkLocationProviderTest, MultipleWifiScansComplete) { |
| 282 scoped_refptr<LocationProviderBase> provider(CreateProvider()); |
| 283 EXPECT_TRUE(provider->StartProvider()); |
| 284 |
| 285 TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); |
| 286 ASSERT_TRUE(fetcher != NULL); |
| 287 CheckEmptyRequestIsValid(fetcher->upload_data()); |
| 288 // Complete the network request with bad position fix (using #define so we |
| 289 // can paste this into various other strings below) |
| 290 #define REFERENCE_ACCESS_TOKEN "2:k7j3G6LaL6u_lafw:4iXOeOpTh1glSXe" |
| 291 const char* kNoFixNetworkResponse = |
| 292 "{" |
| 293 " \"location\": null," |
| 294 " \"access_token\": \"" REFERENCE_ACCESS_TOKEN "\"" |
| 295 "}"; |
| 296 fetcher->delegate()->OnURLFetchComplete( |
| 297 fetcher, test_server_url_, URLRequestStatus(), 200, // OK |
| 298 ResponseCookies(), kNoFixNetworkResponse); |
| 299 |
| 300 // This should have set the access token anyhow |
| 301 EXPECT_EQ(1, static_cast<int>(access_token_store_.token_map_.size())); |
| 302 string16 token; |
| 303 EXPECT_TRUE(access_token_store_.GetAccessToken(test_server_url_, &token)); |
| 304 EXPECT_EQ(REFERENCE_ACCESS_TOKEN, UTF16ToUTF8(token)); |
| 305 |
| 306 Position position; |
| 307 provider->GetPosition(&position); |
| 308 EXPECT_FALSE(position.IsValidFix()); |
| 309 |
| 310 // Now wifi data arrives |
| 311 const int kFirstScanAps = 6; |
| 312 MockDeviceDataProviderImpl<WifiData>::instance()->SetData( |
| 313 CreateReferenceWifiScanData(kFirstScanAps)); // Will notify listeners |
| 314 fetcher = url_fetcher_factory_.GetFetcherByID(kFirstScanAps); |
| 315 ASSERT_TRUE(fetcher != NULL); |
| 316 // The request should have access token (set previously) and the wifi data. |
| 317 CheckRequestIsValid(fetcher->upload_data(), |
| 318 kFirstScanAps, |
| 319 REFERENCE_ACCESS_TOKEN); |
| 320 |
| 321 // Send a reply with good position fix. |
| 322 const char* kReferenceNetworkResponse = |
| 323 "{" |
| 324 " \"location\": {" |
| 325 " \"latitude\": 51.0," |
| 326 " \"longitude\": -0.1," |
| 327 " \"altitude\": 30.1," |
| 328 " \"accuracy\": 1200.4," |
| 329 " \"altitude_accuracy\": 10.6" |
| 330 " }" |
| 331 "}"; |
| 332 fetcher->delegate()->OnURLFetchComplete( |
| 333 fetcher, test_server_url_, URLRequestStatus(), 200, // OK |
| 334 ResponseCookies(), kReferenceNetworkResponse); |
| 335 |
| 336 provider->GetPosition(&position); |
| 337 EXPECT_EQ(51.0, position.latitude); |
| 338 EXPECT_EQ(-0.1, position.longitude); |
| 339 EXPECT_EQ(30.1, position.altitude); |
| 340 EXPECT_EQ(1200.4, position.accuracy); |
| 341 EXPECT_EQ(10.6, position.altitude_accuracy); |
| 342 EXPECT_TRUE(position.is_valid_timestamp()); |
| 343 EXPECT_TRUE(position.IsValidFix()); |
| 344 |
| 345 // Token should still be in the store. |
| 346 EXPECT_EQ(1, static_cast<int>(access_token_store_.token_map_.size())); |
| 347 EXPECT_TRUE(access_token_store_.GetAccessToken(test_server_url_, &token)); |
| 348 EXPECT_EQ(REFERENCE_ACCESS_TOKEN, UTF16ToUTF8(token)); |
| 349 |
| 350 // Wifi updated again, with one less AP. This is 'close enough' to the |
| 351 // previous scan, so no new request made. |
| 352 const int kSecondScanAps = kFirstScanAps - 1; |
| 353 MockDeviceDataProviderImpl<WifiData>::instance()->SetData( |
| 354 CreateReferenceWifiScanData(kSecondScanAps)); |
| 355 fetcher = url_fetcher_factory_.GetFetcherByID(kSecondScanAps); |
| 356 EXPECT_FALSE(fetcher); |
| 357 |
| 358 provider->GetPosition(&position); |
| 359 EXPECT_EQ(51.0, position.latitude); |
| 360 EXPECT_EQ(-0.1, position.longitude); |
| 361 EXPECT_TRUE(position.IsValidFix()); |
| 362 |
| 363 // Now a third scan with more than twice the original amount -> new request. |
| 364 const int kThirdScanAps = kFirstScanAps * 2 + 1; |
| 365 MockDeviceDataProviderImpl<WifiData>::instance()->SetData( |
| 366 CreateReferenceWifiScanData(kThirdScanAps)); |
| 367 fetcher = url_fetcher_factory_.GetFetcherByID(kThirdScanAps); |
| 368 EXPECT_TRUE(fetcher); |
| 369 // ...reply with a network error. |
| 370 fetcher->delegate()->OnURLFetchComplete( |
| 371 fetcher, test_server_url_, |
| 372 URLRequestStatus(URLRequestStatus::FAILED, -1), |
| 373 200, // should be ignored |
| 374 ResponseCookies(), ""); |
| 375 |
| 376 // Error means we now no longer have a fix. |
| 377 provider->GetPosition(&position); |
| 378 EXPECT_FALSE(position.is_valid_latlong()); |
| 379 EXPECT_FALSE(position.IsValidFix()); |
| 380 |
| 381 // Wifi scan returns to original set: should be serviced from cache. |
| 382 const TestURLFetcher* orig_fetcher = |
| 383 url_fetcher_factory_.GetFetcherByID(kFirstScanAps); |
| 384 MockDeviceDataProviderImpl<WifiData>::instance()->SetData( |
| 385 CreateReferenceWifiScanData(kFirstScanAps)); |
| 386 fetcher = url_fetcher_factory_.GetFetcherByID(kFirstScanAps); |
| 387 EXPECT_EQ(orig_fetcher, fetcher); // No new request created. |
| 388 |
| 389 provider->GetPosition(&position); |
| 390 EXPECT_EQ(51.0, position.latitude); |
| 391 EXPECT_EQ(-0.1, position.longitude); |
| 392 EXPECT_TRUE(position.IsValidFix()); |
| 393 } |
| 394 |
| 395 // TODO(joth): Add tests for corner cases around the 2 second startup delay |
| 396 // (e.g. timer firing, or being pre-empted by data arriving) |
| OLD | NEW |