From 7bfaffb85b3a73895bdbb64f4763b288c4ca75f6 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Mon, 19 Aug 2019 21:18:47 +0200 Subject: [PATCH] Add tests --- .../cameraview/filter/MultiFilterTest.java | 209 ++++++++++++++++++ .../cameraview/filter/MultiFilter.java | 8 +- 2 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/MultiFilterTest.java diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/MultiFilterTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/MultiFilterTest.java new file mode 100644 index 00000000..4482abae --- /dev/null +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/MultiFilterTest.java @@ -0,0 +1,209 @@ +package com.otaliastudios.cameraview.filter; + + +import android.opengl.GLES20; + +import androidx.annotation.NonNull; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.otaliastudios.cameraview.BaseEglTest; +import com.otaliastudios.cameraview.filters.AutoFixFilter; +import com.otaliastudios.cameraview.filters.BrightnessFilter; +import com.otaliastudios.cameraview.filters.DuotoneFilter; +import com.otaliastudios.cameraview.filters.VignetteFilter; +import com.otaliastudios.cameraview.internal.GlUtils; +import com.otaliastudios.cameraview.internal.egl.EglViewport; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; +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 static org.mockito.Mockito.when; + + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class MultiFilterTest extends BaseEglTest { + + @Test + public void testConstructor1() { + MultiFilter multiFilter = new MultiFilter( + new DuotoneFilter(), + new AutoFixFilter() + ); + assertEquals(2, multiFilter.filters.size()); + } + + @Test + public void testConstructor2() { + List filters = new ArrayList<>(); + filters.add(new DuotoneFilter()); + filters.add(new AutoFixFilter()); + MultiFilter multiFilter = new MultiFilter(filters); + assertEquals(2, multiFilter.filters.size()); + } + + @Test + public void testAddFilter() { + MultiFilter multiFilter = new MultiFilter(); + assertEquals(0, multiFilter.filters.size()); + multiFilter.addFilter(new DuotoneFilter()); + assertEquals(1, multiFilter.filters.size()); + multiFilter.addFilter(new AutoFixFilter()); + assertEquals(2, multiFilter.filters.size()); + } + + @Test + public void testAddFilter_multi() { + MultiFilter multiFilter = new MultiFilter(new DuotoneFilter()); + assertEquals(1, multiFilter.filters.size()); + MultiFilter other = new MultiFilter( + new AutoFixFilter(), + new BrightnessFilter(), + new VignetteFilter()); + multiFilter.addFilter(other); + assertEquals(4, multiFilter.filters.size()); + } + + @Test + public void testSetSize() { + DuotoneFilter filter = new DuotoneFilter(); + MultiFilter multiFilter = new MultiFilter(filter); + MultiFilter.State state = multiFilter.states.get(filter); + assertNotNull(state); + assertNull(state.size); + multiFilter.setSize(WIDTH, HEIGHT); + assertNotNull(state.size); + } + + @Test + public void testCopy() { + DuotoneFilter filter = spy(new DuotoneFilter()); + MultiFilter multiFilter = new MultiFilter(filter); + MultiFilter multiFilterCopy = (MultiFilter) multiFilter.copy(); + assertEquals(1, multiFilterCopy.filters.size()); + verify(filter, times(1)).onCopy(); + } + + @Test + public void testParameter1() { + DuotoneFilter filter = new DuotoneFilter(); + MultiFilter multiFilter = new MultiFilter(filter); + float desired = 0.21582F; // whatever + multiFilter.setParameter1(desired); + assertEquals(desired, multiFilter.getParameter1(), 0.001F); + assertEquals(desired, filter.getParameter1(), 0.001F); + } + + @Test + public void testParameter2() { + DuotoneFilter filter = new DuotoneFilter(); + MultiFilter multiFilter = new MultiFilter(filter); + float desired = 0.21582F; // whatever + multiFilter.setParameter2(desired); + assertEquals(desired, multiFilter.getParameter2(), 0.001F); + assertEquals(desired, filter.getParameter2(), 0.001F); + } + + @Test + public void testOnCreate_isLazy() { + DuotoneFilter filter = spy(new DuotoneFilter()); + MultiFilter multiFilter = new MultiFilter(filter); + + int program = GlUtils.createProgram(multiFilter.getVertexShader(), + multiFilter.getFragmentShader()); + multiFilter.onCreate(program); + verify(filter, never()).onCreate(anyInt()); + + multiFilter.onDestroy(); + GLES20.glDeleteProgram(program); + verify(filter, never()).onDestroy(); + } + + @Test + public void testDraw_simple() { + DuotoneFilter filter = spy(new DuotoneFilter()); + MultiFilter multiFilter = new MultiFilter(filter); + multiFilter.setSize(WIDTH, HEIGHT); + EglViewport viewport = new EglViewport(multiFilter); + int texture = viewport.createTexture(); + float[] matrix = new float[16]; + viewport.drawFrame(texture, matrix); + viewport.release(); + + // The child should have experienced the whole lifecycle. + verify(filter, atLeastOnce()).getVertexShader(); + verify(filter, atLeastOnce()).getFragmentShader(); + verify(filter, atLeastOnce()).setSize(anyInt(), anyInt()); + verify(filter, times(1)).onCreate(anyInt()); + verify(filter, times(1)).draw(matrix); + verify(filter, times(1)).onDestroy(); + } + + @Test + public void testDraw_multi() { + // Want to test that when filter1 is drawn, the current framebuffer is a + // non-default one. When filter2 is drawn, the current framebuffer is 0. + final DuotoneFilter filter1 = spy(new DuotoneFilter()); + final DuotoneFilter filter2 = spy(new DuotoneFilter()); + final MultiFilter multiFilter = new MultiFilter(filter1, filter2); + multiFilter.setSize(WIDTH, HEIGHT); + float[] matrix = new float[16]; + final int[] result = new int[1]; + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + MultiFilter.State state = multiFilter.states.get(filter1); + assertNotNull(state); + assertTrue(state.framebufferId != -1); + assertTrue(state.textureId != -1); + + GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0); + assertTrue(result[0] != 0); + return null; + } + }).when(filter1).draw(matrix); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + // The last filter has no FBO / texture. + MultiFilter.State state = multiFilter.states.get(filter2); + assertNotNull(state); + assertEquals(-1, state.framebufferId); + assertEquals(-1, state.textureId); + + GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0); + assertEquals(0, result[0]); + return null; + } + }).when(filter2).draw(matrix); + + EglViewport viewport = new EglViewport(multiFilter); + int texture = viewport.createTexture(); + viewport.drawFrame(texture, matrix); + viewport.release(); + + // Verify that both are drawn. + verify(filter1, times(1)).draw(matrix); + verify(filter2, times(1)).draw(matrix); + } + +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java index 312f2a54..4cd3f3ec 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java @@ -4,6 +4,7 @@ import android.opengl.GLES11Ext; import android.opengl.GLES20; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.cameraview.size.Size; @@ -40,7 +41,8 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt private final static int TARGET = GLES20.GL_TEXTURE_2D; - private static class State { + @VisibleForTesting + static class State { boolean isCreated = false; int programHandle = -1; int framebufferId = -1; @@ -48,8 +50,8 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt Size size = null; } - private final List filters = new ArrayList<>(); - private final Map states = new HashMap<>(); + @VisibleForTesting final List filters = new ArrayList<>(); + @VisibleForTesting final Map states = new HashMap<>(); private final Object lock = new Object(); private Size size = null; private float parameter1 = 0F;