Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3617)

Unified Diff: device/generic_sensor/android/junit/src/org/chromium/device/sensors/PlatformSensorProviderTest.java

Issue 2284613002: [sensors] Android platform adaptation for Generic Sensor API (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixes for Tim's comments. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: device/generic_sensor/android/junit/src/org/chromium/device/sensors/PlatformSensorProviderTest.java
diff --git a/device/generic_sensor/android/junit/src/org/chromium/device/sensors/PlatformSensorProviderTest.java b/device/generic_sensor/android/junit/src/org/chromium/device/sensors/PlatformSensorProviderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6252c96e8d7040e55bed104841867b075cc00370
--- /dev/null
+++ b/device/generic_sensor/android/junit/src/org/chromium/device/sensors/PlatformSensorProviderTest.java
@@ -0,0 +1,384 @@
+// 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.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+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.SensorReadBuffer;
+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.Field;
+import java.lang.reflect.Modifier;
+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 PlatformSensorProviderTest {
+ @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;
+
+ @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]);
+ }
+ })
timvolodine 2016/09/07 14:51:06 is the indentation here correct? (looks a bit weir
Ted C 2016/09/07 18:12:24 I would do: doAnswer( new Answer<List<Sen
shalamov 2016/09/07 18:49:18 Thanks, I will format it manually.
shalamov 2016/09/08 13:40:49 Done.
+ .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));
+ doNothing().when(mPlatformSensorProvider).sensorStarted(any(PlatformSensor.class));
+ doNothing().when(mPlatformSensorProvider).sensorStopped(any(PlatformSensor.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()
+ throws NoSuchFieldException, IllegalAccessException {
+ PlatformSensor sensor = createPlatformSensor(50000, Sensor.TYPE_ACCELEROMETER,
+ SensorType.ACCELEROMETER, Sensor.REPORTING_MODE_CONTINUOUS);
+ PlatformSensorNotifier mockNotifier = mock(PlatformSensorNotifier.class);
+ initPlatformSensor(sensor, SensorReadBuffer.READ_BUFFER_SIZE, mockNotifier);
+ SensorEvent event = createFakeEvent(3);
+ sensor.onSensorChanged(event);
+ verify(mockNotifier, never()).sensorReadingChanged();
+ }
+
+ /**
+ * Test that shared buffer is correctly populated from SensorEvent.
+ */
+ @Test
+ @Feature({"PlatformSensor"})
+ public void testSensorBufferFromEvent() throws NoSuchFieldException, IllegalAccessException {
+ PlatformSensor sensor = createPlatformSensor(50000, Sensor.TYPE_LIGHT,
+ SensorType.AMBIENT_LIGHT, Sensor.REPORTING_MODE_ON_CHANGE);
+ PlatformSensorNotifier mockNotifier = mock(PlatformSensorNotifier.class);
+ initPlatformSensor(sensor, SensorReadBuffer.READ_BUFFER_SIZE, mockNotifier);
+ SensorEvent event = createFakeEvent(1);
+ sensor.onSensorChanged(event);
+ verify(mockNotifier, 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() throws NoSuchFieldException, IllegalAccessException {
+ PlatformSensor sensor = createPlatformSensor(50000, Sensor.TYPE_ACCELEROMETER,
+ SensorType.ACCELEROMETER, Sensor.REPORTING_MODE_CONTINUOUS);
+ PlatformSensorNotifier mockNotifier = mock(PlatformSensorNotifier.class);
+ initPlatformSensor(sensor, SensorReadBuffer.READ_BUFFER_SIZE, mockNotifier);
+ // Accelerometer requires 3 reading values x,y and z, create fake event with 1 reading
+ // value.
+ SensorEvent event = createFakeEvent(1);
+ sensor.onSensorChanged(event);
+ verify(mockNotifier, times(1)).sensorError();
+ }
+
+ /**
+ * Test that PlatformSensor notifies client when there is an error.
+ */
+ @Test
+ @Feature({"PlatformSensor"})
+ public void testSensorInvalidBufferSize() throws NoSuchFieldException, IllegalAccessException {
+ PlatformSensor sensor = createPlatformSensor(50000, Sensor.TYPE_ACCELEROMETER,
+ SensorType.ACCELEROMETER, Sensor.REPORTING_MODE_CONTINUOUS);
+ PlatformSensorNotifier mockNotifier = mock(PlatformSensorNotifier.class);
+ // Create buffer that doesn't have enough capacity to hold sensor reading values.
+ initPlatformSensor(sensor, 2, mockNotifier);
+ SensorEvent event = createFakeEvent(3);
+ sensor.onSensorChanged(event);
+ verify(mockNotifier, 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);
+
+ PlatformSensor lightSensor =
+ PlatformSensor.create(Sensor.TYPE_LIGHT, 1, mPlatformSensorProvider);
+ assertNotNull(lightSensor);
+
+ PlatformSensor accelerometerSensor =
+ PlatformSensor.create(Sensor.TYPE_ACCELEROMETER, 3, mPlatformSensorProvider);
+ assertNotNull(accelerometerSensor);
+
+ lightSensor.startSensor(3);
+ accelerometerSensor.startSensor(10);
+ lightSensor.stopSensor();
+ accelerometerSensor.stopSensor();
+
+ verify(mPlatformSensorProvider, times(2)).getHandler();
+ verify(mPlatformSensorProvider, times(1)).sensorStarted(lightSensor);
+ verify(mPlatformSensorProvider, times(1)).sensorStarted(accelerometerSensor);
+ verify(mPlatformSensorProvider, times(1)).sensorStopped(lightSensor);
+ verify(mPlatformSensorProvider, times(1)).sensorStopped(accelerometerSensor);
+ 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));
timvolodine 2016/09/07 14:51:06 Regarding multiple sensors I was actually hoping f
shalamov 2016/09/07 18:49:18 Mockito / JUnit tests are run on 'testing thread',
timvolodine 2016/09/08 12:35:06 Would a simpler approach to just check "mSensorsTh
shalamov 2016/09/08 13:40:49 Done.
+ }
+
+ private SensorEvent createFakeEvent(int readingValuesNum)
+ throws NoSuchFieldException, IllegalAccessException {
+ SensorEvent mockEvent = mock(SensorEvent.class);
Ted C 2016/09/07 18:12:24 This seems more complicated than it needs to be.
shalamov 2016/09/07 18:49:18 That's what I tried in the beginning, but it looks
Ted C 2016/09/07 20:08:12 Oh android...can you use reflection to just make t
shalamov 2016/09/08 13:40:49 Done.
+ mockEvent.timestamp = PLATFORM_SENSOR_TIMESTAMP;
+ float values[] = new float[readingValuesNum];
+ for (int i = 0; i < readingValuesNum; ++i) {
+ values[i] = (float) (i + 1.0f + MILLISECONDS_IN_NANOSECOND);
+ }
+
+ Field valuesField = SensorEvent.class.getDeclaredField("values");
+ valuesField.setAccessible(true);
+ Field modifiersField = Field.class.getDeclaredField("modifiers");
+ modifiersField.setAccessible(true);
+ modifiersField.setInt(valuesField, valuesField.getModifiers() & ~Modifier.FINAL);
+ valuesField.set(mockEvent, values);
+ return mockEvent;
+ }
+
+ private void initPlatformSensor(
+ PlatformSensor sensor, long readingSize, PlatformSensorNotifier notifier) {
+ mSharedBuffer = ByteBuffer.allocate((int) readingSize);
+ mSharedBuffer.order(ByteOrder.nativeOrder());
+ sensor.initPlatformSensorAndroid(notifier, PLATFORM_SENSOR_ANDROID, mSharedBuffer);
+ }
+
+ private void addMockSensor(long minDelayUsec, int sensorType, int reportingMode) {
+ List<Sensor> mockSensorList = new ArrayList<Sensor>();
+ Sensor mockSensor = mock(Sensor.class);
+ doReturn((int) minDelayUsec).when(mockSensor).getMinDelay();
+ doReturn(reportingMode).when(mockSensor).getReportingMode();
+ doReturn(sensorType).when(mockSensor).getType();
+ mockSensorList.add(mockSensor);
+ mMockSensors.put(sensorType, mockSensorList);
+ }
+
+ 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);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698