| 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 explicit MessageLoopQuitListener() | |
| 27 : client_message_loop_(MessageLoop::current()), | |
| 28 updated_provider_(NULL), | |
| 29 movement_provider_(NULL) { | |
| 30 DCHECK(client_message_loop_ != NULL); | |
| 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 RadioDataProvider::SetFactory( | |
| 135 MockDeviceDataProviderImpl<RadioData>::Create); | |
| 136 WifiDataProvider::SetFactory( | |
| 137 MockDeviceDataProviderImpl<WifiData>::Create); | |
| 138 } | |
| 139 | |
| 140 virtual void TearDown() { | |
| 141 WifiDataProvider::ResetFactory(); | |
| 142 RadioDataProvider::ResetFactory(); | |
| 143 URLFetcher::set_factory(NULL); | |
| 144 base::LeakTracker<URLFetcher>::CheckForLeaks(); | |
| 145 } | |
| 146 | |
| 147 LocationProviderBase* CreateProvider() { | |
| 148 return NewNetworkLocationProvider( | |
| 149 &access_token_store_, | |
| 150 NULL, // No URLContextGetter needed, as using test urlfecther factory. | |
| 151 test_server_url_, | |
| 152 ASCIIToUTF16(kTestHost)); | |
| 153 } | |
| 154 | |
| 155 protected: | |
| 156 NetworkLocationProviderTest() : test_server_url_(kTestServerUrl) {} | |
| 157 | |
| 158 static int IndexToChannal(int index) { return index + 4; } | |
| 159 static int IndexToAge(int index) { return (index * 3) + 100; } | |
| 160 | |
| 161 // Creates wifi data containing the specified number of access points, with | |
| 162 // some differentiating charactistics in each. | |
| 163 static WifiData CreateReferenceWifiScanData(int ap_count) { | |
| 164 WifiData data; | |
| 165 for (int i = 0; i < ap_count; ++i) { | |
| 166 AccessPointData ap; | |
| 167 ap.mac_address = ASCIIToUTF16(StringPrintf("%02d-34-56-78-54-32", i)); | |
| 168 ap.radio_signal_strength = i; | |
| 169 ap.age = IndexToAge(i); | |
| 170 ap.channel = IndexToChannal(i); | |
| 171 ap.signal_to_noise = i + 42; | |
| 172 ap.ssid = ASCIIToUTF16("Some nice network"); | |
| 173 data.access_point_data.insert(ap); | |
| 174 } | |
| 175 return data; | |
| 176 } | |
| 177 | |
| 178 static void ParseRequest(const std::string& request_data, | |
| 179 WifiData* wifi_data_out, | |
| 180 std::string* access_token_out) { | |
| 181 CHECK(wifi_data_out && access_token_out); | |
| 182 scoped_ptr<Value> value(base::JSONReader::Read(request_data, false)); | |
| 183 EXPECT_TRUE(value != NULL); | |
| 184 EXPECT_EQ(Value::TYPE_DICTIONARY, value->GetType()); | |
| 185 DictionaryValue* dictionary = static_cast<DictionaryValue*>(value.get()); | |
| 186 std::string attr_value; | |
| 187 EXPECT_TRUE(dictionary->GetString(L"version", &attr_value)); | |
| 188 EXPECT_EQ(attr_value, "1.1.0"); | |
| 189 EXPECT_TRUE(dictionary->GetString(L"host", &attr_value)); | |
| 190 EXPECT_EQ(attr_value, kTestHost); | |
| 191 // Everything else is optional. | |
| 192 ListValue* wifi_aps; | |
| 193 if (dictionary->GetList(L"wifi_towers", &wifi_aps)) { | |
| 194 int i = 0; | |
| 195 for (ListValue::const_iterator it = wifi_aps->begin(); | |
| 196 it < wifi_aps->end(); ++it, ++i) { | |
| 197 EXPECT_EQ(Value::TYPE_DICTIONARY, (*it)->GetType()); | |
| 198 DictionaryValue* ap = static_cast<DictionaryValue*>(*it); | |
| 199 AccessPointData data; | |
| 200 ap->GetStringAsUTF16(L"mac_address", &data.mac_address); | |
| 201 ap->GetInteger(L"signal_strength", &data.radio_signal_strength); | |
| 202 ap->GetInteger(L"age", &data.age); | |
| 203 ap->GetInteger(L"channel", &data.channel); | |
| 204 ap->GetInteger(L"signal_to_noise", &data.signal_to_noise); | |
| 205 ap->GetStringAsUTF16(L"ssid", &data.ssid); | |
| 206 wifi_data_out->access_point_data.insert(data); | |
| 207 } | |
| 208 } else { | |
| 209 wifi_data_out->access_point_data.clear(); | |
| 210 } | |
| 211 if (!dictionary->GetString(L"access_token", access_token_out)) | |
| 212 access_token_out->clear(); | |
| 213 } | |
| 214 | |
| 215 static void CheckEmptyRequestIsValid(const std::string& request_data) { | |
| 216 WifiData wifi_aps; | |
| 217 std::string access_token; | |
| 218 ParseRequest(request_data, &wifi_aps, &access_token); | |
| 219 EXPECT_EQ(0, static_cast<int>(wifi_aps.access_point_data.size())); | |
| 220 EXPECT_TRUE(access_token.empty()); | |
| 221 } | |
| 222 | |
| 223 static void CheckRequestIsValid(const std::string& request_data, | |
| 224 int expected_wifi_aps, | |
| 225 const std::string& expected_access_token) { | |
| 226 WifiData wifi_aps; | |
| 227 std::string access_token; | |
| 228 ParseRequest(request_data, &wifi_aps, &access_token); | |
| 229 EXPECT_EQ(expected_wifi_aps, | |
| 230 static_cast<int>(wifi_aps.access_point_data.size())); | |
| 231 WifiData expected_data = CreateReferenceWifiScanData(expected_wifi_aps); | |
| 232 WifiData::AccessPointDataSet::const_iterator expected = | |
| 233 expected_data.access_point_data.begin(); | |
| 234 WifiData::AccessPointDataSet::const_iterator actual = | |
| 235 wifi_aps.access_point_data.begin(); | |
| 236 for (int i = 0; i < expected_wifi_aps; ++i) { | |
| 237 EXPECT_EQ(expected->mac_address, actual->mac_address) << i; | |
| 238 EXPECT_EQ(expected->radio_signal_strength, actual->radio_signal_strength) | |
| 239 << i; | |
| 240 EXPECT_EQ(expected->age, actual->age) << i; | |
| 241 EXPECT_EQ(expected->channel, actual->channel) << i; | |
| 242 EXPECT_EQ(expected->signal_to_noise, actual->signal_to_noise) << i; | |
| 243 EXPECT_EQ(expected->ssid, actual->ssid) << i; | |
| 244 ++expected; | |
| 245 ++actual; | |
| 246 } | |
| 247 EXPECT_EQ(expected_access_token, access_token); | |
| 248 } | |
| 249 | |
| 250 const GURL test_server_url_; | |
| 251 MessageLoop main_message_loop_; | |
| 252 FakeAccessTokenStore access_token_store_; | |
| 253 TestURLFetcherFactory url_fetcher_factory_; | |
| 254 }; | |
| 255 | |
| 256 | |
| 257 TEST_F(NetworkLocationProviderTest, CreateDestroy) { | |
| 258 // Test fixture members were SetUp correctly. | |
| 259 EXPECT_EQ(&main_message_loop_, MessageLoop::current()); | |
| 260 scoped_refptr<LocationProviderBase> provider(CreateProvider()); | |
| 261 EXPECT_TRUE(NULL != provider.get()); | |
| 262 provider = NULL; | |
| 263 SUCCEED(); | |
| 264 } | |
| 265 | |
| 266 TEST_F(NetworkLocationProviderTest, StartProvider) { | |
| 267 scoped_refptr<LocationProviderBase> provider(CreateProvider()); | |
| 268 EXPECT_TRUE(provider->StartProvider()); | |
| 269 TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); | |
| 270 ASSERT_TRUE(fetcher != NULL); | |
| 271 | |
| 272 EXPECT_EQ(test_server_url_, fetcher->original_url()); | |
| 273 | |
| 274 // No wifi data so expect an empty request. | |
| 275 CheckEmptyRequestIsValid(fetcher->upload_data()); | |
| 276 } | |
| 277 | |
| 278 TEST_F(NetworkLocationProviderTest, MultipleWifiScansComplete) { | |
| 279 scoped_refptr<LocationProviderBase> provider(CreateProvider()); | |
| 280 EXPECT_TRUE(provider->StartProvider()); | |
| 281 TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); | |
| 282 ASSERT_TRUE(fetcher != NULL); | |
| 283 CheckEmptyRequestIsValid(fetcher->upload_data()); | |
| 284 // Complete the network request with bad position fix (using #define so we | |
| 285 // can paste this into various other strings below) | |
| 286 #define REFERENCE_ACCESS_TOKEN "2:k7j3G6LaL6u_lafw:4iXOeOpTh1glSXe" | |
| 287 const char* kNoFixNetworkResponse = | |
| 288 "{" | |
| 289 " \"location\": null," | |
| 290 " \"access_token\": \"" REFERENCE_ACCESS_TOKEN "\"" | |
| 291 "}"; | |
| 292 fetcher->delegate()->OnURLFetchComplete( | |
| 293 fetcher, test_server_url_, URLRequestStatus(), 200, // OK | |
| 294 ResponseCookies(), kNoFixNetworkResponse); | |
| 295 | |
| 296 // This should have set the access token anyhow | |
| 297 EXPECT_EQ(1, static_cast<int>(access_token_store_.token_map_.size())); | |
| 298 string16 token; | |
| 299 EXPECT_TRUE(access_token_store_.GetAccessToken(test_server_url_, &token)); | |
| 300 EXPECT_EQ(REFERENCE_ACCESS_TOKEN, UTF16ToUTF8(token)); | |
| 301 | |
| 302 Position position; | |
| 303 provider->GetPosition(&position); | |
| 304 EXPECT_FALSE(position.IsValidFix()); | |
| 305 | |
| 306 // Now wifi data arrives | |
| 307 const int kFirstScanAps = 6; | |
| 308 MockDeviceDataProviderImpl<WifiData>::instance()->SetData( | |
| 309 CreateReferenceWifiScanData(kFirstScanAps)); // Will notify listeners | |
| 310 fetcher = url_fetcher_factory_.GetFetcherByID(kFirstScanAps); | |
| 311 ASSERT_TRUE(fetcher != NULL); | |
| 312 // The request should have access token (set previously) and the wifi data. | |
| 313 CheckRequestIsValid(fetcher->upload_data(), | |
| 314 kFirstScanAps, | |
| 315 REFERENCE_ACCESS_TOKEN); | |
| 316 | |
| 317 // Send a reply with good position fix. | |
| 318 const char* kReferenceNetworkResponse = | |
| 319 "{" | |
| 320 " \"location\": {" | |
| 321 " \"latitude\": 51.0," | |
| 322 " \"longitude\": -0.1," | |
| 323 " \"altitude\": 30.1," | |
| 324 " \"accuracy\": 1200.4," | |
| 325 " \"altitude_accuracy\": 10.6" | |
| 326 " }" | |
| 327 "}"; | |
| 328 fetcher->delegate()->OnURLFetchComplete( | |
| 329 fetcher, test_server_url_, URLRequestStatus(), 200, // OK | |
| 330 ResponseCookies(), kReferenceNetworkResponse); | |
| 331 | |
| 332 provider->GetPosition(&position); | |
| 333 EXPECT_EQ(51.0, position.latitude); | |
| 334 EXPECT_EQ(-0.1, position.longitude); | |
| 335 EXPECT_EQ(30.1, position.altitude); | |
| 336 EXPECT_EQ(1200.4, position.accuracy); | |
| 337 EXPECT_EQ(10.6, position.altitude_accuracy); | |
| 338 EXPECT_TRUE(position.is_valid_timestamp()); | |
| 339 EXPECT_TRUE(position.IsValidFix()); | |
| 340 | |
| 341 // Token should still be in the store. | |
| 342 EXPECT_EQ(1, static_cast<int>(access_token_store_.token_map_.size())); | |
| 343 EXPECT_TRUE(access_token_store_.GetAccessToken(test_server_url_, &token)); | |
| 344 EXPECT_EQ(REFERENCE_ACCESS_TOKEN, UTF16ToUTF8(token)); | |
| 345 | |
| 346 // Wifi updated again, with one less AP. This is 'close enough' to the | |
| 347 // previous scan, so no new request made. | |
| 348 const int kSecondScanAps = kFirstScanAps - 1; | |
| 349 MockDeviceDataProviderImpl<WifiData>::instance()->SetData( | |
| 350 CreateReferenceWifiScanData(kSecondScanAps)); | |
| 351 fetcher = url_fetcher_factory_.GetFetcherByID(kSecondScanAps); | |
| 352 EXPECT_FALSE(fetcher); | |
| 353 | |
| 354 provider->GetPosition(&position); | |
| 355 EXPECT_EQ(51.0, position.latitude); | |
| 356 EXPECT_EQ(-0.1, position.longitude); | |
| 357 EXPECT_TRUE(position.IsValidFix()); | |
| 358 | |
| 359 // Now a third scan with more than twice the original amount -> new request. | |
| 360 const int kThirdScanAps = kFirstScanAps * 2 + 1; | |
| 361 MockDeviceDataProviderImpl<WifiData>::instance()->SetData( | |
| 362 CreateReferenceWifiScanData(kThirdScanAps)); | |
| 363 fetcher = url_fetcher_factory_.GetFetcherByID(kThirdScanAps); | |
| 364 EXPECT_TRUE(fetcher); | |
| 365 // ...reply with a network error. | |
| 366 fetcher->delegate()->OnURLFetchComplete( | |
| 367 fetcher, test_server_url_, | |
| 368 URLRequestStatus(URLRequestStatus::FAILED, -1), | |
| 369 200, // should be ignored | |
| 370 ResponseCookies(), ""); | |
| 371 | |
| 372 // Error means we now no longer have a fix. | |
| 373 provider->GetPosition(&position); | |
| 374 EXPECT_FALSE(position.is_valid_latlong()); | |
| 375 EXPECT_FALSE(position.IsValidFix()); | |
| 376 | |
| 377 // wifi scan returns to original set: should be serviced from cache | |
| 378 const TestURLFetcher* orig_fetcher = | |
| 379 url_fetcher_factory_.GetFetcherByID(kFirstScanAps); | |
| 380 MockDeviceDataProviderImpl<WifiData>::instance()->SetData( | |
| 381 CreateReferenceWifiScanData(kFirstScanAps)); | |
| 382 fetcher = url_fetcher_factory_.GetFetcherByID(kFirstScanAps); | |
| 383 EXPECT_EQ(orig_fetcher, fetcher); // No new request created. | |
| 384 | |
| 385 provider->GetPosition(&position); | |
| 386 EXPECT_EQ(51.0, position.latitude); | |
| 387 EXPECT_EQ(-0.1, position.longitude); | |
| 388 EXPECT_TRUE(position.IsValidFix()); | |
| 389 } | |
| 390 | |
| 391 // TODO(joth): Add tests for corner cases around the 2 second startup delay | |
| 392 // (e.g. timer firing, or being pre-empted by data arriving) | |
| OLD | NEW |