Roll src/third_party/WebKit eac3800:0237a66 (svn 202606:202607)
[chromium-blink-merge.git] / content / browser / geolocation / network_location_provider_unittest.cc
blobaa3b68730f1a3108f1835186fec0228e619db57a
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/base/net_errors.h"
18 #include "net/url_request/test_url_fetcher_factory.h"
19 #include "net/url_request/url_request_status.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 namespace content {
24 // Constants used in multiple tests.
25 const char kTestServerUrl[] = "https://www.geolocation.test/service";
26 const char kAccessTokenString[] = "accessToken";
28 // Using #define so we can easily paste this into various other strings.
29 #define REFERENCE_ACCESS_TOKEN "2:k7j3G6LaL6u_lafw:4iXOeOpTh1glSXe"
31 // Stops the specified (nested) message loop when the listener is called back.
32 class MessageLoopQuitListener {
33 public:
34 MessageLoopQuitListener()
35 : client_message_loop_(base::MessageLoop::current()),
36 updated_provider_(NULL) {
37 CHECK(client_message_loop_);
40 void OnLocationUpdate(const LocationProvider* provider,
41 const Geoposition& position) {
42 EXPECT_EQ(client_message_loop_, base::MessageLoop::current());
43 updated_provider_ = provider;
44 client_message_loop_->Quit();
47 base::MessageLoop* client_message_loop_;
48 const LocationProvider* updated_provider_;
51 // A mock implementation of WifiDataProvider for testing. Adapted from
52 // http://gears.googlecode.com/svn/trunk/gears/geolocation/geolocation_test.cc
53 class MockWifiDataProvider : public WifiDataProvider {
54 public:
55 // Factory method for use with WifiDataProvider::SetFactoryForTesting.
56 static WifiDataProvider* GetInstance() {
57 CHECK(instance_);
58 return instance_;
61 static MockWifiDataProvider* CreateInstance() {
62 CHECK(!instance_);
63 instance_ = new MockWifiDataProvider;
64 return instance_;
67 MockWifiDataProvider() : start_calls_(0), stop_calls_(0), got_data_(true) {}
69 // WifiDataProvider implementation.
70 void StartDataProvider() override { ++start_calls_; }
72 void StopDataProvider() override { ++stop_calls_; }
74 bool GetData(WifiData* data_out) override {
75 CHECK(data_out);
76 *data_out = data_;
77 return got_data_;
80 void SetData(const WifiData& new_data) {
81 got_data_ = true;
82 const bool differs = data_.DiffersSignificantly(new_data);
83 data_ = new_data;
84 if (differs)
85 this->RunCallbacks();
88 void set_got_data(bool got_data) { got_data_ = got_data; }
89 int start_calls_;
90 int stop_calls_;
92 private:
93 ~MockWifiDataProvider() override {
94 CHECK(this == instance_);
95 instance_ = NULL;
98 static MockWifiDataProvider* instance_;
100 WifiData data_;
101 bool got_data_;
103 DISALLOW_COPY_AND_ASSIGN(MockWifiDataProvider);
106 MockWifiDataProvider* MockWifiDataProvider::instance_ = NULL;
108 // Main test fixture
109 class GeolocationNetworkProviderTest : public testing::Test {
110 public:
111 void SetUp() override {
112 test_server_url_ = GURL(kTestServerUrl);
113 access_token_store_ = new FakeAccessTokenStore;
114 wifi_data_provider_ = MockWifiDataProvider::CreateInstance();
117 void TearDown() override {
118 WifiDataProviderManager::ResetFactoryForTesting();
121 LocationProvider* CreateProvider(bool set_permission_granted) {
122 LocationProvider* provider = NewNetworkLocationProvider(
123 access_token_store_.get(),
124 NULL, // No URLContextGetter needed, as using test urlfecther factory.
125 test_server_url_,
126 access_token_store_->access_token_set_[test_server_url_]);
127 if (set_permission_granted)
128 provider->OnPermissionGranted();
129 return provider;
132 protected:
133 GeolocationNetworkProviderTest() {
134 // TODO(joth): Really these should be in SetUp, not here, but they take no
135 // effect on Mac OS Release builds if done there. I kid not. Figure out why.
136 WifiDataProviderManager::SetFactoryForTesting(
137 MockWifiDataProvider::GetInstance);
140 // Returns the current url fetcher (if any) and advances the id ready for the
141 // next test step.
142 net::TestURLFetcher* get_url_fetcher_and_advance_id() {
143 net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(
144 NetworkLocationRequest::url_fetcher_id_for_tests);
145 if (fetcher)
146 ++NetworkLocationRequest::url_fetcher_id_for_tests;
147 return fetcher;
150 static int IndexToChannel(int index) { return index + 4; }
152 // Creates wifi data containing the specified number of access points, with
153 // some differentiating charactistics in each.
154 static WifiData CreateReferenceWifiScanData(int ap_count) {
155 WifiData data;
156 for (int i = 0; i < ap_count; ++i) {
157 AccessPointData ap;
158 ap.mac_address =
159 base::ASCIIToUTF16(base::StringPrintf("%02d-34-56-78-54-32", i));
160 ap.radio_signal_strength = ap_count - i;
161 ap.channel = IndexToChannel(i);
162 ap.signal_to_noise = i + 42;
163 ap.ssid = base::ASCIIToUTF16("Some nice+network|name\\");
164 data.access_point_data.insert(ap);
166 return data;
169 static void CreateReferenceWifiScanDataJson(
170 int ap_count, int start_index, base::ListValue* wifi_access_point_list) {
171 std::vector<std::string> wifi_data;
172 for (int i = 0; i < ap_count; ++i) {
173 base::DictionaryValue* ap = new base::DictionaryValue();
174 ap->SetString("macAddress", base::StringPrintf("%02d-34-56-78-54-32", i));
175 ap->SetInteger("signalStrength", start_index + ap_count - i);
176 ap->SetInteger("age", 0);
177 ap->SetInteger("channel", IndexToChannel(i));
178 ap->SetInteger("signalToNoiseRatio", i + 42);
179 wifi_access_point_list->Append(ap);
183 static Geoposition CreateReferencePosition(int id) {
184 Geoposition pos;
185 pos.latitude = id;
186 pos.longitude = -(id + 1);
187 pos.altitude = 2 * id;
188 pos.timestamp = base::Time::Now();
189 return pos;
192 static std::string PrettyJson(const base::Value& value) {
193 std::string pretty;
194 base::JSONWriter::WriteWithOptions(
195 value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &pretty);
196 return pretty;
199 static testing::AssertionResult JsonGetList(
200 const std::string& field,
201 const base::DictionaryValue& dict,
202 const base::ListValue** output_list) {
203 if (!dict.GetList(field, output_list))
204 return testing::AssertionFailure() << "Dictionary " << PrettyJson(dict)
205 << " is missing list field " << field;
206 return testing::AssertionSuccess();
209 static testing::AssertionResult JsonFieldEquals(
210 const std::string& field,
211 const base::DictionaryValue& expected,
212 const base::DictionaryValue& actual) {
213 const base::Value* expected_value;
214 const base::Value* actual_value;
215 if (!expected.Get(field, &expected_value))
216 return testing::AssertionFailure()
217 << "Expected dictionary " << PrettyJson(expected)
218 << " is missing field " << field;
219 if (!expected.Get(field, &actual_value))
220 return testing::AssertionFailure()
221 << "Actual dictionary " << PrettyJson(actual)
222 << " is missing field " << field;
223 if (!expected_value->Equals(actual_value))
224 return testing::AssertionFailure()
225 << "Field " << field << " mismatch: " << PrettyJson(*expected_value)
226 << " != " << PrettyJson(*actual_value);
227 return testing::AssertionSuccess();
230 static GURL UrlWithoutQuery(const GURL& url) {
231 url::Replacements<char> replacements;
232 replacements.ClearQuery();
233 return url.ReplaceComponents(replacements);
236 testing::AssertionResult IsTestServerUrl(const GURL& request_url) {
237 const GURL a(UrlWithoutQuery(test_server_url_));
238 const GURL b(UrlWithoutQuery(request_url));
239 if (a == b)
240 return testing::AssertionSuccess();
241 return testing::AssertionFailure() << a << " != " << b;
244 void CheckRequestIsValid(const net::TestURLFetcher& request,
245 int expected_routers,
246 int expected_wifi_aps,
247 int wifi_start_index,
248 const std::string& expected_access_token) {
249 const GURL& request_url = request.GetOriginalURL();
251 EXPECT_TRUE(IsTestServerUrl(request_url));
253 // Check to see that the api key is being appended for the default
254 // network provider url.
255 bool is_default_url = UrlWithoutQuery(request_url) ==
256 UrlWithoutQuery(LocationArbitratorImpl::DefaultNetworkProviderURL());
257 EXPECT_EQ(is_default_url, !request_url.query().empty());
259 const std::string& upload_data = request.upload_data();
260 ASSERT_FALSE(upload_data.empty());
261 std::string json_parse_error_msg;
262 scoped_ptr<base::Value> parsed_json = base::JSONReader::ReadAndReturnError(
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));
273 else {
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(
283 expected_wifi_aps,
284 wifi_start_index,
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,
290 &wifi_aps_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,
297 *actual_json));
298 ASSERT_TRUE(JsonFieldEquals("signalStrength", *expected_json,
299 *actual_json));
300 ASSERT_TRUE(JsonFieldEquals("channel", *expected_json, *actual_json));
301 ASSERT_TRUE(JsonFieldEquals("signalToNoiseRatio", *expected_json,
302 *actual_json));
304 } else {
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());
322 provider.reset();
323 SUCCEED();
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
353 // in the request.
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\""
370 "}";
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,"
395 " \"location\": {"
396 " \"lat\": 51.0,"
397 " \"lng\": -0.1"
398 " }"
399 "}";
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::FromError(net::ERR_FAILED));
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)));
546 } else {
547 const int evicted = i - kCacheSize;
548 EXPECT_FALSE(cache.FindPosition(CreateReferenceWifiScanData(evicted)));
549 EXPECT_TRUE(cache.FindPosition(CreateReferenceWifiScanData(evicted + 1)));
554 } // namespace content