123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- // 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 "services/device/geolocation/geolocation_provider_impl.h"
- #include <memory>
- #include <string>
- #include "base/at_exit.h"
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/location.h"
- #include "base/memory/ptr_util.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/ref_counted.h"
- #include "base/run_loop.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/test/task_environment.h"
- #include "base/threading/thread_checker.h"
- #include "base/time/time.h"
- #include "services/device/geolocation/fake_location_provider.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 device {
- namespace {
- class GeolocationObserver {
- public:
- virtual ~GeolocationObserver() = default;
- virtual void OnLocationUpdate(const mojom::Geoposition& position) = 0;
- };
- class MockGeolocationObserver : public GeolocationObserver {
- public:
- MOCK_METHOD1(OnLocationUpdate, void(const mojom::Geoposition& position));
- };
- class AsyncMockGeolocationObserver : public MockGeolocationObserver {
- public:
- void OnLocationUpdate(const mojom::Geoposition& position) override {
- MockGeolocationObserver::OnLocationUpdate(position);
- base::RunLoop::QuitCurrentWhenIdleDeprecated();
- }
- };
- class MockGeolocationCallbackWrapper {
- public:
- MOCK_METHOD1(Callback, void(const mojom::Geoposition& position));
- };
- class GeopositionEqMatcher
- : public MatcherInterface<const mojom::Geoposition&> {
- public:
- explicit GeopositionEqMatcher(const mojom::Geoposition& expected)
- : expected_(expected) {}
- GeopositionEqMatcher(const GeopositionEqMatcher&) = delete;
- GeopositionEqMatcher& operator=(const GeopositionEqMatcher&) = delete;
- bool MatchAndExplain(const mojom::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:
- mojom::Geoposition expected_;
- };
- Matcher<const mojom::Geoposition&> GeopositionEq(
- const mojom::Geoposition& expected) {
- return MakeMatcher(new GeopositionEqMatcher(expected));
- }
- void DummyFunction(const LocationProvider* provider,
- const mojom::Geoposition& position) {}
- } // namespace
- class GeolocationProviderTest : public testing::Test {
- protected:
- GeolocationProviderTest()
- : task_environment_(
- base::test::SingleThreadTaskEnvironment::MainThreadType::UI),
- arbitrator_(new FakeLocationProvider) {
- provider()->SetArbitratorForTesting(base::WrapUnique(arbitrator_.get()));
- }
- GeolocationProviderTest(const GeolocationProviderTest&) = delete;
- GeolocationProviderTest& operator=(const GeolocationProviderTest&) = delete;
- ~GeolocationProviderTest() override = default;
- GeolocationProviderImpl* provider() {
- return GeolocationProviderImpl::GetInstance();
- }
- FakeLocationProvider* arbitrator() { return arbitrator_; }
- // Called on test thread.
- bool ProvidersStarted();
- void SendMockLocation(const mojom::Geoposition& position);
- private:
- // Called on provider thread.
- void GetProvidersStarted();
- // |at_exit| must be initialized before all other variables so that it is
- // available to register with Singletons and can handle tear down when the
- // test completes.
- base::ShadowingAtExitManager at_exit_;
- base::test::SingleThreadTaskEnvironment task_environment_;
- base::ThreadChecker thread_checker_;
- // Owned by the GeolocationProviderImpl class.
- raw_ptr<FakeLocationProvider> arbitrator_;
- // True if |arbitrator_| is started.
- bool is_started_;
- };
- bool GeolocationProviderTest::ProvidersStarted() {
- DCHECK(provider()->IsRunning());
- DCHECK(thread_checker_.CalledOnValidThread());
- base::RunLoop run_loop;
- provider()->task_runner()->PostTaskAndReply(
- FROM_HERE,
- base::BindOnce(&GeolocationProviderTest::GetProvidersStarted,
- base::Unretained(this)),
- run_loop.QuitClosure());
- run_loop.Run();
- return is_started_;
- }
- void GeolocationProviderTest::GetProvidersStarted() {
- DCHECK(provider()->task_runner()->BelongsToCurrentThread());
- is_started_ = arbitrator()->state() != FakeLocationProvider::STOPPED;
- }
- void GeolocationProviderTest::SendMockLocation(
- const mojom::Geoposition& position) {
- DCHECK(provider()->IsRunning());
- DCHECK(thread_checker_.CalledOnValidThread());
- provider()->task_runner()->PostTask(
- FROM_HERE,
- base::BindOnce(&GeolocationProviderImpl::OnLocationUpdate,
- base::Unretained(provider()), arbitrator_, position));
- }
- // Regression test for http://crbug.com/59377
- TEST_F(GeolocationProviderTest, OnPermissionGrantedWithoutObservers) {
- // Clear |provider|'s arbitrator so the default arbitrator can be used.
- provider()->SetArbitratorForTesting(nullptr);
- EXPECT_FALSE(provider()->user_did_opt_into_location_services_for_testing());
- provider()->UserDidOptIntoLocationServices();
- EXPECT_TRUE(provider()->user_did_opt_into_location_services_for_testing());
- provider()->clear_user_did_opt_into_location_services_for_testing();
- }
- TEST_F(GeolocationProviderTest, StartStop) {
- EXPECT_FALSE(provider()->IsRunning());
- base::CallbackListSubscription subscription =
- provider()->AddLocationUpdateCallback(
- base::BindRepeating(&DummyFunction, arbitrator()), false);
- EXPECT_TRUE(provider()->IsRunning());
- EXPECT_TRUE(ProvidersStarted());
- subscription = {};
- EXPECT_FALSE(ProvidersStarted());
- EXPECT_TRUE(provider()->IsRunning());
- }
- TEST_F(GeolocationProviderTest, StalePositionNotSent) {
- mojom::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::BindRepeating(&MockGeolocationObserver::OnLocationUpdate,
- base::Unretained(&first_observer));
- EXPECT_CALL(first_observer, OnLocationUpdate(GeopositionEq(first_position)));
- base::CallbackListSubscription subscription =
- provider()->AddLocationUpdateCallback(first_callback, false);
- SendMockLocation(first_position);
- base::RunLoop().Run();
- subscription = {};
- mojom::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::BindRepeating(&MockGeolocationObserver::OnLocationUpdate,
- base::Unretained(&second_observer));
- base::CallbackListSubscription 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 = {};
- EXPECT_FALSE(ProvidersStarted());
- }
- TEST_F(GeolocationProviderTest, OverrideLocationForTesting) {
- mojom::Geoposition position;
- position.error_code = mojom::Geoposition::ErrorCode::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::BindRepeating(&MockGeolocationObserver::OnLocationUpdate,
- base::Unretained(&mock_observer));
- base::CallbackListSubscription subscription =
- provider()->AddLocationUpdateCallback(callback, false);
- subscription = {};
- // Wait for the providers to be stopped now that all clients are gone.
- EXPECT_FALSE(ProvidersStarted());
- }
- } // namespace device
|