Home | History | Annotate | Download | only in geolocation
      1 // Copyright 2014 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/bind.h"
      6 #include "base/bind_helpers.h"
      7 #include "base/memory/ref_counted.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/strings/string16.h"
     11 #include "base/time/time.h"
     12 #include "content/browser/geolocation/geolocation_provider_impl.h"
     13 #include "content/browser/geolocation/mock_location_arbitrator.h"
     14 #include "content/public/browser/access_token_store.h"
     15 #include "content/public/browser/browser_thread.h"
     16 #include "content/public/test/test_browser_thread.h"
     17 #include "testing/gmock/include/gmock/gmock.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 using testing::MakeMatcher;
     21 using testing::Matcher;
     22 using testing::MatcherInterface;
     23 using testing::MatchResultListener;
     24 
     25 namespace content {
     26 
     27 class LocationProviderForTestArbitrator : public GeolocationProviderImpl {
     28  public:
     29   LocationProviderForTestArbitrator() : mock_arbitrator_(NULL) {}
     30   virtual ~LocationProviderForTestArbitrator() {}
     31 
     32   // Only valid for use on the geolocation thread.
     33   MockLocationArbitrator* mock_arbitrator() const {
     34     return mock_arbitrator_;
     35   }
     36 
     37  protected:
     38   // GeolocationProviderImpl implementation:
     39   virtual LocationArbitrator* CreateArbitrator() OVERRIDE;
     40 
     41  private:
     42   MockLocationArbitrator* mock_arbitrator_;
     43 };
     44 
     45 LocationArbitrator* LocationProviderForTestArbitrator::CreateArbitrator() {
     46   DCHECK(mock_arbitrator_ == NULL);
     47   mock_arbitrator_ = new MockLocationArbitrator;
     48   return mock_arbitrator_;
     49 }
     50 
     51 class GeolocationObserver {
     52  public:
     53   virtual ~GeolocationObserver() {}
     54   virtual void OnLocationUpdate(const Geoposition& position) = 0;
     55 };
     56 
     57 class MockGeolocationObserver : public GeolocationObserver {
     58  public:
     59   MOCK_METHOD1(OnLocationUpdate, void(const Geoposition& position));
     60 };
     61 
     62 class AsyncMockGeolocationObserver : public MockGeolocationObserver {
     63  public:
     64   virtual void OnLocationUpdate(const Geoposition& position) OVERRIDE {
     65     MockGeolocationObserver::OnLocationUpdate(position);
     66     base::MessageLoop::current()->Quit();
     67   }
     68 };
     69 
     70 class MockGeolocationCallbackWrapper {
     71  public:
     72   MOCK_METHOD1(Callback, void(const Geoposition& position));
     73 };
     74 
     75 class GeopositionEqMatcher
     76     : public MatcherInterface<const Geoposition&> {
     77  public:
     78   explicit GeopositionEqMatcher(const Geoposition& expected)
     79       : expected_(expected) {}
     80 
     81   virtual bool MatchAndExplain(const Geoposition& actual,
     82                                MatchResultListener* listener) const OVERRIDE {
     83     return actual.latitude == expected_.latitude &&
     84            actual.longitude == expected_.longitude &&
     85            actual.altitude == expected_.altitude &&
     86            actual.accuracy == expected_.accuracy &&
     87            actual.altitude_accuracy == expected_.altitude_accuracy &&
     88            actual.heading == expected_.heading &&
     89            actual.speed == expected_.speed &&
     90            actual.timestamp == expected_.timestamp &&
     91            actual.error_code == expected_.error_code &&
     92            actual.error_message == expected_.error_message;
     93   }
     94 
     95   virtual void DescribeTo(::std::ostream* os) const OVERRIDE {
     96     *os << "which matches the expected position";
     97   }
     98 
     99   virtual void DescribeNegationTo(::std::ostream* os) const OVERRIDE {
    100     *os << "which does not match the expected position";
    101   }
    102 
    103  private:
    104   Geoposition expected_;
    105 
    106   DISALLOW_COPY_AND_ASSIGN(GeopositionEqMatcher);
    107 };
    108 
    109 Matcher<const Geoposition&> GeopositionEq(const Geoposition& expected) {
    110   return MakeMatcher(new GeopositionEqMatcher(expected));
    111 }
    112 
    113 class GeolocationProviderTest : public testing::Test {
    114  protected:
    115   GeolocationProviderTest()
    116       : message_loop_(),
    117         ui_thread_(BrowserThread::UI, &message_loop_),
    118         provider_(new LocationProviderForTestArbitrator) {
    119   }
    120 
    121   virtual ~GeolocationProviderTest() {}
    122 
    123   LocationProviderForTestArbitrator* provider() { return provider_.get(); }
    124 
    125   // Called on test thread.
    126   bool ProvidersStarted();
    127   void SendMockLocation(const Geoposition& position);
    128 
    129  private:
    130   // Called on provider thread.
    131   void GetProvidersStarted(bool* started);
    132 
    133   base::MessageLoop message_loop_;
    134   TestBrowserThread ui_thread_;
    135   scoped_ptr<LocationProviderForTestArbitrator> provider_;
    136 };
    137 
    138 
    139 bool GeolocationProviderTest::ProvidersStarted() {
    140   DCHECK(provider_->IsRunning());
    141   DCHECK(base::MessageLoop::current() == &message_loop_);
    142   bool started;
    143   provider_->message_loop_proxy()->PostTaskAndReply(
    144       FROM_HERE,
    145       base::Bind(&GeolocationProviderTest::GetProvidersStarted,
    146                  base::Unretained(this),
    147                  &started),
    148       base::MessageLoop::QuitClosure());
    149   message_loop_.Run();
    150   return started;
    151 }
    152 
    153 void GeolocationProviderTest::GetProvidersStarted(bool* started) {
    154   DCHECK(base::MessageLoop::current() == provider_->message_loop());
    155   *started = provider_->mock_arbitrator()->providers_started();
    156 }
    157 
    158 void GeolocationProviderTest::SendMockLocation(const Geoposition& position) {
    159   DCHECK(provider_->IsRunning());
    160   DCHECK(base::MessageLoop::current() == &message_loop_);
    161   provider_->message_loop()
    162       ->PostTask(FROM_HERE,
    163                  base::Bind(&GeolocationProviderImpl::OnLocationUpdate,
    164                             base::Unretained(provider_.get()),
    165                             position));
    166 }
    167 
    168 // Regression test for http://crbug.com/59377
    169 TEST_F(GeolocationProviderTest, OnPermissionGrantedWithoutObservers) {
    170   EXPECT_FALSE(provider()->user_did_opt_into_location_services_for_testing());
    171   provider()->UserDidOptIntoLocationServices();
    172   EXPECT_TRUE(provider()->user_did_opt_into_location_services_for_testing());
    173 }
    174 
    175 void DummyFunction(const Geoposition& position) {
    176 }
    177 
    178 TEST_F(GeolocationProviderTest, StartStop) {
    179   EXPECT_FALSE(provider()->IsRunning());
    180   GeolocationProviderImpl::LocationUpdateCallback callback =
    181       base::Bind(&DummyFunction);
    182   scoped_ptr<content::GeolocationProvider::Subscription> subscription =
    183       provider()->AddLocationUpdateCallback(callback, false);
    184   EXPECT_TRUE(provider()->IsRunning());
    185   EXPECT_TRUE(ProvidersStarted());
    186 
    187   subscription.reset();
    188 
    189   EXPECT_FALSE(ProvidersStarted());
    190   EXPECT_TRUE(provider()->IsRunning());
    191 }
    192 
    193 TEST_F(GeolocationProviderTest, StalePositionNotSent) {
    194   Geoposition first_position;
    195   first_position.latitude = 12;
    196   first_position.longitude = 34;
    197   first_position.accuracy = 56;
    198   first_position.timestamp = base::Time::Now();
    199 
    200   AsyncMockGeolocationObserver first_observer;
    201   GeolocationProviderImpl::LocationUpdateCallback first_callback = base::Bind(
    202       &MockGeolocationObserver::OnLocationUpdate,
    203       base::Unretained(&first_observer));
    204   EXPECT_CALL(first_observer, OnLocationUpdate(GeopositionEq(first_position)));
    205   scoped_ptr<content::GeolocationProvider::Subscription> subscription =
    206       provider()->AddLocationUpdateCallback(first_callback, false);
    207   SendMockLocation(first_position);
    208   base::MessageLoop::current()->Run();
    209 
    210   subscription.reset();
    211 
    212   Geoposition second_position;
    213   second_position.latitude = 13;
    214   second_position.longitude = 34;
    215   second_position.accuracy = 56;
    216   second_position.timestamp = base::Time::Now();
    217 
    218   AsyncMockGeolocationObserver second_observer;
    219 
    220   // After adding a second observer, check that no unexpected position update
    221   // is sent.
    222   EXPECT_CALL(second_observer, OnLocationUpdate(testing::_)).Times(0);
    223   GeolocationProviderImpl::LocationUpdateCallback second_callback = base::Bind(
    224       &MockGeolocationObserver::OnLocationUpdate,
    225       base::Unretained(&second_observer));
    226   scoped_ptr<content::GeolocationProvider::Subscription> subscription2 =
    227       provider()->AddLocationUpdateCallback(second_callback, false);
    228   base::MessageLoop::current()->RunUntilIdle();
    229 
    230   // The second observer should receive the new position now.
    231   EXPECT_CALL(second_observer,
    232               OnLocationUpdate(GeopositionEq(second_position)));
    233   SendMockLocation(second_position);
    234   base::MessageLoop::current()->Run();
    235 
    236   subscription2.reset();
    237   EXPECT_FALSE(ProvidersStarted());
    238 }
    239 
    240 TEST_F(GeolocationProviderTest, OverrideLocationForTesting) {
    241   Geoposition position;
    242   position.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
    243   provider()->OverrideLocationForTesting(position);
    244   // Adding an observer when the location is overridden should synchronously
    245   // update the observer with our overridden position.
    246   MockGeolocationObserver mock_observer;
    247   EXPECT_CALL(mock_observer, OnLocationUpdate(GeopositionEq(position)));
    248   GeolocationProviderImpl::LocationUpdateCallback callback = base::Bind(
    249       &MockGeolocationObserver::OnLocationUpdate,
    250       base::Unretained(&mock_observer));
    251   scoped_ptr<content::GeolocationProvider::Subscription> subscription =
    252       provider()->AddLocationUpdateCallback(callback, false);
    253   subscription.reset();
    254   // Wait for the providers to be stopped now that all clients are gone.
    255   EXPECT_FALSE(ProvidersStarted());
    256 }
    257 
    258 }  // namespace content
    259