Index: content/browser/geolocation/geolocation_provider_impl_unittest.cc |
diff --git a/content/browser/geolocation/geolocation_provider_impl_unittest.cc b/content/browser/geolocation/geolocation_provider_impl_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..62506f9688be36aeffcedc9a655eeba3b1f4caf9 |
--- /dev/null |
+++ b/content/browser/geolocation/geolocation_provider_impl_unittest.cc |
@@ -0,0 +1,262 @@ |
+// Copyright 2014 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 "content/browser/geolocation/geolocation_provider_impl.h" |
+ |
+#include <memory> |
+ |
+#include "base/bind.h" |
+#include "base/bind_helpers.h" |
+#include "base/location.h" |
+#include "base/macros.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/run_loop.h" |
+#include "base/single_thread_task_runner.h" |
+#include "base/strings/string16.h" |
+#include "base/time/time.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) {} |
+ ~LocationProviderForTestArbitrator() override {} |
+ |
+ // Only valid for use on the geolocation thread. |
+ MockLocationArbitrator* mock_arbitrator() const { |
+ return mock_arbitrator_; |
+ } |
+ |
+ protected: |
+ // GeolocationProviderImpl implementation: |
+ std::unique_ptr<LocationArbitrator> CreateArbitrator() override; |
+ |
+ private: |
+ // An alias to the arbitrator stored in the super class, where it is owned. |
+ MockLocationArbitrator* mock_arbitrator_; |
+}; |
+ |
+std::unique_ptr<LocationArbitrator> |
+LocationProviderForTestArbitrator::CreateArbitrator() { |
+ DCHECK(mock_arbitrator_ == NULL); |
+ mock_arbitrator_ = new MockLocationArbitrator; |
+ return base::WrapUnique(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: |
+ void OnLocationUpdate(const Geoposition& position) override { |
+ MockGeolocationObserver::OnLocationUpdate(position); |
+ base::MessageLoop::current()->QuitWhenIdle(); |
+ } |
+}; |
+ |
+class MockGeolocationCallbackWrapper { |
+ public: |
+ MOCK_METHOD1(Callback, void(const Geoposition& position)); |
+}; |
+ |
+class GeopositionEqMatcher |
+ : public MatcherInterface<const Geoposition&> { |
+ public: |
+ explicit GeopositionEqMatcher(const Geoposition& expected) |
+ : expected_(expected) {} |
+ |
+ 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; |
+ } |
+ |
+ void DescribeTo(::std::ostream* os) const override { |
+ *os << "which matches the expected position"; |
+ } |
+ |
+ 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) { |
+ } |
+ |
+ ~GeolocationProviderTest() override {} |
+ |
+ 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_; |
+ std::unique_ptr<LocationProviderForTestArbitrator> provider_; |
+}; |
+ |
+ |
+bool GeolocationProviderTest::ProvidersStarted() { |
+ DCHECK(provider_->IsRunning()); |
+ DCHECK(base::MessageLoop::current() == &message_loop_); |
+ bool started; |
+ provider_->task_runner()->PostTaskAndReply( |
+ FROM_HERE, base::Bind(&GeolocationProviderTest::GetProvidersStarted, |
+ base::Unretained(this), &started), |
+ base::MessageLoop::QuitWhenIdleClosure()); |
+ base::RunLoop().Run(); |
+ return started; |
+} |
+ |
+void GeolocationProviderTest::GetProvidersStarted(bool* started) { |
+ DCHECK(provider_->task_runner()->BelongsToCurrentThread()); |
+ *started = provider_->mock_arbitrator()->providers_started(); |
+} |
+ |
+void GeolocationProviderTest::SendMockLocation(const Geoposition& position) { |
+ DCHECK(provider_->IsRunning()); |
+ DCHECK(base::MessageLoop::current() == &message_loop_); |
+ provider_->task_runner()->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); |
+ std::unique_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))); |
+ std::unique_ptr<content::GeolocationProvider::Subscription> subscription = |
+ provider()->AddLocationUpdateCallback(first_callback, false); |
+ SendMockLocation(first_position); |
+ base::RunLoop().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)); |
+ std::unique_ptr<content::GeolocationProvider::Subscription> subscription2 = |
+ provider()->AddLocationUpdateCallback(second_callback, false); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ // The second observer should receive the new position now. |
+ EXPECT_CALL(second_observer, |
+ OnLocationUpdate(GeopositionEq(second_position))); |
+ SendMockLocation(second_position); |
+ base::RunLoop().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)); |
+ std::unique_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 |