Home | History | Annotate | Download | only in geolocation
      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/bind.h"
      6 #include "base/memory/scoped_ptr.h"
      7 #include "content/browser/geolocation/fake_access_token_store.h"
      8 #include "content/browser/geolocation/location_arbitrator_impl.h"
      9 #include "content/browser/geolocation/mock_location_provider.h"
     10 #include "content/public/common/geoposition.h"
     11 #include "testing/gmock/include/gmock/gmock.h"
     12 #include "testing/gtest/include/gtest/gtest.h"
     13 
     14 using ::testing::NiceMock;
     15 
     16 namespace content {
     17 
     18 class MockLocationObserver {
     19  public:
     20   // Need a vtable for GMock.
     21   virtual ~MockLocationObserver() {}
     22   void InvalidateLastPosition() {
     23     last_position_.latitude = 100;
     24     last_position_.error_code = Geoposition::ERROR_CODE_NONE;
     25     ASSERT_FALSE(last_position_.Validate());
     26   }
     27   // Delegate
     28   void OnLocationUpdate(const Geoposition& position) {
     29     last_position_ = position;
     30   }
     31 
     32   Geoposition last_position_;
     33 };
     34 
     35 double g_fake_time_now_secs = 1;
     36 
     37 base::Time GetTimeNowForTest() {
     38   return base::Time::FromDoubleT(g_fake_time_now_secs);
     39 }
     40 
     41 void AdvanceTimeNow(const base::TimeDelta& delta) {
     42   g_fake_time_now_secs += delta.InSecondsF();
     43 }
     44 
     45 void SetPositionFix(MockLocationProvider* provider,
     46                     double latitude,
     47                     double longitude,
     48                     double accuracy) {
     49   Geoposition position;
     50   position.error_code = Geoposition::ERROR_CODE_NONE;
     51   position.latitude = latitude;
     52   position.longitude = longitude;
     53   position.accuracy = accuracy;
     54   position.timestamp = GetTimeNowForTest();
     55   ASSERT_TRUE(position.Validate());
     56   provider->HandlePositionChanged(position);
     57 }
     58 
     59 void SetReferencePosition(MockLocationProvider* provider) {
     60   SetPositionFix(provider, 51.0, -0.1, 400);
     61 }
     62 
     63 namespace {
     64 
     65 class TestingGeolocationArbitrator : public GeolocationArbitratorImpl {
     66  public:
     67   TestingGeolocationArbitrator(
     68       const GeolocationArbitratorImpl::LocationUpdateCallback& callback,
     69       AccessTokenStore* access_token_store)
     70       : GeolocationArbitratorImpl(callback),
     71         cell_(NULL),
     72         gps_(NULL),
     73         access_token_store_(access_token_store) {
     74   }
     75 
     76   virtual base::Time GetTimeNow() const OVERRIDE {
     77     return GetTimeNowForTest();
     78   }
     79 
     80   virtual AccessTokenStore* NewAccessTokenStore() OVERRIDE {
     81     return access_token_store_.get();
     82   }
     83 
     84   virtual LocationProvider* NewNetworkLocationProvider(
     85       AccessTokenStore* access_token_store,
     86       net::URLRequestContextGetter* context,
     87       const GURL& url,
     88       const string16& access_token) OVERRIDE {
     89     return new MockLocationProvider(&cell_);
     90   }
     91 
     92   virtual LocationProvider* NewSystemLocationProvider() OVERRIDE {
     93     return new MockLocationProvider(&gps_);
     94   }
     95 
     96   // Two location providers, with nice short names to make the tests more
     97   // readable. Note |gps_| will only be set when there is a high accuracy
     98   // observer registered (and |cell_| when there's at least one observer of any
     99   // type).
    100   MockLocationProvider* cell_;
    101   MockLocationProvider* gps_;
    102   scoped_refptr<AccessTokenStore> access_token_store_;
    103 };
    104 
    105 }  // namespace
    106 
    107 class GeolocationLocationArbitratorTest : public testing::Test {
    108  protected:
    109   // testing::Test
    110   virtual void SetUp() {
    111     access_token_store_ = new NiceMock<FakeAccessTokenStore>;
    112     observer_.reset(new MockLocationObserver);
    113     GeolocationArbitratorImpl::LocationUpdateCallback callback =
    114         base::Bind(&MockLocationObserver::OnLocationUpdate,
    115                    base::Unretained(observer_.get()));
    116     arbitrator_.reset(new TestingGeolocationArbitrator(
    117         callback, access_token_store_.get()));
    118   }
    119 
    120   // testing::Test
    121   virtual void TearDown() {
    122   }
    123 
    124   void CheckLastPositionInfo(double latitude,
    125                              double longitude,
    126                              double accuracy) {
    127     Geoposition geoposition = observer_->last_position_;
    128     EXPECT_TRUE(geoposition.Validate());
    129     EXPECT_DOUBLE_EQ(latitude, geoposition.latitude);
    130     EXPECT_DOUBLE_EQ(longitude, geoposition.longitude);
    131     EXPECT_DOUBLE_EQ(accuracy, geoposition.accuracy);
    132   }
    133 
    134   base::TimeDelta SwitchOnFreshnessCliff() {
    135     // Add 1, to ensure it meets any greater-than test.
    136     return base::TimeDelta::FromMilliseconds(
    137         GeolocationArbitratorImpl::kFixStaleTimeoutMilliseconds + 1);
    138   }
    139 
    140   MockLocationProvider* cell() {
    141     return arbitrator_->cell_;
    142   }
    143 
    144   MockLocationProvider* gps() {
    145     return arbitrator_->gps_;
    146   }
    147 
    148   scoped_refptr<FakeAccessTokenStore> access_token_store_;
    149   scoped_ptr<MockLocationObserver> observer_;
    150   scoped_ptr<TestingGeolocationArbitrator> arbitrator_;
    151   base::MessageLoop loop_;
    152 };
    153 
    154 TEST_F(GeolocationLocationArbitratorTest, CreateDestroy) {
    155   EXPECT_TRUE(access_token_store_.get());
    156   EXPECT_TRUE(arbitrator_ != NULL);
    157   arbitrator_.reset();
    158   SUCCEED();
    159 }
    160 
    161 TEST_F(GeolocationLocationArbitratorTest, OnPermissionGranted) {
    162   EXPECT_FALSE(arbitrator_->HasPermissionBeenGranted());
    163   arbitrator_->OnPermissionGranted();
    164   EXPECT_TRUE(arbitrator_->HasPermissionBeenGranted());
    165   // Can't check the provider has been notified without going through the
    166   // motions to create the provider (see next test).
    167   EXPECT_FALSE(cell());
    168   EXPECT_FALSE(gps());
    169 }
    170 
    171 TEST_F(GeolocationLocationArbitratorTest, NormalUsage) {
    172   ASSERT_TRUE(access_token_store_.get());
    173   ASSERT_TRUE(arbitrator_ != NULL);
    174 
    175   EXPECT_FALSE(cell());
    176   EXPECT_FALSE(gps());
    177   arbitrator_->StartProviders(false);
    178 
    179   EXPECT_TRUE(access_token_store_->access_token_set_.empty());
    180   EXPECT_TRUE(access_token_store_->access_token_set_.empty());
    181 
    182   access_token_store_->NotifyDelegateTokensLoaded();
    183   ASSERT_TRUE(cell());
    184   EXPECT_TRUE(gps());
    185   EXPECT_EQ(MockLocationProvider::LOW_ACCURACY, cell()->state_);
    186   EXPECT_EQ(MockLocationProvider::LOW_ACCURACY, gps()->state_);
    187   EXPECT_FALSE(observer_->last_position_.Validate());
    188   EXPECT_EQ(Geoposition::ERROR_CODE_NONE,
    189             observer_->last_position_.error_code);
    190 
    191   SetReferencePosition(cell());
    192 
    193   EXPECT_TRUE(observer_->last_position_.Validate() ||
    194               observer_->last_position_.error_code !=
    195                   Geoposition::ERROR_CODE_NONE);
    196   EXPECT_EQ(cell()->position_.latitude,
    197             observer_->last_position_.latitude);
    198 
    199   EXPECT_FALSE(cell()->is_permission_granted_);
    200   EXPECT_FALSE(arbitrator_->HasPermissionBeenGranted());
    201   arbitrator_->OnPermissionGranted();
    202   EXPECT_TRUE(arbitrator_->HasPermissionBeenGranted());
    203   EXPECT_TRUE(cell()->is_permission_granted_);
    204 }
    205 
    206 TEST_F(GeolocationLocationArbitratorTest, SetObserverOptions) {
    207   arbitrator_->StartProviders(false);
    208   access_token_store_->NotifyDelegateTokensLoaded();
    209   ASSERT_TRUE(cell());
    210   ASSERT_TRUE(gps());
    211   EXPECT_EQ(MockLocationProvider::LOW_ACCURACY, cell()->state_);
    212   EXPECT_EQ(MockLocationProvider::LOW_ACCURACY, gps()->state_);
    213   SetReferencePosition(cell());
    214   EXPECT_EQ(MockLocationProvider::LOW_ACCURACY, cell()->state_);
    215   EXPECT_EQ(MockLocationProvider::LOW_ACCURACY, gps()->state_);
    216   arbitrator_->StartProviders(true);
    217   EXPECT_EQ(MockLocationProvider::HIGH_ACCURACY, cell()->state_);
    218   EXPECT_EQ(MockLocationProvider::HIGH_ACCURACY, gps()->state_);
    219 }
    220 
    221 TEST_F(GeolocationLocationArbitratorTest, Arbitration) {
    222   arbitrator_->StartProviders(false);
    223   access_token_store_->NotifyDelegateTokensLoaded();
    224   ASSERT_TRUE(cell());
    225   ASSERT_TRUE(gps());
    226 
    227   SetPositionFix(cell(), 1, 2, 150);
    228 
    229   // First position available
    230   EXPECT_TRUE(observer_->last_position_.Validate());
    231   CheckLastPositionInfo(1, 2, 150);
    232 
    233   SetPositionFix(gps(), 3, 4, 50);
    234 
    235   // More accurate fix available
    236   CheckLastPositionInfo(3, 4, 50);
    237 
    238   SetPositionFix(cell(), 5, 6, 150);
    239 
    240   // New fix is available but it's less accurate, older fix should be kept.
    241   CheckLastPositionInfo(3, 4, 50);
    242 
    243   // Advance time, and notify once again
    244   AdvanceTimeNow(SwitchOnFreshnessCliff());
    245   cell()->HandlePositionChanged(cell()->position_);
    246 
    247   // New fix is available, less accurate but fresher
    248   CheckLastPositionInfo(5, 6, 150);
    249 
    250   // Advance time, and set a low accuracy position
    251   AdvanceTimeNow(SwitchOnFreshnessCliff());
    252   SetPositionFix(cell(), 5.676731, 139.629385, 1000);
    253   CheckLastPositionInfo(5.676731, 139.629385, 1000);
    254 
    255   // 15 secs later, step outside. Switches to gps signal.
    256   AdvanceTimeNow(base::TimeDelta::FromSeconds(15));
    257   SetPositionFix(gps(), 3.5676457, 139.629198, 50);
    258   CheckLastPositionInfo(3.5676457, 139.629198, 50);
    259 
    260   // 5 mins later switch cells while walking. Stay on gps.
    261   AdvanceTimeNow(base::TimeDelta::FromMinutes(5));
    262   SetPositionFix(cell(), 3.567832, 139.634648, 300);
    263   SetPositionFix(gps(), 3.5677675, 139.632314, 50);
    264   CheckLastPositionInfo(3.5677675, 139.632314, 50);
    265 
    266   // Ride train and gps signal degrades slightly. Stay on fresher gps
    267   AdvanceTimeNow(base::TimeDelta::FromMinutes(5));
    268   SetPositionFix(gps(), 3.5679026, 139.634777, 300);
    269   CheckLastPositionInfo(3.5679026, 139.634777, 300);
    270 
    271   // 14 minutes later
    272   AdvanceTimeNow(base::TimeDelta::FromMinutes(14));
    273 
    274   // GPS reading misses a beat, but don't switch to cell yet to avoid
    275   // oscillating.
    276   SetPositionFix(gps(), 3.5659005, 139.682579, 300);
    277 
    278   AdvanceTimeNow(base::TimeDelta::FromSeconds(7));
    279   SetPositionFix(cell(), 3.5689579, 139.691420, 1000);
    280   CheckLastPositionInfo(3.5659005, 139.682579, 300);
    281 
    282   // 1 minute later
    283   AdvanceTimeNow(base::TimeDelta::FromMinutes(1));
    284 
    285   // Enter tunnel. Stay on fresher gps for a moment.
    286   SetPositionFix(cell(), 3.5657078, 139.68922, 300);
    287   SetPositionFix(gps(), 3.5657104, 139.690341, 300);
    288   CheckLastPositionInfo(3.5657104, 139.690341, 300);
    289 
    290   // 2 minutes later
    291   AdvanceTimeNow(base::TimeDelta::FromMinutes(2));
    292   // Arrive in station. Cell moves but GPS is stale. Switch to fresher cell.
    293   SetPositionFix(cell(), 3.5658700, 139.069979, 1000);
    294   CheckLastPositionInfo(3.5658700, 139.069979, 1000);
    295 }
    296 
    297 TEST_F(GeolocationLocationArbitratorTest, TwoOneShotsIsNewPositionBetter) {
    298   arbitrator_->StartProviders(false);
    299   access_token_store_->NotifyDelegateTokensLoaded();
    300   ASSERT_TRUE(cell());
    301   ASSERT_TRUE(gps());
    302 
    303   // Set the initial position.
    304   SetPositionFix(cell(), 3, 139, 100);
    305   CheckLastPositionInfo(3, 139, 100);
    306 
    307   // Restart providers to simulate a one-shot request.
    308   arbitrator_->StopProviders();
    309 
    310   // To test 240956, perform a throwaway alloc.
    311   // This convinces the allocator to put the providers in a new memory location.
    312   MockLocationProvider* fakeMockProvider = NULL;
    313   LocationProvider* fakeProvider =
    314       new MockLocationProvider(&fakeMockProvider);
    315 
    316   arbitrator_->StartProviders(false);
    317   access_token_store_->NotifyDelegateTokensLoaded();
    318 
    319   // Advance the time a short while to simulate successive calls.
    320   AdvanceTimeNow(base::TimeDelta::FromMilliseconds(5));
    321 
    322   // Update with a less accurate position to verify 240956.
    323   SetPositionFix(cell(), 3, 139, 150);
    324   CheckLastPositionInfo(3, 139, 150);
    325 
    326   // No delete required for fakeMockProvider. It points to fakeProvider.
    327   delete fakeProvider;
    328 }
    329 
    330 }  // namespace content
    331