diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/CamcorderProfilesTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/CamcorderProfilesTest.java new file mode 100644 index 00000000..a2fef905 --- /dev/null +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/CamcorderProfilesTest.java @@ -0,0 +1,55 @@ +package com.otaliastudios.cameraview.internal.utils; + + +import android.media.CamcorderProfile; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.otaliastudios.cameraview.BaseTest; +import com.otaliastudios.cameraview.CameraUtils; +import com.otaliastudios.cameraview.size.Size; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class CamcorderProfilesTest extends BaseTest { + + private String getCameraId() { + if (CameraUtils.hasCameras(getContext())) { + return "0"; + } + return null; + } + + @Test + public void testInvalidCameraReturnsLowest() { + CamcorderProfile invalid = CamcorderProfiles.get("invalid", new Size(100, 100)); + CamcorderProfile lowest = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW); + assertEquals(lowest.videoFrameWidth, invalid.videoFrameWidth); + assertEquals(lowest.videoFrameHeight, invalid.videoFrameHeight); + } + + @Test + public void testGet() { + String cameraId = getCameraId(); + if (cameraId == null) return; + int cameraIdInt = Integer.parseInt(cameraId); + + // Not much we can test. Let's just ask for lowest and highest. + CamcorderProfile low = CamcorderProfiles.get(cameraId, new Size(1, 1)); + CamcorderProfile high = CamcorderProfiles.get(cameraId, new Size(Integer.MAX_VALUE, Integer.MAX_VALUE)); + + // Compare with lowest + CamcorderProfile lowest = CamcorderProfile.get(cameraIdInt, CamcorderProfile.QUALITY_LOW); + CamcorderProfile highest = CamcorderProfile.get(cameraIdInt, CamcorderProfile.QUALITY_HIGH); + assertEquals(lowest.videoFrameWidth, low.videoFrameWidth); + assertEquals(lowest.videoFrameHeight, low.videoFrameHeight); + assertEquals(highest.videoFrameWidth, high.videoFrameWidth); + assertEquals(highest.videoFrameHeight, high.videoFrameHeight); + } +} diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/RotationHelperTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/RotationHelperTest.java new file mode 100644 index 00000000..b2be77bc --- /dev/null +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/RotationHelperTest.java @@ -0,0 +1,52 @@ +package com.otaliastudios.cameraview.internal.utils; + + +import android.graphics.ImageFormat; +import android.graphics.YuvImage; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.otaliastudios.cameraview.BaseTest; +import com.otaliastudios.cameraview.size.Size; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class RotationHelperTest extends BaseTest { + + @Test(expected = IllegalArgumentException.class) + public void testInvalidRotation1() { + RotationHelper.rotate(new byte[10], new Size(1, 1), -1); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidRotation2() { + RotationHelper.rotate(new byte[10], new Size(1, 1), -90); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidRotation3() { + RotationHelper.rotate(new byte[10], new Size(1, 1), 360); + } + + @Test + public void testRotate() { + // Just test that nothing happens. + Size inputSize = new Size(160, 90); + int inputSizeBits = inputSize.getWidth() * inputSize.getHeight() * ImageFormat.getBitsPerPixel(ImageFormat.NV21); + int inputSizeBytes = (int) Math.ceil(inputSizeBits / 8.0d); + byte[] input = new byte[inputSizeBytes]; + byte[] output = RotationHelper.rotate(input, inputSize, 90); + assertEquals(input.length, output.length); + + Size outputSize = inputSize.flip(); + YuvImage image = new YuvImage(output, ImageFormat.NV21, outputSize.getWidth(), outputSize.getHeight(), null); + assertNotNull(image); + } +} diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/WorkerHandlerTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/WorkerHandlerTest.java index 1dd96c52..acf53a49 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/WorkerHandlerTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/internal/utils/WorkerHandlerTest.java @@ -1,41 +1,229 @@ package com.otaliastudios.cameraview.internal.utils; +import com.google.android.gms.tasks.Task; +import com.google.android.gms.tasks.Tasks; import com.otaliastudios.cameraview.BaseTest; +import androidx.annotation.NonNull; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + import static org.junit.Assert.*; +import static org.junit.Assert.assertNotNull; @RunWith(AndroidJUnit4.class) @SmallTest public class WorkerHandlerTest extends BaseTest { @Test - public void testCache() { - WorkerHandler w1 = WorkerHandler.get("handler1"); - WorkerHandler w1a = WorkerHandler.get("handler1"); - WorkerHandler w2 = WorkerHandler.get("handler2"); - assertSame(w1, w1a); - assertNotSame(w1, w2); + public void testGetFromCache() { + WorkerHandler first = WorkerHandler.get("first"); + WorkerHandler second = WorkerHandler.get("first"); + assertSame(first, second); } @Test - public void testStaticRun() { - final Op op = new Op<>(true); - Runnable action = new Runnable() { + public void testGetAnother() { + WorkerHandler first = WorkerHandler.get("first"); + WorkerHandler second = WorkerHandler.get("second"); + assertNotSame(first, second); + } + + @NonNull + private Runnable getRunnableForOp(final @NonNull Op op) { + return new Runnable() { @Override public void run() { op.end(true); } }; - WorkerHandler.execute(action); + } + + @NonNull + private Callable getCallableForOp(final @NonNull Op op) { + return new Callable() { + @Override + public Boolean call() { + op.end(true); + return true; + } + }; + } + + @NonNull + private Callable getThrowCallable() { + return new Callable() { + @Override + public Void call() { + throw new RuntimeException("Fake error"); + } + }; + } + + private void waitOp(@NonNull Op op) { Boolean result = op.await(500); assertNotNull(result); assertTrue(result); } + + @Test + public void testFallbackExecute() { + final Op op = new Op<>(true); + WorkerHandler.execute(getRunnableForOp(op)); + waitOp(op); + } + + @Test + public void testPostRunnable() { + WorkerHandler handler = WorkerHandler.get("handler"); + final Op op = new Op<>(true); + handler.post(getRunnableForOp(op)); + waitOp(op); + } + + @Test + public void testPostCallable() { + WorkerHandler handler = WorkerHandler.get("handler"); + final Op op = new Op<>(true); + handler.post(getCallableForOp(op)); + waitOp(op); + } + + @Test + public void testPostCallable_throws() { + WorkerHandler handler = WorkerHandler.get("handler"); + Task task = handler.post(getThrowCallable()); + try { Tasks.await(task); } catch (ExecutionException | InterruptedException ignore) {} + assertTrue(task.isComplete()); + assertFalse(task.isSuccessful()); + } + + @Test + public void testRunRunnable_background() { + WorkerHandler handler = WorkerHandler.get("handler"); + final Op op = new Op<>(true); + handler.run(getRunnableForOp(op)); + waitOp(op); + } + + @Test + public void testRunRunnable_sameThread() { + final WorkerHandler handler = WorkerHandler.get("handler"); + final Op op1 = new Op<>(true); + final Op op2 = new Op<>(true); + handler.post(new Runnable() { + @Override + public void run() { + handler.run(getRunnableForOp(op2)); + assertTrue(op2.await(0)); // Do not wait. + op1.end(true); + } + }); + waitOp(op1); + } + + @Test + public void testRunCallable_background() { + WorkerHandler handler = WorkerHandler.get("handler"); + final Op op = new Op<>(true); + handler.run(getCallableForOp(op)); + waitOp(op); + } + + @Test + public void testRunCallable_sameThread() { + final WorkerHandler handler = WorkerHandler.get("handler"); + final Op op1 = new Op<>(true); + final Op op2 = new Op<>(true); + handler.post(new Runnable() { + @Override + public void run() { + handler.run(getCallableForOp(op2)); + assertTrue(op2.await(0)); // Do not wait. + op1.end(true); + } + }); + waitOp(op1); + } + + @Test + public void testRunCallable_sameThread_throws() { + final WorkerHandler handler = WorkerHandler.get("handler"); + final Op op = new Op<>(true); + handler.post(new Runnable() { + @Override + public void run() { + Task task = handler.run(getThrowCallable()); + assertTrue(task.isComplete()); // Already complete + assertFalse(task.isSuccessful()); + op.end(true); + } + }); + waitOp(op); + } + + @Test + public void testPostDelayed_tooEarly() { + final WorkerHandler handler = WorkerHandler.get("handler"); + final Op op = new Op<>(true); + handler.post(1000, getRunnableForOp(op)); + assertNull(op.await(500)); + } + + @Test + public void testPostDelayed() { + final WorkerHandler handler = WorkerHandler.get("handler"); + final Op op = new Op<>(true); + handler.post(1000, getRunnableForOp(op)); + assertNotNull(op.await(2000)); + } + + @Test + public void testRemove() { + final WorkerHandler handler = WorkerHandler.get("handler"); + final Op op = new Op<>(true); + Runnable runnable = getRunnableForOp(op); + handler.post(1000, runnable); + handler.remove(runnable); + assertNull(op.await(2000)); + } + + @Test + public void testGetters() { + final WorkerHandler handler = WorkerHandler.get("handler"); + assertNotNull(handler.getExecutor()); + assertNotNull(handler.getHandler()); + assertNotNull(handler.getLooper()); + assertNotNull(handler.getThread()); + } + + @Test + public void testExecutor() { + final WorkerHandler handler = WorkerHandler.get("handler"); + Executor executor = handler.getExecutor(); + final Op op = new Op<>(true); + executor.execute(getRunnableForOp(op)); + waitOp(op); + } + + @Test + public void testDestroy() { + final WorkerHandler handler = WorkerHandler.get("handler"); + assertTrue(handler.getThread().isAlive()); + WorkerHandler.destroy(); + // Wait for the thread to die. + try { handler.getThread().join(500); } catch (InterruptedException ignore) {} + assertFalse(handler.getThread().isAlive()); + WorkerHandler newHandler = WorkerHandler.get("handler"); + assertNotSame(handler, newHandler); + assertTrue(newHandler.getThread().isAlive()); + } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/WorkerHandler.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/WorkerHandler.java index c2a38843..0b932842 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/WorkerHandler.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/WorkerHandler.java @@ -165,6 +165,7 @@ public class WorkerHandler { * Returns the android backing {@link Handler}. * @return the handler */ + @NonNull public Handler getHandler() { return mHandler; } @@ -207,7 +208,9 @@ public class WorkerHandler { WorkerHandler handler = ref.get(); if (handler != null && handler.getThread().isAlive()) { handler.getThread().interrupt(); - // handler.getThread().quit(); + handler.getThread().quit(); + // after quit(), the thread will die at some point in the future. Might take some ms. + // try { handler.getThread().join(); } catch (InterruptedException ignore) {} } ref.clear(); }