chromium/services/device/geolocation/geolocation_provider_impl_unittest.cc

// Copyright 2014 The Chromium Authors
// 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/barrier_closure.h"
#include "base/functional/bind.h"
#include "base/functional/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/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "services/device/geolocation/fake_location_provider.h"
#include "services/device/public/cpp/device_features.h"
#include "services/device/public/cpp/geolocation/location_system_permission_status.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(OS_LEVEL_GEOLOCATION_PERMISSION_SUPPORTED)
#include "services/device/public/cpp/test/fake_geolocation_system_permission_manager.h"
#endif

namespace device {
namespace {

TestFuture;
LocationSystemPermissionStatus;
MakeMatcher;
Matcher;
MatcherInterface;
MatchResultListener;

std::string kSystemPermissoinDeniedErrorMessage =;

class GeolocationObserver {};

class MockGeolocationObserver : public GeolocationObserver {};

class AsyncMockGeolocationObserver : public MockGeolocationObserver {};

class MockGeolocationCallbackWrapper {};

class GeopositionResultEqMatcher
    : public MatcherInterface<const mojom::GeopositionResult&> {};

Matcher<const mojom::GeopositionResult&> GeopositionResultEq(
    const mojom::GeopositionResult& expected) {}

}  // namespace

class GeolocationProviderTest : public testing::Test {};

void GeolocationProviderTest::SetFakeLocationProviderManager() {}

bool GeolocationProviderTest::ProvidersStarted() {}

void GeolocationProviderTest::GetProvidersStarted() {}

void GeolocationProviderTest::SendMockLocation(
    const mojom::GeopositionResult& result) {}

// Regression test for http://crbug.com/59377
TEST_F(GeolocationProviderTest, OnPermissionGrantedWithoutObservers) {}

TEST_F(GeolocationProviderTest, StartStop) {}

TEST_F(GeolocationProviderTest, StalePositionNotSent) {}

TEST_F(GeolocationProviderTest, OverrideLocationForTesting) {}

namespace {

class MockGeolocationInternalsObserver
    : public mojom::GeolocationInternalsObserver {};

}  // namespace

TEST_F(GeolocationProviderTest, InitializeWhileObservingDiagnostics) {}

TEST_F(GeolocationProviderTest, MultipleDiagnosticsObservers) {}

TEST_F(GeolocationProviderTest, DiagnosticsObserverDisabled) {}

#if BUILDFLAG(OS_LEVEL_GEOLOCATION_PERMISSION_SUPPORTED)
TEST_F(GeolocationProviderTest, StartProviderAfterSystemPermissionGranted) {
  SetFakeLocationProviderManager();

  // The default system permission state is kUndetermined. Adding a location
  // observer should not start provider and observer's callback should not be
  // called.
  MockGeolocationObserver mock_observer;
  EXPECT_CALL(mock_observer, OnLocationUpdate).Times(0);
  GeolocationProviderImpl::LocationUpdateCallback callback =
      base::BindRepeating(&MockGeolocationObserver::OnLocationUpdate,
                          base::Unretained(&mock_observer));
  base::CallbackListSubscription subscription =
      provider()->AddLocationUpdateCallback(callback,
                                            /*enable_high_accuracy=*/true);

  // Verify that the provider hasn't started yet due to permission is not
  // granted.
  EXPECT_FALSE(ProvidersStarted());

  // Simulate system permission being granted. Provider should now be active.
  SetSystemPermission(LocationSystemPermissionStatus::kAllowed);
  EXPECT_TRUE(ProvidersStarted());

  TestFuture<mojom::GeopositionResultPtr> future;
  EXPECT_CALL(mock_observer, OnLocationUpdate)
      .WillOnce([&](const mojom::GeopositionResult& result) {
        future.SetValue(result.Clone());
      });

  // Trigger a location update with the sample data.
  SendMockLocation(*position_result1_);

  // Verify that the mock observer received the correct update.
  EXPECT_EQ(future.Get()->get_position(), position_result1_->get_position());

  subscription = {};
  EXPECT_FALSE(ProvidersStarted());
}

TEST_F(GeolocationProviderTest, AddCallbackWhenSystemPermissionDenied) {
  SetFakeLocationProviderManager();

  // Set system permission state from kUndetermined to kDenied.
  SetSystemPermission(LocationSystemPermissionStatus::kDenied);

  MockGeolocationObserver mock_observer;
  TestFuture<mojom::GeopositionResultPtr> future;

  // Expect that the observer should be notified with permission denied error
  // when subscription is created.
  EXPECT_CALL(mock_observer, OnLocationUpdate)
      .WillOnce([&](const mojom::GeopositionResult& result) {
        future.SetValue(result.Clone());
      });

  GeolocationProviderImpl::LocationUpdateCallback callback =
      base::BindRepeating(&MockGeolocationObserver::OnLocationUpdate,
                          base::Unretained(&mock_observer));
  base::CallbackListSubscription subscription =
      provider()->AddLocationUpdateCallback(callback,
                                            /*enable_high_accuracy=*/true);

  // Verify that callback should be invoked with permission denied error and
  // provider is not started.
  EXPECT_EQ(future.Take()->get_error(), error_result_->get_error());
  EXPECT_FALSE(ProvidersStarted());
}

TEST_F(GeolocationProviderTest,
       ReportPermissionDeniedOnSystemPermissionDenied) {
  SetFakeLocationProviderManager();

  // Set system permission state from kUndetermined to kAllowed.
  SetSystemPermission(LocationSystemPermissionStatus::kAllowed);

  MockGeolocationObserver mock_observer;
  GeolocationProviderImpl::LocationUpdateCallback callback =
      base::BindRepeating(&MockGeolocationObserver::OnLocationUpdate,
                          base::Unretained(&mock_observer));
  base::CallbackListSubscription subscription =
      provider()->AddLocationUpdateCallback(callback,
                                            /*enable_high_accuracy=*/true);

  // Verify that provider is started when subscription is created when system
  // permission is granted.
  EXPECT_TRUE(ProvidersStarted());

  TestFuture<mojom::GeopositionResultPtr> position_future;
  EXPECT_CALL(mock_observer, OnLocationUpdate)
      .WillOnce([&](const mojom::GeopositionResult& result) {
        position_future.SetValue(result.Clone());
      });

  // Simulate a location update and expect that position result to be equal.
  SendMockLocation(*position_result1_);
  EXPECT_EQ(position_future.Get()->get_position(),
            position_result1_->get_position());

  TestFuture<mojom::GeopositionResultPtr> error_future;

  // Set system permission state from kAllowed to kDenied. Expect that callback
  // is invoked with permission denied error.
  EXPECT_CALL(mock_observer, OnLocationUpdate)
      .WillOnce([&](const mojom::GeopositionResult& result) {
        error_future.SetValue(result.Clone());
      });
  SetSystemPermission(LocationSystemPermissionStatus::kDenied);
  EXPECT_EQ(error_future.Get()->get_error(), error_result_->get_error());

  // Clear subscription and expect that provider is stopped.
  subscription = {};
  EXPECT_FALSE(ProvidersStarted());
}

TEST_F(GeolocationProviderTest,
       SystemPermissionAllowedAfterSystemPermissionDenied) {
  SetFakeLocationProviderManager();

  // Set system permission state from kUndetermined to kDenied.
  SetSystemPermission(LocationSystemPermissionStatus::kDenied);

  TestFuture<mojom::GeopositionResultPtr> error_future;

  // Create 1st observer and expected the callback1 is invoked with permission
  // denied error when system permission is denied.
  MockGeolocationObserver mock_observer1;
  GeolocationProviderImpl::LocationUpdateCallback callback1 =
      base::BindRepeating(&MockGeolocationObserver::OnLocationUpdate,
                          base::Unretained(&mock_observer1));
  EXPECT_CALL(mock_observer1, OnLocationUpdate)
      .WillOnce([&](const mojom::GeopositionResult& result) {
        error_future.SetValue(result.Clone());
      });

  base::CallbackListSubscription subscription1 =
      provider()->AddLocationUpdateCallback(callback1,
                                            /*enable_high_accuracy=*/true);
  EXPECT_EQ(error_future.Get()->get_error(), error_result_->get_error());
  subscription1 = {};
  EXPECT_FALSE(ProvidersStarted());

  // Set system permission state from kDenied to kAllowed.
  SetSystemPermission(LocationSystemPermissionStatus::kAllowed);

  // Created 2nd observer and subscription after system permission is set to
  // kAllowed.
  MockGeolocationObserver mock_observer2;
  GeolocationProviderImpl::LocationUpdateCallback callback2 =
      base::BindRepeating(&MockGeolocationObserver::OnLocationUpdate,
                          base::Unretained(&mock_observer2));
  base::CallbackListSubscription subscription2 =
      provider()->AddLocationUpdateCallback(callback2,
                                            /*enable_high_accuracy=*/true);

  // Verify that provider is started when subscription2 is created when system
  // permission is granted.
  EXPECT_TRUE(ProvidersStarted());

  TestFuture<mojom::GeopositionResultPtr> position_future;
  EXPECT_CALL(mock_observer2, OnLocationUpdate)
      .WillOnce([&](const mojom::GeopositionResult& result) {
        position_future.SetValue(result.Clone());
      });

  // Simulate a location update and expect that position result to be equal.
  SendMockLocation(*position_result1_);
  EXPECT_EQ(position_future.Get()->get_position(),
            position_result1_->get_position());

  subscription2 = {};
  EXPECT_FALSE(ProvidersStarted());
}
#endif  // BUILDFLAG(IS_APPLE) ||
        // BUILDFLAG(OS_LEVEL_GEOLOCATION_PERMISSION_SUPPORTED)

}  // namespace device