| // Copyright 2016 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 <SensorsApi.h> |
| #include <objbase.h> |
| #include <sensors.h> |
| |
| #include "base/bind.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/run_loop.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/win/iunknown_impl.h" |
| #include "base/win/propvarutil.h" |
| #include "base/win/scoped_propvariant.h" |
| #include "services/device/generic_sensor/fake_platform_sensor_and_provider.h" |
| #include "services/device/generic_sensor/generic_sensor_consts.h" |
| #include "services/device/generic_sensor/platform_sensor_provider_win.h" |
| #include "services/device/public/mojom/sensor_provider.mojom.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/geometry/angle_conversions.h" |
| |
| using ::testing::_; |
| using ::testing::Invoke; |
| using ::testing::IsNull; |
| using ::testing::NiceMock; |
| using ::testing::NotNull; |
| using ::testing::WithArgs; |
| |
| namespace device { |
| |
| using mojom::SensorType; |
| |
| template <class Interface> |
| class MockCOMInterface : public Interface, public base::win::IUnknownImpl { |
| public: |
| // IUnknown interface |
| ULONG STDMETHODCALLTYPE AddRef() override { return IUnknownImpl::AddRef(); } |
| ULONG STDMETHODCALLTYPE Release() override { return IUnknownImpl::Release(); } |
| |
| STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override { |
| if (riid == __uuidof(Interface)) { |
| *ppv = static_cast<Interface*>(this); |
| AddRef(); |
| return S_OK; |
| } |
| return IUnknownImpl::QueryInterface(riid, ppv); |
| } |
| |
| protected: |
| ~MockCOMInterface() override = default; |
| }; |
| |
| // Mock class for ISensorManager COM interface. |
| class MockISensorManager : public MockCOMInterface<ISensorManager> { |
| public: |
| // ISensorManager interface |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetSensorsByCategory, |
| HRESULT(REFSENSOR_CATEGORY_ID category, |
| ISensorCollection** sensors_found)); |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetSensorsByType, |
| HRESULT(REFSENSOR_TYPE_ID sensor_id, |
| ISensorCollection** sensors_found)); |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetSensorByID, |
| HRESULT(REFSENSOR_ID sensor_id, ISensor** sensor)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| SetEventSink, |
| HRESULT(ISensorManagerEvents* event_sink)); |
| MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| RequestPermissions, |
| HRESULT(HWND parent, |
| ISensorCollection* sensors, |
| BOOL is_modal)); |
| |
| protected: |
| ~MockISensorManager() override = default; |
| }; |
| |
| // Mock class for ISensorCollection COM interface. |
| class MockISensorCollection : public MockCOMInterface<ISensorCollection> { |
| public: |
| // ISensorCollection interface |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetAt, |
| HRESULT(ULONG index, ISensor** sensor)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetCount, |
| HRESULT(ULONG* count)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Add, HRESULT(ISensor* sensor)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| Remove, |
| HRESULT(ISensor* sensor)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| RemoveByID, |
| HRESULT(REFSENSOR_ID sensor_id)); |
| MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Clear, HRESULT()); |
| |
| protected: |
| ~MockISensorCollection() override = default; |
| }; |
| |
| // Mock class for ISensor COM interface. |
| class MockISensor : public MockCOMInterface<ISensor> { |
| public: |
| // ISensor interface |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetID, HRESULT(SENSOR_ID* id)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetCategory, |
| HRESULT(SENSOR_CATEGORY_ID* category)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetType, |
| HRESULT(SENSOR_TYPE_ID* type)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetFriendlyName, |
| HRESULT(BSTR* name)); |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetProperty, |
| HRESULT(REFPROPERTYKEY key, |
| PROPVARIANT* property)); |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetProperties, |
| HRESULT(IPortableDeviceKeyCollection* keys, |
| IPortableDeviceValues** properties)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetSupportedDataFields, |
| HRESULT(IPortableDeviceKeyCollection** data)); |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| SetProperties, |
| HRESULT(IPortableDeviceValues* properties, |
| IPortableDeviceValues** results)); |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| SupportsDataField, |
| HRESULT(REFPROPERTYKEY key, |
| VARIANT_BOOL* is_supported)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetState, |
| HRESULT(SensorState* state)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetData, |
| HRESULT(ISensorDataReport** data_report)); |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| SupportsEvent, |
| HRESULT(REFGUID event_guid, |
| VARIANT_BOOL* is_supported)); |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetEventInterest, |
| HRESULT(GUID** values, ULONG* count)); |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| SetEventInterest, |
| HRESULT(GUID* values, ULONG count)); |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| SetEventSink, |
| HRESULT(ISensorEvents* pEvents)); |
| |
| protected: |
| ~MockISensor() override = default; |
| }; |
| |
| // Mock class for ISensorDataReport COM interface. |
| class MockISensorDataReport : public MockCOMInterface<ISensorDataReport> { |
| public: |
| // ISensorDataReport interface |
| MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetTimestamp, |
| HRESULT(SYSTEMTIME* timestamp)); |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetSensorValue, |
| HRESULT(REFPROPERTYKEY key, PROPVARIANT* value)); |
| MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, |
| GetSensorValues, |
| HRESULT(IPortableDeviceKeyCollection* keys, |
| IPortableDeviceValues** values)); |
| |
| protected: |
| ~MockISensorDataReport() override = default; |
| }; |
| |
| // Class that provides test harness support for generic sensor adaptation for |
| // Windows platform. Testing is mainly done by mocking main COM interfaces that |
| // are used to communicate with Sensors API. |
| // MockISensorManager - mocks ISensorManager and responsible for fetching |
| // list of supported sensors. |
| // MockISensorCollection - mocks collection of ISensor objects. |
| // MockISensor - mocks ISensor intrface. |
| // MockISensorDataReport - mocks IDataReport interface that is used to deliver |
| // data in OnDataUpdated event. |
| class PlatformSensorAndProviderTestWin : public ::testing::Test { |
| public: |
| PlatformSensorAndProviderTestWin() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::IO) {} |
| |
| void SetUp() override { |
| EXPECT_EQ(S_OK, CoInitialize(nullptr)); |
| sensor_ = new NiceMock<MockISensor>(); |
| sensor_collection_ = new NiceMock<MockISensorCollection>(); |
| sensor_manager_ = new NiceMock<MockISensorManager>(); |
| Microsoft::WRL::ComPtr<ISensorManager> manager; |
| sensor_manager_->QueryInterface(IID_PPV_ARGS(&manager)); |
| |
| // Overrides default ISensorManager with mocked interface. |
| PlatformSensorProviderWin::GetInstance()->SetSensorManagerForTesting( |
| manager); |
| } |
| |
| void TearDown() override { |
| Microsoft::WRL::ComPtr<ISensorManager> null_manager; |
| PlatformSensorProviderWin::GetInstance()->SetSensorManagerForTesting( |
| null_manager); |
| } |
| |
| protected: |
| void SensorCreated(scoped_refptr<PlatformSensor> sensor) { |
| platform_sensor_ = sensor; |
| run_loop_->Quit(); |
| } |
| |
| // Sensor creation is asynchronous, therefore inner loop is used to wait for |
| // PlatformSensorProvider::CreateSensorCallback completion. |
| scoped_refptr<PlatformSensor> CreateSensor(mojom::SensorType type) { |
| run_loop_ = std::make_unique<base::RunLoop>(); |
| PlatformSensorProviderWin::GetInstance()->CreateSensor( |
| type, base::Bind(&PlatformSensorAndProviderTestWin::SensorCreated, |
| base::Unretained(this))); |
| run_loop_->Run(); |
| scoped_refptr<PlatformSensor> sensor; |
| sensor.swap(platform_sensor_); |
| run_loop_ = nullptr; |
| return sensor; |
| } |
| |
| // Listening the sensor is asynchronous, therefore inner loop is used to wait |
| // for SetEventSink to be called. |
| bool StartListening(scoped_refptr<PlatformSensor> sensor, |
| PlatformSensor::Client* client, |
| const PlatformSensorConfiguration& config) { |
| run_loop_ = std::make_unique<base::RunLoop>(); |
| bool ret = sensor->StartListening(client, config); |
| if (ret) |
| run_loop_->Run(); |
| run_loop_ = nullptr; |
| return ret; |
| } |
| |
| void QuitInnerLoop() { run_loop_->Quit(); } |
| |
| void SetUnsupportedSensor(REFSENSOR_TYPE_ID sensor) { |
| EXPECT_CALL(*sensor_manager_, GetSensorsByType(sensor, _)) |
| .WillRepeatedly( |
| Invoke([](REFSENSOR_TYPE_ID type, ISensorCollection** collection) { |
| return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); |
| })); |
| } |
| |
| // Sets sensor with REFSENSOR_TYPE_ID |sensor| to be supported by mocked |
| // ISensorMager and it will be present in ISensorCollection. |
| void SetSupportedSensor(REFSENSOR_TYPE_ID sensor) { |
| // Returns mock ISensorCollection. |
| EXPECT_CALL(*sensor_manager_, GetSensorsByType(sensor, _)) |
| .WillOnce(Invoke( |
| [this](REFSENSOR_TYPE_ID type, ISensorCollection** collection) { |
| sensor_collection_->QueryInterface( |
| __uuidof(ISensorCollection), |
| reinterpret_cast<void**>(collection)); |
| return S_OK; |
| })); |
| |
| // Returns number of ISensor objects in ISensorCollection, at the moment |
| // only one ISensor interface instance is suported. |
| EXPECT_CALL(*sensor_collection_, GetCount(_)) |
| .WillOnce(Invoke([](ULONG* count) { |
| *count = 1; |
| return S_OK; |
| })); |
| |
| // Returns ISensor interface instance at index 0. |
| EXPECT_CALL(*sensor_collection_, GetAt(0, _)) |
| .WillOnce(Invoke([this](ULONG index, ISensor** sensor) { |
| sensor_->QueryInterface(__uuidof(ISensor), |
| reinterpret_cast<void**>(sensor)); |
| return S_OK; |
| })); |
| |
| // Handles |SetEventSink| call that is used to subscribe to sensor events |
| // through ISensorEvents interface. ISensorEvents is stored and attached to |
| // |sensor_events_| that is used later to generate fake error, state and |
| // data change events. |
| ON_CALL(*sensor_, SetEventSink(NotNull())) |
| .WillByDefault(Invoke([this](ISensorEvents* events) { |
| events->AddRef(); |
| sensor_events_.Attach(events); |
| if (this->run_loop_) { |
| scoped_task_environment_.GetMainThreadTaskRunner()->PostTask( |
| FROM_HERE, |
| base::Bind(&PlatformSensorAndProviderTestWin::QuitInnerLoop, |
| base::Unretained(this))); |
| } |
| return S_OK; |
| })); |
| |
| // When |SetEventSink| is called with nullptr, it means that client is no |
| // longer interested in sensor events and ISensorEvents can be released. |
| ON_CALL(*sensor_, SetEventSink(IsNull())) |
| .WillByDefault(Invoke([this](ISensorEvents* events) { |
| sensor_events_.Reset(); |
| if (this->run_loop_) { |
| scoped_task_environment_.GetMainThreadTaskRunner()->PostTask( |
| FROM_HERE, |
| base::Bind(&PlatformSensorAndProviderTestWin::QuitInnerLoop, |
| base::Unretained(this))); |
| } |
| return S_OK; |
| })); |
| } |
| |
| // Sets minimal reporting frequency for the mock sensor. |
| void SetSupportedReportingFrequency(double frequency) { |
| ON_CALL(*sensor_, GetProperty(SENSOR_PROPERTY_MIN_REPORT_INTERVAL, _)) |
| .WillByDefault( |
| Invoke([frequency](REFPROPERTYKEY key, PROPVARIANT* pProperty) { |
| pProperty->vt = VT_UI4; |
| pProperty->ulVal = |
| (1 / frequency) * base::Time::kMillisecondsPerSecond; |
| return S_OK; |
| })); |
| } |
| |
| // Generates OnLeave event, e.g. when sensor is disconnected. |
| void GenerateLeaveEvent() { |
| if (!sensor_events_) |
| return; |
| sensor_events_->OnLeave(SENSOR_ID()); |
| } |
| |
| // Generates OnStateChangedLeave event. |
| void GenerateStateChangeEvent(SensorState state) { |
| if (!sensor_events_) |
| return; |
| sensor_events_->OnStateChanged(sensor_.get(), state); |
| } |
| |
| struct PropertyKeyCompare { |
| bool operator()(REFPROPERTYKEY a, REFPROPERTYKEY b) const { |
| if (a.fmtid == b.fmtid) |
| return a.pid < b.pid; |
| return false; |
| } |
| }; |
| |
| using SensorData = |
| std::map<PROPERTYKEY, const PROPVARIANT*, PropertyKeyCompare>; |
| |
| // Generates OnDataUpdated event and creates ISensorDataReport with fake |
| // |value| for property with |key|. |
| void GenerateDataUpdatedEvent(const SensorData& values) { |
| if (!sensor_events_) |
| return; |
| |
| // MockISensorDataReport implements IUnknown that provides ref counting. |
| // IUnknown::QueryInterface increases refcount if an object implements |
| // requested interface. ComPtr wraps received interface and destructs |
| // it when there are not more references. |
| auto* mock_report = new NiceMock<MockISensorDataReport>(); |
| Microsoft::WRL::ComPtr<ISensorDataReport> data_report; |
| mock_report->QueryInterface(IID_PPV_ARGS(&data_report)); |
| |
| EXPECT_CALL(*mock_report, GetTimestamp(_)) |
| .WillOnce(Invoke([](SYSTEMTIME* timestamp) { |
| GetSystemTime(timestamp); |
| return S_OK; |
| })); |
| |
| EXPECT_CALL(*mock_report, GetSensorValue(_, _)) |
| .WillRepeatedly(WithArgs<0, 1>( |
| Invoke([&values](REFPROPERTYKEY key, PROPVARIANT* variant) { |
| auto it = values.find(key); |
| if (it == values.end()) |
| return E_FAIL; |
| |
| PropVariantCopy(variant, it->second); |
| return S_OK; |
| }))); |
| |
| sensor_events_->OnDataUpdated(sensor_.get(), data_report.Get()); |
| } |
| |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| scoped_refptr<MockISensorManager> sensor_manager_; |
| scoped_refptr<MockISensorCollection> sensor_collection_; |
| scoped_refptr<MockISensor> sensor_; |
| Microsoft::WRL::ComPtr<ISensorEvents> sensor_events_; |
| scoped_refptr<PlatformSensor> platform_sensor_; |
| // Inner run loop used to wait for async sensor creation callback. |
| std::unique_ptr<base::RunLoop> run_loop_; |
| }; |
| |
| // Tests that PlatformSensorManager returns null sensor when sensor |
| // is not implemented. |
| TEST_F(PlatformSensorAndProviderTestWin, SensorIsNotImplemented) { |
| EXPECT_CALL(*sensor_manager_, GetSensorsByType(SENSOR_TYPE_PRESSURE, _)) |
| .Times(0); |
| EXPECT_FALSE(CreateSensor(SensorType::PRESSURE)); |
| } |
| |
| // Tests that PlatformSensorManager returns null sensor when sensor |
| // is implemented, but not supported by the hardware. |
| TEST_F(PlatformSensorAndProviderTestWin, SensorIsNotSupported) { |
| EXPECT_CALL(*sensor_manager_, GetSensorsByType(SENSOR_TYPE_AMBIENT_LIGHT, _)) |
| .WillOnce(Invoke([](REFSENSOR_TYPE_ID, ISensorCollection** result) { |
| *result = nullptr; |
| return E_FAIL; |
| })); |
| |
| EXPECT_FALSE(CreateSensor(SensorType::AMBIENT_LIGHT)); |
| } |
| |
| // Tests that PlatformSensorManager returns correct sensor when sensor |
| // is supported by the hardware. |
| TEST_F(PlatformSensorAndProviderTestWin, SensorIsSupported) { |
| SetSupportedSensor(SENSOR_TYPE_AMBIENT_LIGHT); |
| auto sensor = CreateSensor(SensorType::AMBIENT_LIGHT); |
| EXPECT_TRUE(sensor); |
| EXPECT_EQ(SensorType::AMBIENT_LIGHT, sensor->GetType()); |
| } |
| |
| // Tests that PlatformSensor::StartListening fails when provided reporting |
| // frequency is above hardware capabilities. |
| TEST_F(PlatformSensorAndProviderTestWin, StartFails) { |
| SetSupportedReportingFrequency(1); |
| SetSupportedSensor(SENSOR_TYPE_AMBIENT_LIGHT); |
| |
| auto sensor = CreateSensor(SensorType::AMBIENT_LIGHT); |
| EXPECT_TRUE(sensor); |
| |
| auto client = std::make_unique<NiceMock<MockPlatformSensorClient>>(sensor); |
| PlatformSensorConfiguration configuration(10); |
| EXPECT_FALSE(sensor->StartListening(client.get(), configuration)); |
| } |
| |
| // Tests that PlatformSensor::StartListening succeeds and notification about |
| // modified sensor reading is sent to the PlatformSensor::Client interface. |
| TEST_F(PlatformSensorAndProviderTestWin, SensorStarted) { |
| SetSupportedReportingFrequency(10); |
| SetSupportedSensor(SENSOR_TYPE_AMBIENT_LIGHT); |
| |
| EXPECT_CALL(*sensor_, SetEventSink(NotNull())).Times(1); |
| EXPECT_CALL(*sensor_, SetEventSink(IsNull())).Times(1); |
| EXPECT_CALL(*sensor_, SetProperties(NotNull(), _)) |
| .WillRepeatedly(Invoke( |
| [](IPortableDeviceValues* props, IPortableDeviceValues** result) { |
| ULONG value = 0; |
| HRESULT hr = props->GetUnsignedIntegerValue( |
| SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL, &value); |
| EXPECT_TRUE(SUCCEEDED(hr)); |
| // 10Hz is 100msec |
| EXPECT_THAT(value, 100); |
| return hr; |
| })); |
| |
| auto sensor = CreateSensor(SensorType::AMBIENT_LIGHT); |
| EXPECT_TRUE(sensor); |
| |
| auto client = std::make_unique<NiceMock<MockPlatformSensorClient>>(sensor); |
| PlatformSensorConfiguration configuration(10); |
| EXPECT_TRUE(StartListening(sensor, client.get(), configuration)); |
| |
| EXPECT_CALL(*client, OnSensorReadingChanged(sensor->GetType())).Times(1); |
| base::win::ScopedPropVariant pvLux; |
| InitPropVariantFromDouble(3.14, pvLux.Receive()); |
| GenerateDataUpdatedEvent({{SENSOR_DATA_TYPE_LIGHT_LEVEL_LUX, pvLux.ptr()}}); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(sensor->StopListening(client.get(), configuration)); |
| } |
| |
| // Tests that OnSensorError is called when sensor is disconnected. |
| TEST_F(PlatformSensorAndProviderTestWin, SensorRemoved) { |
| SetSupportedSensor(SENSOR_TYPE_AMBIENT_LIGHT); |
| auto sensor = CreateSensor(SensorType::AMBIENT_LIGHT); |
| EXPECT_TRUE(sensor); |
| |
| auto client = std::make_unique<NiceMock<MockPlatformSensorClient>>(sensor); |
| PlatformSensorConfiguration configuration(10); |
| EXPECT_TRUE(StartListening(sensor, client.get(), configuration)); |
| EXPECT_CALL(*client, OnSensorError()).Times(1); |
| |
| GenerateLeaveEvent(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // Tests that OnSensorError is called when sensor is in an error state. |
| TEST_F(PlatformSensorAndProviderTestWin, SensorStateChangedToError) { |
| SetSupportedSensor(SENSOR_TYPE_AMBIENT_LIGHT); |
| auto sensor = CreateSensor(SensorType::AMBIENT_LIGHT); |
| EXPECT_TRUE(sensor); |
| |
| auto client = std::make_unique<NiceMock<MockPlatformSensorClient>>(sensor); |
| PlatformSensorConfiguration configuration(10); |
| EXPECT_TRUE(StartListening(sensor, client.get(), configuration)); |
| EXPECT_CALL(*client, OnSensorError()).Times(1); |
| |
| GenerateStateChangeEvent(SENSOR_STATE_ERROR); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // Tests that OnSensorError is not called when sensor is in a ready state. |
| TEST_F(PlatformSensorAndProviderTestWin, SensorStateChangedToReady) { |
| SetSupportedSensor(SENSOR_TYPE_AMBIENT_LIGHT); |
| auto sensor = CreateSensor(SensorType::AMBIENT_LIGHT); |
| EXPECT_TRUE(sensor); |
| |
| auto client = std::make_unique<NiceMock<MockPlatformSensorClient>>(sensor); |
| PlatformSensorConfiguration configuration(10); |
| EXPECT_TRUE(StartListening(sensor, client.get(), configuration)); |
| EXPECT_CALL(*client, OnSensorError()).Times(0); |
| |
| GenerateStateChangeEvent(SENSOR_STATE_READY); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // Tests that GetMaximumSupportedFrequency provides correct value. |
| TEST_F(PlatformSensorAndProviderTestWin, GetMaximumSupportedFrequency) { |
| SetSupportedReportingFrequency(20); |
| SetSupportedSensor(SENSOR_TYPE_AMBIENT_LIGHT); |
| auto sensor = CreateSensor(SensorType::AMBIENT_LIGHT); |
| EXPECT_TRUE(sensor); |
| EXPECT_THAT(sensor->GetMaximumSupportedFrequency(), 20); |
| } |
| |
| // Tests that GetMaximumSupportedFrequency returns fallback value. |
| TEST_F(PlatformSensorAndProviderTestWin, GetMaximumSupportedFrequencyFallback) { |
| SetSupportedReportingFrequency(0); |
| SetSupportedSensor(SENSOR_TYPE_AMBIENT_LIGHT); |
| auto sensor = CreateSensor(SensorType::AMBIENT_LIGHT); |
| EXPECT_TRUE(sensor); |
| EXPECT_THAT(sensor->GetMaximumSupportedFrequency(), 5); |
| } |
| |
| // Tests that Accelerometer readings are correctly converted. |
| TEST_F(PlatformSensorAndProviderTestWin, CheckAccelerometerReadingConversion) { |
| mojo::ScopedSharedBufferHandle handle = |
| PlatformSensorProviderWin::GetInstance()->CloneSharedBufferHandle(); |
| mojo::ScopedSharedBufferMapping mapping = handle->MapAtOffset( |
| sizeof(SensorReadingSharedBuffer), |
| SensorReadingSharedBuffer::GetOffset(SensorType::ACCELEROMETER)); |
| |
| SetSupportedSensor(SENSOR_TYPE_ACCELEROMETER_3D); |
| auto sensor = CreateSensor(SensorType::ACCELEROMETER); |
| EXPECT_TRUE(sensor); |
| |
| auto client = std::make_unique<NiceMock<MockPlatformSensorClient>>(sensor); |
| PlatformSensorConfiguration configuration(10); |
| EXPECT_TRUE(StartListening(sensor, client.get(), configuration)); |
| EXPECT_CALL(*client, OnSensorReadingChanged(sensor->GetType())).Times(1); |
| |
| double x_accel = 0.25; |
| double y_accel = -0.25; |
| double z_accel = -0.5; |
| |
| base::win::ScopedPropVariant pvX, pvY, pvZ; |
| InitPropVariantFromDouble(x_accel, pvX.Receive()); |
| InitPropVariantFromDouble(y_accel, pvY.Receive()); |
| InitPropVariantFromDouble(z_accel, pvZ.Receive()); |
| |
| GenerateDataUpdatedEvent({{SENSOR_DATA_TYPE_ACCELERATION_X_G, pvX.ptr()}, |
| {SENSOR_DATA_TYPE_ACCELERATION_Y_G, pvY.ptr()}, |
| {SENSOR_DATA_TYPE_ACCELERATION_Z_G, pvZ.ptr()}}); |
| |
| base::RunLoop().RunUntilIdle(); |
| SensorReadingSharedBuffer* buffer = |
| static_cast<SensorReadingSharedBuffer*>(mapping.get()); |
| EXPECT_THAT(buffer->reading.accel.x, -x_accel * kMeanGravity); |
| EXPECT_THAT(buffer->reading.accel.y, -y_accel * kMeanGravity); |
| EXPECT_THAT(buffer->reading.accel.z, -z_accel * kMeanGravity); |
| EXPECT_TRUE(sensor->StopListening(client.get(), configuration)); |
| } |
| |
| // Tests that Gyroscope readings are correctly converted. |
| TEST_F(PlatformSensorAndProviderTestWin, CheckGyroscopeReadingConversion) { |
| mojo::ScopedSharedBufferHandle handle = |
| PlatformSensorProviderWin::GetInstance()->CloneSharedBufferHandle(); |
| mojo::ScopedSharedBufferMapping mapping = handle->MapAtOffset( |
| sizeof(SensorReadingSharedBuffer), |
| SensorReadingSharedBuffer::GetOffset(SensorType::GYROSCOPE)); |
| |
| SetSupportedSensor(SENSOR_TYPE_GYROMETER_3D); |
| auto sensor = CreateSensor(SensorType::GYROSCOPE); |
| EXPECT_TRUE(sensor); |
| |
| auto client = std::make_unique<NiceMock<MockPlatformSensorClient>>(sensor); |
| PlatformSensorConfiguration configuration(10); |
| EXPECT_TRUE(StartListening(sensor, client.get(), configuration)); |
| EXPECT_CALL(*client, OnSensorReadingChanged(sensor->GetType())).Times(1); |
| |
| double x_ang_accel = 0.0; |
| double y_ang_accel = -1.8; |
| double z_ang_accel = -98.7; |
| |
| base::win::ScopedPropVariant pvX, pvY, pvZ; |
| InitPropVariantFromDouble(x_ang_accel, pvX.Receive()); |
| InitPropVariantFromDouble(y_ang_accel, pvY.Receive()); |
| InitPropVariantFromDouble(z_ang_accel, pvZ.Receive()); |
| |
| GenerateDataUpdatedEvent( |
| {{SENSOR_DATA_TYPE_ANGULAR_VELOCITY_X_DEGREES_PER_SECOND, pvX.ptr()}, |
| {SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Y_DEGREES_PER_SECOND, pvY.ptr()}, |
| {SENSOR_DATA_TYPE_ANGULAR_VELOCITY_Z_DEGREES_PER_SECOND, pvZ.ptr()}}); |
| |
| base::RunLoop().RunUntilIdle(); |
| SensorReadingSharedBuffer* buffer = |
| static_cast<SensorReadingSharedBuffer*>(mapping.get()); |
| EXPECT_THAT(buffer->reading.gyro.x, gfx::DegToRad(x_ang_accel)); |
| EXPECT_THAT(buffer->reading.gyro.y, gfx::DegToRad(y_ang_accel)); |
| EXPECT_THAT(buffer->reading.gyro.z, gfx::DegToRad(z_ang_accel)); |
| EXPECT_TRUE(sensor->StopListening(client.get(), configuration)); |
| } |
| |
| // Tests that Magnetometer readings are correctly converted. |
| TEST_F(PlatformSensorAndProviderTestWin, CheckMagnetometerReadingConversion) { |
| mojo::ScopedSharedBufferHandle handle = |
| PlatformSensorProviderWin::GetInstance()->CloneSharedBufferHandle(); |
| mojo::ScopedSharedBufferMapping mapping = handle->MapAtOffset( |
| sizeof(SensorReadingSharedBuffer), |
| SensorReadingSharedBuffer::GetOffset(SensorType::MAGNETOMETER)); |
| |
| SetSupportedSensor(SENSOR_TYPE_COMPASS_3D); |
| auto sensor = CreateSensor(SensorType::MAGNETOMETER); |
| EXPECT_TRUE(sensor); |
| |
| auto client = std::make_unique<NiceMock<MockPlatformSensorClient>>(sensor); |
| PlatformSensorConfiguration configuration(10); |
| EXPECT_TRUE(StartListening(sensor, client.get(), configuration)); |
| EXPECT_CALL(*client, OnSensorReadingChanged(sensor->GetType())).Times(1); |
| |
| double x_magn_field = 112.0; |
| double y_magn_field = -162.0; |
| double z_magn_field = 457.0; |
| |
| base::win::ScopedPropVariant pvX, pvY, pvZ; |
| InitPropVariantFromDouble(x_magn_field, pvX.Receive()); |
| InitPropVariantFromDouble(y_magn_field, pvY.Receive()); |
| InitPropVariantFromDouble(z_magn_field, pvZ.Receive()); |
| |
| GenerateDataUpdatedEvent( |
| {{SENSOR_DATA_TYPE_MAGNETIC_FIELD_STRENGTH_X_MILLIGAUSS, pvX.ptr()}, |
| {SENSOR_DATA_TYPE_MAGNETIC_FIELD_STRENGTH_Y_MILLIGAUSS, pvY.ptr()}, |
| {SENSOR_DATA_TYPE_MAGNETIC_FIELD_STRENGTH_Z_MILLIGAUSS, pvZ.ptr()}}); |
| |
| base::RunLoop().RunUntilIdle(); |
| SensorReadingSharedBuffer* buffer = |
| static_cast<SensorReadingSharedBuffer*>(mapping.get()); |
| EXPECT_THAT(buffer->reading.magn.x, x_magn_field * kMicroteslaInMilligauss); |
| EXPECT_THAT(buffer->reading.magn.y, y_magn_field * kMicroteslaInMilligauss); |
| EXPECT_THAT(buffer->reading.magn.z, z_magn_field * kMicroteslaInMilligauss); |
| EXPECT_TRUE(sensor->StopListening(client.get(), configuration)); |
| } |
| |
| // Tests that AbsoluteOrientationEulerAngles sensor readings are correctly |
| // provided. |
| TEST_F(PlatformSensorAndProviderTestWin, |
| CheckDeviceOrientationEulerAnglesReadingConversion) { |
| mojo::ScopedSharedBufferHandle handle = |
| PlatformSensorProviderWin::GetInstance()->CloneSharedBufferHandle(); |
| mojo::ScopedSharedBufferMapping mapping = |
| handle->MapAtOffset(sizeof(SensorReadingSharedBuffer), |
| SensorReadingSharedBuffer::GetOffset( |
| SensorType::ABSOLUTE_ORIENTATION_EULER_ANGLES)); |
| |
| SetSupportedSensor(SENSOR_TYPE_INCLINOMETER_3D); |
| auto sensor = CreateSensor(SensorType::ABSOLUTE_ORIENTATION_EULER_ANGLES); |
| EXPECT_TRUE(sensor); |
| |
| auto client = std::make_unique<NiceMock<MockPlatformSensorClient>>(sensor); |
| PlatformSensorConfiguration configuration(10); |
| EXPECT_TRUE(StartListening(sensor, client.get(), configuration)); |
| EXPECT_CALL(*client, OnSensorReadingChanged(sensor->GetType())).Times(1); |
| |
| double x = 10; |
| double y = 20; |
| double z = 30; |
| |
| base::win::ScopedPropVariant pvX, pvY, pvZ; |
| InitPropVariantFromDouble(x, pvX.Receive()); |
| InitPropVariantFromDouble(y, pvY.Receive()); |
| InitPropVariantFromDouble(z, pvZ.Receive()); |
| |
| GenerateDataUpdatedEvent({{SENSOR_DATA_TYPE_TILT_X_DEGREES, pvX.ptr()}, |
| {SENSOR_DATA_TYPE_TILT_Y_DEGREES, pvY.ptr()}, |
| {SENSOR_DATA_TYPE_TILT_Z_DEGREES, pvZ.ptr()}}); |
| |
| base::RunLoop().RunUntilIdle(); |
| SensorReadingSharedBuffer* buffer = |
| static_cast<SensorReadingSharedBuffer*>(mapping.get()); |
| |
| EXPECT_THAT(buffer->reading.orientation_euler.x, x); |
| EXPECT_THAT(buffer->reading.orientation_euler.y, y); |
| EXPECT_THAT(buffer->reading.orientation_euler.z, z); |
| EXPECT_TRUE(sensor->StopListening(client.get(), configuration)); |
| } |
| |
| // Tests that AbsoluteOrientationQuaternion sensor readings are correctly |
| // provided. |
| TEST_F(PlatformSensorAndProviderTestWin, |
| CheckDeviceOrientationQuaternionReadingConversion) { |
| mojo::ScopedSharedBufferHandle handle = |
| PlatformSensorProviderWin::GetInstance()->CloneSharedBufferHandle(); |
| mojo::ScopedSharedBufferMapping mapping = |
| handle->MapAtOffset(sizeof(SensorReadingSharedBuffer), |
| SensorReadingSharedBuffer::GetOffset( |
| SensorType::ABSOLUTE_ORIENTATION_QUATERNION)); |
| |
| SetSupportedSensor(SENSOR_TYPE_AGGREGATED_DEVICE_ORIENTATION); |
| auto sensor = CreateSensor(SensorType::ABSOLUTE_ORIENTATION_QUATERNION); |
| EXPECT_TRUE(sensor); |
| |
| auto client = std::make_unique<NiceMock<MockPlatformSensorClient>>(sensor); |
| PlatformSensorConfiguration configuration(10); |
| EXPECT_TRUE(StartListening(sensor, client.get(), configuration)); |
| EXPECT_CALL(*client, OnSensorReadingChanged(sensor->GetType())).Times(1); |
| |
| double x = -0.5; |
| double y = -0.5; |
| double z = 0.5; |
| double w = 0.5; |
| float quat_elements[4] = {x, y, z, w}; |
| |
| base::win::ScopedPropVariant pvQuat; |
| |
| // The SENSOR_DATA_TYPE_QUATERNION property has [VT_VECTOR | VT_UI1] type. |
| // https://msdn.microsoft.com/en-us/library/windows/hardware/dn265187(v=vs.85).aspx |
| // Helper functions e.g., InitVariantFromDoubleArray cannot be used for its |
| // intialization and the only way to initialize it, is to use |
| // InitPropVariantFromGUIDAsBuffer with quaternion format GUID. |
| InitPropVariantFromGUIDAsBuffer(SENSOR_DATA_TYPE_QUATERNION.fmtid, |
| pvQuat.Receive()); |
| memcpy(pvQuat.get().caub.pElems, &quat_elements, sizeof(quat_elements)); |
| GenerateDataUpdatedEvent({{SENSOR_DATA_TYPE_QUATERNION, pvQuat.ptr()}}); |
| |
| base::RunLoop().RunUntilIdle(); |
| SensorReadingSharedBuffer* buffer = |
| static_cast<SensorReadingSharedBuffer*>(mapping.get()); |
| |
| EXPECT_THAT(buffer->reading.orientation_quat.x, x); |
| EXPECT_THAT(buffer->reading.orientation_quat.y, y); |
| EXPECT_THAT(buffer->reading.orientation_quat.z, z); |
| EXPECT_THAT(buffer->reading.orientation_quat.w, w); |
| EXPECT_TRUE(sensor->StopListening(client.get(), configuration)); |
| } |
| |
| // Tests that when only the quaternion version of the absolute orientation |
| // sensor is available the provider falls back to using a fusion algorithm |
| // to provide the euler angles version. |
| TEST_F(PlatformSensorAndProviderTestWin, |
| CheckDeviceOrientationEulerAnglesFallback) { |
| SetUnsupportedSensor(SENSOR_TYPE_INCLINOMETER_3D); |
| SetSupportedSensor(SENSOR_TYPE_AGGREGATED_DEVICE_ORIENTATION); |
| |
| auto sensor = CreateSensor(SensorType::ABSOLUTE_ORIENTATION_EULER_ANGLES); |
| EXPECT_TRUE(sensor); |
| } |
| |
| // Tests that with neither absolute orientation sensor type available |
| // the fallback logic does not generate an infinite loop. |
| TEST_F(PlatformSensorAndProviderTestWin, |
| CheckDeviceOrientationFallbackFailure) { |
| SetUnsupportedSensor(SENSOR_TYPE_INCLINOMETER_3D); |
| SetUnsupportedSensor(SENSOR_TYPE_AGGREGATED_DEVICE_ORIENTATION); |
| |
| auto euler_angles_sensor = |
| CreateSensor(SensorType::ABSOLUTE_ORIENTATION_EULER_ANGLES); |
| EXPECT_FALSE(euler_angles_sensor); |
| auto quaternion_sensor = |
| CreateSensor(SensorType::ABSOLUTE_ORIENTATION_QUATERNION); |
| EXPECT_FALSE(quaternion_sensor); |
| } |
| |
| } // namespace device |