Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / geolocation / network_location_provider_unittest.cc
blobb8ef6cabb5228eeadadcd7172eaa5aa499c31bac
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"
21 namespace content {
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 {
32 public:
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 {
53 public:
54 // Factory method for use with WifiDataProvider::SetFactoryForTesting.
55 static WifiDataProvider* GetInstance() {
56 CHECK(instance_);
57 return instance_;
60 static MockWifiDataProvider* CreateInstance() {
61 CHECK(!instance_);
62 instance_ = new MockWifiDataProvider;
63 return instance_;
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 {
74 CHECK(data_out);
75 *data_out = data_;
76 return got_data_;
79 void SetData(const WifiData& new_data) {
80 got_data_ = true;
81 const bool differs = data_.DiffersSignificantly(new_data);
82 data_ = new_data;
83 if (differs)
84 this->RunCallbacks();
87 void set_got_data(bool got_data) { got_data_ = got_data; }
88 int start_calls_;
89 int stop_calls_;
91 private:
92 ~MockWifiDataProvider() override {
93 CHECK(this == instance_);
94 instance_ = NULL;
97 static MockWifiDataProvider* instance_;
99 WifiData data_;
100 bool got_data_;
102 DISALLOW_COPY_AND_ASSIGN(MockWifiDataProvider);
105 MockWifiDataProvider* MockWifiDataProvider::instance_ = NULL;
107 // Main test fixture
108 class GeolocationNetworkProviderTest : public testing::Test {
109 public:
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.
124 test_server_url_,
125 access_token_store_->access_token_set_[test_server_url_]);
126 if (set_permission_granted)
127 provider->OnPermissionGranted();
128 return provider;
131 protected:
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
140 // next test step.
141 net::TestURLFetcher* get_url_fetcher_and_advance_id() {
142 net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(
143 NetworkLocationRequest::url_fetcher_id_for_tests);
144 if (fetcher)
145 ++NetworkLocationRequest::url_fetcher_id_for_tests;
146 return fetcher;
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) {
154 WifiData data;
155 for (int i = 0; i < ap_count; ++i) {
156 AccessPointData ap;
157 ap.mac_address =
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);
165 return data;
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) {
183 Geoposition pos;
184 pos.latitude = id;
185 pos.longitude = -(id + 1);
186 pos.altitude = 2 * id;
187 pos.timestamp = base::Time::Now();
188 return pos;
191 static std::string PrettyJson(const base::Value& value) {
192 std::string pretty;
193 base::JSONWriter::WriteWithOptions(
194 &value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &pretty);
195 return 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));
238 if (a == b)
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::ReadAndReturnError(
263 upload_data,
264 base::JSON_PARSE_RFC,
265 NULL,
266 &json_parse_error_msg));
267 EXPECT_TRUE(json_parse_error_msg.empty());
268 ASSERT_TRUE(parsed_json.get() != NULL);
270 const base::DictionaryValue* request_json;
271 ASSERT_TRUE(parsed_json->GetAsDictionary(&request_json));
273 if (!is_default_url) {
274 if (expected_access_token.empty())
275 ASSERT_FALSE(request_json->HasKey(kAccessTokenString));
276 else {
277 std::string access_token;
278 EXPECT_TRUE(request_json->GetString(kAccessTokenString, &access_token));
279 EXPECT_EQ(expected_access_token, access_token);
283 if (expected_wifi_aps) {
284 base::ListValue expected_wifi_aps_json;
285 CreateReferenceWifiScanDataJson(
286 expected_wifi_aps,
287 wifi_start_index,
288 &expected_wifi_aps_json);
289 EXPECT_EQ(size_t(expected_wifi_aps), expected_wifi_aps_json.GetSize());
291 const base::ListValue* wifi_aps_json;
292 ASSERT_TRUE(JsonGetList("wifiAccessPoints", *request_json,
293 &wifi_aps_json));
294 for (size_t i = 0; i < expected_wifi_aps_json.GetSize(); ++i ) {
295 const base::DictionaryValue* expected_json;
296 ASSERT_TRUE(expected_wifi_aps_json.GetDictionary(i, &expected_json));
297 const base::DictionaryValue* actual_json;
298 ASSERT_TRUE(wifi_aps_json->GetDictionary(i, &actual_json));
299 ASSERT_TRUE(JsonFieldEquals("macAddress", *expected_json,
300 *actual_json));
301 ASSERT_TRUE(JsonFieldEquals("signalStrength", *expected_json,
302 *actual_json));
303 ASSERT_TRUE(JsonFieldEquals("channel", *expected_json, *actual_json));
304 ASSERT_TRUE(JsonFieldEquals("signalToNoiseRatio", *expected_json,
305 *actual_json));
307 } else {
308 ASSERT_FALSE(request_json->HasKey("wifiAccessPoints"));
310 EXPECT_TRUE(request_url.is_valid());
313 GURL test_server_url_;
314 base::MessageLoop main_message_loop_;
315 scoped_refptr<FakeAccessTokenStore> access_token_store_;
316 net::TestURLFetcherFactory url_fetcher_factory_;
317 scoped_refptr<MockWifiDataProvider> wifi_data_provider_;
320 TEST_F(GeolocationNetworkProviderTest, CreateDestroy) {
321 // Test fixture members were SetUp correctly.
322 EXPECT_EQ(&main_message_loop_, base::MessageLoop::current());
323 scoped_ptr<LocationProvider> provider(CreateProvider(true));
324 EXPECT_TRUE(NULL != provider.get());
325 provider.reset();
326 SUCCEED();
329 TEST_F(GeolocationNetworkProviderTest, StartProvider) {
330 scoped_ptr<LocationProvider> provider(CreateProvider(true));
331 EXPECT_TRUE(provider->StartProvider(false));
332 net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
333 ASSERT_TRUE(fetcher != NULL);
334 CheckRequestIsValid(*fetcher, 0, 0, 0, std::string());
337 TEST_F(GeolocationNetworkProviderTest, StartProviderDefaultUrl) {
338 test_server_url_ = LocationArbitratorImpl::DefaultNetworkProviderURL();
339 scoped_ptr<LocationProvider> provider(CreateProvider(true));
340 EXPECT_TRUE(provider->StartProvider(false));
341 net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
342 ASSERT_TRUE(fetcher != NULL);
343 CheckRequestIsValid(*fetcher, 0, 0, 0, std::string());
346 TEST_F(GeolocationNetworkProviderTest, StartProviderLongRequest) {
347 scoped_ptr<LocationProvider> provider(CreateProvider(true));
348 EXPECT_TRUE(provider->StartProvider(false));
349 const int kFirstScanAps = 20;
350 wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
351 main_message_loop_.RunUntilIdle();
352 net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
353 ASSERT_TRUE(fetcher != NULL);
354 // The request url should have been shortened to less than 2048 characters
355 // in length by not including access points with the lowest signal strength
356 // in the request.
357 EXPECT_LT(fetcher->GetOriginalURL().spec().size(), size_t(2048));
358 CheckRequestIsValid(*fetcher, 0, 16, 4, std::string());
361 TEST_F(GeolocationNetworkProviderTest, MultipleWifiScansComplete) {
362 scoped_ptr<LocationProvider> provider(CreateProvider(true));
363 EXPECT_TRUE(provider->StartProvider(false));
365 net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
366 ASSERT_TRUE(fetcher != NULL);
367 EXPECT_TRUE(IsTestServerUrl(fetcher->GetOriginalURL()));
369 // Complete the network request with bad position fix.
370 const char* kNoFixNetworkResponse =
372 " \"status\": \"ZERO_RESULTS\""
373 "}";
374 fetcher->set_url(test_server_url_);
375 fetcher->set_status(net::URLRequestStatus());
376 fetcher->set_response_code(200); // OK
377 fetcher->SetResponseString(kNoFixNetworkResponse);
378 fetcher->delegate()->OnURLFetchComplete(fetcher);
380 Geoposition position;
381 provider->GetPosition(&position);
382 EXPECT_FALSE(position.Validate());
384 // Now wifi data arrives -- SetData will notify listeners.
385 const int kFirstScanAps = 6;
386 wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
387 main_message_loop_.RunUntilIdle();
388 fetcher = get_url_fetcher_and_advance_id();
389 ASSERT_TRUE(fetcher != NULL);
390 // The request should have the wifi data.
391 CheckRequestIsValid(*fetcher, 0, kFirstScanAps, 0, std::string());
393 // Send a reply with good position fix.
394 const char* kReferenceNetworkResponse =
396 " \"accessToken\": \"" REFERENCE_ACCESS_TOKEN "\","
397 " \"accuracy\": 1200.4,"
398 " \"location\": {"
399 " \"lat\": 51.0,"
400 " \"lng\": -0.1"
401 " }"
402 "}";
403 fetcher->set_url(test_server_url_);
404 fetcher->set_status(net::URLRequestStatus());
405 fetcher->set_response_code(200); // OK
406 fetcher->SetResponseString(kReferenceNetworkResponse);
407 fetcher->delegate()->OnURLFetchComplete(fetcher);
409 provider->GetPosition(&position);
410 EXPECT_EQ(51.0, position.latitude);
411 EXPECT_EQ(-0.1, position.longitude);
412 EXPECT_EQ(1200.4, position.accuracy);
413 EXPECT_FALSE(position.timestamp.is_null());
414 EXPECT_TRUE(position.Validate());
416 // Token should be in the store.
417 EXPECT_EQ(base::UTF8ToUTF16(REFERENCE_ACCESS_TOKEN),
418 access_token_store_->access_token_set_[test_server_url_]);
420 // Wifi updated again, with one less AP. This is 'close enough' to the
421 // previous scan, so no new request made.
422 const int kSecondScanAps = kFirstScanAps - 1;
423 wifi_data_provider_->SetData(CreateReferenceWifiScanData(kSecondScanAps));
424 main_message_loop_.RunUntilIdle();
425 fetcher = get_url_fetcher_and_advance_id();
426 EXPECT_FALSE(fetcher);
428 provider->GetPosition(&position);
429 EXPECT_EQ(51.0, position.latitude);
430 EXPECT_EQ(-0.1, position.longitude);
431 EXPECT_TRUE(position.Validate());
433 // Now a third scan with more than twice the original amount -> new request.
434 const int kThirdScanAps = kFirstScanAps * 2 + 1;
435 wifi_data_provider_->SetData(CreateReferenceWifiScanData(kThirdScanAps));
436 main_message_loop_.RunUntilIdle();
437 fetcher = get_url_fetcher_and_advance_id();
438 EXPECT_TRUE(fetcher);
439 CheckRequestIsValid(*fetcher, 0, kThirdScanAps, 0, REFERENCE_ACCESS_TOKEN);
440 // ...reply with a network error.
442 fetcher->set_url(test_server_url_);
443 fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED, -1));
444 fetcher->set_response_code(200); // should be ignored
445 fetcher->SetResponseString(std::string());
446 fetcher->delegate()->OnURLFetchComplete(fetcher);
448 // Error means we now no longer have a fix.
449 provider->GetPosition(&position);
450 EXPECT_FALSE(position.Validate());
452 // Wifi scan returns to original set: should be serviced from cache.
453 wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
454 main_message_loop_.RunUntilIdle();
455 EXPECT_FALSE(get_url_fetcher_and_advance_id()); // No new request created.
457 provider->GetPosition(&position);
458 EXPECT_EQ(51.0, position.latitude);
459 EXPECT_EQ(-0.1, position.longitude);
460 EXPECT_TRUE(position.Validate());
463 TEST_F(GeolocationNetworkProviderTest, NoRequestOnStartupUntilWifiData) {
464 MessageLoopQuitListener listener;
465 wifi_data_provider_->set_got_data(false);
466 scoped_ptr<LocationProvider> provider(CreateProvider(true));
467 EXPECT_TRUE(provider->StartProvider(false));
469 provider->SetUpdateCallback(base::Bind(
470 &MessageLoopQuitListener::OnLocationUpdate, base::Unretained(&listener)));
472 main_message_loop_.RunUntilIdle();
473 EXPECT_FALSE(get_url_fetcher_and_advance_id())
474 << "Network request should not be created right away on startup when "
475 "wifi data has not yet arrived";
477 wifi_data_provider_->SetData(CreateReferenceWifiScanData(1));
478 main_message_loop_.RunUntilIdle();
479 EXPECT_TRUE(get_url_fetcher_and_advance_id());
482 TEST_F(GeolocationNetworkProviderTest, NewDataReplacesExistingNetworkRequest) {
483 // Send initial request with empty data
484 scoped_ptr<LocationProvider> provider(CreateProvider(true));
485 EXPECT_TRUE(provider->StartProvider(false));
486 net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
487 EXPECT_TRUE(fetcher);
489 // Now wifi data arrives; new request should be sent.
490 wifi_data_provider_->SetData(CreateReferenceWifiScanData(4));
491 main_message_loop_.RunUntilIdle();
492 fetcher = get_url_fetcher_and_advance_id();
493 EXPECT_TRUE(fetcher);
496 TEST_F(GeolocationNetworkProviderTest, NetworkRequestDeferredForPermission) {
497 scoped_ptr<LocationProvider> provider(CreateProvider(false));
498 EXPECT_TRUE(provider->StartProvider(false));
499 net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
500 EXPECT_FALSE(fetcher);
501 provider->OnPermissionGranted();
503 fetcher = get_url_fetcher_and_advance_id();
504 ASSERT_TRUE(fetcher != NULL);
506 EXPECT_TRUE(IsTestServerUrl(fetcher->GetOriginalURL()));
509 TEST_F(GeolocationNetworkProviderTest,
510 NetworkRequestWithWifiDataDeferredForPermission) {
511 access_token_store_->access_token_set_[test_server_url_] =
512 base::UTF8ToUTF16(REFERENCE_ACCESS_TOKEN);
513 scoped_ptr<LocationProvider> provider(CreateProvider(false));
514 EXPECT_TRUE(provider->StartProvider(false));
515 net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
516 EXPECT_FALSE(fetcher);
518 static const int kScanCount = 4;
519 wifi_data_provider_->SetData(CreateReferenceWifiScanData(kScanCount));
520 main_message_loop_.RunUntilIdle();
522 fetcher = get_url_fetcher_and_advance_id();
523 EXPECT_FALSE(fetcher);
525 provider->OnPermissionGranted();
527 fetcher = get_url_fetcher_and_advance_id();
528 ASSERT_TRUE(fetcher != NULL);
530 CheckRequestIsValid(*fetcher, 0, kScanCount, 0, REFERENCE_ACCESS_TOKEN);
533 TEST_F(GeolocationNetworkProviderTest, NetworkPositionCache) {
534 NetworkLocationProvider::PositionCache cache;
536 const int kCacheSize = NetworkLocationProvider::PositionCache::kMaximumSize;
537 for (int i = 1; i < kCacheSize * 2 + 1; ++i) {
538 Geoposition pos = CreateReferencePosition(i);
539 bool ret = cache.CachePosition(CreateReferenceWifiScanData(i), pos);
540 EXPECT_TRUE(ret) << i;
541 const Geoposition* item =
542 cache.FindPosition(CreateReferenceWifiScanData(i));
543 ASSERT_TRUE(item) << i;
544 EXPECT_EQ(pos.latitude, item->latitude) << i;
545 EXPECT_EQ(pos.longitude, item->longitude) << i;
546 if (i <= kCacheSize) {
547 // Nothing should have spilled yet; check oldest item is still there.
548 EXPECT_TRUE(cache.FindPosition(CreateReferenceWifiScanData(1)));
549 } else {
550 const int evicted = i - kCacheSize;
551 EXPECT_FALSE(cache.FindPosition(CreateReferenceWifiScanData(evicted)));
552 EXPECT_TRUE(cache.FindPosition(CreateReferenceWifiScanData(evicted + 1)));
557 } // namespace content