Camera2: Refactor preview-based test case
- Rename the class to follow the convention: If a particular class X is being
tested, then the test class name should be XTest. This makes the test class
much less verbose.
- Extract the common code from all preview related tests, and form one base
class to avoid duplicate code.
Change-Id: Ia56432578d5cecfb277ee98e3b7c10606d6c99c8
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 0f05816..ad16635 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -53,7 +53,7 @@
private CameraManager mCameraManager;
private BlockingStateListener mCameraListener;
private HandlerThread mHandlerThread;
- private Handler mCallbackHandler;
+ private Handler mHandler;
private int mLatestState = STATE_UNINITIALIZED;
private ImageReader mReader;
@@ -114,7 +114,7 @@
assertNotNull("Can't connect to camera manager", mCameraManager);
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
- mCallbackHandler = new Handler(mHandlerThread.getLooper());
+ mHandler = new Handler(mHandlerThread.getLooper());
createDefaultSurface();
}
@@ -130,7 +130,7 @@
for (int i = 0; i < ids.length; i++) {
CameraDevice camera = null;
try {
- camera = CameraTestUtils.openCamera(mCameraManager, ids[i], mCallbackHandler);
+ camera = CameraTestUtils.openCamera(mCameraManager, ids[i], mHandler);
assertNotNull(
String.format("Failed to open camera device ID: %s", ids[i]), camera);
@@ -163,7 +163,7 @@
CameraDevice camera = null;
try {
camera = CameraTestUtils.openCamera(mCameraManager, ids[i],
- mCameraListener, mCallbackHandler);
+ mCameraListener, mHandler);
assertNotNull(
String.format("Failed to open camera device %s", ids[i]), camera);
@@ -224,7 +224,7 @@
CameraDevice camera = null;
try {
camera = CameraTestUtils.openCamera(mCameraManager, ids[i],
- mCameraListener, mCallbackHandler);
+ mCameraListener, mHandler);
assertNotNull(
String.format("Failed to open camera device %s", ids[i]), camera);
waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
@@ -291,11 +291,11 @@
id, template));
}
if (!repeating) {
- camera.capture(requestBuilder.build(), mockCaptureListener, mCallbackHandler);
+ camera.capture(requestBuilder.build(), mockCaptureListener, mHandler);
}
else {
camera.setRepeatingRequest(requestBuilder.build(), mockCaptureListener,
- mCallbackHandler);
+ mHandler);
}
waitForState(STATE_ACTIVE, CAMERA_CONFIGURE_TIMEOUT_MS);
@@ -334,10 +334,10 @@
}
if (!repeating) {
- camera.captureBurst(requests, mockCaptureListener, mCallbackHandler);
+ camera.captureBurst(requests, mockCaptureListener, mHandler);
}
else {
- camera.setRepeatingBurst(requests, mockCaptureListener, mCallbackHandler);
+ camera.setRepeatingBurst(requests, mockCaptureListener, mHandler);
}
waitForState(STATE_ACTIVE, CAMERA_CONFIGURE_TIMEOUT_MS);
@@ -394,7 +394,7 @@
mSurface = mReader.getSurface();
// Create dummy image listener since we don't care the image data in this test.
ImageReader.OnImageAvailableListener listener = new ImageDropperListener();
- mReader.setOnImageAvailableListener(listener, mCallbackHandler);
+ mReader.setOnImageAvailableListener(listener, mHandler);
}
private void waitForState(int state, long timeout) {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraSurfaceViewPreviewTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraSurfaceViewPreviewTest.java
deleted file mode 100644
index 1e3764e..0000000
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraSurfaceViewPreviewTest.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.cts;
-
-import static android.hardware.camera2.cts.CameraTestUtils.*;
-import static com.android.ex.camera2.blocking.BlockingStateListener.*;
-
-import android.content.Context;
-import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraDevice.CaptureListener;
-import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CaptureFailure;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.Size;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-import android.view.SurfaceHolder;
-import android.view.Surface;
-
-import com.android.ex.camera2.blocking.BlockingStateListener;
-
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
-import static org.mockito.Mockito.*;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * CameraDevice capture use case tests, including preview, still capture, burst
- * capture etc.
- */
-public class CameraSurfaceViewPreviewTest
- extends ActivityInstrumentationTestCase2<Camera2SurfaceViewStubActivity> {
- // Can not use class name exactly as it exceed the log tag character limit.
- private static final String TAG = "CameraPreviewTest";
- private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
- private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
- private static final int FRAME_TIMEOUT_MS = 500;
- private static final int NUM_FRAMES_VERIFIED = 30;
-
- private Context mContext;
- private CameraManager mCameraManager;
- private String[] mCameraIds;
- private CameraDevice mCamera;
- private HandlerThread mHandlerThread;
- private Handler mHandler;
- private BlockingStateListener mCameraListener;
-
- public CameraSurfaceViewPreviewTest() {
- super(Camera2SurfaceViewStubActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getActivity();
- mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
- assertNotNull("Unable to get CameraManager", mCameraManager);
- mCameraIds = mCameraManager.getCameraIdList();
- assertNotNull("Unable to get camera ids", mCameraIds);
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
- mCameraListener = new BlockingStateListener();
-
- /**
- * Workaround for mockito and JB-MR2 incompatibility
- *
- * Avoid java.lang.IllegalArgumentException: dexcache == null
- * https://code.google.com/p/dexmaker/issues/detail?id=2
- */
- System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
- }
-
- @Override
- protected void tearDown() throws Exception {
- mHandlerThread.quitSafely();
- mHandler = null;
- mCameraListener = null;
- super.tearDown();
- }
-
- public void testCameraPreview() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
- try {
- Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
- mCamera = openCamera(mCameraManager, mCameraIds[i], mCameraListener, mHandler);
- assertNotNull(
- String.format("Failed to open camera device ID: %s", mCameraIds[i]),
- mCamera);
- previewTestByCamera();
- } finally {
- if (mCamera != null) {
- mCamera.close();
- mCamera = null;
- }
- }
- }
- }
-
- /**
- * Test all supported preview sizes for a camera device
- *
- * @throws Exception
- */
- private void previewTestByCamera() throws Exception {
- List<Size> previewSizes = getSupportedPreviewSizes(
- mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
- Camera2SurfaceViewStubActivity stubActivity = getActivity();
-
- for (final Size sz : previewSizes) {
- if (VERBOSE) {
- Log.v(TAG, "Testing camera preview size: " + sz.toString());
- }
- // Change the preview size
- final SurfaceHolder holder = stubActivity.getSurfaceView().getHolder();
- Handler handler = new Handler(Looper.getMainLooper());
- handler.post(new Runnable() {
- @Override
- public void run() {
- holder.setFixedSize(sz.getWidth(), sz.getHeight());
- }
- });
-
- boolean res = stubActivity.waitForSurfaceSizeChanged(
- WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, sz.getWidth(), sz.getHeight());
- assertTrue("wait for surface change to " + sz.toString() + " timed out", res);
- Surface previewSurface = holder.getSurface();
- assertTrue("Preview surface is invalid", previewSurface.isValid());
-
- CaptureListener mockCaptureListener =
- mock(CameraDevice.CaptureListener.class);
-
- startPreview(previewSurface, mockCaptureListener);
-
- verifyCaptureResults(mCamera, mockCaptureListener, NUM_FRAMES_VERIFIED,
- NUM_FRAMES_VERIFIED * FRAME_TIMEOUT_MS);
-
- stopPreview();
- }
- }
-
- private void startPreview(Surface surface, CaptureListener listener) throws Exception {
- List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1);
- outputSurfaces.add(surface);
- mCamera.configureOutputs(outputSurfaces);
- mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
- mCameraListener.waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
-
- // TODO: vary the different settings like crop region to cover more cases.
- CaptureRequest.Builder captureBuilder =
- mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-
- captureBuilder.addTarget(surface);
- mCamera.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
- }
-
- private void stopPreview() throws Exception {
- if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle");
- // Stop repeat, wait for captures to complete, and disconnect from surfaces
- mCamera.configureOutputs(/*outputs*/ null);
- mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
- mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_IDLE_TIMEOUT_MS);
- }
-
- private class IsCaptureResultValid extends ArgumentMatcher<CaptureResult> {
- @Override
- public boolean matches(Object obj) {
- CaptureResult result = (CaptureResult)obj;
- Long timeStamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
- if (timeStamp != null && timeStamp.longValue() > 0L) {
- return true;
- }
- return false;
- }
- }
-
- private void verifyCaptureResults(
- CameraDevice camera,
- CameraDevice.CaptureListener mockListener,
- int expectResultCount,
- int timeOutMs) {
- // Should receive expected number of onCaptureStarted callbacks.
- ArgumentCaptor<Long> timestamps = ArgumentCaptor.forClass(Long.class);
- verify(mockListener,
- timeout(timeOutMs).atLeast(expectResultCount))
- .onCaptureStarted(
- eq(camera),
- isA(CaptureRequest.class),
- timestamps.capture());
-
- // Validate timestamps: all timestamps should be larger than 0 and monotonically increase.
- long timestamp = 0;
- for (Long nextTimestamp : timestamps.getAllValues()) {
- Log.v(TAG, "next t: " + nextTimestamp + " current t: " + timestamp);
- assertTrue("Captures are out of order", timestamp < nextTimestamp);
- timestamp = nextTimestamp;
- }
-
- // Should receive expected number of capture results.
- verify(mockListener,
- timeout(timeOutMs).atLeast(expectResultCount))
- .onCaptureCompleted(
- eq(camera),
- isA(CaptureRequest.class),
- argThat(new IsCaptureResultValid()));
-
- // Should not receive any capture failed callbacks.
- verify(mockListener, never())
- .onCaptureFailed(
- eq(camera),
- isA(CaptureRequest.class),
- isA(CaptureFailure.class));
- }
-
-}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index 9f18d10..998f3f4 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -56,7 +56,7 @@
/**
* A package private utility class for wrapping up the camera2 cts test common utility functions
*/
-class CameraTestUtils extends Assert {
+public class CameraTestUtils extends Assert {
private static final String TAG = "CameraTestUtils";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -165,7 +165,14 @@
return openCamera(manager, cameraId, /*listener*/null, handler);
}
- public static void configureOutputs(CameraDevice camera, List<Surface> outputSurfaces,
+ /**
+ * Configure camera output surfaces.
+ *
+ * @param camera The CameraDevice to be configured.
+ * @param outputSurfaces The surface list that used for camera output.
+ * @param listener The callback CameraDevice will notify when capture results are available.
+ */
+ public static void configureCameraOutputs(CameraDevice camera, List<Surface> outputSurfaces,
BlockingStateListener listener) throws Exception {
camera.configureOutputs(outputSurfaces);
listener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 1da9aad..3131ba2 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -17,30 +17,14 @@
package android.hardware.camera2.cts;
import static android.hardware.camera2.cts.CameraTestUtils.*;
-import static com.android.ex.camera2.blocking.BlockingStateListener.*;
import static android.hardware.camera2.CameraMetadata.*;
-import android.content.Context;
-import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraDevice.CaptureListener;
-import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.Size;
-import android.hardware.camera2.cts.helpers.StaticMetadata;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.test.ActivityInstrumentationTestCase2;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-
-import com.android.ex.camera2.blocking.BlockingStateListener;
-
-import java.util.ArrayList;
-import java.util.List;
/**
* <p>
@@ -51,50 +35,21 @@
* manual ISP control and other per-frame control and synchronization.
* </p>
*/
-public class CaptureRequestTest extends
- ActivityInstrumentationTestCase2<Camera2SurfaceViewStubActivity> {
+public class CaptureRequestTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "CaptureRequestTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final int NUM_FRAMES_VERIFIED = 15;
private static final long WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
- private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
private static final long DEFAULT_EXP_TIME_NS = 30000000L; // 30ms
private static final int DEFAULT_SENSITIVITY = 100; // 10ms
- private Context mContext;
- private CameraManager mCameraManager;
- private CameraDevice mCamera;
- private HandlerThread mHandlerThread;
- private Handler mHandler;
- private Surface mPreviewSurface;
- private BlockingStateListener mCameraListener;
- private StaticMetadata mStaticInfo;
- private Size mPreviewSize;
- private String[] mCameraIds;
-
- public CaptureRequestTest() {
- super(Camera2SurfaceViewStubActivity.class);
- }
-
@Override
protected void setUp() throws Exception {
super.setUp();
- mContext = getActivity();
- mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
- assertNotNull("Unable to get CameraManager", mCameraManager);
- mCameraIds = mCameraManager.getCameraIdList();
- assertNotNull("Unable to get camera ids", mCameraIds);
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
- mCameraListener = new BlockingStateListener();
}
@Override
protected void tearDown() throws Exception {
- mHandlerThread.quitSafely();
- mHandler = null;
- mCameraListener = null;
super.tearDown();
}
@@ -115,14 +70,16 @@
requestBuilder.set(CaptureRequest.BLACK_LEVEL_LOCK, true);
changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS, DEFAULT_SENSITIVITY);
- startPreview(requestBuilder, listener);
+ Size previewSz =
+ getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+ startPreview(requestBuilder, previewSz, listener);
// No lock OFF state is allowed as the exposure is not changed.
verifyBlackLevelLockResults(listener, NUM_FRAMES_VERIFIED, /*maxLockOffCnt*/0);
- // change exposure, with black level still being locked.
+ // Double the exposure time and gain, with black level still being locked.
changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS * 2, DEFAULT_SENSITIVITY * 2);
- startPreview(requestBuilder, listener);
+ startPreview(requestBuilder, previewSz, listener);
// Allow at most one lock OFF state as the exposure is changed once.
verifyBlackLevelLockResults(listener, NUM_FRAMES_VERIFIED, /*maxLockOffCnt*/1);
@@ -179,69 +136,4 @@
requestBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, expTime);
requestBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, sensitivity);
}
-
- /**
- * Open a camera device and get the StaticMetadata.
- */
- private void openDevice(String cameraId) throws Exception {
- mCamera = CameraTestUtils.openCamera(
- mCameraManager, cameraId, mCameraListener, mHandler);
- assertNotNull("Failed to open camera device " + cameraId, mCamera);
-
- mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
- mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId));
- }
-
- private void closeDevice() {
- if (mCamera != null) {
- mCamera.close();
- mCamera = null;
- }
- mStaticInfo = null;
- }
-
- /**
- * Start preview with the largest supported preview size
- */
- private void startPreview(CaptureRequest.Builder request, CaptureListener listener)
- throws Exception {
- // Prepare preview surface.
- Size previewSz = getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
- if (!previewSz.equals(mPreviewSize)) {
- mPreviewSize = previewSz;
- Camera2SurfaceViewStubActivity stubActivity = getActivity();
- final SurfaceHolder holder = getActivity().getSurfaceView().getHolder();
- Handler handler = new Handler(Looper.getMainLooper());
- handler.post(new Runnable() {
- @Override
- public void run() {
- holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
- }
- });
-
- boolean res = stubActivity.waitForSurfaceSizeChanged(
- WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(), mPreviewSize.getHeight());
- assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res);
- mPreviewSurface = holder.getSurface();
- assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
- }
-
- if (VERBOSE) {
- Log.v(TAG, "start preview with size " + mPreviewSize.toString());
- }
-
- List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1);
- outputSurfaces.add(mPreviewSurface);
- configureOutputs(mCamera, outputSurfaces, mCameraListener);
-
- request.addTarget(mPreviewSurface);
-
- mCamera.setRepeatingRequest(request.build(), listener, mHandler);
- }
-
- private void stopPreview() throws Exception {
- if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle");
- // Stop repeat, wait for captures to complete, and disconnect from surfaces
- configureOutputs(mCamera, /*outputSurfaces*/null, mCameraListener);
- }
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
similarity index 98%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java
rename to tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
index 0188ed9..4cf402d 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -39,8 +39,8 @@
import java.util.ArrayList;
import java.util.List;
-public class CameraCaptureResultTest extends AndroidTestCase {
- private static final String TAG = "CameraCaptureResultTest";
+public class CaptureResultTest extends AndroidTestCase {
+ private static final String TAG = "CaptureResultTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private CameraManager mCameraManager;
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
new file mode 100644
index 0000000..d6f2d6a
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraDevice.CaptureListener;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.util.Log;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+
+import static org.mockito.Mockito.*;
+
+import java.util.List;
+
+/**
+ * CameraDevice preview test by using SurfaceView.
+ */
+public class SurfaceViewPreviewTest extends Camera2SurfaceViewTestCase {
+ private static final String TAG = "SurfaceViewPreviewTest";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private static final int FRAME_TIMEOUT_MS = 1000;
+ private static final int NUM_FRAMES_VERIFIED = 30;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Test all supported preview sizes for each camera device.
+ * <p>
+ * For the first {@link #NUM_FRAMES_VERIFIED} of capture results,
+ * the {@link CaptureListener} callback availability and the capture timestamp
+ * (monotonically increasing) ordering are verified.
+ * </p>
+ */
+ public void testCameraPreview() throws Exception {
+ for (int i = 0; i < mCameraIds.length; i++) {
+ try {
+ Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
+ openDevice(mCameraIds[i]);
+
+ previewTestByCamera();
+ } finally {
+ closeDevice();
+ }
+ }
+ }
+
+ /**
+ * Test all supported preview sizes for a camera device
+ *
+ * @throws Exception
+ */
+ private void previewTestByCamera() throws Exception {
+ List<Size> previewSizes = getSupportedPreviewSizes(
+ mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+
+ for (final Size sz : previewSizes) {
+ if (VERBOSE) {
+ Log.v(TAG, "Testing camera preview size: " + sz.toString());
+ }
+
+ // TODO: vary the different settings like crop region to cover more cases.
+ CaptureRequest.Builder requestBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ CaptureListener mockCaptureListener =
+ mock(CameraDevice.CaptureListener.class);
+
+ startPreview(requestBuilder, sz, mockCaptureListener);
+ verifyCaptureResults(mCamera, mockCaptureListener, NUM_FRAMES_VERIFIED,
+ NUM_FRAMES_VERIFIED * FRAME_TIMEOUT_MS);
+ stopPreview();
+ }
+ }
+
+ private class IsCaptureResultValid extends ArgumentMatcher<CaptureResult> {
+ @Override
+ public boolean matches(Object obj) {
+ CaptureResult result = (CaptureResult)obj;
+ Long timeStamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+ if (timeStamp != null && timeStamp.longValue() > 0L) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private void verifyCaptureResults(
+ CameraDevice camera,
+ CameraDevice.CaptureListener mockListener,
+ int expectResultCount,
+ int timeOutMs) {
+ // Should receive expected number of onCaptureStarted callbacks.
+ ArgumentCaptor<Long> timestamps = ArgumentCaptor.forClass(Long.class);
+ verify(mockListener,
+ timeout(timeOutMs).atLeast(expectResultCount))
+ .onCaptureStarted(
+ eq(camera),
+ isA(CaptureRequest.class),
+ timestamps.capture());
+
+ // Validate timestamps: all timestamps should be larger than 0 and monotonically increase.
+ long timestamp = 0;
+ for (Long nextTimestamp : timestamps.getAllValues()) {
+ Log.v(TAG, "next t: " + nextTimestamp + " current t: " + timestamp);
+ assertNotNull("Next timestamp is null!", nextTimestamp);
+ assertTrue("Captures are out of order", timestamp < nextTimestamp);
+ timestamp = nextTimestamp;
+ }
+
+ // Should receive expected number of capture results.
+ verify(mockListener,
+ timeout(timeOutMs).atLeast(expectResultCount))
+ .onCaptureCompleted(
+ eq(camera),
+ isA(CaptureRequest.class),
+ argThat(new IsCaptureResultValid()));
+
+ // Should not receive any capture failed callbacks.
+ verify(mockListener, never())
+ .onCaptureFailed(
+ eq(camera),
+ isA(CaptureRequest.class),
+ isA(CaptureFailure.class));
+ }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
new file mode 100644
index 0000000..7712440
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts.testcases;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.content.Context;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.CameraDevice.CaptureListener;
+import android.hardware.camera2.cts.Camera2SurfaceViewStubActivity;
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+
+import com.android.ex.camera2.blocking.BlockingStateListener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Camera2 Preview test case base class by using SurfaceView as rendering target.
+ *
+ * <p>This class encapsulates the SurfaceView based preview common functionalities.
+ * The setup and teardown of CameraManager, test HandlerThread, Activity, Camera IDs
+ * and CameraStateListener are handled in this class. Some basic preview related utility
+ * functions are provided to facilitate the derived preview-based test classes.
+ * </p>
+ */
+
+public class Camera2SurfaceViewTestCase extends
+ ActivityInstrumentationTestCase2<Camera2SurfaceViewStubActivity> {
+ private static final String TAG = "SurfaceViewTestCase";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
+
+ private Size mPreviewSize;
+ private Surface mPreviewSurface;
+
+ protected Context mContext;
+ protected CameraManager mCameraManager;
+ protected String[] mCameraIds;
+ protected HandlerThread mHandlerThread;
+ protected Handler mHandler;
+ protected BlockingStateListener mCameraListener;
+ // Per device fields:
+ protected StaticMetadata mStaticInfo;
+ protected CameraDevice mCamera;
+
+ public Camera2SurfaceViewTestCase() {
+ super(Camera2SurfaceViewStubActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ /**
+ * Set up the camera preview required environments, including activity,
+ * CameraManager, HandlerThread, Camera IDs, and CameraStateListener.
+ */
+ super.setUp();
+ mContext = getActivity();
+ mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ assertNotNull("Unable to get CameraManager", mCameraManager);
+ mCameraIds = mCameraManager.getCameraIdList();
+ assertNotNull("Unable to get camera ids", mCameraIds);
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mCameraListener = new BlockingStateListener();
+
+ /**
+ * Workaround for mockito and JB-MR2 incompatibility
+ *
+ * Avoid java.lang.IllegalArgumentException: dexcache == null
+ * https://code.google.com/p/dexmaker/issues/detail?id=2
+ */
+ System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // Teardown the camera preview required environments.
+ mHandlerThread.quitSafely();
+ mHandler = null;
+ mCameraListener = null;
+
+ super.tearDown();
+ }
+
+ /**
+ * Start camera preview by using the given request, preview size and capture listener.
+ *
+ * @param request The request builder used to start the preview.
+ * @param previewSz The size of the camera device output preview stream.
+ * @param listener The callbacks the camera device will notify when preview capture is
+ * available.
+ */
+ protected void startPreview(CaptureRequest.Builder request, Size previewSz,
+ CaptureListener listener) throws Exception {
+ if (!previewSz.equals(mPreviewSize)) {
+ mPreviewSize = previewSz;
+ Camera2SurfaceViewStubActivity stubActivity = getActivity();
+ final SurfaceHolder holder = getActivity().getSurfaceView().getHolder();
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
+ }
+ });
+
+ boolean res = stubActivity.waitForSurfaceSizeChanged(
+ WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(), mPreviewSize.getHeight());
+ assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res);
+ mPreviewSurface = holder.getSurface();
+ assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "start preview with size " + mPreviewSize.toString());
+ }
+
+ List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1);
+ outputSurfaces.add(mPreviewSurface);
+ configureCameraOutputs(mCamera, outputSurfaces, mCameraListener);
+
+ request.addTarget(mPreviewSurface);
+
+ mCamera.setRepeatingRequest(request.build(), listener, mHandler);
+ }
+ /**
+ * Stop preview for current camera device.
+ */
+ protected void stopPreview() throws Exception {
+ if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle");
+ // Stop repeat, wait for captures to complete, and disconnect from surfaces
+ configureCameraOutputs(mCamera, /*outputSurfaces*/null, mCameraListener);
+ }
+
+ /**
+ * Open a camera device and get the StaticMetadata for a given camera id.
+ *
+ * @param cameraId The id of the camera device to be opened.
+ */
+ protected void openDevice(String cameraId) throws Exception {
+ mCamera = CameraTestUtils.openCamera(
+ mCameraManager, cameraId, mCameraListener, mHandler);
+ assertNotNull("Failed to open camera device " + cameraId, mCamera);
+ mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId));
+ }
+
+ /**
+ * Close the current actively used camera device.
+ */
+ protected void closeDevice() {
+ if (mCamera != null) {
+ mCamera.close();
+ mCamera = null;
+ }
+ mStaticInfo = null;
+ }
+}