// 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/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