1 /* 2 * Copyright 2018 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.cts.verifier.camera.performance; 18 19 import android.app.AlertDialog; 20 import android.app.Instrumentation; 21 import android.app.ProgressDialog; 22 import android.content.Context; 23 import android.hardware.camera2.cts.PerformanceTest; 24 import android.hardware.cts.LegacyCameraPerformanceTest; 25 import android.os.Bundle; 26 import android.util.Log; 27 28 import androidx.test.InstrumentationRegistry; 29 30 import com.android.compatibility.common.util.ReportLog.Metric; 31 import com.android.cts.verifier.ArrayTestListAdapter; 32 import com.android.cts.verifier.DialogTestListActivity; 33 import com.android.cts.verifier.R; 34 import com.android.cts.verifier.TestResult; 35 36 import org.junit.runner.Description; 37 import org.junit.runner.JUnitCore; 38 import org.junit.runner.Request; 39 import org.junit.runner.Result; 40 import org.junit.runner.notification.Failure; 41 import org.junit.runner.notification.RunListener; 42 43 import java.lang.annotation.Annotation; 44 import java.lang.reflect.Method; 45 import java.util.ArrayList; 46 import java.util.HashMap; 47 import java.util.Set; 48 import java.util.concurrent.ExecutorService; 49 import java.util.concurrent.Executors; 50 51 /** 52 * This test checks the camera performance by running the respective CTS performance test cases 53 * and collecting the corresponding KPIs. 54 */ 55 public class CameraPerformanceActivity extends DialogTestListActivity { 56 private static final String TAG = "CameraPerformanceActivity"; 57 private static final Class[] TEST_CLASSES = 58 { PerformanceTest.class, LegacyCameraPerformanceTest.class }; 59 60 private ExecutorService mExecutorService; 61 private CameraTestInstrumentation mCameraInstrumentation = new CameraTestInstrumentation(); 62 private Instrumentation mCachedInstrumentation; 63 private Bundle mCachedInstrumentationArgs; 64 private HashMap<String, Class> mTestCaseMap = new HashMap<String, Class>(); 65 private ProgressDialog mSpinnerDialog; 66 private AlertDialog mResultDialog; 67 private ArrayList<Metric> mResults = new ArrayList<Metric>(); 68 CameraPerformanceActivity()69 public CameraPerformanceActivity() { 70 super(R.layout.camera_performance, R.string.camera_performance_test, 71 R.string.camera_performance_test_info, R.string.camera_performance_test_info); 72 } 73 executeTest(Class testClass, String testName)74 private void executeTest(Class testClass, String testName) { 75 JUnitCore testRunner = new JUnitCore(); 76 Log.v(TAG, String.format("Execute Test: %s#%s", testClass.getSimpleName(), testName)); 77 Request request = Request.method(testClass, testName); 78 testRunner.addListener(new CameraRunListener()); 79 testRunner.run(request); 80 } 81 82 private class MetricListener implements CameraTestInstrumentation.MetricListener { 83 @Override onResultMetric(Metric metric)84 public void onResultMetric(Metric metric) { 85 runOnUiThread(new Runnable() { 86 @Override 87 public void run() { 88 mResults.add(metric); 89 } 90 }); 91 } 92 } 93 94 /** 95 * Basic {@link RunListener} implementation. 96 * It is only used to handle logging into the UI. 97 */ 98 private class CameraRunListener extends RunListener { 99 private volatile boolean mCurrentTestReported; 100 101 @Override testRunStarted(Description description)102 public void testRunStarted(Description description) { 103 runOnUiThread(new Runnable() { 104 @Override 105 public void run() { 106 mResults.clear(); 107 mSpinnerDialog.show(); 108 } 109 }); 110 } 111 112 @Override testRunFinished(Result result)113 public void testRunFinished(Result result) { 114 runOnUiThread(new Runnable() { 115 @Override 116 public void run() { 117 mSpinnerDialog.dismiss(); 118 119 if (!mResults.isEmpty()) { 120 StringBuilder message = new StringBuilder(); 121 for (Metric m : mResults) { 122 message.append(String.format("%s : %5.2f %s\n", 123 m.getMessage().replaceAll("_", " "), m.getValues()[0], 124 m.getUnit())); 125 } 126 mResultDialog.setMessage(message); 127 mResultDialog.show(); 128 } 129 130 mResults.clear(); 131 } 132 }); 133 } 134 135 @Override testStarted(Description description)136 public void testStarted(Description description) { 137 mCurrentTestReported = false; 138 } 139 140 @Override testFinished(Description description)141 public void testFinished(Description description) { 142 if (!mCurrentTestReported) { 143 runOnUiThread(new Runnable() { 144 @Override 145 public void run() { 146 setTestResult(description.getMethodName(), TestResult.TEST_RESULT_PASSED); 147 } 148 }); 149 } 150 } 151 152 @Override testFailure(Failure failure)153 public void testFailure(Failure failure) { 154 mCurrentTestReported = true; 155 156 runOnUiThread(new Runnable() { 157 @Override 158 public void run() { 159 setTestResult(failure.getDescription().getMethodName(), 160 TestResult.TEST_RESULT_FAILED); 161 mSpinnerDialog.dismiss(); 162 mResults.clear(); 163 String message = new String(); 164 String failureMessage = failure.getMessage(); 165 String failureTrace = failure.getTrace(); 166 if ((failureMessage != null) && (!failureMessage.isEmpty())) { 167 message = failureMessage + "\n"; 168 } else if ((failureTrace != null) && (!failureTrace.isEmpty())) { 169 message += failureTrace; 170 } 171 172 if (!message.isEmpty()) { 173 mResultDialog.setMessage(message); 174 mResultDialog.show(); 175 } 176 } 177 }); 178 } 179 180 @Override testAssumptionFailure(Failure failure)181 public void testAssumptionFailure(Failure failure) { 182 mCurrentTestReported = true; 183 } 184 185 @Override testIgnored(Description description)186 public void testIgnored(Description description) { 187 mCurrentTestReported = true; 188 } 189 } 190 initializeTestCases(Context ctx)191 private void initializeTestCases(Context ctx) { 192 for (Class testClass : TEST_CLASSES) { 193 Log.v(TAG, String.format("Test class: %s", testClass.getSimpleName())); 194 for (Method method : testClass.getMethods()) { 195 Annotation an = method.getAnnotation((Class) org.junit.Test.class); 196 Log.v(TAG, String.format("Test method: %s; Annotation: %s", method.getName(), an)); 197 if (an == null) { 198 continue; 199 } 200 mTestCaseMap.put(method.getName(), testClass); 201 } 202 } 203 } 204 205 @Override onCreate(Bundle savedInstanceState)206 protected void onCreate(Bundle savedInstanceState) { 207 // Need to enumerate and initialize the available test cases first 208 // before calling the base 'onCreate' implementation. 209 initializeTestCases(getApplicationContext()); 210 211 super.onCreate(savedInstanceState); 212 213 String spinnerMessage = (String) getResources().getString( 214 R.string.camera_performance_spinner_text); 215 String resultTitle = (String) getResources().getString( 216 R.string.camera_performance_result_title); 217 mSpinnerDialog = new ProgressDialog(this); 218 mSpinnerDialog.setIndeterminate(true); 219 mSpinnerDialog.setCancelable(false); 220 mSpinnerDialog.setMessage(spinnerMessage); 221 mResultDialog = 222 new AlertDialog.Builder(this).setCancelable(true).setTitle(resultTitle).create(); 223 224 } 225 226 private class TestListItem extends DialogTestListActivity.DialogTestListItem { 227 private String mTestId; 228 TestListItem(Context context, String nameId, String testId)229 public TestListItem(Context context, String nameId, String testId) { 230 super(context, nameId, testId); 231 mTestId = testId; 232 } 233 234 @Override performTest(DialogTestListActivity activity)235 public void performTest(DialogTestListActivity activity) { 236 Class testClass = mTestCaseMap.get(mTestId); 237 if (testClass == null) { 238 Log.e(TAG, "Test case with name: " + mTestId + " not found!"); 239 return; 240 } 241 242 mExecutorService.execute(new Runnable() { 243 @Override 244 public void run() { 245 executeTest(testClass, mTestId); 246 } 247 }); 248 } 249 } 250 251 @Override setupTests(ArrayTestListAdapter adapter)252 protected void setupTests(ArrayTestListAdapter adapter) { 253 Set<String> testCaseNames = mTestCaseMap.keySet(); 254 for (String testCaseName : testCaseNames) { 255 adapter.add(new TestListItem(this, testCaseName, testCaseName)); 256 } 257 } 258 259 @Override onResume()260 protected void onResume() { 261 super.onResume(); 262 263 mCameraInstrumentation.initialize(this, new MetricListener()); 264 265 try { 266 mCachedInstrumentation = InstrumentationRegistry.getInstrumentation(); 267 mCachedInstrumentationArgs = InstrumentationRegistry.getArguments(); 268 } catch (IllegalStateException e) { 269 // This is expected in case there was no prior instrumentation. 270 } 271 InstrumentationRegistry.registerInstance(mCameraInstrumentation, new Bundle()); 272 273 mExecutorService = Executors.newSingleThreadExecutor(); 274 } 275 276 @Override onPause()277 protected void onPause() { 278 super.onPause(); 279 280 // Terminate any running test cases. 281 mExecutorService.shutdownNow(); 282 283 // Restore any cached instrumentation. 284 if ((mCachedInstrumentation != null) && (mCachedInstrumentationArgs != null)) { 285 InstrumentationRegistry.registerInstance(mCachedInstrumentation, 286 mCachedInstrumentationArgs); 287 } 288 mCameraInstrumentation.release(); 289 } 290 }