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/strings/string_number_conversions.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/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
{
32 MessageLoopQuitListener()
33 : client_message_loop_(base::MessageLoop::current()),
34 updated_provider_(NULL
) {
35 CHECK(client_message_loop_
);
38 void LocationUpdateAvailable(const LocationProvider
* provider
,
39 const Geoposition
& position
) {
40 EXPECT_EQ(client_message_loop_
, base::MessageLoop::current());
41 updated_provider_
= provider
;
42 client_message_loop_
->Quit();
45 base::MessageLoop
* client_message_loop_
;
46 const LocationProvider
* updated_provider_
;
49 // A mock implementation of WifiDataProviderImplBase for testing. Adapted from
50 // http://gears.googlecode.com/svn/trunk/gears/geolocation/geolocation_test.cc
51 class MockWifiDataProviderImpl
: public WifiDataProviderImplBase
{
53 // Factory method for use with WifiDataProvider::SetFactory.
54 static WifiDataProviderImplBase
* GetInstance() {
59 static MockWifiDataProviderImpl
* CreateInstance() {
61 instance_
= new MockWifiDataProviderImpl
;
65 MockWifiDataProviderImpl()
71 // WifiDataProviderImplBase implementation.
72 virtual void StartDataProvider() OVERRIDE
{
76 virtual void StopDataProvider() OVERRIDE
{
80 virtual bool GetData(WifiData
* data_out
) OVERRIDE
{
86 void SetData(const WifiData
& new_data
) {
88 const bool differs
= data_
.DiffersSignificantly(new_data
);
94 void set_got_data(bool got_data
) { got_data_
= got_data
; }
99 virtual ~MockWifiDataProviderImpl() {
100 CHECK(this == instance_
);
104 static MockWifiDataProviderImpl
* instance_
;
109 DISALLOW_COPY_AND_ASSIGN(MockWifiDataProviderImpl
);
112 MockWifiDataProviderImpl
* MockWifiDataProviderImpl::instance_
= NULL
;
115 class GeolocationNetworkProviderTest
: public testing::Test
{
117 virtual void SetUp() {
118 test_server_url_
= GURL(kTestServerUrl
);
119 access_token_store_
= new FakeAccessTokenStore
;
120 wifi_data_provider_
=
121 MockWifiDataProviderImpl::CreateInstance();
124 virtual void TearDown() {
125 WifiDataProvider::ResetFactory();
128 LocationProvider
* CreateProvider(bool set_permission_granted
) {
129 LocationProvider
* provider
= NewNetworkLocationProvider(
130 access_token_store_
.get(),
131 NULL
, // No URLContextGetter needed, as using test urlfecther factory.
133 access_token_store_
->access_token_set_
[test_server_url_
]);
134 if (set_permission_granted
)
135 provider
->OnPermissionGranted();
140 GeolocationNetworkProviderTest() {
141 // TODO(joth): Really these should be in SetUp, not here, but they take no
142 // effect on Mac OS Release builds if done there. I kid not. Figure out why.
143 WifiDataProvider::SetFactory(MockWifiDataProviderImpl::GetInstance
);
146 // Returns the current url fetcher (if any) and advances the id ready for the
148 net::TestURLFetcher
* get_url_fetcher_and_advance_id() {
149 net::TestURLFetcher
* fetcher
= url_fetcher_factory_
.GetFetcherByID(
150 NetworkLocationRequest::url_fetcher_id_for_tests
);
152 ++NetworkLocationRequest::url_fetcher_id_for_tests
;
156 static int IndexToChannel(int index
) { return index
+ 4; }
158 // Creates wifi data containing the specified number of access points, with
159 // some differentiating charactistics in each.
160 static WifiData
CreateReferenceWifiScanData(int ap_count
) {
162 for (int i
= 0; i
< ap_count
; ++i
) {
165 base::ASCIIToUTF16(base::StringPrintf("%02d-34-56-78-54-32", i
));
166 ap
.radio_signal_strength
= ap_count
- i
;
167 ap
.channel
= IndexToChannel(i
);
168 ap
.signal_to_noise
= i
+ 42;
169 ap
.ssid
= base::ASCIIToUTF16("Some nice+network|name\\");
170 data
.access_point_data
.insert(ap
);
175 static void CreateReferenceWifiScanDataJson(
176 int ap_count
, int start_index
, base::ListValue
* wifi_access_point_list
) {
177 std::vector
<std::string
> wifi_data
;
178 for (int i
= 0; i
< ap_count
; ++i
) {
179 base::DictionaryValue
* ap
= new base::DictionaryValue();
180 ap
->SetString("macAddress", base::StringPrintf("%02d-34-56-78-54-32", i
));
181 ap
->SetInteger("signalStrength", start_index
+ ap_count
- i
);
182 ap
->SetInteger("age", 0);
183 ap
->SetInteger("channel", IndexToChannel(i
));
184 ap
->SetInteger("signalToNoiseRatio", i
+ 42);
185 wifi_access_point_list
->Append(ap
);
189 static Geoposition
CreateReferencePosition(int id
) {
192 pos
.longitude
= -(id
+ 1);
193 pos
.altitude
= 2 * id
;
194 pos
.timestamp
= base::Time::Now();
198 static std::string
PrettyJson(const base::Value
& value
) {
200 base::JSONWriter::WriteWithOptions(
201 &value
, base::JSONWriter::OPTIONS_PRETTY_PRINT
, &pretty
);
205 static testing::AssertionResult
JsonGetList(
206 const std::string
& field
,
207 const base::DictionaryValue
& dict
,
208 const base::ListValue
** output_list
) {
209 if (!dict
.GetList(field
, output_list
))
210 return testing::AssertionFailure() << "Dictionary " << PrettyJson(dict
)
211 << " is missing list field " << field
;
212 return testing::AssertionSuccess();
215 static testing::AssertionResult
JsonFieldEquals(
216 const std::string
& field
,
217 const base::DictionaryValue
& expected
,
218 const base::DictionaryValue
& actual
) {
219 const base::Value
* expected_value
;
220 const base::Value
* actual_value
;
221 if (!expected
.Get(field
, &expected_value
))
222 return testing::AssertionFailure()
223 << "Expected dictionary " << PrettyJson(expected
)
224 << " is missing field " << field
;
225 if (!expected
.Get(field
, &actual_value
))
226 return testing::AssertionFailure()
227 << "Actual dictionary " << PrettyJson(actual
)
228 << " is missing field " << field
;
229 if (!expected_value
->Equals(actual_value
))
230 return testing::AssertionFailure()
231 << "Field " << field
<< " mismatch: " << PrettyJson(*expected_value
)
232 << " != " << PrettyJson(*actual_value
);
233 return testing::AssertionSuccess();
236 static GURL
UrlWithoutQuery(const GURL
& url
) {
237 url_canon::Replacements
<char> replacements
;
238 replacements
.ClearQuery();
239 return url
.ReplaceComponents(replacements
);
242 testing::AssertionResult
IsTestServerUrl(const GURL
& request_url
) {
243 const GURL
a(UrlWithoutQuery(test_server_url_
));
244 const GURL
b(UrlWithoutQuery(request_url
));
246 return testing::AssertionSuccess();
247 return testing::AssertionFailure() << a
<< " != " << b
;
250 void CheckRequestIsValid(const net::TestURLFetcher
& request
,
251 int expected_routers
,
252 int expected_wifi_aps
,
253 int wifi_start_index
,
254 const std::string
& expected_access_token
) {
255 const GURL
& request_url
= request
.GetOriginalURL();
257 EXPECT_TRUE(IsTestServerUrl(request_url
));
259 // Check to see that the api key is being appended for the default
260 // network provider url.
261 bool is_default_url
= UrlWithoutQuery(request_url
) ==
262 UrlWithoutQuery(LocationArbitratorImpl::DefaultNetworkProviderURL());
263 EXPECT_EQ(is_default_url
, !request_url
.query().empty());
265 const std::string
& upload_data
= request
.upload_data();
266 ASSERT_FALSE(upload_data
.empty());
267 std::string json_parse_error_msg
;
268 scoped_ptr
<base::Value
> parsed_json(
269 base::JSONReader::ReadAndReturnError(
271 base::JSON_PARSE_RFC
,
273 &json_parse_error_msg
));
274 EXPECT_TRUE(json_parse_error_msg
.empty());
275 ASSERT_TRUE(parsed_json
.get() != NULL
);
277 const base::DictionaryValue
* request_json
;
278 ASSERT_TRUE(parsed_json
->GetAsDictionary(&request_json
));
280 if (!is_default_url
) {
281 if (expected_access_token
.empty())
282 ASSERT_FALSE(request_json
->HasKey(kAccessTokenString
));
284 std::string access_token
;
285 EXPECT_TRUE(request_json
->GetString(kAccessTokenString
, &access_token
));
286 EXPECT_EQ(expected_access_token
, access_token
);
290 if (expected_wifi_aps
) {
291 base::ListValue expected_wifi_aps_json
;
292 CreateReferenceWifiScanDataJson(
295 &expected_wifi_aps_json
);
296 EXPECT_EQ(size_t(expected_wifi_aps
), expected_wifi_aps_json
.GetSize());
298 const base::ListValue
* wifi_aps_json
;
299 ASSERT_TRUE(JsonGetList("wifiAccessPoints", *request_json
,
301 for (size_t i
= 0; i
< expected_wifi_aps_json
.GetSize(); ++i
) {
302 const base::DictionaryValue
* expected_json
;
303 ASSERT_TRUE(expected_wifi_aps_json
.GetDictionary(i
, &expected_json
));
304 const base::DictionaryValue
* actual_json
;
305 ASSERT_TRUE(wifi_aps_json
->GetDictionary(i
, &actual_json
));
306 ASSERT_TRUE(JsonFieldEquals("macAddress", *expected_json
,
308 ASSERT_TRUE(JsonFieldEquals("signalStrength", *expected_json
,
310 ASSERT_TRUE(JsonFieldEquals("channel", *expected_json
, *actual_json
));
311 ASSERT_TRUE(JsonFieldEquals("signalToNoiseRatio", *expected_json
,
315 ASSERT_FALSE(request_json
->HasKey("wifiAccessPoints"));
317 EXPECT_TRUE(request_url
.is_valid());
320 GURL test_server_url_
;
321 base::MessageLoop main_message_loop_
;
322 scoped_refptr
<FakeAccessTokenStore
> access_token_store_
;
323 net::TestURLFetcherFactory url_fetcher_factory_
;
324 scoped_refptr
<MockWifiDataProviderImpl
> wifi_data_provider_
;
327 TEST_F(GeolocationNetworkProviderTest
, CreateDestroy
) {
328 // Test fixture members were SetUp correctly.
329 EXPECT_EQ(&main_message_loop_
, base::MessageLoop::current());
330 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
331 EXPECT_TRUE(NULL
!= provider
.get());
336 TEST_F(GeolocationNetworkProviderTest
, StartProvider
) {
337 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
338 EXPECT_TRUE(provider
->StartProvider(false));
339 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
340 ASSERT_TRUE(fetcher
!= NULL
);
341 CheckRequestIsValid(*fetcher
, 0, 0, 0, std::string());
344 TEST_F(GeolocationNetworkProviderTest
, StartProviderDefaultUrl
) {
345 test_server_url_
= LocationArbitratorImpl::DefaultNetworkProviderURL();
346 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
347 EXPECT_TRUE(provider
->StartProvider(false));
348 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
349 ASSERT_TRUE(fetcher
!= NULL
);
350 CheckRequestIsValid(*fetcher
, 0, 0, 0, std::string());
353 TEST_F(GeolocationNetworkProviderTest
, StartProviderLongRequest
) {
354 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
355 EXPECT_TRUE(provider
->StartProvider(false));
356 const int kFirstScanAps
= 20;
357 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kFirstScanAps
));
358 main_message_loop_
.RunUntilIdle();
359 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
360 ASSERT_TRUE(fetcher
!= NULL
);
361 // The request url should have been shortened to less than 2048 characters
362 // in length by not including access points with the lowest signal strength
364 EXPECT_LT(fetcher
->GetOriginalURL().spec().size(), size_t(2048));
365 CheckRequestIsValid(*fetcher
, 0, 16, 4, std::string());
368 TEST_F(GeolocationNetworkProviderTest
, MultipleWifiScansComplete
) {
369 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
370 EXPECT_TRUE(provider
->StartProvider(false));
372 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
373 ASSERT_TRUE(fetcher
!= NULL
);
374 EXPECT_TRUE(IsTestServerUrl(fetcher
->GetOriginalURL()));
376 // Complete the network request with bad position fix.
377 const char* kNoFixNetworkResponse
=
379 " \"status\": \"ZERO_RESULTS\""
381 fetcher
->set_url(test_server_url_
);
382 fetcher
->set_status(net::URLRequestStatus());
383 fetcher
->set_response_code(200); // OK
384 fetcher
->SetResponseString(kNoFixNetworkResponse
);
385 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
387 Geoposition position
;
388 provider
->GetPosition(&position
);
389 EXPECT_FALSE(position
.Validate());
391 // Now wifi data arrives -- SetData will notify listeners.
392 const int kFirstScanAps
= 6;
393 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kFirstScanAps
));
394 main_message_loop_
.RunUntilIdle();
395 fetcher
= get_url_fetcher_and_advance_id();
396 ASSERT_TRUE(fetcher
!= NULL
);
397 // The request should have the wifi data.
398 CheckRequestIsValid(*fetcher
, 0, kFirstScanAps
, 0, std::string());
400 // Send a reply with good position fix.
401 const char* kReferenceNetworkResponse
=
403 " \"accessToken\": \"" REFERENCE_ACCESS_TOKEN
"\","
404 " \"accuracy\": 1200.4,"
410 fetcher
->set_url(test_server_url_
);
411 fetcher
->set_status(net::URLRequestStatus());
412 fetcher
->set_response_code(200); // OK
413 fetcher
->SetResponseString(kReferenceNetworkResponse
);
414 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
416 provider
->GetPosition(&position
);
417 EXPECT_EQ(51.0, position
.latitude
);
418 EXPECT_EQ(-0.1, position
.longitude
);
419 EXPECT_EQ(1200.4, position
.accuracy
);
420 EXPECT_FALSE(position
.timestamp
.is_null());
421 EXPECT_TRUE(position
.Validate());
423 // Token should be in the store.
424 EXPECT_EQ(base::UTF8ToUTF16(REFERENCE_ACCESS_TOKEN
),
425 access_token_store_
->access_token_set_
[test_server_url_
]);
427 // Wifi updated again, with one less AP. This is 'close enough' to the
428 // previous scan, so no new request made.
429 const int kSecondScanAps
= kFirstScanAps
- 1;
430 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kSecondScanAps
));
431 main_message_loop_
.RunUntilIdle();
432 fetcher
= get_url_fetcher_and_advance_id();
433 EXPECT_FALSE(fetcher
);
435 provider
->GetPosition(&position
);
436 EXPECT_EQ(51.0, position
.latitude
);
437 EXPECT_EQ(-0.1, position
.longitude
);
438 EXPECT_TRUE(position
.Validate());
440 // Now a third scan with more than twice the original amount -> new request.
441 const int kThirdScanAps
= kFirstScanAps
* 2 + 1;
442 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kThirdScanAps
));
443 main_message_loop_
.RunUntilIdle();
444 fetcher
= get_url_fetcher_and_advance_id();
445 EXPECT_TRUE(fetcher
);
446 CheckRequestIsValid(*fetcher
, 0, kThirdScanAps
, 0, REFERENCE_ACCESS_TOKEN
);
447 // ...reply with a network error.
449 fetcher
->set_url(test_server_url_
);
450 fetcher
->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED
, -1));
451 fetcher
->set_response_code(200); // should be ignored
452 fetcher
->SetResponseString(std::string());
453 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
455 // Error means we now no longer have a fix.
456 provider
->GetPosition(&position
);
457 EXPECT_FALSE(position
.Validate());
459 // Wifi scan returns to original set: should be serviced from cache.
460 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kFirstScanAps
));
461 main_message_loop_
.RunUntilIdle();
462 EXPECT_FALSE(get_url_fetcher_and_advance_id()); // No new request created.
464 provider
->GetPosition(&position
);
465 EXPECT_EQ(51.0, position
.latitude
);
466 EXPECT_EQ(-0.1, position
.longitude
);
467 EXPECT_TRUE(position
.Validate());
470 TEST_F(GeolocationNetworkProviderTest
, NoRequestOnStartupUntilWifiData
) {
471 MessageLoopQuitListener listener
;
472 wifi_data_provider_
->set_got_data(false);
473 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
474 EXPECT_TRUE(provider
->StartProvider(false));
476 provider
->SetUpdateCallback(
477 base::Bind(&MessageLoopQuitListener::LocationUpdateAvailable
,
478 base::Unretained(&listener
)));
480 main_message_loop_
.RunUntilIdle();
481 EXPECT_FALSE(get_url_fetcher_and_advance_id())
482 << "Network request should not be created right away on startup when "
483 "wifi data has not yet arrived";
485 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(1));
486 main_message_loop_
.RunUntilIdle();
487 EXPECT_TRUE(get_url_fetcher_and_advance_id());
490 TEST_F(GeolocationNetworkProviderTest
, NewDataReplacesExistingNetworkRequest
) {
491 // Send initial request with empty data
492 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
493 EXPECT_TRUE(provider
->StartProvider(false));
494 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
495 EXPECT_TRUE(fetcher
);
497 // Now wifi data arrives; new request should be sent.
498 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(4));
499 main_message_loop_
.RunUntilIdle();
500 fetcher
= get_url_fetcher_and_advance_id();
501 EXPECT_TRUE(fetcher
);
504 TEST_F(GeolocationNetworkProviderTest
, NetworkRequestDeferredForPermission
) {
505 scoped_ptr
<LocationProvider
> provider(CreateProvider(false));
506 EXPECT_TRUE(provider
->StartProvider(false));
507 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
508 EXPECT_FALSE(fetcher
);
509 provider
->OnPermissionGranted();
511 fetcher
= get_url_fetcher_and_advance_id();
512 ASSERT_TRUE(fetcher
!= NULL
);
514 EXPECT_TRUE(IsTestServerUrl(fetcher
->GetOriginalURL()));
517 TEST_F(GeolocationNetworkProviderTest
,
518 NetworkRequestWithWifiDataDeferredForPermission
) {
519 access_token_store_
->access_token_set_
[test_server_url_
] =
520 base::UTF8ToUTF16(REFERENCE_ACCESS_TOKEN
);
521 scoped_ptr
<LocationProvider
> provider(CreateProvider(false));
522 EXPECT_TRUE(provider
->StartProvider(false));
523 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
524 EXPECT_FALSE(fetcher
);
526 static const int kScanCount
= 4;
527 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kScanCount
));
528 main_message_loop_
.RunUntilIdle();
530 fetcher
= get_url_fetcher_and_advance_id();
531 EXPECT_FALSE(fetcher
);
533 provider
->OnPermissionGranted();
535 fetcher
= get_url_fetcher_and_advance_id();
536 ASSERT_TRUE(fetcher
!= NULL
);
538 CheckRequestIsValid(*fetcher
, 0, kScanCount
, 0, REFERENCE_ACCESS_TOKEN
);
541 TEST_F(GeolocationNetworkProviderTest
, NetworkPositionCache
) {
542 NetworkLocationProvider::PositionCache cache
;
544 const int kCacheSize
= NetworkLocationProvider::PositionCache::kMaximumSize
;
545 for (int i
= 1; i
< kCacheSize
* 2 + 1; ++i
) {
546 Geoposition pos
= CreateReferencePosition(i
);
547 bool ret
= cache
.CachePosition(CreateReferenceWifiScanData(i
), pos
);
548 EXPECT_TRUE(ret
) << i
;
549 const Geoposition
* item
=
550 cache
.FindPosition(CreateReferenceWifiScanData(i
));
551 ASSERT_TRUE(item
) << i
;
552 EXPECT_EQ(pos
.latitude
, item
->latitude
) << i
;
553 EXPECT_EQ(pos
.longitude
, item
->longitude
) << i
;
554 if (i
<= kCacheSize
) {
555 // Nothing should have spilled yet; check oldest item is still there.
556 EXPECT_TRUE(cache
.FindPosition(CreateReferenceWifiScanData(1)));
558 const int evicted
= i
- kCacheSize
;
559 EXPECT_FALSE(cache
.FindPosition(CreateReferenceWifiScanData(evicted
)));
560 EXPECT_TRUE(cache
.FindPosition(CreateReferenceWifiScanData(evicted
+ 1)));
565 } // namespace content