| // 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.h" |
| #include "base/string16.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/time.h" |
| #include "content/browser/geolocation/arbitrator_dependency_factory.h" |
| #include "content/browser/geolocation/geolocation_provider.h" |
| #include "content/browser/geolocation/location_arbitrator.h" |
| #include "content/browser/geolocation/location_provider.h" |
| #include "content/browser/geolocation/mock_location_provider.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 "googleurl/src/gurl.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using content::Geoposition; |
| using testing::MakeMatcher; |
| using testing::Matcher; |
| using testing::MatcherInterface; |
| using testing::MatchResultListener; |
| |
| namespace { |
| class NonSingletonGeolocationProvider : public GeolocationProvider { |
| public: |
| NonSingletonGeolocationProvider() {} |
| |
| virtual ~NonSingletonGeolocationProvider() {} |
| }; |
| |
| class StartStopMockLocationProvider : public MockLocationProvider { |
| public: |
| StartStopMockLocationProvider(base::WaitableEvent* event) : |
| MockLocationProvider(&instance_), |
| event_(event) { |
| } |
| |
| virtual ~StartStopMockLocationProvider() { |
| event_->Signal(); |
| } |
| |
| private: |
| base::WaitableEvent* event_; |
| }; |
| |
| // The AccessTokenStore will be accessed from the geolocation helper thread. The |
| // existing FakeAccessTokenStore class cannot be used here because it is based |
| // on gmock and gmock is not thread-safe on Windows. |
| // See: http://code.google.com/p/googlemock/issues/detail?id=156 |
| class TestingAccessTokenStore : public content::AccessTokenStore { |
| public: |
| TestingAccessTokenStore(base::WaitableEvent* event) : event_(event) {} |
| |
| virtual void LoadAccessTokens(const LoadAccessTokensCallbackType& callback) |
| OVERRIDE { |
| callback.Run(AccessTokenSet(), NULL); |
| event_->Signal(); |
| } |
| |
| virtual void SaveAccessToken(const GURL& server_url, |
| const string16& access_token) OVERRIDE {} |
| |
| protected: |
| virtual ~TestingAccessTokenStore() {} |
| |
| private: |
| base::WaitableEvent* event_; |
| }; |
| |
| class TestingDependencyFactory |
| : public DefaultGeolocationArbitratorDependencyFactory { |
| public: |
| TestingDependencyFactory(base::WaitableEvent* event) : event_(event) {} |
| |
| virtual content::AccessTokenStore* NewAccessTokenStore() OVERRIDE { |
| return new TestingAccessTokenStore(event_); |
| } |
| |
| virtual LocationProviderBase* NewNetworkLocationProvider( |
| content::AccessTokenStore* access_token_store, |
| net::URLRequestContextGetter* context, |
| const GURL& url, |
| const string16& access_token) OVERRIDE { |
| return new StartStopMockLocationProvider(event_); |
| } |
| |
| virtual LocationProviderBase* NewSystemLocationProvider() OVERRIDE { |
| return NULL; |
| } |
| |
| protected: |
| virtual ~TestingDependencyFactory() {} |
| |
| private: |
| base::WaitableEvent* event_; |
| }; |
| |
| class NullGeolocationObserver : public GeolocationObserver { |
| public: |
| // GeolocationObserver |
| virtual void OnLocationUpdate(const content::Geoposition& position) {} |
| }; |
| |
| class MockGeolocationObserver : public GeolocationObserver { |
| public: |
| // GeolocationObserver |
| MOCK_METHOD1(OnLocationUpdate, void(const content::Geoposition& position)); |
| }; |
| |
| class MockGeolocationCallbackWrapper { |
| public: |
| MOCK_METHOD1(Callback, void(const content::Geoposition& position)); |
| }; |
| |
| class GeopositionEqMatcher |
| : public MatcherInterface<const content::Geoposition&> { |
| public: |
| explicit GeopositionEqMatcher(const content::Geoposition& expected) |
| : expected_(expected) {} |
| |
| virtual bool MatchAndExplain(const content::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: |
| content::Geoposition expected_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GeopositionEqMatcher); |
| }; |
| |
| Matcher<const content::Geoposition&> GeopositionEq( |
| const content::Geoposition& expected) { |
| return MakeMatcher(new GeopositionEqMatcher(expected)); |
| } |
| |
| class GeolocationProviderTest : public testing::Test { |
| protected: |
| GeolocationProviderTest() |
| : message_loop_(), |
| io_thread_(content::BrowserThread::IO, &message_loop_), |
| event_(false, false), |
| dependency_factory_(new TestingDependencyFactory(&event_)), |
| provider_(new NonSingletonGeolocationProvider) { |
| GeolocationArbitrator::SetDependencyFactoryForTest( |
| dependency_factory_.get()); |
| } |
| |
| ~GeolocationProviderTest() { |
| GeolocationArbitrator::SetDependencyFactoryForTest(NULL); |
| } |
| |
| void WaitAndReset() { |
| event_.Wait(); |
| event_.Reset(); |
| } |
| |
| MessageLoop message_loop_; |
| content::TestBrowserThread io_thread_; |
| |
| base::WaitableEvent event_; |
| scoped_refptr<TestingDependencyFactory> dependency_factory_; |
| scoped_ptr<NonSingletonGeolocationProvider> provider_; |
| }; |
| |
| // Regression test for http://crbug.com/59377 |
| TEST_F(GeolocationProviderTest, OnPermissionGrantedWithoutObservers) { |
| EXPECT_FALSE(provider_->HasPermissionBeenGranted()); |
| provider_->OnPermissionGranted(); |
| EXPECT_TRUE(provider_->HasPermissionBeenGranted()); |
| } |
| |
| TEST_F(GeolocationProviderTest, StartStop) { |
| EXPECT_FALSE(provider_->IsRunning()); |
| NullGeolocationObserver null_observer; |
| GeolocationObserverOptions options; |
| provider_->AddObserver(&null_observer, options); |
| EXPECT_TRUE(provider_->IsRunning()); |
| // Wait for token load request from the arbitrator to come through. |
| WaitAndReset(); |
| |
| EXPECT_EQ(MockLocationProvider::instance_->state_, |
| MockLocationProvider::LOW_ACCURACY); |
| provider_->RemoveObserver(&null_observer); |
| // Wait for the providers to be stopped now that all clients are gone. |
| WaitAndReset(); |
| EXPECT_TRUE(provider_->IsRunning()); |
| } |
| |
| TEST_F(GeolocationProviderTest, OverrideLocationForTesting) { |
| content::Geoposition position; |
| position.error_code = content::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))); |
| provider_->AddObserver(&mock_observer, GeolocationObserverOptions()); |
| // Wait for token load request from the arbitrator to come through. |
| WaitAndReset(); |
| |
| provider_->RemoveObserver(&mock_observer); |
| // Wait for the providers to be stopped now that all clients are gone. |
| WaitAndReset(); |
| } |
| |
| TEST_F(GeolocationProviderTest, Callback) { |
| MockGeolocationCallbackWrapper callback_wrapper; |
| provider_->RequestCallback( |
| base::Bind(&MockGeolocationCallbackWrapper::Callback, |
| base::Unretained(&callback_wrapper))); |
| // Wait for token load request from the arbitrator to come through. |
| WaitAndReset(); |
| |
| content::Geoposition position; |
| position.latitude = 12; |
| position.longitude = 34; |
| position.accuracy = 56; |
| position.timestamp = base::Time::Now(); |
| EXPECT_CALL(callback_wrapper, Callback(GeopositionEq(position))); |
| provider_->OverrideLocationForTesting(position); |
| // Wait for the providers to be stopped now that all clients are gone. |
| WaitAndReset(); |
| } |
| |
| } // namespace |