From d8c674c4a912af91bc6d904f80b2eb0bb4f1dcc9 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Sun, 14 Jul 2019 12:14:57 -0300 Subject: [PATCH] Add integration tests --- .../engine/CameraIntegrationTest.java | 70 ++++++ .../cameraview/overlay/OverlayLayoutTest.java | 201 ++++++++++++++++++ .../cameraview/overlay/OverlayLayout.java | 15 +- 3 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 cameraview/src/androidTest/java/com/otaliastudios/cameraview/overlay/OverlayLayoutTest.java diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java index f3300011..d8bf0e7d 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/CameraIntegrationTest.java @@ -2,6 +2,7 @@ package com.otaliastudios.cameraview.engine; import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.PointF; import android.hardware.Camera; import android.os.Build; @@ -25,6 +26,7 @@ import com.otaliastudios.cameraview.frame.Frame; import com.otaliastudios.cameraview.frame.FrameProcessor; import com.otaliastudios.cameraview.internal.utils.Op; import com.otaliastudios.cameraview.internal.utils.WorkerHandler; +import com.otaliastudios.cameraview.overlay.Overlay; import com.otaliastudios.cameraview.size.Size; import androidx.annotation.NonNull; @@ -48,10 +50,15 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public abstract class CameraIntegrationTest extends BaseTest { @@ -502,6 +509,14 @@ public abstract class CameraIntegrationTest extends BaseTest { waitForVideoResult(true); } + @Test + public void testStartEndVideoSnapshot() { + // TODO should check api + waitForOpen(true); + camera.takeVideoSnapshot(new File(context().getFilesDir(), "video.mp4"), 4000); + waitForVideoEnd(true); + } + @Test public void testEndVideo_withoutStarting() { camera.setMode(Mode.VIDEO); @@ -510,6 +525,13 @@ public abstract class CameraIntegrationTest extends BaseTest { waitForVideoResult(false); } + @Test + public void testEndVideoSnapshot_withoutStarting() { + waitForOpen(true); + camera.stopVideo(); + waitForVideoEnd(false); + } + @Test public void testEndVideo_withMaxSize() { camera.setMode(Mode.VIDEO); @@ -519,6 +541,15 @@ public abstract class CameraIntegrationTest extends BaseTest { waitForVideoResult(true); } + @Test + public void testEndVideoSnapshot_withMaxSize() { + // TODO + // camera.setVideoMaxSize(3000*1000); + // waitForOpen(true); + // waitForVideoStart(); + // waitForVideoEnd(true); + } + @Test public void testEndVideo_withMaxDuration() { camera.setMode(Mode.VIDEO); @@ -528,6 +559,15 @@ public abstract class CameraIntegrationTest extends BaseTest { waitForVideoResult(true); } + @Test + public void testEndVideoSnapshot_withMaxDuration() { + // TODO + // camera.setVideoMaxDuration(4000); + // waitForOpen(true); + // waitForVideoStart(); + // waitForVideoEnd(true); + } + //endregion //region startAutoFocus @@ -601,6 +641,7 @@ public abstract class CameraIntegrationTest extends BaseTest { camera.takePicture(); PictureResult result = waitForPictureResult(true); Bitmap bitmap = CameraUtils.decodeBitmap(result.getData(), Integer.MAX_VALUE, Integer.MAX_VALUE); + assertNotNull(bitmap); assertEquals(result.getSize(), size); assertEquals(bitmap.getWidth(), size.getWidth()); assertEquals(bitmap.getHeight(), size.getHeight()); @@ -735,4 +776,33 @@ public abstract class CameraIntegrationTest extends BaseTest { } //endregion + + //region Overlays + + @Test + public void testOverlay_forPictureSnapshot() { + Overlay overlay = mock(Overlay.class); + when(overlay.drawsOn(any(Overlay.Target.class))).thenReturn(true); + controller.setOverlay(overlay); + waitForOpen(true); + camera.takePictureSnapshot(); + waitForPicture(true); + verify(overlay, atLeastOnce()).drawsOn(Overlay.Target.PICTURE_SNAPSHOT); + verify(overlay, times(1)).drawOn(eq(Overlay.Target.PICTURE_SNAPSHOT), any(Canvas.class)); + } + + @Test + public void testOverlay_forVideoSnapshot() { + Overlay overlay = mock(Overlay.class); + when(overlay.drawsOn(any(Overlay.Target.class))).thenReturn(true); + controller.setOverlay(overlay); + waitForOpen(true); + camera.takeVideoSnapshot(new File(context().getFilesDir(), "video.mp4"), 4000); + waitForVideoStart(); + waitForVideoEnd(true); + verify(overlay, atLeastOnce()).drawsOn(Overlay.Target.VIDEO_SNAPSHOT); + verify(overlay, atLeastOnce()).drawOn(eq(Overlay.Target.VIDEO_SNAPSHOT), any(Canvas.class)); + } + + //endregion } diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/overlay/OverlayLayoutTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/overlay/OverlayLayoutTest.java new file mode 100644 index 00000000..8f390f0e --- /dev/null +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/overlay/OverlayLayoutTest.java @@ -0,0 +1,201 @@ +package com.otaliastudios.cameraview.overlay; + + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.util.Xml; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.otaliastudios.cameraview.BaseTest; +import com.otaliastudios.cameraview.R; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.w3c.dom.Attr; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class OverlayLayoutTest extends BaseTest { + + private OverlayLayout overlayLayout; + + @Before + public void setUp() { + overlayLayout = spy(new OverlayLayout(context())); + } + + @After + public void tearDown() { + overlayLayout = null; + } + + @Test + public void testIsOverlay_LayoutParams() { + ViewGroup.LayoutParams params; + + params = new ViewGroup.LayoutParams(10, 10); + assertFalse(overlayLayout.isOverlay(params)); + + params = new OverlayLayout.LayoutParams(10, 10, Gravity.CENTER); + assertTrue(overlayLayout.isOverlay(params)); + + // Remove the isOverlay flag. + ((OverlayLayout.LayoutParams) params).isOverlay = false; + assertFalse(overlayLayout.isOverlay(params)); + } + + @Test + public void testIsOverlay_attributeSet() throws Exception { + int layout1 = com.otaliastudios.cameraview.test.R.layout.overlay; + int layout2 = com.otaliastudios.cameraview.test.R.layout.not_overlay; + + AttributeSet set1 = getAttributeSet(layout1); + assertTrue(overlayLayout.isOverlay(set1)); + + AttributeSet set2 = getAttributeSet(layout2); + assertFalse(overlayLayout.isOverlay(set2)); + } + + @NonNull + private AttributeSet getAttributeSet(int layout) throws Exception { + // Get the attribute set in the correct state: use a parser and move to START_TAG + XmlResourceParser parser = context().getResources().getLayout(layout); + //noinspection StatementWithEmptyBody + while (parser.next() != XmlResourceParser.START_TAG) {} + return Xml.asAttributeSet(parser); + } + + @Test + public void testLayoutParams_drawsOn() { + OverlayLayout.LayoutParams params = new OverlayLayout.LayoutParams(10, 10, Gravity.CENTER); + + assertFalse(params.drawsOn(Overlay.Target.PREVIEW)); + assertFalse(params.drawsOn(Overlay.Target.PICTURE_SNAPSHOT)); + assertFalse(params.drawsOn(Overlay.Target.VIDEO_SNAPSHOT)); + + params.drawOnPreview = true; + assertTrue(params.drawsOn(Overlay.Target.PREVIEW)); + params.drawOnPictureSnapshot = true; + assertTrue(params.drawsOn(Overlay.Target.PICTURE_SNAPSHOT)); + params.drawOnVideoSnapshot = true; + assertTrue(params.drawsOn(Overlay.Target.VIDEO_SNAPSHOT)); + } + + @Test + public void testLayoutParams_toString() { + OverlayLayout.LayoutParams params = new OverlayLayout.LayoutParams(10, 10, Gravity.CENTER); + String string = params.toString(); + assertTrue(string.contains("drawOnPreview")); + assertTrue(string.contains("drawOnPictureSnapshot")); + assertTrue(string.contains("drawOnVideoSnapshot")); + } + + @Test + public void testDrawChild() { + Canvas canvas = new Canvas(); + OverlayLayout.LayoutParams params = new OverlayLayout.LayoutParams(10, 10, Gravity.CENTER); + View child = new View(context()); + child.setLayoutParams(params); + when(overlayLayout.doDrawChild(canvas, child, 0)).thenReturn(true); + + overlayLayout.currentTarget = Overlay.Target.PREVIEW; + assertFalse(overlayLayout.drawChild(canvas, child, 0)); + params.drawOnPreview = true; + assertTrue(overlayLayout.drawChild(canvas, child, 0)); + + overlayLayout.currentTarget = Overlay.Target.PICTURE_SNAPSHOT; + assertFalse(overlayLayout.drawChild(canvas, child, 0)); + params.drawOnPictureSnapshot = true; + assertTrue(overlayLayout.drawChild(canvas, child, 0)); + + overlayLayout.currentTarget = Overlay.Target.VIDEO_SNAPSHOT; + assertFalse(overlayLayout.drawChild(canvas, child, 0)); + params.drawOnVideoSnapshot = true; + assertTrue(overlayLayout.drawChild(canvas, child, 0)); + } + + @UiThreadTest + @Test + public void testDraw() { + Canvas canvas = new Canvas(); + when(overlayLayout.drawsOn(Overlay.Target.PREVIEW)).thenReturn(false); + overlayLayout.draw(canvas); + verify(overlayLayout, never()).drawOn(Overlay.Target.PREVIEW, canvas); + + when(overlayLayout.drawsOn(Overlay.Target.PREVIEW)).thenReturn(true); + overlayLayout.draw(canvas); + verify(overlayLayout, times(1)).drawOn(Overlay.Target.PREVIEW, canvas); + } + + @UiThreadTest + @Test + public void testDrawOn() { + Canvas canvas = spy(new Canvas()); + View child = new View(context()); + OverlayLayout.LayoutParams params = new OverlayLayout.LayoutParams(10, 10, Gravity.CENTER); + params.drawOnPreview = true; + params.drawOnPictureSnapshot = true; + params.drawOnVideoSnapshot = true; + overlayLayout.addView(child, params); + + overlayLayout.drawOn(Overlay.Target.PREVIEW, canvas); + verify(canvas, never()).scale(anyFloat(), anyFloat()); + verify(overlayLayout, times(1)).doDrawChild(eq(canvas), eq(child), anyLong()); + reset(canvas); + reset(overlayLayout); + + overlayLayout.drawOn(Overlay.Target.PICTURE_SNAPSHOT, canvas); + verify(canvas, times(1)).scale(anyFloat(), anyFloat()); + verify(overlayLayout, times(1)).doDrawChild(eq(canvas), eq(child), anyLong()); + reset(canvas); + reset(overlayLayout); + + overlayLayout.drawOn(Overlay.Target.VIDEO_SNAPSHOT, canvas); + verify(canvas, times(1)).scale(anyFloat(), anyFloat()); + verify(overlayLayout, times(1)).doDrawChild(eq(canvas), eq(child), anyLong()); + reset(canvas); + reset(overlayLayout); + } +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayLayout.java b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayLayout.java index d0acc44c..2d3c6d90 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayLayout.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayLayout.java @@ -12,6 +12,7 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.R; @@ -23,7 +24,7 @@ public class OverlayLayout extends FrameLayout implements Overlay { private static final String TAG = OverlayLayout.class.getSimpleName(); private static final CameraLogger LOG = CameraLogger.create(TAG); - private Target currentTarget = Target.PREVIEW; + @VisibleForTesting Target currentTarget = Target.PREVIEW; /** * We set {@link #setWillNotDraw(boolean)} to false even if we don't draw anything. @@ -149,7 +150,7 @@ public class OverlayLayout extends FrameLayout implements Overlay { LOG.v("Performing drawing for view:", child.getClass().getSimpleName(), "target:", currentTarget, "params:", params); - return super.drawChild(canvas, child, drawingTime); + return doDrawChild(canvas, child, drawingTime); } else { LOG.v("Skipping drawing for view:", child.getClass().getSimpleName(), "target:", currentTarget, @@ -158,11 +159,16 @@ public class OverlayLayout extends FrameLayout implements Overlay { } } + @VisibleForTesting + boolean doDrawChild(Canvas canvas, View child, long drawingTime) { + return super.drawChild(canvas, child, drawingTime); + } + @SuppressWarnings("WeakerAccess") public static class LayoutParams extends FrameLayout.LayoutParams { @SuppressWarnings("unused") - private boolean isOverlay; + @VisibleForTesting boolean isOverlay; public boolean drawOnPreview = false; public boolean drawOnPictureSnapshot = false; public boolean drawOnVideoSnapshot = false; @@ -186,7 +192,8 @@ public class OverlayLayout extends FrameLayout implements Overlay { } } - private boolean drawsOn(@NonNull Target target) { + @VisibleForTesting + boolean drawsOn(@NonNull Target target) { return ((target == Target.PREVIEW && drawOnPreview) || (target == Target.VIDEO_SNAPSHOT && drawOnVideoSnapshot) || (target == Target.PICTURE_SNAPSHOT && drawOnPictureSnapshot));