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