| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/strings/string16.h" |
| #include "base/time/time.h" |
| #include "content/browser/geolocation/geolocation_provider_impl.h" |
| #include "content/browser/geolocation/mock_location_arbitrator.h" |
| #include "content/public/browser/access_token_store.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/test/test_browser_thread.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using testing::MakeMatcher; |
| using testing::Matcher; |
| using testing::MatcherInterface; |
| using testing::MatchResultListener; |
| |
| namespace content { |
| |
| class LocationProviderForTestArbitrator : public GeolocationProviderImpl { |
| public: |
| LocationProviderForTestArbitrator() : mock_arbitrator_(NULL) {} |
| virtual ~LocationProviderForTestArbitrator() {} |
| |
| // Only valid for use on the geolocation thread. |
| MockLocationArbitrator* mock_arbitrator() const { |
| return mock_arbitrator_; |
| } |
| |
| protected: |
| // GeolocationProviderImpl implementation: |
| virtual LocationArbitrator* CreateArbitrator() OVERRIDE; |
| |
| private: |
| MockLocationArbitrator* mock_arbitrator_; |
| }; |
| |
| LocationArbitrator* LocationProviderForTestArbitrator::CreateArbitrator() { |
| DCHECK(mock_arbitrator_ == NULL); |
| mock_arbitrator_ = new MockLocationArbitrator; |
| return mock_arbitrator_; |
| } |
| |
| class GeolocationObserver { |
| public: |
| virtual ~GeolocationObserver() {} |
| virtual void OnLocationUpdate(const Geoposition& position) = 0; |
| }; |
| |
| class MockGeolocationObserver : public GeolocationObserver { |
| public: |
| MOCK_METHOD1(OnLocationUpdate, void(const Geoposition& position)); |
| }; |
| |
| class AsyncMockGeolocationObserver : public MockGeolocationObserver { |
| public: |
| virtual void OnLocationUpdate(const Geoposition& position) OVERRIDE { |
| MockGeolocationObserver::OnLocationUpdate(position); |
| base::MessageLoop::current()->Quit(); |
| } |
| }; |
| |
| class MockGeolocationCallbackWrapper { |
| public: |
| MOCK_METHOD1(Callback, void(const Geoposition& position)); |
| }; |
| |
| class GeopositionEqMatcher |
| : public MatcherInterface<const Geoposition&> { |
| public: |
| explicit GeopositionEqMatcher(const Geoposition& expected) |
| : expected_(expected) {} |
| |
| virtual bool MatchAndExplain(const Geoposition& actual, |
| MatchResultListener* listener) const OVERRIDE { |
| return actual.latitude == expected_.latitude && |
| actual.longitude == expected_.longitude && |
| actual.altitude == expected_.altitude && |
| actual.accuracy == expected_.accuracy && |
| actual.altitude_accuracy == expected_.altitude_accuracy && |
| actual.heading == expected_.heading && |
| actual.speed == expected_.speed && |
| actual.timestamp == expected_.timestamp && |
| actual.error_code == expected_.error_code && |
| actual.error_message == expected_.error_message; |
| } |
| |
| virtual void DescribeTo(::std::ostream* os) const OVERRIDE { |
| *os << "which matches the expected position"; |
| } |
| |
| virtual void DescribeNegationTo(::std::ostream* os) const OVERRIDE { |
| *os << "which does not match the expected position"; |
| } |
| |
| private: |
| Geoposition expected_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GeopositionEqMatcher); |
| }; |
| |
| Matcher<const Geoposition&> GeopositionEq(const Geoposition& expected) { |
| return MakeMatcher(new GeopositionEqMatcher(expected)); |
| } |
| |
| class GeolocationProviderTest : public testing::Test { |
| protected: |
| GeolocationProviderTest() |
| : message_loop_(), |
| ui_thread_(BrowserThread::UI, &message_loop_), |
| provider_(new LocationProviderForTestArbitrator) { |
| } |
| |
| virtual ~GeolocationProviderTest() {} |
| |
| LocationProviderForTestArbitrator* provider() { return provider_.get(); } |
| |
| // Called on test thread. |
| bool ProvidersStarted(); |
| void SendMockLocation(const Geoposition& position); |
| |
| private: |
| // Called on provider thread. |
| void GetProvidersStarted(bool* started); |
| |
| base::MessageLoop message_loop_; |
| TestBrowserThread ui_thread_; |
| scoped_ptr<LocationProviderForTestArbitrator> provider_; |
| }; |
| |
| |
| bool GeolocationProviderTest::ProvidersStarted() { |
| DCHECK(provider_->IsRunning()); |
| DCHECK(base::MessageLoop::current() == &message_loop_); |
| bool started; |
| provider_->message_loop_proxy()->PostTaskAndReply( |
| FROM_HERE, |
| base::Bind(&GeolocationProviderTest::GetProvidersStarted, |
| base::Unretained(this), |
| &started), |
| base::MessageLoop::QuitClosure()); |
| message_loop_.Run(); |
| return started; |
| } |
| |
| void GeolocationProviderTest::GetProvidersStarted(bool* started) { |
| DCHECK(base::MessageLoop::current() == provider_->message_loop()); |
| *started = provider_->mock_arbitrator()->providers_started(); |
| } |
| |
| void GeolocationProviderTest::SendMockLocation(const Geoposition& position) { |
| DCHECK(provider_->IsRunning()); |
| DCHECK(base::MessageLoop::current() == &message_loop_); |
| provider_->message_loop() |
| ->PostTask(FROM_HERE, |
| base::Bind(&GeolocationProviderImpl::OnLocationUpdate, |
| base::Unretained(provider_.get()), |
| position)); |
| } |
| |
| // Regression test for http://crbug.com/59377 |
| TEST_F(GeolocationProviderTest, OnPermissionGrantedWithoutObservers) { |
| EXPECT_FALSE(provider()->user_did_opt_into_location_services_for_testing()); |
| provider()->UserDidOptIntoLocationServices(); |
| EXPECT_TRUE(provider()->user_did_opt_into_location_services_for_testing()); |
| } |
| |
| void DummyFunction(const Geoposition& position) { |
| } |
| |
| TEST_F(GeolocationProviderTest, StartStop) { |
| EXPECT_FALSE(provider()->IsRunning()); |
| GeolocationProviderImpl::LocationUpdateCallback callback = |
| base::Bind(&DummyFunction); |
| scoped_ptr<content::GeolocationProvider::Subscription> subscription = |
| provider()->AddLocationUpdateCallback(callback, false); |
| EXPECT_TRUE(provider()->IsRunning()); |
| EXPECT_TRUE(ProvidersStarted()); |
| |
| subscription.reset(); |
| |
| EXPECT_FALSE(ProvidersStarted()); |
| EXPECT_TRUE(provider()->IsRunning()); |
| } |
| |
| TEST_F(GeolocationProviderTest, StalePositionNotSent) { |
| Geoposition first_position; |
| first_position.latitude = 12; |
| first_position.longitude = 34; |
| first_position.accuracy = 56; |
| first_position.timestamp = base::Time::Now(); |
| |
| AsyncMockGeolocationObserver first_observer; |
| GeolocationProviderImpl::LocationUpdateCallback first_callback = base::Bind( |
| &MockGeolocationObserver::OnLocationUpdate, |
| base::Unretained(&first_observer)); |
| EXPECT_CALL(first_observer, OnLocationUpdate(GeopositionEq(first_position))); |
| scoped_ptr<content::GeolocationProvider::Subscription> subscription = |
| provider()->AddLocationUpdateCallback(first_callback, false); |
| SendMockLocation(first_position); |
| base::MessageLoop::current()->Run(); |
| |
| subscription.reset(); |
| |
| Geoposition second_position; |
| second_position.latitude = 13; |
| second_position.longitude = 34; |
| second_position.accuracy = 56; |
| second_position.timestamp = base::Time::Now(); |
| |
| AsyncMockGeolocationObserver second_observer; |
| |
| // After adding a second observer, check that no unexpected position update |
| // is sent. |
| EXPECT_CALL(second_observer, OnLocationUpdate(testing::_)).Times(0); |
| GeolocationProviderImpl::LocationUpdateCallback second_callback = base::Bind( |
| &MockGeolocationObserver::OnLocationUpdate, |
| base::Unretained(&second_observer)); |
| scoped_ptr<content::GeolocationProvider::Subscription> subscription2 = |
| provider()->AddLocationUpdateCallback(second_callback, false); |
| base::MessageLoop::current()->RunUntilIdle(); |
| |
| // The second observer should receive the new position now. |
| EXPECT_CALL(second_observer, |
| OnLocationUpdate(GeopositionEq(second_position))); |
| SendMockLocation(second_position); |
| base::MessageLoop::current()->Run(); |
| |
| subscription2.reset(); |
| EXPECT_FALSE(ProvidersStarted()); |
| } |
| |
| TEST_F(GeolocationProviderTest, OverrideLocationForTesting) { |
| Geoposition position; |
| position.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; |
| provider()->OverrideLocationForTesting(position); |
| // Adding an observer when the location is overridden should synchronously |
| // update the observer with our overridden position. |
| MockGeolocationObserver mock_observer; |
| EXPECT_CALL(mock_observer, OnLocationUpdate(GeopositionEq(position))); |
| GeolocationProviderImpl::LocationUpdateCallback callback = base::Bind( |
| &MockGeolocationObserver::OnLocationUpdate, |
| base::Unretained(&mock_observer)); |
| scoped_ptr<content::GeolocationProvider::Subscription> subscription = |
| provider()->AddLocationUpdateCallback(callback, false); |
| subscription.reset(); |
| // Wait for the providers to be stopped now that all clients are gone. |
| EXPECT_FALSE(ProvidersStarted()); |
| } |
| |
| } // namespace content |