Chromium Code Reviews| 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); |
| + } |
| +} |