| 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_EVIL_CONSTRUCTORS(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\": {" |
| 290 " }," |
| 291 " \"access_token\": \"" REFERENCE_ACCESS_TOKEN "\"" |
| 292 "}"; |
| 293 fetcher->delegate()->OnURLFetchComplete( |
| 294 fetcher, test_server_url_, URLRequestStatus(), 200, // OK |
| 295 ResponseCookies(), kNoFixNetworkResponse); |
| 296 |
| 297 // This should have set the access token anyhow |
| 298 EXPECT_EQ(1, static_cast<int>(access_token_store_.token_map_.size())); |
| 299 string16 token; |
| 300 EXPECT_TRUE(access_token_store_.GetAccessToken(test_server_url_, &token)); |
| 301 EXPECT_EQ(REFERENCE_ACCESS_TOKEN, UTF16ToUTF8(token)); |
| 302 |
| 303 Position position; |
| 304 provider->GetPosition(&position); |
| 305 EXPECT_FALSE(position.IsValidFix()); |
| 306 |
| 307 // Now wifi data arrives |
| 308 const int kFirstScanAps = 6; |
| 309 MockDeviceDataProviderImpl<WifiData>::instance()->SetData( |
| 310 CreateReferenceWifiScanData(kFirstScanAps)); // Will notify listeners |
| 311 fetcher = url_fetcher_factory_.GetFetcherByID(kFirstScanAps); |
| 312 ASSERT_TRUE(fetcher != NULL); |
| 313 // The request should have access token (set previously) and the wifi data. |
| 314 CheckRequestIsValid(fetcher->upload_data(), |
| 315 kFirstScanAps, |
| 316 REFERENCE_ACCESS_TOKEN); |
| 317 |
| 318 // Send a reply with good position fix. |
| 319 const char* kReferenceNetworkResponse = |
| 320 "{" |
| 321 " \"location\": {" |
| 322 " \"latitude\": 51.0," |
| 323 " \"longitude\": -0.1," |
| 324 " \"altitude\": 30.1," |
| 325 " \"accuracy\": 1200.4," |
| 326 " \"altitude_accuracy\": 10.6" |
| 327 " }" |
| 328 "}"; |
| 329 fetcher->delegate()->OnURLFetchComplete( |
| 330 fetcher, test_server_url_, URLRequestStatus(), 200, // OK |
| 331 ResponseCookies(), kReferenceNetworkResponse); |
| 332 |
| 333 provider->GetPosition(&position); |
| 334 EXPECT_EQ(51.0, position.latitude); |
| 335 EXPECT_EQ(-0.1, position.longitude); |
| 336 EXPECT_EQ(30.1, position.altitude); |
| 337 EXPECT_EQ(1200.4, position.accuracy); |
| 338 EXPECT_EQ(10.6, position.altitude_accuracy); |
| 339 EXPECT_TRUE(position.is_valid_timestamp()); |
| 340 EXPECT_TRUE(position.IsValidFix()); |
| 341 |
| 342 // Token should still be in the store. |
| 343 EXPECT_EQ(1, static_cast<int>(access_token_store_.token_map_.size())); |
| 344 EXPECT_TRUE(access_token_store_.GetAccessToken(test_server_url_, &token)); |
| 345 EXPECT_EQ(REFERENCE_ACCESS_TOKEN, UTF16ToUTF8(token)); |
| 346 |
| 347 // Wifi updated again, with one less AP. This is 'close enough' to the |
| 348 // previous scan, so no new request made. |
| 349 const int kSecondScanAps = kFirstScanAps - 1; |
| 350 MockDeviceDataProviderImpl<WifiData>::instance()->SetData( |
| 351 CreateReferenceWifiScanData(kSecondScanAps)); |
| 352 fetcher = url_fetcher_factory_.GetFetcherByID(kSecondScanAps); |
| 353 EXPECT_FALSE(fetcher); |
| 354 |
| 355 provider->GetPosition(&position); |
| 356 EXPECT_EQ(51.0, position.latitude); |
| 357 EXPECT_EQ(-0.1, position.longitude); |
| 358 EXPECT_TRUE(position.IsValidFix()); |
| 359 |
| 360 // Now a third scan with more than twice the original amount -> new request. |
| 361 const int kThirdScanAps = kFirstScanAps * 2 + 1; |
| 362 MockDeviceDataProviderImpl<WifiData>::instance()->SetData( |
| 363 CreateReferenceWifiScanData(kThirdScanAps)); |
| 364 fetcher = url_fetcher_factory_.GetFetcherByID(kThirdScanAps); |
| 365 EXPECT_TRUE(fetcher); |
| 366 // ...reply with a network error. |
| 367 fetcher->delegate()->OnURLFetchComplete( |
| 368 fetcher, test_server_url_, |
| 369 URLRequestStatus(URLRequestStatus::FAILED, -1), |
| 370 200, // should be ignored |
| 371 ResponseCookies(), ""); |
| 372 |
| 373 // Error means we now no longer have a fix. |
| 374 provider->GetPosition(&position); |
| 375 EXPECT_FALSE(position.is_valid_latlong()); |
| 376 EXPECT_FALSE(position.IsValidFix()); |
| 377 } |
| 378 |
| 379 // TODO(joth): Add tests for corner cases around the 2 second startup delay |
| 380 // (e.g. timer firing, or being pre-empted by data arriving) |
| OLD | NEW |