| Index: services/shape_detection/android/junit/src/org/chromium/shape_detection/FaceDetectionImplTest.java
|
| diff --git a/services/shape_detection/android/junit/src/org/chromium/shape_detection/FaceDetectionImplTest.java b/services/shape_detection/android/junit/src/org/chromium/shape_detection/FaceDetectionImplTest.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ae2725ae300d852c73c98109fe473b2d1fb77705
|
| --- /dev/null
|
| +++ b/services/shape_detection/android/junit/src/org/chromium/shape_detection/FaceDetectionImplTest.java
|
| @@ -0,0 +1,222 @@
|
| +// Copyright 2017 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.shape_detection;
|
| +
|
| +import static org.junit.Assert.assertEquals;
|
| +import static org.junit.Assert.assertTrue;
|
| +
|
| +import android.graphics.Bitmap;
|
| +import android.graphics.PointF;
|
| +import android.media.FaceDetector;
|
| +import android.media.FaceDetector.Face;
|
| +
|
| +import org.junit.Before;
|
| +import org.junit.Test;
|
| +import org.junit.runner.RunWith;
|
| +import org.mockito.ArgumentCaptor;
|
| +import org.mockito.Mockito;
|
| +import org.mockito.MockitoAnnotations;
|
| +import org.mockito.invocation.InvocationOnMock;
|
| +import org.mockito.stubbing.Answer;
|
| +import org.robolectric.ParameterizedRobolectricTestRunner;
|
| +import org.robolectric.ParameterizedRobolectricTestRunner.Parameters;
|
| +import org.robolectric.annotation.Config;
|
| +import org.robolectric.shadows.ShadowLog;
|
| +
|
| +import org.chromium.base.ThreadUtils;
|
| +import org.chromium.base.test.util.Feature;
|
| +import org.chromium.mojo.system.SharedBufferHandle;
|
| +import org.chromium.mojo.system.SharedBufferHandle.MapFlags;
|
| +import org.chromium.shape_detection.mojom.FaceDetection;
|
| +import org.chromium.shape_detection.mojom.FaceDetectionResult;
|
| +import org.chromium.shape_detection.mojom.FaceDetectorOptions;
|
| +
|
| +import java.nio.ByteBuffer;
|
| +import java.util.Arrays;
|
| +import java.util.Collection;
|
| +
|
| +/**
|
| + * Test suite for Java Face Detection.
|
| + */
|
| +@RunWith(ParameterizedRobolectricTestRunner.class)
|
| +@Config(sdk = 21, manifest = Config.NONE)
|
| +public class FaceDetectionImplTest {
|
| + private static final int VALID_WIDTH = 1;
|
| + private static final int VALID_HEIGHT = 1;
|
| + private static final int INVALID_WIDTH = 0;
|
| + private static final long NUM_BYTES = VALID_WIDTH * VALID_HEIGHT * 4;
|
| +
|
| + @Parameters
|
| + public static Collection<Object[]> data() {
|
| + return Arrays.asList(new Object[][] {{true}, {false}});
|
| + }
|
| +
|
| + // Class under test.
|
| + private final FaceDetectionImpl mFaceDetectionImpl;
|
| +
|
| + public FaceDetectionImplTest(boolean fastMode) {
|
| + FaceDetectorOptions options = new FaceDetectorOptions();
|
| + options.fastMode = fastMode;
|
| + options.maxDetectedFaces = 1;
|
| + mFaceDetectionImpl = new FaceDetectionImpl(options);
|
| + }
|
| +
|
| + @Before
|
| + public void setUp() {
|
| + ShadowLog.stream = System.out;
|
| + MockitoAnnotations.initMocks(this);
|
| + }
|
| +
|
| + /**
|
| + * Verify construction works.
|
| + */
|
| + @Test
|
| + @Feature({"ShapeDetection"})
|
| + public void testConstruction() {
|
| + assertTrue("Should be on UI thread", ThreadUtils.runningOnUiThread());
|
| + }
|
| +
|
| + /**
|
| + * Verify detection fails if the SharedBufferHandle is invalid.
|
| + */
|
| + @Test
|
| + @Feature({"ShapeDetection"})
|
| + public void testDetectionFailsWithInvalidHandle() {
|
| + assertTrue("Should be on UI thread", ThreadUtils.runningOnUiThread());
|
| +
|
| + SharedBufferHandle sharedBuffer = Mockito.mock(SharedBufferHandle.class);
|
| + Mockito.when(sharedBuffer.isValid()).thenReturn(false);
|
| +
|
| + FaceDetection.DetectResponse callback = Mockito.mock(FaceDetection.DetectResponse.class);
|
| + ArgumentCaptor<FaceDetectionResult[]> argument =
|
| + ArgumentCaptor.forClass(FaceDetectionResult[].class);
|
| +
|
| + mFaceDetectionImpl.detect(sharedBuffer, VALID_WIDTH, VALID_HEIGHT, callback);
|
| +
|
| + Mockito.verify(sharedBuffer, Mockito.times(1)).isValid();
|
| + Mockito.verify(callback, Mockito.times(1)).call(argument.capture());
|
| + assertEquals("result", 0, argument.getValue().length);
|
| + }
|
| +
|
| + /**
|
| + * Verify detection fails if the sent dimensions are ugly.
|
| + */
|
| + @Test
|
| + @Feature({"ShapeDetection"})
|
| + public void testDetectionFailsWithInvalidDimensions() {
|
| + assertTrue("Should be on UI thread", ThreadUtils.runningOnUiThread());
|
| +
|
| + SharedBufferHandle sharedBuffer = Mockito.mock(SharedBufferHandle.class);
|
| + Mockito.when(sharedBuffer.isValid()).thenReturn(true);
|
| +
|
| + FaceDetection.DetectResponse callback = Mockito.mock(FaceDetection.DetectResponse.class);
|
| + ArgumentCaptor<FaceDetectionResult[]> argument =
|
| + ArgumentCaptor.forClass(FaceDetectionResult[].class);
|
| +
|
| + mFaceDetectionImpl.detect(sharedBuffer, INVALID_WIDTH, VALID_HEIGHT, callback);
|
| +
|
| + Mockito.verify(sharedBuffer, Mockito.times(1)).isValid();
|
| + Mockito.verify(callback, Mockito.times(1)).call(argument.capture());
|
| + assertEquals("result", 0, argument.getValue().length);
|
| + }
|
| +
|
| + /**
|
| + * Verify detection fails if SharedBufferHandle fails to map().
|
| + */
|
| + @Test
|
| + @Feature({"ShapeDetection"})
|
| + public void testDetectionFailsWithWronglyMappedBuffer() {
|
| + assertTrue("Should be on UI thread", ThreadUtils.runningOnUiThread());
|
| +
|
| + SharedBufferHandle sharedBuffer = Mockito.mock(SharedBufferHandle.class);
|
| + Mockito.when(sharedBuffer.isValid()).thenReturn(true);
|
| + Mockito.when(sharedBuffer.map(
|
| + Mockito.eq(0L), Mockito.eq(NUM_BYTES), Mockito.any(MapFlags.class)))
|
| + .thenReturn(ByteBuffer.allocate(0));
|
| +
|
| + FaceDetection.DetectResponse callback = Mockito.mock(FaceDetection.DetectResponse.class);
|
| + ArgumentCaptor<FaceDetectionResult[]> argument =
|
| + ArgumentCaptor.forClass(FaceDetectionResult[].class);
|
| +
|
| + mFaceDetectionImpl.detect(sharedBuffer, VALID_WIDTH, VALID_HEIGHT, callback);
|
| +
|
| + Mockito.verify(sharedBuffer, Mockito.times(1)).isValid();
|
| + Mockito.verify(sharedBuffer, Mockito.times(1))
|
| + .map(Mockito.eq(0L), Mockito.eq(NUM_BYTES), Mockito.any(MapFlags.class));
|
| + Mockito.verify(callback, Mockito.times(1)).call(argument.capture());
|
| + assertEquals("result", 0, argument.getValue().length);
|
| + }
|
| +
|
| + /**
|
| + * Verify detection works.
|
| + */
|
| + @Test
|
| + @Feature({"ShapeDetection"})
|
| + public void testDetection() {
|
| + assertTrue("Should be on UI thread", ThreadUtils.runningOnUiThread());
|
| +
|
| + SharedBufferHandle sharedBuffer = Mockito.mock(SharedBufferHandle.class);
|
| + Mockito.when(sharedBuffer.isValid()).thenReturn(true);
|
| + Mockito.when(sharedBuffer.map(
|
| + Mockito.eq(0L), Mockito.eq(NUM_BYTES), Mockito.any(MapFlags.class)))
|
| + .thenReturn(ByteBuffer.allocate((int) NUM_BYTES));
|
| +
|
| + FaceDetection.DetectResponse callback = Mockito.mock(FaceDetection.DetectResponse.class);
|
| + ArgumentCaptor<FaceDetectionResult[]> argument =
|
| + ArgumentCaptor.forClass(FaceDetectionResult[].class);
|
| +
|
| + // We have to add a |faceDetector| mock that inside returns a |face| mock. To spice things
|
| + // up, some methods, findFaces() and getMidPoint(), modify parameters so we need doAnswer().
|
| + final float eyesDistance = 1.0f;
|
| + final float faceCenterX = 2.0f;
|
| + final float faceCenterY = 3.0f;
|
| +
|
| + Face face = Mockito.mock(Face.class);
|
| + Mockito.when(face.eyesDistance()).thenReturn(eyesDistance);
|
| + Mockito.doAnswer(new Answer<Void>() {
|
| + @Override
|
| + public Void answer(InvocationOnMock invocation) {
|
| + Object[] args = invocation.getArguments();
|
| + PointF midPoint = (PointF) args[0];
|
| + midPoint.set(faceCenterX, faceCenterY);
|
| + return null;
|
| + }
|
| + })
|
| + .when(face)
|
| + .getMidPoint(Mockito.any(PointF.class));
|
| +
|
| + FaceDetector faceDetector = Mockito.mock(FaceDetector.class);
|
| + FaceDetectionImpl.setFaceDetector(faceDetector);
|
| + Mockito.doAnswer(new Answer<Integer>() {
|
| + @Override
|
| + public Integer answer(InvocationOnMock invocation) throws Throwable {
|
| + Object[] args = invocation.getArguments();
|
| + Face[] faceArray = (Face[]) args[1];
|
| + faceArray[0] = face;
|
| + return 1;
|
| + }
|
| + })
|
| + .when(faceDetector)
|
| + .findFaces(Mockito.any(Bitmap.class), Mockito.any());
|
| +
|
| + mFaceDetectionImpl.detect(sharedBuffer, VALID_WIDTH, VALID_HEIGHT, callback);
|
| +
|
| + Mockito.verify(sharedBuffer, Mockito.times(1)).isValid();
|
| + Mockito.verify(sharedBuffer, Mockito.times(1))
|
| + .map(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(MapFlags.class));
|
| +
|
| + Mockito.verify(faceDetector, Mockito.times(1))
|
| + .findFaces(Mockito.any(Bitmap.class), Mockito.any());
|
| +
|
| + // Detection happens asynchronously: give some timeout.
|
| + Mockito.verify(callback, Mockito.timeout(3000 /* milliseconds */).times(1))
|
| + .call(argument.capture());
|
| + assertEquals("result", 1, argument.getValue().length);
|
| + assertEquals("width", 2 * eyesDistance, argument.getValue()[0].boundingBox.width, 0.1);
|
| + assertEquals("height", 2 * eyesDistance, argument.getValue()[0].boundingBox.height, 0.1);
|
| + assertEquals("x", faceCenterX - eyesDistance, argument.getValue()[0].boundingBox.x, 0.1);
|
| + assertEquals("y", faceCenterY - eyesDistance, argument.getValue()[0].boundingBox.y, 0.1);
|
| + }
|
| +}
|
|
|