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.
4
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"
20
21namespace content {
22
23// Constants used in multiple tests.
24const char kTestServerUrl[] = "https://www.geolocation.test/service";
25const char kAccessTokenString[] = "accessToken";
26
27// Using #define so we can easily paste this into various other strings.
28#define REFERENCE_ACCESS_TOKEN "2:k7j3G6LaL6u_lafw:4iXOeOpTh1glSXe"
29
30// Stops the specified (nested) message loop when the listener is called back.
31class MessageLoopQuitListener {
32 public:
33  MessageLoopQuitListener()
34      : client_message_loop_(base::MessageLoop::current()),
35        updated_provider_(NULL) {
36    CHECK(client_message_loop_);
37  }
38
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();
44  }
45
46  base::MessageLoop* client_message_loop_;
47  const LocationProvider* updated_provider_;
48};
49
50// A mock implementation of WifiDataProvider for testing. Adapted from
51// http://gears.googlecode.com/svn/trunk/gears/geolocation/geolocation_test.cc
52class MockWifiDataProvider : public WifiDataProvider {
53 public:
54  // Factory method for use with WifiDataProvider::SetFactoryForTesting.
55  static WifiDataProvider* GetInstance() {
56    CHECK(instance_);
57    return instance_;
58  }
59
60  static MockWifiDataProvider* CreateInstance() {
61    CHECK(!instance_);
62    instance_ = new MockWifiDataProvider;
63    return instance_;
64  }
65
66  MockWifiDataProvider() : start_calls_(0), stop_calls_(0), got_data_(true) {}
67
68  // WifiDataProvider implementation.
69  virtual void StartDataProvider() OVERRIDE {
70    ++start_calls_;
71  }
72
73  virtual void StopDataProvider() OVERRIDE {
74    ++stop_calls_;
75  }
76
77  virtual bool GetData(WifiData* data_out) OVERRIDE {
78    CHECK(data_out);
79    *data_out = data_;
80    return got_data_;
81  }
82
83  void SetData(const WifiData& new_data) {
84    got_data_ = true;
85    const bool differs = data_.DiffersSignificantly(new_data);
86    data_ = new_data;
87    if (differs)
88      this->RunCallbacks();
89  }
90
91  void set_got_data(bool got_data) { got_data_ = got_data; }
92  int start_calls_;
93  int stop_calls_;
94
95 private:
96  virtual ~MockWifiDataProvider() {
97    CHECK(this == instance_);
98    instance_ = NULL;
99  }
100
101  static MockWifiDataProvider* instance_;
102
103  WifiData data_;
104  bool got_data_;
105
106  DISALLOW_COPY_AND_ASSIGN(MockWifiDataProvider);
107};
108
109MockWifiDataProvider* MockWifiDataProvider::instance_ = NULL;
110
111// Main test fixture
112class GeolocationNetworkProviderTest : public testing::Test {
113 public:
114  virtual void SetUp() {
115    test_server_url_ = GURL(kTestServerUrl);
116    access_token_store_ = new FakeAccessTokenStore;
117    wifi_data_provider_ = MockWifiDataProvider::CreateInstance();
118  }
119
120  virtual void TearDown() { WifiDataProviderManager::ResetFactoryForTesting(); }
121
122  LocationProvider* CreateProvider(bool set_permission_granted) {
123    LocationProvider* provider = NewNetworkLocationProvider(
124        access_token_store_.get(),
125        NULL,  // No URLContextGetter needed, as using test urlfecther factory.
126        test_server_url_,
127        access_token_store_->access_token_set_[test_server_url_]);
128    if (set_permission_granted)
129      provider->OnPermissionGranted();
130    return provider;
131  }
132
133 protected:
134  GeolocationNetworkProviderTest() {
135    // TODO(joth): Really these should be in SetUp, not here, but they take no
136    // effect on Mac OS Release builds if done there. I kid not. Figure out why.
137    WifiDataProviderManager::SetFactoryForTesting(
138        MockWifiDataProvider::GetInstance);
139  }
140
141  // Returns the current url fetcher (if any) and advances the id ready for the
142  // next test step.
143  net::TestURLFetcher* get_url_fetcher_and_advance_id() {
144    net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(
145            NetworkLocationRequest::url_fetcher_id_for_tests);
146    if (fetcher)
147      ++NetworkLocationRequest::url_fetcher_id_for_tests;
148    return fetcher;
149  }
150
151  static int IndexToChannel(int index) { return index + 4; }
152
153  // Creates wifi data containing the specified number of access points, with
154  // some differentiating charactistics in each.
155  static WifiData CreateReferenceWifiScanData(int ap_count) {
156    WifiData data;
157    for (int i = 0; i < ap_count; ++i) {
158      AccessPointData ap;
159      ap.mac_address =
160          base::ASCIIToUTF16(base::StringPrintf("%02d-34-56-78-54-32", i));
161      ap.radio_signal_strength = ap_count - i;
162      ap.channel = IndexToChannel(i);
163      ap.signal_to_noise = i + 42;
164      ap.ssid = base::ASCIIToUTF16("Some nice+network|name\\");
165      data.access_point_data.insert(ap);
166    }
167    return data;
168  }
169
170  static void CreateReferenceWifiScanDataJson(
171      int ap_count, int start_index, base::ListValue* wifi_access_point_list) {
172    std::vector<std::string> wifi_data;
173    for (int i = 0; i < ap_count; ++i) {
174      base::DictionaryValue* ap = new base::DictionaryValue();
175      ap->SetString("macAddress", base::StringPrintf("%02d-34-56-78-54-32", i));
176      ap->SetInteger("signalStrength", start_index + ap_count - i);
177      ap->SetInteger("age", 0);
178      ap->SetInteger("channel", IndexToChannel(i));
179      ap->SetInteger("signalToNoiseRatio", i + 42);
180      wifi_access_point_list->Append(ap);
181    }
182  }
183
184  static Geoposition CreateReferencePosition(int id) {
185    Geoposition pos;
186    pos.latitude = id;
187    pos.longitude = -(id + 1);
188    pos.altitude = 2 * id;
189    pos.timestamp = base::Time::Now();
190    return pos;
191  }
192
193  static std::string PrettyJson(const base::Value& value) {
194    std::string pretty;
195    base::JSONWriter::WriteWithOptions(
196        &value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &pretty);
197    return pretty;
198  }
199
200  static testing::AssertionResult JsonGetList(
201      const std::string& field,
202      const base::DictionaryValue& dict,
203      const base::ListValue** output_list) {
204    if (!dict.GetList(field, output_list))
205      return testing::AssertionFailure() << "Dictionary " << PrettyJson(dict)
206                                         << " is missing list field " << field;
207    return testing::AssertionSuccess();
208  }
209
210  static testing::AssertionResult JsonFieldEquals(
211      const std::string& field,
212      const base::DictionaryValue& expected,
213      const base::DictionaryValue& actual) {
214    const base::Value* expected_value;
215    const base::Value* actual_value;
216    if (!expected.Get(field, &expected_value))
217      return testing::AssertionFailure()
218          << "Expected dictionary " << PrettyJson(expected)
219          << " is missing field " << field;
220    if (!expected.Get(field, &actual_value))
221      return testing::AssertionFailure()
222          << "Actual dictionary " << PrettyJson(actual)
223          << " is missing field " << field;
224    if (!expected_value->Equals(actual_value))
225      return testing::AssertionFailure()
226          << "Field " << field << " mismatch: " << PrettyJson(*expected_value)
227          << " != " << PrettyJson(*actual_value);
228    return testing::AssertionSuccess();
229  }
230
231  static GURL UrlWithoutQuery(const GURL& url) {
232    url::Replacements<char> replacements;
233    replacements.ClearQuery();
234    return url.ReplaceComponents(replacements);
235  }
236
237  testing::AssertionResult IsTestServerUrl(const GURL& request_url) {
238    const GURL a(UrlWithoutQuery(test_server_url_));
239    const GURL b(UrlWithoutQuery(request_url));
240    if (a == b)
241      return testing::AssertionSuccess();
242    return testing::AssertionFailure() << a << " != " << b;
243  }
244
245  void CheckRequestIsValid(const net::TestURLFetcher& request,
246                           int expected_routers,
247                           int expected_wifi_aps,
248                           int wifi_start_index,
249                           const std::string& expected_access_token) {
250    const GURL& request_url = request.GetOriginalURL();
251
252    EXPECT_TRUE(IsTestServerUrl(request_url));
253
254    // Check to see that the api key is being appended for the default
255    // network provider url.
256    bool is_default_url = UrlWithoutQuery(request_url) ==
257        UrlWithoutQuery(LocationArbitratorImpl::DefaultNetworkProviderURL());
258    EXPECT_EQ(is_default_url, !request_url.query().empty());
259
260    const std::string& upload_data = request.upload_data();
261    ASSERT_FALSE(upload_data.empty());
262    std::string json_parse_error_msg;
263    scoped_ptr<base::Value> parsed_json(
264        base::JSONReader::ReadAndReturnError(
265            upload_data,
266            base::JSON_PARSE_RFC,
267            NULL,
268            &json_parse_error_msg));
269    EXPECT_TRUE(json_parse_error_msg.empty());
270    ASSERT_TRUE(parsed_json.get() != NULL);
271
272    const base::DictionaryValue* request_json;
273    ASSERT_TRUE(parsed_json->GetAsDictionary(&request_json));
274
275    if (!is_default_url) {
276      if (expected_access_token.empty())
277        ASSERT_FALSE(request_json->HasKey(kAccessTokenString));
278      else {
279        std::string access_token;
280        EXPECT_TRUE(request_json->GetString(kAccessTokenString, &access_token));
281        EXPECT_EQ(expected_access_token, access_token);
282      }
283    }
284
285    if (expected_wifi_aps) {
286      base::ListValue expected_wifi_aps_json;
287      CreateReferenceWifiScanDataJson(
288          expected_wifi_aps,
289          wifi_start_index,
290          &expected_wifi_aps_json);
291      EXPECT_EQ(size_t(expected_wifi_aps), expected_wifi_aps_json.GetSize());
292
293      const base::ListValue* wifi_aps_json;
294      ASSERT_TRUE(JsonGetList("wifiAccessPoints", *request_json,
295                              &wifi_aps_json));
296      for (size_t i = 0; i < expected_wifi_aps_json.GetSize(); ++i ) {
297        const base::DictionaryValue* expected_json;
298        ASSERT_TRUE(expected_wifi_aps_json.GetDictionary(i, &expected_json));
299        const base::DictionaryValue* actual_json;
300        ASSERT_TRUE(wifi_aps_json->GetDictionary(i, &actual_json));
301        ASSERT_TRUE(JsonFieldEquals("macAddress", *expected_json,
302                                    *actual_json));
303        ASSERT_TRUE(JsonFieldEquals("signalStrength", *expected_json,
304                                    *actual_json));
305        ASSERT_TRUE(JsonFieldEquals("channel", *expected_json, *actual_json));
306        ASSERT_TRUE(JsonFieldEquals("signalToNoiseRatio", *expected_json,
307                                    *actual_json));
308      }
309    } else {
310      ASSERT_FALSE(request_json->HasKey("wifiAccessPoints"));
311    }
312    EXPECT_TRUE(request_url.is_valid());
313  }
314
315  GURL test_server_url_;
316  base::MessageLoop main_message_loop_;
317  scoped_refptr<FakeAccessTokenStore> access_token_store_;
318  net::TestURLFetcherFactory url_fetcher_factory_;
319  scoped_refptr<MockWifiDataProvider> wifi_data_provider_;
320};
321
322TEST_F(GeolocationNetworkProviderTest, CreateDestroy) {
323  // Test fixture members were SetUp correctly.
324  EXPECT_EQ(&main_message_loop_, base::MessageLoop::current());
325  scoped_ptr<LocationProvider> provider(CreateProvider(true));
326  EXPECT_TRUE(NULL != provider.get());
327  provider.reset();
328  SUCCEED();
329}
330
331TEST_F(GeolocationNetworkProviderTest, StartProvider) {
332  scoped_ptr<LocationProvider> provider(CreateProvider(true));
333  EXPECT_TRUE(provider->StartProvider(false));
334  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
335  ASSERT_TRUE(fetcher != NULL);
336  CheckRequestIsValid(*fetcher, 0, 0, 0, std::string());
337}
338
339TEST_F(GeolocationNetworkProviderTest, StartProviderDefaultUrl) {
340  test_server_url_ = LocationArbitratorImpl::DefaultNetworkProviderURL();
341  scoped_ptr<LocationProvider> provider(CreateProvider(true));
342  EXPECT_TRUE(provider->StartProvider(false));
343  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
344  ASSERT_TRUE(fetcher != NULL);
345  CheckRequestIsValid(*fetcher, 0, 0, 0, std::string());
346}
347
348TEST_F(GeolocationNetworkProviderTest, StartProviderLongRequest) {
349  scoped_ptr<LocationProvider> provider(CreateProvider(true));
350  EXPECT_TRUE(provider->StartProvider(false));
351  const int kFirstScanAps = 20;
352  wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
353  main_message_loop_.RunUntilIdle();
354  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
355  ASSERT_TRUE(fetcher != NULL);
356  // The request url should have been shortened to less than 2048 characters
357  // in length by not including access points with the lowest signal strength
358  // in the request.
359  EXPECT_LT(fetcher->GetOriginalURL().spec().size(), size_t(2048));
360  CheckRequestIsValid(*fetcher, 0, 16, 4, std::string());
361}
362
363TEST_F(GeolocationNetworkProviderTest, MultipleWifiScansComplete) {
364  scoped_ptr<LocationProvider> provider(CreateProvider(true));
365  EXPECT_TRUE(provider->StartProvider(false));
366
367  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
368  ASSERT_TRUE(fetcher != NULL);
369  EXPECT_TRUE(IsTestServerUrl(fetcher->GetOriginalURL()));
370
371  // Complete the network request with bad position fix.
372  const char* kNoFixNetworkResponse =
373      "{"
374      "  \"status\": \"ZERO_RESULTS\""
375      "}";
376  fetcher->set_url(test_server_url_);
377  fetcher->set_status(net::URLRequestStatus());
378  fetcher->set_response_code(200);  // OK
379  fetcher->SetResponseString(kNoFixNetworkResponse);
380  fetcher->delegate()->OnURLFetchComplete(fetcher);
381
382  Geoposition position;
383  provider->GetPosition(&position);
384  EXPECT_FALSE(position.Validate());
385
386  // Now wifi data arrives -- SetData will notify listeners.
387  const int kFirstScanAps = 6;
388  wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
389  main_message_loop_.RunUntilIdle();
390  fetcher = get_url_fetcher_and_advance_id();
391  ASSERT_TRUE(fetcher != NULL);
392  // The request should have the wifi data.
393  CheckRequestIsValid(*fetcher, 0, kFirstScanAps, 0, std::string());
394
395  // Send a reply with good position fix.
396  const char* kReferenceNetworkResponse =
397      "{"
398      "  \"accessToken\": \"" REFERENCE_ACCESS_TOKEN "\","
399      "  \"accuracy\": 1200.4,"
400      "  \"location\": {"
401      "    \"lat\": 51.0,"
402      "    \"lng\": -0.1"
403      "  }"
404      "}";
405  fetcher->set_url(test_server_url_);
406  fetcher->set_status(net::URLRequestStatus());
407  fetcher->set_response_code(200);  // OK
408  fetcher->SetResponseString(kReferenceNetworkResponse);
409  fetcher->delegate()->OnURLFetchComplete(fetcher);
410
411  provider->GetPosition(&position);
412  EXPECT_EQ(51.0, position.latitude);
413  EXPECT_EQ(-0.1, position.longitude);
414  EXPECT_EQ(1200.4, position.accuracy);
415  EXPECT_FALSE(position.timestamp.is_null());
416  EXPECT_TRUE(position.Validate());
417
418  // Token should be in the store.
419  EXPECT_EQ(base::UTF8ToUTF16(REFERENCE_ACCESS_TOKEN),
420            access_token_store_->access_token_set_[test_server_url_]);
421
422  // Wifi updated again, with one less AP. This is 'close enough' to the
423  // previous scan, so no new request made.
424  const int kSecondScanAps = kFirstScanAps - 1;
425  wifi_data_provider_->SetData(CreateReferenceWifiScanData(kSecondScanAps));
426  main_message_loop_.RunUntilIdle();
427  fetcher = get_url_fetcher_and_advance_id();
428  EXPECT_FALSE(fetcher);
429
430  provider->GetPosition(&position);
431  EXPECT_EQ(51.0, position.latitude);
432  EXPECT_EQ(-0.1, position.longitude);
433  EXPECT_TRUE(position.Validate());
434
435  // Now a third scan with more than twice the original amount -> new request.
436  const int kThirdScanAps = kFirstScanAps * 2 + 1;
437  wifi_data_provider_->SetData(CreateReferenceWifiScanData(kThirdScanAps));
438  main_message_loop_.RunUntilIdle();
439  fetcher = get_url_fetcher_and_advance_id();
440  EXPECT_TRUE(fetcher);
441  CheckRequestIsValid(*fetcher, 0, kThirdScanAps, 0, REFERENCE_ACCESS_TOKEN);
442  // ...reply with a network error.
443
444  fetcher->set_url(test_server_url_);
445  fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED, -1));
446  fetcher->set_response_code(200);  // should be ignored
447  fetcher->SetResponseString(std::string());
448  fetcher->delegate()->OnURLFetchComplete(fetcher);
449
450  // Error means we now no longer have a fix.
451  provider->GetPosition(&position);
452  EXPECT_FALSE(position.Validate());
453
454  // Wifi scan returns to original set: should be serviced from cache.
455  wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
456  main_message_loop_.RunUntilIdle();
457  EXPECT_FALSE(get_url_fetcher_and_advance_id());  // No new request created.
458
459  provider->GetPosition(&position);
460  EXPECT_EQ(51.0, position.latitude);
461  EXPECT_EQ(-0.1, position.longitude);
462  EXPECT_TRUE(position.Validate());
463}
464
465TEST_F(GeolocationNetworkProviderTest, NoRequestOnStartupUntilWifiData) {
466  MessageLoopQuitListener listener;
467  wifi_data_provider_->set_got_data(false);
468  scoped_ptr<LocationProvider> provider(CreateProvider(true));
469  EXPECT_TRUE(provider->StartProvider(false));
470
471  provider->SetUpdateCallback(base::Bind(
472      &MessageLoopQuitListener::OnLocationUpdate, base::Unretained(&listener)));
473
474  main_message_loop_.RunUntilIdle();
475  EXPECT_FALSE(get_url_fetcher_and_advance_id())
476      << "Network request should not be created right away on startup when "
477         "wifi data has not yet arrived";
478
479  wifi_data_provider_->SetData(CreateReferenceWifiScanData(1));
480  main_message_loop_.RunUntilIdle();
481  EXPECT_TRUE(get_url_fetcher_and_advance_id());
482}
483
484TEST_F(GeolocationNetworkProviderTest, NewDataReplacesExistingNetworkRequest) {
485  // Send initial request with empty data
486  scoped_ptr<LocationProvider> provider(CreateProvider(true));
487  EXPECT_TRUE(provider->StartProvider(false));
488  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
489  EXPECT_TRUE(fetcher);
490
491  // Now wifi data arrives; new request should be sent.
492  wifi_data_provider_->SetData(CreateReferenceWifiScanData(4));
493  main_message_loop_.RunUntilIdle();
494  fetcher = get_url_fetcher_and_advance_id();
495  EXPECT_TRUE(fetcher);
496}
497
498TEST_F(GeolocationNetworkProviderTest, NetworkRequestDeferredForPermission) {
499  scoped_ptr<LocationProvider> provider(CreateProvider(false));
500  EXPECT_TRUE(provider->StartProvider(false));
501  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
502  EXPECT_FALSE(fetcher);
503  provider->OnPermissionGranted();
504
505  fetcher = get_url_fetcher_and_advance_id();
506  ASSERT_TRUE(fetcher != NULL);
507
508  EXPECT_TRUE(IsTestServerUrl(fetcher->GetOriginalURL()));
509}
510
511TEST_F(GeolocationNetworkProviderTest,
512       NetworkRequestWithWifiDataDeferredForPermission) {
513  access_token_store_->access_token_set_[test_server_url_] =
514      base::UTF8ToUTF16(REFERENCE_ACCESS_TOKEN);
515  scoped_ptr<LocationProvider> provider(CreateProvider(false));
516  EXPECT_TRUE(provider->StartProvider(false));
517  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
518  EXPECT_FALSE(fetcher);
519
520  static const int kScanCount = 4;
521  wifi_data_provider_->SetData(CreateReferenceWifiScanData(kScanCount));
522  main_message_loop_.RunUntilIdle();
523
524  fetcher = get_url_fetcher_and_advance_id();
525  EXPECT_FALSE(fetcher);
526
527  provider->OnPermissionGranted();
528
529  fetcher = get_url_fetcher_and_advance_id();
530  ASSERT_TRUE(fetcher != NULL);
531
532  CheckRequestIsValid(*fetcher, 0, kScanCount, 0, REFERENCE_ACCESS_TOKEN);
533}
534
535TEST_F(GeolocationNetworkProviderTest, NetworkPositionCache) {
536  NetworkLocationProvider::PositionCache cache;
537
538  const int kCacheSize = NetworkLocationProvider::PositionCache::kMaximumSize;
539  for (int i = 1; i < kCacheSize * 2 + 1; ++i) {
540    Geoposition pos = CreateReferencePosition(i);
541    bool ret = cache.CachePosition(CreateReferenceWifiScanData(i), pos);
542    EXPECT_TRUE(ret)  << i;
543    const Geoposition* item =
544        cache.FindPosition(CreateReferenceWifiScanData(i));
545    ASSERT_TRUE(item) << i;
546    EXPECT_EQ(pos.latitude, item->latitude)  << i;
547    EXPECT_EQ(pos.longitude, item->longitude)  << i;
548    if (i <= kCacheSize) {
549      // Nothing should have spilled yet; check oldest item is still there.
550      EXPECT_TRUE(cache.FindPosition(CreateReferenceWifiScanData(1)));
551    } else {
552      const int evicted = i - kCacheSize;
553      EXPECT_FALSE(cache.FindPosition(CreateReferenceWifiScanData(evicted)));
554      EXPECT_TRUE(cache.FindPosition(CreateReferenceWifiScanData(evicted + 1)));
555    }
556  }
557}
558
559}  // namespace content
560