1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.camera.activity; 18 19 import android.app.Activity; 20 import android.app.Instrumentation; 21 import android.hardware.Camera; 22 import android.hardware.Camera.AutoFocusCallback; 23 import android.hardware.Camera.CameraInfo; 24 import android.hardware.Camera.Parameters; 25 import android.hardware.Camera.PictureCallback; 26 import android.hardware.Camera.ShutterCallback; 27 import android.test.ActivityInstrumentationTestCase2; 28 import android.view.KeyEvent; 29 import android.view.MotionEvent; 30 import android.view.View; 31 32 import com.android.camera.CameraHolder; 33 import com.android.camera.CameraManager.CameraProxy; 34 import com.android.camera.R; 35 import com.android.camera.Util; 36 37 import static com.google.testing.littlemock.LittleMock.mock; 38 import static com.google.testing.littlemock.LittleMock.doAnswer; 39 import static com.google.testing.littlemock.LittleMock.doReturn; 40 import static com.google.testing.littlemock.LittleMock.anyObject; 41 import com.google.testing.littlemock.AppDataDirGuesser; 42 import com.google.testing.littlemock.ArgumentCaptor; 43 import com.google.testing.littlemock.Captor; 44 import com.google.testing.littlemock.LittleMock; 45 import com.google.testing.littlemock.Mock; 46 47 import java.io.File; 48 import java.io.ByteArrayOutputStream; 49 import java.io.InputStream; 50 import java.io.IOException; 51 import java.util.concurrent.Callable; 52 53 54 public class CameraTestCase<T extends Activity> extends ActivityInstrumentationTestCase2<T> { 55 protected CameraInfo mCameraInfo[]; 56 protected CameraProxy mMockCamera[]; 57 protected CameraInfo mOneCameraInfo[]; 58 protected CameraProxy mOneMockCamera[]; 59 private static Parameters mParameters; 60 private byte[] mBlankJpeg; 61 @Mock private CameraProxy mMockBackCamera; 62 @Mock private CameraProxy mMockFrontCamera; 63 @Captor private ArgumentCaptor<ShutterCallback> mShutterCallback; 64 @Captor private ArgumentCaptor<PictureCallback> mRawPictureCallback; 65 @Captor private ArgumentCaptor<PictureCallback> mJpegPictureCallback; 66 @Captor private ArgumentCaptor<AutoFocusCallback> mAutoFocusCallback; 67 Callable<Object> mAutoFocusCallable = new AutoFocusCallable(); 68 Callable<Object> mTakePictureCallable = new TakePictureCallable(); 69 70 private class TakePictureCallable implements Callable<Object> { 71 @Override call()72 public Object call() throws Exception { 73 Runnable runnable = new Runnable() { 74 @Override 75 public void run() { 76 readBlankJpeg(); 77 Camera camera = mOneMockCamera[0].getCamera(); 78 mShutterCallback.getValue().onShutter(); 79 mRawPictureCallback.getValue().onPictureTaken(null, camera); 80 mJpegPictureCallback.getValue().onPictureTaken(mBlankJpeg, camera); 81 } 82 }; 83 // Probably need some delay. Make sure shutter callback is called 84 // after onShutterButtonFocus(false). 85 getActivity().findViewById(R.id.gl_root_view).postDelayed(runnable, 50); 86 return null; 87 } 88 } 89 90 private class AutoFocusCallable implements Callable<Object> { 91 @Override call()92 public Object call() throws Exception { 93 Runnable runnable = new Runnable() { 94 @Override 95 public void run() { 96 Camera camera = mOneMockCamera[0].getCamera(); 97 mAutoFocusCallback.getValue().onAutoFocus(true, camera); 98 } 99 }; 100 // Need some delay. Otherwise, focus callback will be run before 101 // onShutterButtonClick 102 getActivity().findViewById(R.id.gl_root_view).postDelayed(runnable, 50); 103 return null; 104 } 105 } 106 CameraTestCase(Class<T> activityClass)107 public CameraTestCase(Class<T> activityClass) { 108 super(activityClass); 109 } 110 111 @Override setUp()112 protected void setUp() throws Exception { 113 super.setUp(); 114 AppDataDirGuesser.setInstance(new AppDataDirGuesser() { 115 @Override 116 public File guessSuitableDirectoryForGeneratedClasses() { 117 return getInstrumentation().getTargetContext().getCacheDir(); 118 } 119 }); 120 AppDataDirGuesser.getsInstance().guessSuitableDirectoryForGeneratedClasses(); 121 LittleMock.initMocks(this); 122 mCameraInfo = new CameraInfo[2]; 123 mCameraInfo[0] = new CameraInfo(); 124 mCameraInfo[0].facing = CameraInfo.CAMERA_FACING_BACK; 125 mCameraInfo[1] = new CameraInfo(); 126 mCameraInfo[1].facing = CameraInfo.CAMERA_FACING_FRONT; 127 mMockCamera = new CameraProxy[2]; 128 mMockCamera[0] = mMockBackCamera; 129 mMockCamera[1] = mMockFrontCamera; 130 doReturn(getParameters()).when(mMockCamera[0]).getParameters(); 131 doReturn(getParameters()).when(mMockCamera[1]).getParameters(); 132 133 mOneCameraInfo = new CameraInfo[1]; 134 mOneCameraInfo[0] = new CameraInfo(); 135 mOneCameraInfo[0].facing = CameraInfo.CAMERA_FACING_BACK; 136 mOneMockCamera = new CameraProxy[1]; 137 mOneMockCamera[0] = mMockBackCamera; 138 doReturn(getParameters()).when(mOneMockCamera[0]).getParameters(); 139 140 // Mock takePicture call. 141 doAnswer(mTakePictureCallable).when(mMockBackCamera).takePicture( 142 mShutterCallback.capture(), mRawPictureCallback.capture(), 143 (PictureCallback) anyObject(), mJpegPictureCallback.capture()); 144 145 // Mock autoFocus call. 146 doAnswer(mAutoFocusCallable).when(mMockBackCamera).autoFocus( 147 mAutoFocusCallback.capture()); 148 } 149 readBlankJpeg()150 private void readBlankJpeg() { 151 InputStream ins = getActivity().getResources().openRawResource(R.raw.blank); 152 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 153 int size = 0; 154 155 // Read the entire resource into a local byte buffer. 156 byte[] buffer = new byte[1024]; 157 try { 158 while((size = ins.read(buffer, 0, 1024)) >= 0){ 159 outputStream.write(buffer, 0, size); 160 } 161 } catch (IOException e) { 162 // ignore 163 } finally { 164 Util.closeSilently(ins); 165 } 166 mBlankJpeg = outputStream.toByteArray(); 167 } 168 169 @Override tearDown()170 protected void tearDown() throws Exception { 171 super.tearDown(); 172 CameraHolder.injectMockCamera(null, null); 173 } 174 internalTestFailToConnect()175 protected void internalTestFailToConnect() throws Exception { 176 CameraHolder.injectMockCamera(mCameraInfo, null); 177 178 getActivity(); 179 Instrumentation inst = getInstrumentation(); 180 inst.waitForIdleSync(); 181 inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); // close dialog 182 } 183 performClick(final int id)184 protected void performClick(final int id) { 185 Activity activity = getActivity(); 186 getInstrumentation().waitForIdleSync(); 187 assertNotNull(activity.findViewById(id)); 188 Instrumentation inst = getInstrumentation(); 189 inst.runOnMainSync(new Runnable() { 190 @Override 191 public void run() { 192 View v = getActivity().findViewById(id); 193 float x = (v.getLeft() + v.getRight()) / 2; 194 float y = (v.getTop() + v.getBottom()) / 2; 195 MotionEvent down = MotionEvent.obtain(0, 0, 196 MotionEvent.ACTION_DOWN, x, y, 0, 0, 0, 0, 0, 0, 0); 197 MotionEvent up = MotionEvent.obtain(0, 0, 198 MotionEvent.ACTION_UP, x, y, 0, 0, 0, 0, 0, 0, 0); 199 View parent = (View) v.getParent(); 200 parent.dispatchTouchEvent(down); 201 parent.dispatchTouchEvent(up); 202 } 203 }); 204 inst.waitForIdleSync(); 205 } 206 assertViewNotExist(int id)207 protected void assertViewNotExist(int id) { 208 Activity activity = getActivity(); 209 getInstrumentation().waitForIdleSync(); 210 assertNull(activity.findViewById(id)); 211 } 212 assertViewNotVisible(int id)213 protected void assertViewNotVisible(int id) { 214 Activity activity = getActivity(); 215 getInstrumentation().waitForIdleSync(); 216 View view = activity.findViewById(id); 217 assertTrue(view.getVisibility() != View.VISIBLE); 218 } 219 getParameters()220 protected static Parameters getParameters() { 221 synchronized (CameraTestCase.class) { 222 if (mParameters == null) { 223 mParameters = android.hardware.Camera.getEmptyParameters(); 224 mParameters.unflatten("preview-format-values=yuv420sp,yuv420p,yuv422i-yuyv,yuv420p;" + 225 "preview-format=yuv420sp;" + 226 "preview-size-values=800x480;preview-size=800x480;" + 227 "picture-size-values=320x240;picture-size=320x240;" + 228 "jpeg-thumbnail-size-values=320x240,0x0;jpeg-thumbnail-width=320;jpeg-thumbnail-height=240;" + 229 "jpeg-thumbnail-quality=60;jpeg-quality=95;" + 230 "preview-frame-rate-values=30,15;preview-frame-rate=30;" + 231 "focus-mode-values=continuous-video,auto,macro,infinity,continuous-picture;focus-mode=auto;" + 232 "preview-fps-range-values=(15000,30000);preview-fps-range=15000,30000;" + 233 "scene-mode-values=auto,action,night;scene-mode=auto;" + 234 "flash-mode-values=off,on,auto,torch;flash-mode=off;" + 235 "whitebalance-values=auto,daylight,fluorescent,incandescent;whitebalance=auto;" + 236 "effect-values=none,mono,sepia;effect=none;" + 237 "zoom-supported=true;zoom-ratios=100,200,400;max-zoom=2;" + 238 "picture-format-values=jpeg;picture-format=jpeg;" + 239 "min-exposure-compensation=-30;max-exposure-compensation=30;" + 240 "exposure-compensation=0;exposure-compensation-step=0.1;" + 241 "horizontal-view-angle=40;vertical-view-angle=40;"); 242 } 243 } 244 return mParameters; 245 } 246 } 247