1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "services/device/geolocation/win/location_provider_winrt.h"
6 
7 #include "base/run_loop.h"
8 #include "base/test/task_environment.h"
9 #include "base/win/core_winrt_util.h"
10 #include "services/device/geolocation/win/fake_geocoordinate_winrt.h"
11 #include "services/device/geolocation/win/fake_geolocator_winrt.h"
12 #include "services/device/public/cpp/geolocation/geoposition.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 
15 namespace device {
16 namespace {
17 using ABI::Windows::Devices::Geolocation::IGeolocator;
18 using ABI::Windows::Devices::Geolocation::PositionStatus;
19 
20 class MockLocationObserver {
21  public:
MockLocationObserver(base::OnceClosure update_called)22   MockLocationObserver(base::OnceClosure update_called)
23       : update_called_(std::move(update_called)) {}
24   ~MockLocationObserver() = default;
25 
InvalidateLastPosition()26   void InvalidateLastPosition() {
27     last_position_.error_code = mojom::Geoposition::ErrorCode::NONE;
28     EXPECT_FALSE(ValidateGeoposition(last_position_));
29   }
30 
OnLocationUpdate(const LocationProvider * provider,const mojom::Geoposition & position)31   void OnLocationUpdate(const LocationProvider* provider,
32                         const mojom::Geoposition& position) {
33     last_position_ = position;
34     on_location_update_called_ = true;
35     std::move(update_called_).Run();
36   }
37 
last_position()38   mojom::Geoposition last_position() { return last_position_; }
39 
on_location_update_called()40   bool on_location_update_called() { return on_location_update_called_; }
41 
42  private:
43   base::OnceClosure update_called_;
44   mojom::Geoposition last_position_;
45   bool on_location_update_called_ = false;
46 };
47 
48 }  // namespace
49 
50 class TestingLocationProviderWinrt : public LocationProviderWinrt {
51  public:
TestingLocationProviderWinrt(std::unique_ptr<FakeGeocoordinateData> position_data,PositionStatus position_status)52   TestingLocationProviderWinrt(
53       std::unique_ptr<FakeGeocoordinateData> position_data,
54       PositionStatus position_status)
55       : position_data_(std::move(position_data)),
56         position_status_(position_status) {}
57 
HasPermissionBeenGrantedForTest()58   bool HasPermissionBeenGrantedForTest() { return permission_granted_; }
59 
IsHighAccuracyEnabled()60   bool IsHighAccuracyEnabled() { return enable_high_accuracy_; }
61 
GetStatusChangedToken()62   base::Optional<EventRegistrationToken> GetStatusChangedToken() {
63     return status_changed_token_;
64   }
65 
GetPositionChangedToken()66   base::Optional<EventRegistrationToken> GetPositionChangedToken() {
67     return position_changed_token_;
68   }
69 
GetGeolocator(IGeolocator ** geo_locator)70   HRESULT GetGeolocator(IGeolocator** geo_locator) override {
71     *geo_locator = Microsoft::WRL::Make<FakeGeolocatorWinrt>(
72                        std::move(position_data_), position_status_)
73                        .Detach();
74     return S_OK;
75   }
76 
77  private:
78   std::unique_ptr<FakeGeocoordinateData> position_data_;
79   const PositionStatus position_status_;
80 };
81 
82 class LocationProviderWinrtTest : public testing::Test {
83  public:
SetUpTestSuite()84   static void SetUpTestSuite() {
85     base::win::RoInitialize(RO_INIT_TYPE::RO_INIT_MULTITHREADED);
86   }
87 
88  protected:
LocationProviderWinrtTest()89   LocationProviderWinrtTest()
90       : observer_(
91             std::make_unique<MockLocationObserver>(run_loop_.QuitClosure())),
92         callback_(base::BindRepeating(&MockLocationObserver::OnLocationUpdate,
93                                       base::Unretained(observer_.get()))) {}
94 
InitializeProvider(PositionStatus position_status=PositionStatus::PositionStatus_Ready)95   void InitializeProvider(
96       PositionStatus position_status = PositionStatus::PositionStatus_Ready) {
97     auto test_data = FakeGeocoordinateData();
98     test_data.longitude = 0;
99     test_data.latitude = 0;
100     test_data.accuracy = 0;
101     InitializeProvider(test_data, position_status);
102   }
103 
InitializeProvider(FakeGeocoordinateData position_data,PositionStatus position_status=PositionStatus::PositionStatus_Ready)104   void InitializeProvider(
105       FakeGeocoordinateData position_data,
106       PositionStatus position_status = PositionStatus::PositionStatus_Ready) {
107     provider_ = std::make_unique<TestingLocationProviderWinrt>(
108         std::make_unique<FakeGeocoordinateData>(position_data),
109         position_status);
110     provider_->SetUpdateCallback(callback_);
111   }
112 
113   base::test::TaskEnvironment task_environment_;
114   base::RunLoop run_loop_;
115   const std::unique_ptr<MockLocationObserver> observer_;
116   const LocationProvider::LocationProviderUpdateCallback callback_;
117   std::unique_ptr<TestingLocationProviderWinrt> provider_;
118 };
119 
TEST_F(LocationProviderWinrtTest,CreateDestroy)120 TEST_F(LocationProviderWinrtTest, CreateDestroy) {
121   InitializeProvider();
122   EXPECT_TRUE(provider_);
123   provider_.reset();
124 }
125 
TEST_F(LocationProviderWinrtTest,OnPermissionGranted)126 TEST_F(LocationProviderWinrtTest, OnPermissionGranted) {
127   InitializeProvider();
128   EXPECT_FALSE(provider_->HasPermissionBeenGrantedForTest());
129   provider_->OnPermissionGranted();
130   EXPECT_TRUE(provider_->HasPermissionBeenGrantedForTest());
131 }
132 
TEST_F(LocationProviderWinrtTest,SetAccuracyOptions)133 TEST_F(LocationProviderWinrtTest, SetAccuracyOptions) {
134   InitializeProvider();
135   provider_->StartProvider(/*enable_high_accuracy=*/false);
136   EXPECT_EQ(false, provider_->IsHighAccuracyEnabled());
137   provider_->StartProvider(/*enable_high_accuracy=*/true);
138   EXPECT_EQ(true, provider_->IsHighAccuracyEnabled());
139 }
140 
141 // Tests when OnPermissionGranted() called location update is provided.
TEST_F(LocationProviderWinrtTest,HasPermissions)142 TEST_F(LocationProviderWinrtTest, HasPermissions) {
143   auto test_data = FakeGeocoordinateData();
144   test_data.longitude = 1;
145   test_data.latitude = 2;
146   test_data.accuracy = 3;
147   test_data.speed = 4;
148 
149   InitializeProvider(test_data);
150   provider_->OnPermissionGranted();
151   provider_->StartProvider(/*enable_high_accuracy=*/false);
152 
153   EXPECT_FALSE(observer_->on_location_update_called());
154   EXPECT_FALSE(ValidateGeoposition(observer_->last_position()));
155 
156   EXPECT_TRUE(provider_->GetStatusChangedToken().has_value());
157   EXPECT_TRUE(provider_->GetPositionChangedToken().has_value());
158 
159   run_loop_.Run();
160 
161   EXPECT_TRUE(observer_->on_location_update_called());
162   auto position = observer_->last_position();
163   EXPECT_TRUE(ValidateGeoposition(position));
164   EXPECT_EQ(position.latitude, test_data.latitude);
165   EXPECT_EQ(position.longitude, test_data.longitude);
166   EXPECT_EQ(position.accuracy, test_data.accuracy);
167   EXPECT_EQ(position.altitude, device::mojom::kBadAltitude);
168   EXPECT_EQ(position.altitude_accuracy, device::mojom::kBadAccuracy);
169   EXPECT_EQ(position.speed, test_data.speed.value());
170   EXPECT_EQ(position.heading, device::mojom::kBadHeading);
171 }
172 
173 // Tests when OnPermissionGranted() called location update is provided with all
174 // possible values populated.
TEST_F(LocationProviderWinrtTest,HasPermissionsAllValues)175 TEST_F(LocationProviderWinrtTest, HasPermissionsAllValues) {
176   auto test_data = FakeGeocoordinateData();
177   test_data.longitude = 1;
178   test_data.latitude = 2;
179   test_data.accuracy = 3;
180   test_data.altitude = 4;
181   test_data.altitude_accuracy = 5;
182   test_data.heading = 6;
183   test_data.speed = 7;
184 
185   InitializeProvider(test_data);
186   provider_->OnPermissionGranted();
187   provider_->StartProvider(/*enable_high_accuracy=*/false);
188 
189   EXPECT_FALSE(observer_->on_location_update_called());
190   EXPECT_FALSE(ValidateGeoposition(observer_->last_position()));
191 
192   EXPECT_TRUE(provider_->GetStatusChangedToken().has_value());
193   EXPECT_TRUE(provider_->GetPositionChangedToken().has_value());
194 
195   run_loop_.Run();
196 
197   EXPECT_TRUE(observer_->on_location_update_called());
198   auto position = observer_->last_position();
199   EXPECT_TRUE(ValidateGeoposition(position));
200   EXPECT_EQ(position.latitude, test_data.latitude);
201   EXPECT_EQ(position.longitude, test_data.longitude);
202   EXPECT_EQ(position.accuracy, test_data.accuracy);
203   EXPECT_EQ(position.altitude, test_data.altitude.value());
204   EXPECT_EQ(position.altitude_accuracy, test_data.altitude_accuracy.value());
205   EXPECT_EQ(position.speed, test_data.speed.value());
206   EXPECT_EQ(position.heading, test_data.heading.value());
207 }
208 
209 // Tests when provider is stopped and started quickly access errors
210 // do not occur and location update is not called.
TEST_F(LocationProviderWinrtTest,StartStopProviderRunTasks)211 TEST_F(LocationProviderWinrtTest, StartStopProviderRunTasks) {
212   InitializeProvider();
213   provider_->OnPermissionGranted();
214   provider_->StartProvider(/*enable_high_accuracy=*/false);
215   provider_->StopProvider();
216 
217   EXPECT_FALSE(observer_->on_location_update_called());
218   EXPECT_FALSE(ValidateGeoposition(observer_->last_position()));
219 
220   run_loop_.RunUntilIdle();
221 
222   EXPECT_FALSE(observer_->on_location_update_called());
223   EXPECT_FALSE(provider_->GetStatusChangedToken().has_value());
224   EXPECT_FALSE(provider_->GetPositionChangedToken().has_value());
225 }
226 
227 // Tests when OnPermissionGranted() has not been called location update
228 // is not provided.
TEST_F(LocationProviderWinrtTest,NoPermissions)229 TEST_F(LocationProviderWinrtTest, NoPermissions) {
230   InitializeProvider();
231   provider_->StartProvider(/*enable_high_accuracy=*/false);
232 
233   EXPECT_FALSE(observer_->on_location_update_called());
234   EXPECT_FALSE(ValidateGeoposition(observer_->last_position()));
235 
236   run_loop_.RunUntilIdle();
237 
238   EXPECT_FALSE(observer_->on_location_update_called());
239   EXPECT_FALSE(provider_->GetStatusChangedToken().has_value());
240   EXPECT_FALSE(provider_->GetPositionChangedToken().has_value());
241 }
242 
243 // Tests when a PositionStatus_Disabled is returned from the OS indicating
244 // access to location on the OS is disabled, a permission denied is returned.
TEST_F(LocationProviderWinrtTest,PositionStatusDisabledOsPermissions)245 TEST_F(LocationProviderWinrtTest, PositionStatusDisabledOsPermissions) {
246   InitializeProvider(PositionStatus::PositionStatus_Disabled);
247   provider_->OnPermissionGranted();
248   provider_->StartProvider(/*enable_high_accuracy=*/false);
249 
250   EXPECT_FALSE(observer_->on_location_update_called());
251   EXPECT_FALSE(ValidateGeoposition(observer_->last_position()));
252 
253   EXPECT_TRUE(provider_->GetStatusChangedToken().has_value());
254   EXPECT_TRUE(provider_->GetPositionChangedToken().has_value());
255 
256   run_loop_.Run();
257 
258   EXPECT_TRUE(observer_->on_location_update_called());
259   auto position = observer_->last_position();
260   EXPECT_FALSE(ValidateGeoposition(position));
261   EXPECT_EQ(position.error_code,
262             mojom::Geoposition::ErrorCode::PERMISSION_DENIED);
263 }
264 }  // namespace device
265