chromium/services/device/geolocation/core_location_provider_unittest.cc

// Copyright 2020 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/core_location_provider.h"

#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "services/device/public/cpp/geolocation/geoposition.h"
#include "services/device/public/cpp/geolocation/system_geolocation_source.h"
#include "services/device/public/cpp/test/fake_geolocation_system_permission_manager.h"
#include "services/device/public/cpp/test/fake_system_geolocation_source.h"
#include "services/device/public/mojom/geolocation_internals.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace device {

using ::base::test::TestFuture;

class CoreLocationProviderTest : public testing::Test {
 public:
  std::unique_ptr<CoreLocationProvider> provider_;

  void SetUp() override {
    fake_geolocation_system_permission_manager_ =
        std::make_unique<FakeGeolocationSystemPermissionManager>();
    fake_system_geolocation_source_ =
        &fake_geolocation_system_permission_manager_
             ->SystemGeolocationSourceForTest();
    provider_ = std::make_unique<CoreLocationProvider>(
        *fake_system_geolocation_source_);
  }

 protected:
  CoreLocationProviderTest() = default;

  bool IsUpdating() {
    return static_cast<FakeSystemGeolocationSource*>(
               fake_system_geolocation_source_)
        ->watching_position();
  }

  // Updates the position synchronously.
  void FakeUpdatePosition(const mojom::Geoposition& result) {
    static_cast<FakeSystemGeolocationSource*>(fake_system_geolocation_source_)
        ->FakePositionUpdatedForTesting(result);
  }

  // Updates the error synchronously.
  void FakeUpdateError(const mojom::GeopositionError& error) {
    static_cast<FakeSystemGeolocationSource*>(fake_system_geolocation_source_)
        ->FakePositionErrorForTesting(error);
  }

  const mojom::GeopositionResult* GetLatestPosition() {
    return provider_->GetPosition();
  }

  mojom::GeolocationDiagnostics::ProviderState GetProviderState() {
    mojom::GeolocationDiagnostics diagnostics;
    provider_->FillDiagnostics(diagnostics);
    return diagnostics.provider_state;
  }

  base::test::TaskEnvironment task_environment_;
  const LocationProvider::LocationProviderUpdateCallback callback_;
  std::unique_ptr<FakeGeolocationSystemPermissionManager>
      fake_geolocation_system_permission_manager_;
  raw_ptr<SystemGeolocationSource> fake_system_geolocation_source_ = nullptr;
};

TEST_F(CoreLocationProviderTest, CreateDestroy) {
  EXPECT_TRUE(provider_);
  provider_.reset();
}

TEST_F(CoreLocationProviderTest, StartAndStopUpdating) {
  provider_->StartProvider(/*high_accuracy=*/true);
  EXPECT_TRUE(IsUpdating());
  EXPECT_EQ(GetProviderState(),
            mojom::GeolocationDiagnostics::ProviderState::kHighAccuracy);
  provider_->StopProvider();
  EXPECT_FALSE(IsUpdating());
  EXPECT_EQ(GetProviderState(),
            mojom::GeolocationDiagnostics::ProviderState::kStopped);
  provider_.reset();
}

TEST_F(CoreLocationProviderTest, GetPositionUpdates) {
  provider_->StartProvider(/*high_accuracy=*/true);
  EXPECT_TRUE(IsUpdating());
  EXPECT_EQ(GetProviderState(),
            mojom::GeolocationDiagnostics::ProviderState::kHighAccuracy);

  // test info
  double latitude = 147.147;
  double longitude = 101.101;
  double altitude = 417.417;
  double accuracy = 10.5;
  double altitude_accuracy = 15.5;

  auto test_position = mojom::Geoposition::New();
  test_position->latitude = latitude;
  test_position->longitude = longitude;
  test_position->altitude = altitude;
  test_position->accuracy = accuracy;
  test_position->altitude_accuracy = altitude_accuracy;
  test_position->timestamp = base::Time::Now();

  TestFuture<const LocationProvider*, mojom::GeopositionResultPtr>
      location_update_future;
  provider_->SetUpdateCallback(location_update_future.GetRepeatingCallback());
  FakeUpdatePosition(*test_position);
  auto [provider, result] = location_update_future.Take();
  ASSERT_TRUE(result);
  ASSERT_TRUE(result->is_position());
  EXPECT_TRUE(test_position.Equals(result->get_position()));
  ASSERT_TRUE(GetLatestPosition());
  ASSERT_TRUE(GetLatestPosition()->is_position());
  EXPECT_TRUE(GetLatestPosition()->get_position().Equals(test_position));

  provider_->StopProvider();
  EXPECT_FALSE(IsUpdating());
  EXPECT_EQ(GetProviderState(),
            mojom::GeolocationDiagnostics::ProviderState::kStopped);
  provider_.reset();
}

TEST_F(CoreLocationProviderTest, GetPositionError) {
  provider_->StartProvider(/*high_accuracy=*/true);
  EXPECT_TRUE(IsUpdating());
  EXPECT_EQ(GetProviderState(),
            mojom::GeolocationDiagnostics::ProviderState::kHighAccuracy);

  auto error = mojom::GeopositionError::New();
  error->error_code = mojom::GeopositionErrorCode::kPermissionDenied;

  TestFuture<const LocationProvider*, mojom::GeopositionResultPtr>
      location_update_future;
  provider_->SetUpdateCallback(location_update_future.GetRepeatingCallback());
  FakeUpdateError(*error);
  auto [provider, result] = location_update_future.Take();
  ASSERT_TRUE(result);
  ASSERT_TRUE(result->is_error());
  EXPECT_TRUE(error.Equals(result->get_error()));
  ASSERT_TRUE(GetLatestPosition());
  ASSERT_TRUE(GetLatestPosition()->is_error());
  EXPECT_TRUE(GetLatestPosition()->get_error().Equals(error));

  provider_->StopProvider();
  EXPECT_FALSE(IsUpdating());
  EXPECT_EQ(GetProviderState(),
            mojom::GeolocationDiagnostics::ProviderState::kStopped);
  provider_.reset();
}

}  // namespace device