 Chromium Code Reviews
 Chromium Code Reviews Issue 2447973003:
  [sensors] [win] Implement ambient light sensor for Windows platform  (Closed)
    
  
    Issue 2447973003:
  [sensors] [win] Implement ambient light sensor for Windows platform  (Closed) 
  | Index: device/generic_sensor/platform_sensor_and_provider_unittest_win.cc | 
| diff --git a/device/generic_sensor/platform_sensor_and_provider_unittest_win.cc b/device/generic_sensor/platform_sensor_and_provider_unittest_win.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..c3e67a17c3ef5aa2017a9d77e7c48309aa3a6c73 | 
| --- /dev/null | 
| +++ b/device/generic_sensor/platform_sensor_and_provider_unittest_win.cc | 
| @@ -0,0 +1,474 @@ | 
| +// 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 <Sensors.h> // NOLINT | 
| + | 
| +#include "base/bind.h" | 
| +#include "base/message_loop/message_loop.h" | 
| +#include "base/run_loop.h" | 
| +#include "base/win/iunknown_impl.h" | 
| + | 
| 
Lei Zhang
2016/10/29 05:10:41
nit: no blank lines between all the non-system inc
 
shalamov
2016/10/31 13:12:57
Done.
 | 
| +#include "device/generic_sensor/platform_sensor_provider_win.h" | 
| +#include "device/generic_sensor/public/interfaces/sensor_provider.mojom.h" | 
| + | 
| +#include "testing/gmock/include/gmock/gmock.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| + | 
| +using ::testing::_; | 
| +using ::testing::Invoke; | 
| +using ::testing::IsNull; | 
| +using ::testing::NiceMock; | 
| +using ::testing::NotNull; | 
| +using ::testing::Return; | 
| + | 
| +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: | 
| + void SetUp() override { | 
| + message_loop_.reset(new base::MessageLoopForIO); | 
| + sensor_ = new NiceMock<MockISensor>(); | 
| + sensor_collection_ = new NiceMock<MockISensorCollection>(); | 
| + sensor_manager_ = new NiceMock<MockISensorManager>(); | 
| + base::win::ScopedComPtr<ISensorManager> manager; | 
| + sensor_manager_->QueryInterface(__uuidof(ISensorManager), | 
| + manager.ReceiveVoid()); | 
| + | 
| + // Overrides default ISensorManager with mocked interface. | 
| + PlatformSensorProviderWin::GetInstance()->SetSensorManagerForTesing( | 
| + manager); | 
| + } | 
| + | 
| + void TearDown() override { | 
| + base::win::ScopedComPtr<ISensorManager> null_manager; | 
| + PlatformSensorProviderWin::GetInstance()->SetSensorManagerForTesing( | 
| + 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>(); | 
| 
Lei Zhang
2016/10/29 05:10:41
Use nullptr like line 223?
 
shalamov
2016/10/31 13:12:57
It is inner loop that is used at line 220. I canno
 | 
| + 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; | 
| + } | 
| + | 
| + // 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); | 
| + 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_.Release(); | 
| + 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); | 
| + } | 
| + | 
| + // Generates OnDataUpdated event and creates ISensorDataReport with fake | 
| + // |value| for property with |key|. | 
| + void GenerateDataUpdatedEvent(REFPROPERTYKEY key, double value) { | 
| + if (!sensor_events_) | 
| + return; | 
| + | 
| + auto mock_report = new NiceMock<MockISensorDataReport>(); | 
| 
Lei Zhang
2016/10/29 05:10:41
And who frees |mock_report| ?
 
Lei Zhang
2016/10/29 05:10:41
auto*
 
shalamov
2016/10/31 13:12:57
MockISensorDataReport is derived from IUnknownImpl
 
shalamov
2016/10/31 13:12:57
Done.
 
Lei Zhang
2016/10/31 18:22:16
So it's self releasing? Please add a comment, beca
 
shalamov
2016/11/01 11:00:02
Added comments.
 | 
| + | 
| + EXPECT_CALL(*mock_report, GetTimestamp(_)) | 
| + .WillOnce(Invoke([](SYSTEMTIME* timestamp) { | 
| + GetSystemTime(timestamp); | 
| + return S_OK; | 
| + })); | 
| + | 
| + EXPECT_CALL(*mock_report, GetSensorValue(key, _)) | 
| + .WillOnce(Invoke([value](REFPROPERTYKEY, PROPVARIANT* variant) { | 
| + variant->vt = VT_R8; | 
| + variant->dblVal = value; | 
| + return S_OK; | 
| + })); | 
| + | 
| + base::win::ScopedComPtr<ISensorDataReport> data_report; | 
| + mock_report->QueryInterface(__uuidof(ISensorDataReport), | 
| + data_report.ReceiveVoid()); | 
| + | 
| + sensor_events_->OnDataUpdated(sensor_.get(), data_report.get()); | 
| + } | 
| + | 
| + scoped_refptr<MockISensorManager> sensor_manager_; | 
| + scoped_refptr<MockISensorCollection> sensor_collection_; | 
| + scoped_refptr<MockISensor> sensor_; | 
| + base::win::ScopedComPtr<ISensorEvents> sensor_events_; | 
| + std::unique_ptr<base::MessageLoop> message_loop_; | 
| 
Lei Zhang
2016/10/29 05:10:41
Can this just be a base::MessageLoopForIO? (not an
 
shalamov
2016/10/31 13:12:57
Done.
 | 
| + scoped_refptr<PlatformSensor> platform_sensor_; | 
| + // Inner run loop used to wait for async sensor creation callback. | 
| + std::unique_ptr<base::RunLoop> run_loop_; | 
| +}; | 
| + | 
| +// Mock for PlatformSensor's client interface that is used to deliver | 
| +// error and data changes notifications. | 
| +class MockPlatformSensorClient : public PlatformSensor::Client { | 
| + public: | 
| + MockPlatformSensorClient() = default; | 
| + explicit MockPlatformSensorClient(scoped_refptr<PlatformSensor> sensor) | 
| + : sensor_(sensor) { | 
| + if (sensor_) | 
| + sensor_->AddClient(this); | 
| + | 
| + ON_CALL(*this, IsNotificationSuspended()).WillByDefault(Return(false)); | 
| + } | 
| + | 
| + ~MockPlatformSensorClient() override { | 
| + if (sensor_) | 
| + sensor_->RemoveClient(this); | 
| + } | 
| + | 
| + // PlatformSensor::Client interface. | 
| + MOCK_METHOD0(OnSensorReadingChanged, void()); | 
| + MOCK_METHOD0(OnSensorError, void()); | 
| + MOCK_METHOD0(IsNotificationSuspended, bool()); | 
| + | 
| + private: | 
| + scoped_refptr<PlatformSensor> sensor_; | 
| +}; | 
| + | 
| +// Tests that PlatformSensorManager returns null sensor when sensor | 
| +// is not implemented. | 
| +TEST_F(PlatformSensorAndProviderTestWin, SensorIsNotImplemented) { | 
| + EXPECT_CALL(*sensor_manager_, | 
| + GetSensorsByType(SENSOR_TYPE_ACCELEROMETER_3D, _)) | 
| + .Times(0); | 
| + EXPECT_FALSE(CreateSensor(SensorType::ACCELEROMETER)); | 
| +} | 
| + | 
| +// 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); | 
| 
Lei Zhang
2016/10/29 05:10:41
We're using base::MakeUnique for now.
 
shalamov
2016/10/31 13:12:57
Done.
 | 
| + 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(), _)) | 
| + .WillOnce(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(sensor->StartListening(client.get(), configuration)); | 
| + | 
| + EXPECT_CALL(*client, OnSensorReadingChanged()).Times(1); | 
| + GenerateDataUpdatedEvent(SENSOR_DATA_TYPE_LIGHT_LEVEL_LUX, 3.14); | 
| + 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(sensor->StartListening(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(sensor->StartListening(client.get(), configuration)); | 
| + EXPECT_CALL(*client, OnSensorError()).Times(1); | 
| + | 
| + GenerateStateChangeEvent(SENSOR_STATE_ERROR); | 
| + base::RunLoop().RunUntilIdle(); | 
| +} | 
| + | 
| +} // namespace device |