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 }