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/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 MockGeolocationArbitrator* mock_arbitrator() const { 34 return mock_arbitrator_; 35 } 36 37 protected: 38 // GeolocationProviderImpl implementation: 39 virtual GeolocationArbitrator* CreateArbitrator() OVERRIDE; 40 41 private: 42 MockGeolocationArbitrator* mock_arbitrator_; 43 }; 44 45 GeolocationArbitrator* LocationProviderForTestArbitrator::CreateArbitrator() { 46 DCHECK(mock_arbitrator_ == NULL); 47 mock_arbitrator_ = new MockGeolocationArbitrator; 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 io_thread_(BrowserThread::IO, &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 io_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()->LocationServicesOptedIn()); 171 provider()->UserDidOptIntoLocationServices(); 172 EXPECT_TRUE(provider()->LocationServicesOptedIn()); 173 } 174 175 TEST_F(GeolocationProviderTest, StartStop) { 176 EXPECT_FALSE(provider()->IsRunning()); 177 GeolocationProviderImpl::LocationUpdateCallback null_callback; 178 provider()->AddLocationUpdateCallback(null_callback, false); 179 EXPECT_TRUE(provider()->IsRunning()); 180 EXPECT_TRUE(ProvidersStarted()); 181 182 provider()->RemoveLocationUpdateCallback(null_callback); 183 EXPECT_FALSE(ProvidersStarted()); 184 EXPECT_TRUE(provider()->IsRunning()); 185 } 186 187 TEST_F(GeolocationProviderTest, StalePositionNotSent) { 188 Geoposition first_position; 189 first_position.latitude = 12; 190 first_position.longitude = 34; 191 first_position.accuracy = 56; 192 first_position.timestamp = base::Time::Now(); 193 194 AsyncMockGeolocationObserver first_observer; 195 GeolocationProviderImpl::LocationUpdateCallback first_callback = base::Bind( 196 &MockGeolocationObserver::OnLocationUpdate, 197 base::Unretained(&first_observer)); 198 EXPECT_CALL(first_observer, OnLocationUpdate(GeopositionEq(first_position))); 199 provider()->AddLocationUpdateCallback(first_callback, false); 200 SendMockLocation(first_position); 201 base::MessageLoop::current()->Run(); 202 203 provider()->RemoveLocationUpdateCallback(first_callback); 204 205 Geoposition second_position; 206 second_position.latitude = 13; 207 second_position.longitude = 34; 208 second_position.accuracy = 56; 209 second_position.timestamp = base::Time::Now(); 210 211 AsyncMockGeolocationObserver second_observer; 212 213 // After adding a second observer, check that no unexpected position update 214 // is sent. 215 EXPECT_CALL(second_observer, OnLocationUpdate(testing::_)).Times(0); 216 GeolocationProviderImpl::LocationUpdateCallback second_callback = base::Bind( 217 &MockGeolocationObserver::OnLocationUpdate, 218 base::Unretained(&second_observer)); 219 provider()->AddLocationUpdateCallback(second_callback, false); 220 base::MessageLoop::current()->RunUntilIdle(); 221 222 // The second observer should receive the new position now. 223 EXPECT_CALL(second_observer, 224 OnLocationUpdate(GeopositionEq(second_position))); 225 SendMockLocation(second_position); 226 base::MessageLoop::current()->Run(); 227 228 provider()->RemoveLocationUpdateCallback(second_callback); 229 EXPECT_FALSE(ProvidersStarted()); 230 } 231 232 TEST_F(GeolocationProviderTest, OverrideLocationForTesting) { 233 Geoposition position; 234 position.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; 235 provider()->OverrideLocationForTesting(position); 236 // Adding an observer when the location is overridden should synchronously 237 // update the observer with our overridden position. 238 MockGeolocationObserver mock_observer; 239 EXPECT_CALL(mock_observer, OnLocationUpdate(GeopositionEq(position))); 240 GeolocationProviderImpl::LocationUpdateCallback callback = base::Bind( 241 &MockGeolocationObserver::OnLocationUpdate, 242 base::Unretained(&mock_observer)); 243 provider()->AddLocationUpdateCallback(callback, false); 244 provider()->RemoveLocationUpdateCallback(callback); 245 // Wait for the providers to be stopped now that all clients are gone. 246 EXPECT_FALSE(ProvidersStarted()); 247 } 248 249 } // namespace content 250