1 /* 2 3 * Copyright (C) 2014 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.cts.verifier.sensors.base; 19 20 import android.hardware.cts.helpers.reporting.ISensorTestNode; 21 22 import com.android.cts.verifier.sensors.reporting.SensorTestDetails; 23 import com.android.cts.verifier.sensors.reporting.SensorTestDetails.ResultCode; 24 25 import java.lang.reflect.InvocationTargetException; 26 import java.lang.reflect.Method; 27 import java.lang.reflect.Modifier; 28 import java.util.ArrayList; 29 import java.util.Iterator; 30 import java.util.List; 31 32 /** 33 * An Activity that provides a test execution engine for Sensor CtsVerifier tests. The tests are 34 * able to interact with an operator. 35 * 36 * Sub-classes reuse its own class definition to 'load' tests at runtime through reflection. 37 */ 38 public abstract class SensorCtsVerifierTestActivity extends BaseSensorTestActivity { 39 private volatile int mTestPassedCounter; 40 private volatile int mTestSkippedCounter; 41 private volatile int mTestFailedCounter; 42 private volatile ISensorTestNode mCurrentTestNode; 43 private volatile boolean mEnableRetry = false; 44 45 /** 46 * {@inheritDoc} 47 */ SensorCtsVerifierTestActivity( Class<? extends SensorCtsVerifierTestActivity> testClass)48 protected SensorCtsVerifierTestActivity( 49 Class<? extends SensorCtsVerifierTestActivity> testClass) { 50 super(testClass); 51 } 52 53 /** 54 * {@inheritDoc} 55 * Constructor to be used by subclasses. 56 * 57 * @param testClass The class that contains the tests. It is dependant on test executor 58 * implemented by subclasses. 59 * @param enableRetry Subclass can enable retry mechanism for subtests. 60 */ SensorCtsVerifierTestActivity( Class<? extends SensorCtsVerifierTestActivity> testClass, boolean enableRetry)61 protected SensorCtsVerifierTestActivity( 62 Class<? extends SensorCtsVerifierTestActivity> testClass, boolean enableRetry) { 63 super(testClass); 64 mEnableRetry = enableRetry; 65 } 66 67 /** 68 * Executes Semi-automated Sensor tests. 69 * Execution is driven by this class, and allows discovery of tests using reflection. 70 */ 71 @Override executeTests()72 protected SensorTestDetails executeTests() throws InterruptedException { 73 // TODO: use reporting to log individual test results 74 Iterator<Method> testMethodIt = findTestMethods().iterator(); 75 while (testMethodIt.hasNext()) { 76 Method testMethod = testMethodIt.next(); 77 boolean isLastSubtest = !testMethodIt.hasNext(); 78 getTestLogger().logTestStart(testMethod.getName()); 79 SensorTestDetails testDetails = executeTest(testMethod); 80 getTestLogger().logTestDetails(testDetails); 81 82 // If tests enable retry and get failed result, trigger the retry process. 83 while (mEnableRetry && testDetails.getResultCode().equals(ResultCode.FAIL)) { 84 if (isLastSubtest) { 85 waitForUserToFinish(); 86 } else { 87 waitForUserToRetry(); 88 } 89 if (!getShouldRetry()) { 90 break; 91 } 92 mTestFailedCounter--; 93 testDetails = executeTest(testMethod); 94 getTestLogger().logTestDetails(testDetails); 95 } 96 } 97 return new SensorTestDetails( 98 getApplicationContext(), 99 getTestClassName(), 100 mTestPassedCounter, 101 mTestSkippedCounter, 102 mTestFailedCounter); 103 } 104 getCurrentTestNode()105 protected ISensorTestNode getCurrentTestNode() { 106 return mCurrentTestNode; 107 } 108 findTestMethods()109 private List<Method> findTestMethods() { 110 ArrayList<Method> testMethods = new ArrayList<>(); 111 for (Method method : mTestClass.getDeclaredMethods()) { 112 if (Modifier.isPublic(method.getModifiers()) 113 && method.getParameterTypes().length == 0 114 && method.getName().startsWith("test") 115 && method.getReturnType().equals(String.class)) { 116 testMethods.add(method); 117 } 118 } 119 return testMethods; 120 } 121 executeTest(Method testMethod)122 private SensorTestDetails executeTest(Method testMethod) throws InterruptedException { 123 String testMethodName = testMethod.getName(); 124 String testName = String.format("%s#%s", getTestClassName(), testMethodName); 125 mCurrentTestNode = new TestNode(testMethod); 126 127 SensorTestDetails testDetails; 128 try { 129 String testSummary = (String) testMethod.invoke(this); 130 testDetails = 131 new SensorTestDetails(testName, SensorTestDetails.ResultCode.PASS, testSummary); 132 } catch (InvocationTargetException e) { 133 // get the inner exception, because we use reflection APIs to execute the test 134 testDetails = new SensorTestDetails(testName, "TestExecution", e.getCause()); 135 } catch (Throwable e) { 136 testDetails = new SensorTestDetails(testName, "TestInfrastructure", e); 137 } 138 139 SensorTestDetails.ResultCode resultCode = testDetails.getResultCode(); 140 switch(resultCode) { 141 case PASS: 142 // Warning should still be treated as a pass, but with more detail to the test runner. 143 case WARNING: 144 ++mTestPassedCounter; 145 break; 146 case SKIPPED: 147 ++mTestSkippedCounter; 148 break; 149 case INTERRUPTED: 150 throw new InterruptedException(); 151 case FAIL: 152 ++mTestFailedCounter; 153 break; 154 default: 155 throw new IllegalStateException("Unknown ResultCode: " + resultCode); 156 } 157 158 return testDetails; 159 } 160 161 private class TestNode implements ISensorTestNode { 162 private final Method mTestMethod; 163 TestNode(Method testMethod)164 public TestNode(Method testMethod) { 165 mTestMethod = testMethod; 166 } 167 168 @Override getName()169 public String getName() { 170 return mTestClass.getSimpleName() + "_" + mTestMethod.getName(); 171 } 172 } 173 174 /** 175 * Show the instruction for the first time execution and wait for user to begin the test. 176 * 177 * @param descriptionResId The description for the first time execution. 178 */ setFirstExecutionInstruction(int ... descriptionResId)179 protected void setFirstExecutionInstruction(int ... descriptionResId) throws Throwable { 180 if (!getShouldRetry()) { 181 SensorTestLogger logger = getTestLogger(); 182 for (int id : descriptionResId) { 183 logger.logInstructions(id); 184 } 185 waitForUserToBegin(); 186 } 187 } 188 } 189