| Index: device/generic_sensor/android/junit/src/org/chromium/device/sensors/PlatformSensorAndProviderTest.java
|
| diff --git a/device/generic_sensor/android/junit/src/org/chromium/device/sensors/PlatformSensorAndProviderTest.java b/device/generic_sensor/android/junit/src/org/chromium/device/sensors/PlatformSensorAndProviderTest.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a886c670d6b1e95076e3ced9a939d4d2e6b4dd1c
|
| --- /dev/null
|
| +++ b/device/generic_sensor/android/junit/src/org/chromium/device/sensors/PlatformSensorAndProviderTest.java
|
| @@ -0,0 +1,438 @@
|
| +// 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.
|
| +
|
| +package org.chromium.device.sensors;
|
| +
|
| +import static org.junit.Assert.assertEquals;
|
| +import static org.junit.Assert.assertNotNull;
|
| +import static org.junit.Assert.assertNull;
|
| +import static org.mockito.ArgumentMatchers.any;
|
| +import static org.mockito.ArgumentMatchers.anyInt;
|
| +import static org.mockito.Mockito.doAnswer;
|
| +import static org.mockito.Mockito.doReturn;
|
| +import static org.mockito.Mockito.mock;
|
| +import static org.mockito.Mockito.never;
|
| +import static org.mockito.Mockito.spy;
|
| +import static org.mockito.Mockito.times;
|
| +import static org.mockito.Mockito.verify;
|
| +
|
| +import android.content.Context;
|
| +import android.hardware.Sensor;
|
| +import android.hardware.SensorEvent;
|
| +import android.hardware.SensorEventListener;
|
| +import android.hardware.SensorManager;
|
| +import android.os.Handler;
|
| +import android.util.SparseArray;
|
| +
|
| +import org.chromium.base.test.util.Feature;
|
| +import org.chromium.mojom.device.mojom.ReportingMode;
|
| +import org.chromium.mojom.device.mojom.SensorInitParams;
|
| +import org.chromium.mojom.device.mojom.SensorType;
|
| +import org.chromium.testing.local.LocalRobolectricTestRunner;
|
| +
|
| +import org.junit.Before;
|
| +import org.junit.Test;
|
| +import org.junit.runner.RunWith;
|
| +import org.mockito.Mock;
|
| +import org.mockito.MockitoAnnotations;
|
| +import org.mockito.invocation.InvocationOnMock;
|
| +import org.mockito.stubbing.Answer;
|
| +import org.robolectric.annotation.Config;
|
| +
|
| +import java.lang.reflect.Constructor;
|
| +import java.lang.reflect.InvocationTargetException;
|
| +import java.nio.ByteBuffer;
|
| +import java.nio.ByteOrder;
|
| +import java.util.ArrayList;
|
| +import java.util.List;
|
| +
|
| +/**
|
| + * Unit tests for PlatformSensor and PlatformSensorProvider.
|
| + */
|
| +@RunWith(LocalRobolectricTestRunner.class)
|
| +@Config(manifest = Config.NONE)
|
| +public class PlatformSensorAndProviderTest {
|
| + @Mock
|
| + private Context mContext;
|
| + @Mock
|
| + private SensorManager mSensorManager;
|
| + @Mock
|
| + private PlatformSensorProvider mPlatformSensorProvider;
|
| + private ByteBuffer mSharedBuffer;
|
| + private final SparseArray<List<Sensor>> mMockSensors = new SparseArray<>();
|
| + private static final long PLATFORM_SENSOR_ANDROID = 123456789L;
|
| + private static final long PLATFORM_SENSOR_TIMESTAMP = 314159265358979L;
|
| + private static final double MILLISECONDS_IN_NANOSECOND = 0.000001d;
|
| +
|
| + /**
|
| + * Class that overrides thread management callbacks for testing purposes.
|
| + */
|
| + private static class TestPlatformSensorProvider extends PlatformSensorProvider {
|
| + public TestPlatformSensorProvider(Context context) {
|
| + super(context);
|
| + }
|
| +
|
| + @Override
|
| + public Handler getHandler() {
|
| + return new Handler();
|
| + }
|
| +
|
| + @Override
|
| + protected void startSensorThread() {}
|
| +
|
| + @Override
|
| + protected void stopSensorThread() {}
|
| + }
|
| +
|
| + /**
|
| + * Class that overrides native callbacks for testing purposes.
|
| + */
|
| + private static class TestPlatformSensor extends PlatformSensor {
|
| + public TestPlatformSensor(
|
| + Sensor sensor, int readingCount, PlatformSensorProvider provider) {
|
| + super(sensor, readingCount, provider);
|
| + }
|
| +
|
| + @Override
|
| + protected void sensorReadingChanged() {}
|
| + @Override
|
| + protected void sensorError() {}
|
| + }
|
| +
|
| + @Before
|
| + public void setUp() {
|
| + MockitoAnnotations.initMocks(this);
|
| + // Remove all mock sensors before the test.
|
| + mMockSensors.clear();
|
| + doReturn(mSensorManager).when(mContext).getSystemService(Context.SENSOR_SERVICE);
|
| + doAnswer(
|
| + new Answer<List<Sensor>>() {
|
| + @Override
|
| + public List<Sensor> answer(final InvocationOnMock invocation)
|
| + throws Throwable {
|
| + return getMockSensors((int) (Integer) (invocation.getArguments())[0]);
|
| + }
|
| + })
|
| + .when(mSensorManager)
|
| + .getSensorList(anyInt());
|
| + doReturn(mSensorManager).when(mPlatformSensorProvider).getSensorManager();
|
| + doReturn(new Handler()).when(mPlatformSensorProvider).getHandler();
|
| + // By default, allow successful registration of SensorEventListeners.
|
| + doReturn(true)
|
| + .when(mSensorManager)
|
| + .registerListener(any(SensorEventListener.class), any(Sensor.class), anyInt(),
|
| + any(Handler.class));
|
| + }
|
| +
|
| + /**
|
| + * Test that PlatformSensorProvider cannot create sensors if sensor manager is null.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensorProvider"})
|
| + public void testNullSensorManager() {
|
| + doReturn(null).when(mContext).getSystemService(Context.SENSOR_SERVICE);
|
| + PlatformSensorProvider provider = PlatformSensorProvider.create(mContext);
|
| + PlatformSensor sensor = provider.createSensor(SensorType.AMBIENT_LIGHT);
|
| + assertNull(sensor);
|
| + }
|
| +
|
| + /**
|
| + * Test that PlatformSensorProvider cannot create sensors that are not supported.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensorProvider"})
|
| + public void testSensorNotSupported() {
|
| + PlatformSensorProvider provider = PlatformSensorProvider.create(mContext);
|
| + PlatformSensor sensor = provider.createSensor(SensorType.AMBIENT_LIGHT);
|
| + assertNull(sensor);
|
| + }
|
| +
|
| + /**
|
| + * Test that PlatformSensorProvider maps device::SensorType to android.hardware.Sensor.TYPE_*.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensorProvider"})
|
| + public void testSensorTypeMappings() {
|
| + PlatformSensorProvider provider = PlatformSensorProvider.create(mContext);
|
| + provider.createSensor(SensorType.AMBIENT_LIGHT);
|
| + verify(mSensorManager).getSensorList(Sensor.TYPE_LIGHT);
|
| + provider.createSensor(SensorType.ACCELEROMETER);
|
| + verify(mSensorManager).getSensorList(Sensor.TYPE_ACCELEROMETER);
|
| + provider.createSensor(SensorType.LINEAR_ACCELERATION);
|
| + verify(mSensorManager).getSensorList(Sensor.TYPE_LINEAR_ACCELERATION);
|
| + provider.createSensor(SensorType.GYROSCOPE);
|
| + verify(mSensorManager).getSensorList(Sensor.TYPE_GYROSCOPE);
|
| + provider.createSensor(SensorType.MAGNETOMETER);
|
| + verify(mSensorManager).getSensorList(Sensor.TYPE_MAGNETIC_FIELD);
|
| + }
|
| +
|
| + /**
|
| + * Test that PlatformSensorProvider can create sensors that are supported.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensorProvider"})
|
| + public void testSensorSupported() {
|
| + PlatformSensor sensor = createPlatformSensor(50000, Sensor.TYPE_LIGHT,
|
| + SensorType.AMBIENT_LIGHT, Sensor.REPORTING_MODE_ON_CHANGE);
|
| + assertNotNull(sensor);
|
| + }
|
| +
|
| + /**
|
| + * Test that PlatformSensor notifies PlatformSensorProvider when it starts (stops) polling,
|
| + * and SensorEventListener is registered (unregistered) to sensor manager.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensor"})
|
| + public void testSensorStartStop() {
|
| + addMockSensor(50000, Sensor.TYPE_ACCELEROMETER, Sensor.REPORTING_MODE_CONTINUOUS);
|
| + PlatformSensor sensor =
|
| + PlatformSensor.create(Sensor.TYPE_ACCELEROMETER, 3, mPlatformSensorProvider);
|
| + assertNotNull(sensor);
|
| +
|
| + sensor.startSensor(5);
|
| + sensor.stopSensor();
|
| +
|
| + // Multiple start invocations.
|
| + sensor.startSensor(1);
|
| + sensor.startSensor(2);
|
| + sensor.startSensor(3);
|
| + // Same frequency, should not restart sensor
|
| + sensor.startSensor(3);
|
| +
|
| + // Started polling with 5, 1, 2 and 3 Hz frequency.
|
| + verify(mPlatformSensorProvider, times(4)).getHandler();
|
| + verify(mPlatformSensorProvider, times(4)).sensorStarted(sensor);
|
| + verify(mSensorManager, times(4))
|
| + .registerListener(any(SensorEventListener.class), any(Sensor.class), anyInt(),
|
| + any(Handler.class));
|
| +
|
| + sensor.stopSensor();
|
| + sensor.stopSensor();
|
| + verify(mPlatformSensorProvider, times(3)).sensorStopped(sensor);
|
| + verify(mSensorManager, times(4))
|
| + .unregisterListener(any(SensorEventListener.class), any(Sensor.class));
|
| + }
|
| +
|
| + /**
|
| + * Test that PlatformSensorProvider is notified when PlatformSensor starts and in case of
|
| + * failure, tells PlatformSensorProvider that the sensor is stopped, so that polling thread
|
| + * can be stopped.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensor"})
|
| + public void testSensorStartFails() {
|
| + addMockSensor(50000, Sensor.TYPE_ACCELEROMETER, Sensor.REPORTING_MODE_CONTINUOUS);
|
| + PlatformSensor sensor =
|
| + PlatformSensor.create(Sensor.TYPE_ACCELEROMETER, 3, mPlatformSensorProvider);
|
| + assertNotNull(sensor);
|
| +
|
| + doReturn(false)
|
| + .when(mSensorManager)
|
| + .registerListener(any(SensorEventListener.class), any(Sensor.class), anyInt(),
|
| + any(Handler.class));
|
| +
|
| + sensor.startSensor(5);
|
| + verify(mPlatformSensorProvider, times(1)).sensorStarted(sensor);
|
| + verify(mPlatformSensorProvider, times(1)).sensorStopped(sensor);
|
| + verify(mPlatformSensorProvider, times(1)).getHandler();
|
| + }
|
| +
|
| + /**
|
| + * Test that PlatformSensor correctly checks supported configuration.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensor"})
|
| + public void testSensorConfiguration() {
|
| + // 5Hz min delay
|
| + PlatformSensor sensor = createPlatformSensor(200000, Sensor.TYPE_ACCELEROMETER,
|
| + SensorType.ACCELEROMETER, Sensor.REPORTING_MODE_CONTINUOUS);
|
| + assertEquals(true, sensor.checkSensorConfiguration(5));
|
| + assertEquals(false, sensor.checkSensorConfiguration(6));
|
| + }
|
| +
|
| + /**
|
| + * Test that PlatformSensor correctly returns its reporting mode.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensor"})
|
| + public void testSensorOnChangeReportingMode() {
|
| + PlatformSensor sensor = createPlatformSensor(50000, Sensor.TYPE_LIGHT,
|
| + SensorType.AMBIENT_LIGHT, Sensor.REPORTING_MODE_ON_CHANGE);
|
| + assertEquals(ReportingMode.ON_CHANGE, sensor.getReportingMode());
|
| + }
|
| +
|
| + /**
|
| + * Test that PlatformSensor doesn't notify client about sensor reading changes when
|
| + * sensor reporting mode is ReportingMode.CONTINUOUS.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensor"})
|
| + public void testSensorNotifierIsNotCalled() {
|
| + TestPlatformSensor sensor = createTestPlatformSensor(
|
| + 50000, Sensor.TYPE_ACCELEROMETER, 3, Sensor.REPORTING_MODE_CONTINUOUS);
|
| + initPlatformSensor(sensor, SensorInitParams.READ_BUFFER_SIZE);
|
| + TestPlatformSensor spySensor = spy(sensor);
|
| + SensorEvent event = createFakeEvent(3);
|
| + assertNotNull(event);
|
| + spySensor.onSensorChanged(event);
|
| + verify(spySensor, never()).sensorReadingChanged();
|
| + }
|
| +
|
| + /**
|
| + * Test that shared buffer is correctly populated from SensorEvent.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensor"})
|
| + public void testSensorBufferFromEvent() {
|
| + TestPlatformSensor sensor = createTestPlatformSensor(
|
| + 50000, Sensor.TYPE_LIGHT, 1, Sensor.REPORTING_MODE_ON_CHANGE);
|
| + initPlatformSensor(sensor, SensorInitParams.READ_BUFFER_SIZE);
|
| + TestPlatformSensor spySensor = spy(sensor);
|
| + SensorEvent event = createFakeEvent(1);
|
| + assertNotNull(event);
|
| + spySensor.onSensorChanged(event);
|
| + verify(spySensor, times(1)).sensorReadingChanged();
|
| + // Verify that timestamp correctly stored in buffer.
|
| + assertEquals(MILLISECONDS_IN_NANOSECOND * PLATFORM_SENSOR_TIMESTAMP,
|
| + mSharedBuffer.getDouble(), MILLISECONDS_IN_NANOSECOND);
|
| + // Verify illuminance value is correctly stored in buffer and precision is not lost.
|
| + assertEquals(1.0d + MILLISECONDS_IN_NANOSECOND, mSharedBuffer.getDouble(),
|
| + MILLISECONDS_IN_NANOSECOND);
|
| + }
|
| +
|
| + /**
|
| + * Test that PlatformSensor notifies client when there is an error.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensor"})
|
| + public void testSensorInvalidReadingSize() {
|
| + TestPlatformSensor sensor = createTestPlatformSensor(
|
| + 50000, Sensor.TYPE_ACCELEROMETER, 3, Sensor.REPORTING_MODE_CONTINUOUS);
|
| + initPlatformSensor(sensor, SensorInitParams.READ_BUFFER_SIZE);
|
| + TestPlatformSensor spySensor = spy(sensor);
|
| + // Accelerometer requires 3 reading values x,y and z, create fake event with 1 reading
|
| + // value.
|
| + SensorEvent event = createFakeEvent(1);
|
| + assertNotNull(event);
|
| + spySensor.onSensorChanged(event);
|
| + verify(spySensor, times(1)).sensorError();
|
| + }
|
| +
|
| + /**
|
| + * Test that PlatformSensor notifies client when there is an error.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensor"})
|
| + public void testSensorInvalidBufferSize() {
|
| + TestPlatformSensor sensor = createTestPlatformSensor(
|
| + 50000, Sensor.TYPE_ACCELEROMETER, 3, Sensor.REPORTING_MODE_CONTINUOUS);
|
| + // Create buffer that doesn't have enough capacity to hold sensor reading values.
|
| + initPlatformSensor(sensor, 2);
|
| + TestPlatformSensor spySensor = spy(sensor);
|
| + SensorEvent event = createFakeEvent(3);
|
| + assertNotNull(event);
|
| + spySensor.onSensorChanged(event);
|
| + verify(spySensor, times(1)).sensorError();
|
| + }
|
| +
|
| + /**
|
| + * Test that multiple PlatformSensor instances correctly register (unregister) to
|
| + * sensor manager and notify PlatformSensorProvider when they start (stop) polling for data.
|
| + */
|
| + @Test
|
| + @Feature({"PlatformSensor"})
|
| + public void testMultipleSensorTypeInstances() {
|
| + addMockSensor(200000, Sensor.TYPE_LIGHT, Sensor.REPORTING_MODE_ON_CHANGE);
|
| + addMockSensor(50000, Sensor.TYPE_ACCELEROMETER, Sensor.REPORTING_MODE_CONTINUOUS);
|
| +
|
| + TestPlatformSensorProvider spyProvider = spy(new TestPlatformSensorProvider(mContext));
|
| + PlatformSensor lightSensor = PlatformSensor.create(Sensor.TYPE_LIGHT, 1, spyProvider);
|
| + assertNotNull(lightSensor);
|
| +
|
| + PlatformSensor accelerometerSensor =
|
| + PlatformSensor.create(Sensor.TYPE_ACCELEROMETER, 3, spyProvider);
|
| + assertNotNull(accelerometerSensor);
|
| +
|
| + lightSensor.startSensor(3);
|
| + accelerometerSensor.startSensor(10);
|
| + lightSensor.stopSensor();
|
| + accelerometerSensor.stopSensor();
|
| +
|
| + verify(spyProvider, times(2)).getHandler();
|
| + verify(spyProvider, times(1)).sensorStarted(lightSensor);
|
| + verify(spyProvider, times(1)).sensorStarted(accelerometerSensor);
|
| + verify(spyProvider, times(1)).sensorStopped(lightSensor);
|
| + verify(spyProvider, times(1)).sensorStopped(accelerometerSensor);
|
| + verify(spyProvider, times(1)).startSensorThread();
|
| + verify(spyProvider, times(1)).stopSensorThread();
|
| + verify(mSensorManager, times(2))
|
| + .registerListener(any(SensorEventListener.class), any(Sensor.class), anyInt(),
|
| + any(Handler.class));
|
| + verify(mSensorManager, times(2))
|
| + .unregisterListener(any(SensorEventListener.class), any(Sensor.class));
|
| + }
|
| +
|
| + /**
|
| + * Creates fake event. The SensorEvent constructor is not accessible outside android.hardware
|
| + * package, therefore, java reflection is used to make constructor accessible to construct
|
| + * SensorEvent instance.
|
| + */
|
| + private SensorEvent createFakeEvent(int readingValuesNum) {
|
| + try {
|
| + Constructor<SensorEvent> sensorEventConstructor =
|
| + SensorEvent.class.getDeclaredConstructor(Integer.TYPE);
|
| + sensorEventConstructor.setAccessible(true);
|
| + SensorEvent event = sensorEventConstructor.newInstance(readingValuesNum);
|
| + event.timestamp = PLATFORM_SENSOR_TIMESTAMP;
|
| + for (int i = 0; i < readingValuesNum; ++i) {
|
| + event.values[i] = (float) (i + 1.0f + MILLISECONDS_IN_NANOSECOND);
|
| + }
|
| + return event;
|
| + } catch (InvocationTargetException | NoSuchMethodException | InstantiationException
|
| + | IllegalAccessException e) {
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + private void initPlatformSensor(PlatformSensor sensor, long readingSize) {
|
| + mSharedBuffer = ByteBuffer.allocate((int) readingSize);
|
| + mSharedBuffer.order(ByteOrder.nativeOrder());
|
| + sensor.initPlatformSensorAndroid(PLATFORM_SENSOR_ANDROID, mSharedBuffer);
|
| + }
|
| +
|
| + private void addMockSensor(long minDelayUsec, int sensorType, int reportingMode) {
|
| + List<Sensor> mockSensorList = new ArrayList<Sensor>();
|
| + mockSensorList.add(createMockSensor(minDelayUsec, sensorType, reportingMode));
|
| + mMockSensors.put(sensorType, mockSensorList);
|
| + }
|
| +
|
| + private Sensor createMockSensor(long minDelayUsec, int sensorType, int reportingMode) {
|
| + Sensor mockSensor = mock(Sensor.class);
|
| + doReturn((int) minDelayUsec).when(mockSensor).getMinDelay();
|
| + doReturn(reportingMode).when(mockSensor).getReportingMode();
|
| + doReturn(sensorType).when(mockSensor).getType();
|
| + return mockSensor;
|
| + }
|
| +
|
| + private List<Sensor> getMockSensors(int sensorType) {
|
| + if (mMockSensors.indexOfKey(sensorType) >= 0) {
|
| + return mMockSensors.get(sensorType);
|
| + }
|
| + return new ArrayList<Sensor>();
|
| + }
|
| +
|
| + private PlatformSensor createPlatformSensor(
|
| + long minDelayUsec, int androidSensorType, int mojoSensorType, int reportingMode) {
|
| + addMockSensor(minDelayUsec, androidSensorType, reportingMode);
|
| + PlatformSensorProvider provider = PlatformSensorProvider.create(mContext);
|
| + return provider.createSensor(mojoSensorType);
|
| + }
|
| +
|
| + private TestPlatformSensor createTestPlatformSensor(
|
| + long minDelayUsec, int androidSensorType, int readingCount, int reportingMode) {
|
| + return new TestPlatformSensor(
|
| + createMockSensor(minDelayUsec, androidSensorType, reportingMode), readingCount,
|
| + mPlatformSensorProvider);
|
| + }
|
| +}
|
|
|