1 // Copyright (c) 2012 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.
5 #include "base/json/json_reader.h"
6 #include "base/json/json_writer.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/string_number_conversions.h"
9 #include "base/string_util.h"
10 #include "base/stringprintf.h"
11 #include "base/utf_string_conversions.h"
12 #include "base/values.h"
13 #include "content/browser/geolocation/fake_access_token_store.h"
14 #include "content/browser/geolocation/location_arbitrator_impl.h"
15 #include "content/browser/geolocation/network_location_provider.h"
16 #include "net/url_request/test_url_fetcher_factory.h"
17 #include "net/url_request/url_request_status.h"
18 #include "testing/gtest/include/gtest/gtest.h"
22 // Constants used in multiple tests.
23 const char kTestServerUrl
[] = "https://www.geolocation.test/service";
24 const char kAccessTokenString
[] = "accessToken";
26 // Using #define so we can easily paste this into various other strings.
27 #define REFERENCE_ACCESS_TOKEN "2:k7j3G6LaL6u_lafw:4iXOeOpTh1glSXe"
29 // Stops the specified (nested) message loop when the listener is called back.
30 class MessageLoopQuitListener
31 : public LocationProviderBase::ListenerInterface
{
33 MessageLoopQuitListener()
34 : client_message_loop_(MessageLoop::current()),
35 updated_provider_(NULL
),
36 movement_provider_(NULL
) {
37 CHECK(client_message_loop_
);
40 virtual void LocationUpdateAvailable(LocationProviderBase
* provider
) {
41 EXPECT_EQ(client_message_loop_
, MessageLoop::current());
42 updated_provider_
= provider
;
43 client_message_loop_
->Quit();
45 MessageLoop
* client_message_loop_
;
46 LocationProviderBase
* updated_provider_
;
47 LocationProviderBase
* movement_provider_
;
50 // A mock implementation of DeviceDataProviderImplBase for testing. Adapted from
51 // http://gears.googlecode.com/svn/trunk/gears/geolocation/geolocation_test.cc
52 template<typename DataType
>
53 class MockDeviceDataProviderImpl
54 : public DeviceDataProviderImplBase
<DataType
> {
56 // Factory method for use with DeviceDataProvider::SetFactory.
57 static DeviceDataProviderImplBase
<DataType
>* GetInstance() {
62 static MockDeviceDataProviderImpl
<DataType
>* CreateInstance() {
64 instance_
= new MockDeviceDataProviderImpl
<DataType
>;
68 MockDeviceDataProviderImpl()
74 virtual ~MockDeviceDataProviderImpl() {
75 CHECK(this == instance_
);
79 // DeviceDataProviderImplBase implementation.
80 virtual bool StartDataProvider() {
84 virtual void StopDataProvider() {
87 virtual bool GetData(DataType
* data_out
) {
93 void SetData(const DataType
& new_data
) {
95 const bool differs
= data_
.DiffersSignificantly(new_data
);
98 this->NotifyListeners();
101 void set_got_data(bool got_data
) { got_data_
= got_data
; }
106 static MockDeviceDataProviderImpl
<DataType
>* instance_
;
111 DISALLOW_COPY_AND_ASSIGN(MockDeviceDataProviderImpl
);
114 template<typename DataType
>
115 MockDeviceDataProviderImpl
<DataType
>*
116 MockDeviceDataProviderImpl
<DataType
>::instance_
= NULL
;
119 class GeolocationNetworkProviderTest
: public testing::Test
{
121 virtual void SetUp() {
122 test_server_url_
= GURL(kTestServerUrl
);
123 access_token_store_
= new FakeAccessTokenStore
;
124 wifi_data_provider_
=
125 MockDeviceDataProviderImpl
<WifiData
>::CreateInstance();
128 virtual void TearDown() {
129 WifiDataProvider::ResetFactory();
132 LocationProviderBase
* CreateProvider(bool set_permission_granted
) {
133 LocationProviderBase
* provider
= NewNetworkLocationProvider(
134 access_token_store_
.get(),
135 NULL
, // No URLContextGetter needed, as using test urlfecther factory.
137 access_token_store_
->access_token_set_
[test_server_url_
]);
138 if (set_permission_granted
)
139 provider
->OnPermissionGranted();
144 GeolocationNetworkProviderTest() {
145 // TODO(joth): Really these should be in SetUp, not here, but they take no
146 // effect on Mac OS Release builds if done there. I kid not. Figure out why.
147 WifiDataProvider::SetFactory(
148 MockDeviceDataProviderImpl
<WifiData
>::GetInstance
);
151 // Returns the current url fetcher (if any) and advances the id ready for the
153 net::TestURLFetcher
* get_url_fetcher_and_advance_id() {
154 net::TestURLFetcher
* fetcher
= url_fetcher_factory_
.GetFetcherByID(
155 NetworkLocationRequest::url_fetcher_id_for_tests
);
157 ++NetworkLocationRequest::url_fetcher_id_for_tests
;
161 static int IndexToChannel(int index
) { return index
+ 4; }
163 // Creates wifi data containing the specified number of access points, with
164 // some differentiating charactistics in each.
165 static WifiData
CreateReferenceWifiScanData(int ap_count
) {
167 for (int i
= 0; i
< ap_count
; ++i
) {
170 ASCIIToUTF16(base::StringPrintf("%02d-34-56-78-54-32", i
));
171 ap
.radio_signal_strength
= ap_count
- i
;
172 ap
.channel
= IndexToChannel(i
);
173 ap
.signal_to_noise
= i
+ 42;
174 ap
.ssid
= ASCIIToUTF16("Some nice+network|name\\");
175 data
.access_point_data
.insert(ap
);
180 static void CreateReferenceWifiScanDataJson(
181 int ap_count
, int start_index
, base::ListValue
* wifi_access_point_list
) {
182 std::vector
<std::string
> wifi_data
;
183 for (int i
= 0; i
< ap_count
; ++i
) {
184 base::DictionaryValue
* ap
= new base::DictionaryValue();
185 ap
->SetString("macAddress", base::StringPrintf("%02d-34-56-78-54-32", i
));
186 ap
->SetInteger("signalStrength", start_index
+ ap_count
- i
);
187 ap
->SetInteger("age", 0);
188 ap
->SetInteger("channel", IndexToChannel(i
));
189 ap
->SetInteger("signalToNoiseRatio", i
+ 42);
190 wifi_access_point_list
->Append(ap
);
194 static Geoposition
CreateReferencePosition(int id
) {
197 pos
.longitude
= -(id
+ 1);
198 pos
.altitude
= 2 * id
;
199 pos
.timestamp
= base::Time::Now();
203 static std::string
PrettyJson(const base::Value
& value
) {
205 base::JSONWriter::WriteWithOptions(
206 &value
, base::JSONWriter::OPTIONS_PRETTY_PRINT
, &pretty
);
210 static testing::AssertionResult
JsonGetList(
211 const std::string
& field
,
212 const base::DictionaryValue
& dict
,
213 const base::ListValue
** output_list
) {
214 if (!dict
.GetList(field
, output_list
))
215 return testing::AssertionFailure() << "Dictionary " << PrettyJson(dict
)
216 << " is missing list field " << field
;
217 return testing::AssertionSuccess();
220 static testing::AssertionResult
JsonFieldEquals(
221 const std::string
& field
,
222 const base::DictionaryValue
& expected
,
223 const base::DictionaryValue
& actual
) {
224 const base::Value
* expected_value
;
225 const base::Value
* actual_value
;
226 if (!expected
.Get(field
, &expected_value
))
227 return testing::AssertionFailure()
228 << "Expected dictionary " << PrettyJson(expected
)
229 << " is missing field " << field
;
230 if (!expected
.Get(field
, &actual_value
))
231 return testing::AssertionFailure()
232 << "Actual dictionary " << PrettyJson(actual
)
233 << " is missing field " << field
;
234 if (!expected_value
->Equals(actual_value
))
235 return testing::AssertionFailure()
236 << "Field " << field
<< " mismatch: " << PrettyJson(*expected_value
)
237 << " != " << PrettyJson(*actual_value
);
238 return testing::AssertionSuccess();
241 static GURL
UrlWithoutQuery(const GURL
& url
) {
242 url_canon::Replacements
<char> replacements
;
243 replacements
.ClearQuery();
244 return url
.ReplaceComponents(replacements
);
247 testing::AssertionResult
IsTestServerUrl(const GURL
& request_url
) {
248 const GURL
a(UrlWithoutQuery(test_server_url_
));
249 const GURL
b(UrlWithoutQuery(request_url
));
251 return testing::AssertionSuccess();
252 return testing::AssertionFailure() << a
<< " != " << b
;
255 void CheckRequestIsValid(const net::TestURLFetcher
& request
,
256 int expected_routers
,
257 int expected_wifi_aps
,
258 int wifi_start_index
,
259 const std::string
& expected_access_token
) {
260 const GURL
& request_url
= request
.GetOriginalURL();
262 EXPECT_TRUE(IsTestServerUrl(request_url
));
264 // Check to see that the api key is being appended for the default
265 // network provider url.
266 bool is_default_url
= UrlWithoutQuery(request_url
) ==
267 UrlWithoutQuery(GeolocationArbitratorImpl::DefaultNetworkProviderURL());
268 EXPECT_EQ(is_default_url
, !request_url
.query().empty());
270 const std::string
& upload_data
= request
.upload_data();
271 ASSERT_FALSE(upload_data
.empty());
272 std::string json_parse_error_msg
;
273 scoped_ptr
<base::Value
> parsed_json(
274 base::JSONReader::ReadAndReturnError(
276 base::JSON_PARSE_RFC
,
278 &json_parse_error_msg
));
279 EXPECT_TRUE(json_parse_error_msg
.empty());
280 ASSERT_TRUE(parsed_json
.get() != NULL
);
282 const base::DictionaryValue
* request_json
;
283 ASSERT_TRUE(parsed_json
->GetAsDictionary(&request_json
));
285 if (!is_default_url
) {
286 if (expected_access_token
.empty())
287 ASSERT_FALSE(request_json
->HasKey(kAccessTokenString
));
289 std::string access_token
;
290 EXPECT_TRUE(request_json
->GetString(kAccessTokenString
, &access_token
));
291 EXPECT_EQ(expected_access_token
, access_token
);
295 if (expected_wifi_aps
) {
296 base::ListValue expected_wifi_aps_json
;
297 CreateReferenceWifiScanDataJson(
300 &expected_wifi_aps_json
);
301 EXPECT_EQ(size_t(expected_wifi_aps
), expected_wifi_aps_json
.GetSize());
303 const base::ListValue
* wifi_aps_json
;
304 ASSERT_TRUE(JsonGetList("wifiAccessPoints", *request_json
,
306 for (size_t i
= 0; i
< expected_wifi_aps_json
.GetSize(); ++i
) {
307 const base::DictionaryValue
* expected_json
;
308 ASSERT_TRUE(expected_wifi_aps_json
.GetDictionary(i
, &expected_json
));
309 const base::DictionaryValue
* actual_json
;
310 ASSERT_TRUE(wifi_aps_json
->GetDictionary(i
, &actual_json
));
311 ASSERT_TRUE(JsonFieldEquals("macAddress", *expected_json
,
313 ASSERT_TRUE(JsonFieldEquals("signalStrength", *expected_json
,
315 ASSERT_TRUE(JsonFieldEquals("channel", *expected_json
, *actual_json
));
316 ASSERT_TRUE(JsonFieldEquals("signalToNoiseRatio", *expected_json
,
320 ASSERT_FALSE(request_json
->HasKey("wifiAccessPoints"));
322 EXPECT_TRUE(request_url
.is_valid());
325 GURL test_server_url_
;
326 MessageLoop main_message_loop_
;
327 scoped_refptr
<FakeAccessTokenStore
> access_token_store_
;
328 net::TestURLFetcherFactory url_fetcher_factory_
;
329 scoped_refptr
<MockDeviceDataProviderImpl
<WifiData
> > wifi_data_provider_
;
332 TEST_F(GeolocationNetworkProviderTest
, CreateDestroy
) {
333 // Test fixture members were SetUp correctly.
334 EXPECT_EQ(&main_message_loop_
, MessageLoop::current());
335 scoped_ptr
<LocationProviderBase
> provider(CreateProvider(true));
336 EXPECT_TRUE(NULL
!= provider
.get());
341 TEST_F(GeolocationNetworkProviderTest
, StartProvider
) {
342 scoped_ptr
<LocationProviderBase
> provider(CreateProvider(true));
343 EXPECT_TRUE(provider
->StartProvider(false));
344 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
345 ASSERT_TRUE(fetcher
!= NULL
);
346 CheckRequestIsValid(*fetcher
, 0, 0, 0, "");
349 TEST_F(GeolocationNetworkProviderTest
, StartProviderDefaultUrl
) {
350 test_server_url_
= GeolocationArbitratorImpl::DefaultNetworkProviderURL();
351 scoped_ptr
<LocationProviderBase
> provider(CreateProvider(true));
352 EXPECT_TRUE(provider
->StartProvider(false));
353 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
354 ASSERT_TRUE(fetcher
!= NULL
);
355 CheckRequestIsValid(*fetcher
, 0, 0, 0, "");
359 TEST_F(GeolocationNetworkProviderTest
, StartProviderLongRequest
) {
360 scoped_ptr
<LocationProviderBase
> provider(CreateProvider(true));
361 EXPECT_TRUE(provider
->StartProvider(false));
362 const int kFirstScanAps
= 20;
363 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kFirstScanAps
));
364 main_message_loop_
.RunUntilIdle();
365 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
366 ASSERT_TRUE(fetcher
!= NULL
);
367 // The request url should have been shortened to less than 2048 characters
368 // in length by not including access points with the lowest signal strength
370 EXPECT_LT(fetcher
->GetOriginalURL().spec().size(), size_t(2048));
371 CheckRequestIsValid(*fetcher
, 0, 16, 4, "");
374 TEST_F(GeolocationNetworkProviderTest
, MultiRegistrations
) {
375 // TODO(joth): Strictly belongs in a base-class unit test file.
376 MessageLoopQuitListener listener
;
377 scoped_ptr
<LocationProviderBase
> provider(CreateProvider(true));
378 EXPECT_FALSE(provider
->has_listeners());
379 provider
->RegisterListener(&listener
);
380 EXPECT_TRUE(provider
->has_listeners());
381 provider
->RegisterListener(&listener
);
382 EXPECT_TRUE(provider
->has_listeners());
384 provider
->UnregisterListener(&listener
);
385 EXPECT_TRUE(provider
->has_listeners());
386 provider
->UnregisterListener(&listener
);
387 EXPECT_FALSE(provider
->has_listeners());
390 TEST_F(GeolocationNetworkProviderTest
, MultipleWifiScansComplete
) {
391 scoped_ptr
<LocationProviderBase
> provider(CreateProvider(true));
392 EXPECT_TRUE(provider
->StartProvider(false));
394 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
395 ASSERT_TRUE(fetcher
!= NULL
);
396 EXPECT_TRUE(IsTestServerUrl(fetcher
->GetOriginalURL()));
398 // Complete the network request with bad position fix.
399 const char* kNoFixNetworkResponse
=
401 " \"status\": \"ZERO_RESULTS\""
403 fetcher
->set_url(test_server_url_
);
404 fetcher
->set_status(net::URLRequestStatus());
405 fetcher
->set_response_code(200); // OK
406 fetcher
->SetResponseString(kNoFixNetworkResponse
);
407 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
409 Geoposition position
;
410 provider
->GetPosition(&position
);
411 EXPECT_FALSE(position
.Validate());
413 // Now wifi data arrives -- SetData will notify listeners.
414 const int kFirstScanAps
= 6;
415 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kFirstScanAps
));
416 main_message_loop_
.RunUntilIdle();
417 fetcher
= get_url_fetcher_and_advance_id();
418 ASSERT_TRUE(fetcher
!= NULL
);
419 // The request should have the wifi data.
420 CheckRequestIsValid(*fetcher
, 0, kFirstScanAps
, 0, "");
422 // Send a reply with good position fix.
423 const char* kReferenceNetworkResponse
=
425 " \"accessToken\": \"" REFERENCE_ACCESS_TOKEN
"\","
426 " \"accuracy\": 1200.4,"
432 fetcher
->set_url(test_server_url_
);
433 fetcher
->set_status(net::URLRequestStatus());
434 fetcher
->set_response_code(200); // OK
435 fetcher
->SetResponseString(kReferenceNetworkResponse
);
436 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
438 provider
->GetPosition(&position
);
439 EXPECT_EQ(51.0, position
.latitude
);
440 EXPECT_EQ(-0.1, position
.longitude
);
441 EXPECT_EQ(1200.4, position
.accuracy
);
442 EXPECT_FALSE(position
.timestamp
.is_null());
443 EXPECT_TRUE(position
.Validate());
445 // Token should be in the store.
446 EXPECT_EQ(UTF8ToUTF16(REFERENCE_ACCESS_TOKEN
),
447 access_token_store_
->access_token_set_
[test_server_url_
]);
449 // Wifi updated again, with one less AP. This is 'close enough' to the
450 // previous scan, so no new request made.
451 const int kSecondScanAps
= kFirstScanAps
- 1;
452 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kSecondScanAps
));
453 main_message_loop_
.RunUntilIdle();
454 fetcher
= get_url_fetcher_and_advance_id();
455 EXPECT_FALSE(fetcher
);
457 provider
->GetPosition(&position
);
458 EXPECT_EQ(51.0, position
.latitude
);
459 EXPECT_EQ(-0.1, position
.longitude
);
460 EXPECT_TRUE(position
.Validate());
462 // Now a third scan with more than twice the original amount -> new request.
463 const int kThirdScanAps
= kFirstScanAps
* 2 + 1;
464 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kThirdScanAps
));
465 main_message_loop_
.RunUntilIdle();
466 fetcher
= get_url_fetcher_and_advance_id();
467 EXPECT_TRUE(fetcher
);
468 CheckRequestIsValid(*fetcher
, 0, kThirdScanAps
, 0, REFERENCE_ACCESS_TOKEN
);
469 // ...reply with a network error.
471 fetcher
->set_url(test_server_url_
);
472 fetcher
->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED
, -1));
473 fetcher
->set_response_code(200); // should be ignored
474 fetcher
->SetResponseString("");
475 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
477 // Error means we now no longer have a fix.
478 provider
->GetPosition(&position
);
479 EXPECT_FALSE(position
.Validate());
481 // Wifi scan returns to original set: should be serviced from cache.
482 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kFirstScanAps
));
483 main_message_loop_
.RunUntilIdle();
484 EXPECT_FALSE(get_url_fetcher_and_advance_id()); // No new request created.
486 provider
->GetPosition(&position
);
487 EXPECT_EQ(51.0, position
.latitude
);
488 EXPECT_EQ(-0.1, position
.longitude
);
489 EXPECT_TRUE(position
.Validate());
492 TEST_F(GeolocationNetworkProviderTest
, NoRequestOnStartupUntilWifiData
) {
493 MessageLoopQuitListener listener
;
494 wifi_data_provider_
->set_got_data(false);
495 scoped_ptr
<LocationProviderBase
> provider(CreateProvider(true));
496 EXPECT_TRUE(provider
->StartProvider(false));
497 provider
->RegisterListener(&listener
);
499 main_message_loop_
.RunUntilIdle();
500 EXPECT_FALSE(get_url_fetcher_and_advance_id())
501 << "Network request should not be created right away on startup when "
502 "wifi data has not yet arrived";
504 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(1));
505 main_message_loop_
.RunUntilIdle();
506 EXPECT_TRUE(get_url_fetcher_and_advance_id());
509 TEST_F(GeolocationNetworkProviderTest
, NewDataReplacesExistingNetworkRequest
) {
510 // Send initial request with empty device data
511 scoped_ptr
<LocationProviderBase
> provider(CreateProvider(true));
512 EXPECT_TRUE(provider
->StartProvider(false));
513 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
514 EXPECT_TRUE(fetcher
);
516 // Now wifi data arrives; new request should be sent.
517 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(4));
518 main_message_loop_
.RunUntilIdle();
519 fetcher
= get_url_fetcher_and_advance_id();
520 EXPECT_TRUE(fetcher
);
523 TEST_F(GeolocationNetworkProviderTest
, NetworkRequestDeferredForPermission
) {
524 scoped_ptr
<LocationProviderBase
> provider(CreateProvider(false));
525 EXPECT_TRUE(provider
->StartProvider(false));
526 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
527 EXPECT_FALSE(fetcher
);
528 provider
->OnPermissionGranted();
530 fetcher
= get_url_fetcher_and_advance_id();
531 ASSERT_TRUE(fetcher
!= NULL
);
533 EXPECT_TRUE(IsTestServerUrl(fetcher
->GetOriginalURL()));
536 TEST_F(GeolocationNetworkProviderTest
,
537 NetworkRequestWithWifiDataDeferredForPermission
) {
538 access_token_store_
->access_token_set_
[test_server_url_
] =
539 UTF8ToUTF16(REFERENCE_ACCESS_TOKEN
);
540 scoped_ptr
<LocationProviderBase
> provider(CreateProvider(false));
541 EXPECT_TRUE(provider
->StartProvider(false));
542 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
543 EXPECT_FALSE(fetcher
);
545 static const int kScanCount
= 4;
546 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kScanCount
));
547 main_message_loop_
.RunUntilIdle();
549 fetcher
= get_url_fetcher_and_advance_id();
550 EXPECT_FALSE(fetcher
);
552 provider
->OnPermissionGranted();
554 fetcher
= get_url_fetcher_and_advance_id();
555 ASSERT_TRUE(fetcher
!= NULL
);
557 CheckRequestIsValid(*fetcher
, 0, kScanCount
, 0, REFERENCE_ACCESS_TOKEN
);
560 TEST_F(GeolocationNetworkProviderTest
, NetworkPositionCache
) {
561 NetworkLocationProvider::PositionCache cache
;
563 const int kCacheSize
= NetworkLocationProvider::PositionCache::kMaximumSize
;
564 for (int i
= 1; i
< kCacheSize
* 2 + 1; ++i
) {
565 Geoposition pos
= CreateReferencePosition(i
);
566 bool ret
= cache
.CachePosition(CreateReferenceWifiScanData(i
), pos
);
567 EXPECT_TRUE(ret
) << i
;
568 const Geoposition
* item
=
569 cache
.FindPosition(CreateReferenceWifiScanData(i
));
570 ASSERT_TRUE(item
) << i
;
571 EXPECT_EQ(pos
.latitude
, item
->latitude
) << i
;
572 EXPECT_EQ(pos
.longitude
, item
->longitude
) << i
;
573 if (i
<= kCacheSize
) {
574 // Nothing should have spilled yet; check oldest item is still there.
575 EXPECT_TRUE(cache
.FindPosition(CreateReferenceWifiScanData(1)));
577 const int evicted
= i
- kCacheSize
;
578 EXPECT_FALSE(cache
.FindPosition(CreateReferenceWifiScanData(evicted
)));
579 EXPECT_TRUE(cache
.FindPosition(CreateReferenceWifiScanData(evicted
+ 1)));
584 } // namespace content