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 "content/browser/geolocation/wifi_data_provider.h"
17 #include "net/url_request/test_url_fetcher_factory.h"
18 #include "net/url_request/url_request_status.h"
19 #include "testing/gtest/include/gtest/gtest.h"
23 // Constants used in multiple tests.
24 const char kTestServerUrl
[] = "https://www.geolocation.test/service";
25 const char kAccessTokenString
[] = "accessToken";
27 // Using #define so we can easily paste this into various other strings.
28 #define REFERENCE_ACCESS_TOKEN "2:k7j3G6LaL6u_lafw:4iXOeOpTh1glSXe"
30 // Stops the specified (nested) message loop when the listener is called back.
31 class MessageLoopQuitListener
{
33 MessageLoopQuitListener()
34 : client_message_loop_(base::MessageLoop::current()),
35 updated_provider_(NULL
) {
36 CHECK(client_message_loop_
);
39 void OnLocationUpdate(const LocationProvider
* provider
,
40 const Geoposition
& position
) {
41 EXPECT_EQ(client_message_loop_
, base::MessageLoop::current());
42 updated_provider_
= provider
;
43 client_message_loop_
->Quit();
46 base::MessageLoop
* client_message_loop_
;
47 const LocationProvider
* updated_provider_
;
50 // A mock implementation of WifiDataProvider for testing. Adapted from
51 // http://gears.googlecode.com/svn/trunk/gears/geolocation/geolocation_test.cc
52 class MockWifiDataProvider
: public WifiDataProvider
{
54 // Factory method for use with WifiDataProvider::SetFactoryForTesting.
55 static WifiDataProvider
* GetInstance() {
60 static MockWifiDataProvider
* CreateInstance() {
62 instance_
= new MockWifiDataProvider
;
66 MockWifiDataProvider() : start_calls_(0), stop_calls_(0), got_data_(true) {}
68 // WifiDataProvider implementation.
69 void StartDataProvider() override
{ ++start_calls_
; }
71 void StopDataProvider() override
{ ++stop_calls_
; }
73 bool GetData(WifiData
* data_out
) override
{
79 void SetData(const WifiData
& new_data
) {
81 const bool differs
= data_
.DiffersSignificantly(new_data
);
87 void set_got_data(bool got_data
) { got_data_
= got_data
; }
92 ~MockWifiDataProvider() override
{
93 CHECK(this == instance_
);
97 static MockWifiDataProvider
* instance_
;
102 DISALLOW_COPY_AND_ASSIGN(MockWifiDataProvider
);
105 MockWifiDataProvider
* MockWifiDataProvider::instance_
= NULL
;
108 class GeolocationNetworkProviderTest
: public testing::Test
{
110 void SetUp() override
{
111 test_server_url_
= GURL(kTestServerUrl
);
112 access_token_store_
= new FakeAccessTokenStore
;
113 wifi_data_provider_
= MockWifiDataProvider::CreateInstance();
116 void TearDown() override
{
117 WifiDataProviderManager::ResetFactoryForTesting();
120 LocationProvider
* CreateProvider(bool set_permission_granted
) {
121 LocationProvider
* provider
= NewNetworkLocationProvider(
122 access_token_store_
.get(),
123 NULL
, // No URLContextGetter needed, as using test urlfecther factory.
125 access_token_store_
->access_token_set_
[test_server_url_
]);
126 if (set_permission_granted
)
127 provider
->OnPermissionGranted();
132 GeolocationNetworkProviderTest() {
133 // TODO(joth): Really these should be in SetUp, not here, but they take no
134 // effect on Mac OS Release builds if done there. I kid not. Figure out why.
135 WifiDataProviderManager::SetFactoryForTesting(
136 MockWifiDataProvider::GetInstance
);
139 // Returns the current url fetcher (if any) and advances the id ready for the
141 net::TestURLFetcher
* get_url_fetcher_and_advance_id() {
142 net::TestURLFetcher
* fetcher
= url_fetcher_factory_
.GetFetcherByID(
143 NetworkLocationRequest::url_fetcher_id_for_tests
);
145 ++NetworkLocationRequest::url_fetcher_id_for_tests
;
149 static int IndexToChannel(int index
) { return index
+ 4; }
151 // Creates wifi data containing the specified number of access points, with
152 // some differentiating charactistics in each.
153 static WifiData
CreateReferenceWifiScanData(int ap_count
) {
155 for (int i
= 0; i
< ap_count
; ++i
) {
158 base::ASCIIToUTF16(base::StringPrintf("%02d-34-56-78-54-32", i
));
159 ap
.radio_signal_strength
= ap_count
- i
;
160 ap
.channel
= IndexToChannel(i
);
161 ap
.signal_to_noise
= i
+ 42;
162 ap
.ssid
= base::ASCIIToUTF16("Some nice+network|name\\");
163 data
.access_point_data
.insert(ap
);
168 static void CreateReferenceWifiScanDataJson(
169 int ap_count
, int start_index
, base::ListValue
* wifi_access_point_list
) {
170 std::vector
<std::string
> wifi_data
;
171 for (int i
= 0; i
< ap_count
; ++i
) {
172 base::DictionaryValue
* ap
= new base::DictionaryValue();
173 ap
->SetString("macAddress", base::StringPrintf("%02d-34-56-78-54-32", i
));
174 ap
->SetInteger("signalStrength", start_index
+ ap_count
- i
);
175 ap
->SetInteger("age", 0);
176 ap
->SetInteger("channel", IndexToChannel(i
));
177 ap
->SetInteger("signalToNoiseRatio", i
+ 42);
178 wifi_access_point_list
->Append(ap
);
182 static Geoposition
CreateReferencePosition(int id
) {
185 pos
.longitude
= -(id
+ 1);
186 pos
.altitude
= 2 * id
;
187 pos
.timestamp
= base::Time::Now();
191 static std::string
PrettyJson(const base::Value
& value
) {
193 base::JSONWriter::WriteWithOptions(
194 value
, base::JSONWriter::OPTIONS_PRETTY_PRINT
, &pretty
);
198 static testing::AssertionResult
JsonGetList(
199 const std::string
& field
,
200 const base::DictionaryValue
& dict
,
201 const base::ListValue
** output_list
) {
202 if (!dict
.GetList(field
, output_list
))
203 return testing::AssertionFailure() << "Dictionary " << PrettyJson(dict
)
204 << " is missing list field " << field
;
205 return testing::AssertionSuccess();
208 static testing::AssertionResult
JsonFieldEquals(
209 const std::string
& field
,
210 const base::DictionaryValue
& expected
,
211 const base::DictionaryValue
& actual
) {
212 const base::Value
* expected_value
;
213 const base::Value
* actual_value
;
214 if (!expected
.Get(field
, &expected_value
))
215 return testing::AssertionFailure()
216 << "Expected dictionary " << PrettyJson(expected
)
217 << " is missing field " << field
;
218 if (!expected
.Get(field
, &actual_value
))
219 return testing::AssertionFailure()
220 << "Actual dictionary " << PrettyJson(actual
)
221 << " is missing field " << field
;
222 if (!expected_value
->Equals(actual_value
))
223 return testing::AssertionFailure()
224 << "Field " << field
<< " mismatch: " << PrettyJson(*expected_value
)
225 << " != " << PrettyJson(*actual_value
);
226 return testing::AssertionSuccess();
229 static GURL
UrlWithoutQuery(const GURL
& url
) {
230 url::Replacements
<char> replacements
;
231 replacements
.ClearQuery();
232 return url
.ReplaceComponents(replacements
);
235 testing::AssertionResult
IsTestServerUrl(const GURL
& request_url
) {
236 const GURL
a(UrlWithoutQuery(test_server_url_
));
237 const GURL
b(UrlWithoutQuery(request_url
));
239 return testing::AssertionSuccess();
240 return testing::AssertionFailure() << a
<< " != " << b
;
243 void CheckRequestIsValid(const net::TestURLFetcher
& request
,
244 int expected_routers
,
245 int expected_wifi_aps
,
246 int wifi_start_index
,
247 const std::string
& expected_access_token
) {
248 const GURL
& request_url
= request
.GetOriginalURL();
250 EXPECT_TRUE(IsTestServerUrl(request_url
));
252 // Check to see that the api key is being appended for the default
253 // network provider url.
254 bool is_default_url
= UrlWithoutQuery(request_url
) ==
255 UrlWithoutQuery(LocationArbitratorImpl::DefaultNetworkProviderURL());
256 EXPECT_EQ(is_default_url
, !request_url
.query().empty());
258 const std::string
& upload_data
= request
.upload_data();
259 ASSERT_FALSE(upload_data
.empty());
260 std::string json_parse_error_msg
;
261 scoped_ptr
<base::Value
> parsed_json(
262 base::JSONReader::DeprecatedReadAndReturnError(
263 upload_data
, base::JSON_PARSE_RFC
, NULL
, &json_parse_error_msg
));
264 EXPECT_TRUE(json_parse_error_msg
.empty());
265 ASSERT_TRUE(parsed_json
.get() != NULL
);
267 const base::DictionaryValue
* request_json
;
268 ASSERT_TRUE(parsed_json
->GetAsDictionary(&request_json
));
270 if (!is_default_url
) {
271 if (expected_access_token
.empty())
272 ASSERT_FALSE(request_json
->HasKey(kAccessTokenString
));
274 std::string access_token
;
275 EXPECT_TRUE(request_json
->GetString(kAccessTokenString
, &access_token
));
276 EXPECT_EQ(expected_access_token
, access_token
);
280 if (expected_wifi_aps
) {
281 base::ListValue expected_wifi_aps_json
;
282 CreateReferenceWifiScanDataJson(
285 &expected_wifi_aps_json
);
286 EXPECT_EQ(size_t(expected_wifi_aps
), expected_wifi_aps_json
.GetSize());
288 const base::ListValue
* wifi_aps_json
;
289 ASSERT_TRUE(JsonGetList("wifiAccessPoints", *request_json
,
291 for (size_t i
= 0; i
< expected_wifi_aps_json
.GetSize(); ++i
) {
292 const base::DictionaryValue
* expected_json
;
293 ASSERT_TRUE(expected_wifi_aps_json
.GetDictionary(i
, &expected_json
));
294 const base::DictionaryValue
* actual_json
;
295 ASSERT_TRUE(wifi_aps_json
->GetDictionary(i
, &actual_json
));
296 ASSERT_TRUE(JsonFieldEquals("macAddress", *expected_json
,
298 ASSERT_TRUE(JsonFieldEquals("signalStrength", *expected_json
,
300 ASSERT_TRUE(JsonFieldEquals("channel", *expected_json
, *actual_json
));
301 ASSERT_TRUE(JsonFieldEquals("signalToNoiseRatio", *expected_json
,
305 ASSERT_FALSE(request_json
->HasKey("wifiAccessPoints"));
307 EXPECT_TRUE(request_url
.is_valid());
310 GURL test_server_url_
;
311 base::MessageLoop main_message_loop_
;
312 scoped_refptr
<FakeAccessTokenStore
> access_token_store_
;
313 net::TestURLFetcherFactory url_fetcher_factory_
;
314 scoped_refptr
<MockWifiDataProvider
> wifi_data_provider_
;
317 TEST_F(GeolocationNetworkProviderTest
, CreateDestroy
) {
318 // Test fixture members were SetUp correctly.
319 EXPECT_EQ(&main_message_loop_
, base::MessageLoop::current());
320 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
321 EXPECT_TRUE(NULL
!= provider
.get());
326 TEST_F(GeolocationNetworkProviderTest
, StartProvider
) {
327 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
328 EXPECT_TRUE(provider
->StartProvider(false));
329 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
330 ASSERT_TRUE(fetcher
!= NULL
);
331 CheckRequestIsValid(*fetcher
, 0, 0, 0, std::string());
334 TEST_F(GeolocationNetworkProviderTest
, StartProviderDefaultUrl
) {
335 test_server_url_
= LocationArbitratorImpl::DefaultNetworkProviderURL();
336 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
337 EXPECT_TRUE(provider
->StartProvider(false));
338 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
339 ASSERT_TRUE(fetcher
!= NULL
);
340 CheckRequestIsValid(*fetcher
, 0, 0, 0, std::string());
343 TEST_F(GeolocationNetworkProviderTest
, StartProviderLongRequest
) {
344 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
345 EXPECT_TRUE(provider
->StartProvider(false));
346 const int kFirstScanAps
= 20;
347 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kFirstScanAps
));
348 main_message_loop_
.RunUntilIdle();
349 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
350 ASSERT_TRUE(fetcher
!= NULL
);
351 // The request url should have been shortened to less than 2048 characters
352 // in length by not including access points with the lowest signal strength
354 EXPECT_LT(fetcher
->GetOriginalURL().spec().size(), size_t(2048));
355 CheckRequestIsValid(*fetcher
, 0, 16, 4, std::string());
358 TEST_F(GeolocationNetworkProviderTest
, MultipleWifiScansComplete
) {
359 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
360 EXPECT_TRUE(provider
->StartProvider(false));
362 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
363 ASSERT_TRUE(fetcher
!= NULL
);
364 EXPECT_TRUE(IsTestServerUrl(fetcher
->GetOriginalURL()));
366 // Complete the network request with bad position fix.
367 const char* kNoFixNetworkResponse
=
369 " \"status\": \"ZERO_RESULTS\""
371 fetcher
->set_url(test_server_url_
);
372 fetcher
->set_status(net::URLRequestStatus());
373 fetcher
->set_response_code(200); // OK
374 fetcher
->SetResponseString(kNoFixNetworkResponse
);
375 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
377 Geoposition position
;
378 provider
->GetPosition(&position
);
379 EXPECT_FALSE(position
.Validate());
381 // Now wifi data arrives -- SetData will notify listeners.
382 const int kFirstScanAps
= 6;
383 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kFirstScanAps
));
384 main_message_loop_
.RunUntilIdle();
385 fetcher
= get_url_fetcher_and_advance_id();
386 ASSERT_TRUE(fetcher
!= NULL
);
387 // The request should have the wifi data.
388 CheckRequestIsValid(*fetcher
, 0, kFirstScanAps
, 0, std::string());
390 // Send a reply with good position fix.
391 const char* kReferenceNetworkResponse
=
393 " \"accessToken\": \"" REFERENCE_ACCESS_TOKEN
"\","
394 " \"accuracy\": 1200.4,"
400 fetcher
->set_url(test_server_url_
);
401 fetcher
->set_status(net::URLRequestStatus());
402 fetcher
->set_response_code(200); // OK
403 fetcher
->SetResponseString(kReferenceNetworkResponse
);
404 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
406 provider
->GetPosition(&position
);
407 EXPECT_EQ(51.0, position
.latitude
);
408 EXPECT_EQ(-0.1, position
.longitude
);
409 EXPECT_EQ(1200.4, position
.accuracy
);
410 EXPECT_FALSE(position
.timestamp
.is_null());
411 EXPECT_TRUE(position
.Validate());
413 // Token should be in the store.
414 EXPECT_EQ(base::UTF8ToUTF16(REFERENCE_ACCESS_TOKEN
),
415 access_token_store_
->access_token_set_
[test_server_url_
]);
417 // Wifi updated again, with one less AP. This is 'close enough' to the
418 // previous scan, so no new request made.
419 const int kSecondScanAps
= kFirstScanAps
- 1;
420 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kSecondScanAps
));
421 main_message_loop_
.RunUntilIdle();
422 fetcher
= get_url_fetcher_and_advance_id();
423 EXPECT_FALSE(fetcher
);
425 provider
->GetPosition(&position
);
426 EXPECT_EQ(51.0, position
.latitude
);
427 EXPECT_EQ(-0.1, position
.longitude
);
428 EXPECT_TRUE(position
.Validate());
430 // Now a third scan with more than twice the original amount -> new request.
431 const int kThirdScanAps
= kFirstScanAps
* 2 + 1;
432 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kThirdScanAps
));
433 main_message_loop_
.RunUntilIdle();
434 fetcher
= get_url_fetcher_and_advance_id();
435 EXPECT_TRUE(fetcher
);
436 CheckRequestIsValid(*fetcher
, 0, kThirdScanAps
, 0, REFERENCE_ACCESS_TOKEN
);
437 // ...reply with a network error.
439 fetcher
->set_url(test_server_url_
);
440 fetcher
->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED
, -1));
441 fetcher
->set_response_code(200); // should be ignored
442 fetcher
->SetResponseString(std::string());
443 fetcher
->delegate()->OnURLFetchComplete(fetcher
);
445 // Error means we now no longer have a fix.
446 provider
->GetPosition(&position
);
447 EXPECT_FALSE(position
.Validate());
449 // Wifi scan returns to original set: should be serviced from cache.
450 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kFirstScanAps
));
451 main_message_loop_
.RunUntilIdle();
452 EXPECT_FALSE(get_url_fetcher_and_advance_id()); // No new request created.
454 provider
->GetPosition(&position
);
455 EXPECT_EQ(51.0, position
.latitude
);
456 EXPECT_EQ(-0.1, position
.longitude
);
457 EXPECT_TRUE(position
.Validate());
460 TEST_F(GeolocationNetworkProviderTest
, NoRequestOnStartupUntilWifiData
) {
461 MessageLoopQuitListener listener
;
462 wifi_data_provider_
->set_got_data(false);
463 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
464 EXPECT_TRUE(provider
->StartProvider(false));
466 provider
->SetUpdateCallback(base::Bind(
467 &MessageLoopQuitListener::OnLocationUpdate
, base::Unretained(&listener
)));
469 main_message_loop_
.RunUntilIdle();
470 EXPECT_FALSE(get_url_fetcher_and_advance_id())
471 << "Network request should not be created right away on startup when "
472 "wifi data has not yet arrived";
474 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(1));
475 main_message_loop_
.RunUntilIdle();
476 EXPECT_TRUE(get_url_fetcher_and_advance_id());
479 TEST_F(GeolocationNetworkProviderTest
, NewDataReplacesExistingNetworkRequest
) {
480 // Send initial request with empty data
481 scoped_ptr
<LocationProvider
> provider(CreateProvider(true));
482 EXPECT_TRUE(provider
->StartProvider(false));
483 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
484 EXPECT_TRUE(fetcher
);
486 // Now wifi data arrives; new request should be sent.
487 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(4));
488 main_message_loop_
.RunUntilIdle();
489 fetcher
= get_url_fetcher_and_advance_id();
490 EXPECT_TRUE(fetcher
);
493 TEST_F(GeolocationNetworkProviderTest
, NetworkRequestDeferredForPermission
) {
494 scoped_ptr
<LocationProvider
> provider(CreateProvider(false));
495 EXPECT_TRUE(provider
->StartProvider(false));
496 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
497 EXPECT_FALSE(fetcher
);
498 provider
->OnPermissionGranted();
500 fetcher
= get_url_fetcher_and_advance_id();
501 ASSERT_TRUE(fetcher
!= NULL
);
503 EXPECT_TRUE(IsTestServerUrl(fetcher
->GetOriginalURL()));
506 TEST_F(GeolocationNetworkProviderTest
,
507 NetworkRequestWithWifiDataDeferredForPermission
) {
508 access_token_store_
->access_token_set_
[test_server_url_
] =
509 base::UTF8ToUTF16(REFERENCE_ACCESS_TOKEN
);
510 scoped_ptr
<LocationProvider
> provider(CreateProvider(false));
511 EXPECT_TRUE(provider
->StartProvider(false));
512 net::TestURLFetcher
* fetcher
= get_url_fetcher_and_advance_id();
513 EXPECT_FALSE(fetcher
);
515 static const int kScanCount
= 4;
516 wifi_data_provider_
->SetData(CreateReferenceWifiScanData(kScanCount
));
517 main_message_loop_
.RunUntilIdle();
519 fetcher
= get_url_fetcher_and_advance_id();
520 EXPECT_FALSE(fetcher
);
522 provider
->OnPermissionGranted();
524 fetcher
= get_url_fetcher_and_advance_id();
525 ASSERT_TRUE(fetcher
!= NULL
);
527 CheckRequestIsValid(*fetcher
, 0, kScanCount
, 0, REFERENCE_ACCESS_TOKEN
);
530 TEST_F(GeolocationNetworkProviderTest
, NetworkPositionCache
) {
531 NetworkLocationProvider::PositionCache cache
;
533 const int kCacheSize
= NetworkLocationProvider::PositionCache::kMaximumSize
;
534 for (int i
= 1; i
< kCacheSize
* 2 + 1; ++i
) {
535 Geoposition pos
= CreateReferencePosition(i
);
536 bool ret
= cache
.CachePosition(CreateReferenceWifiScanData(i
), pos
);
537 EXPECT_TRUE(ret
) << i
;
538 const Geoposition
* item
=
539 cache
.FindPosition(CreateReferenceWifiScanData(i
));
540 ASSERT_TRUE(item
) << i
;
541 EXPECT_EQ(pos
.latitude
, item
->latitude
) << i
;
542 EXPECT_EQ(pos
.longitude
, item
->longitude
) << i
;
543 if (i
<= kCacheSize
) {
544 // Nothing should have spilled yet; check oldest item is still there.
545 EXPECT_TRUE(cache
.FindPosition(CreateReferenceWifiScanData(1)));
547 const int evicted
= i
- kCacheSize
;
548 EXPECT_FALSE(cache
.FindPosition(CreateReferenceWifiScanData(evicted
)));
549 EXPECT_TRUE(cache
.FindPosition(CreateReferenceWifiScanData(evicted
+ 1)));
554 } // namespace content