1 /* 2 * Copyright (C) 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 android.atrace.cts; 18 19 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 20 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 21 import com.android.tradefed.build.IBuildInfo; 22 import com.android.tradefed.device.DeviceNotAvailableException; 23 import com.android.tradefed.log.LogUtil; 24 import com.android.tradefed.result.CollectingTestListener; 25 import com.android.tradefed.result.TestDescription; 26 import com.android.tradefed.result.TestResult; 27 import com.android.tradefed.result.TestRunResult; 28 import com.android.tradefed.testtype.DeviceTestCase; 29 import com.android.tradefed.testtype.IBuildReceiver; 30 31 import trebuchet.io.BufferProducer; 32 import trebuchet.io.DataSlice; 33 import trebuchet.model.Model; 34 import trebuchet.task.ImportTask; 35 import trebuchet.util.PrintlnImportFeedback; 36 37 import java.io.FileNotFoundException; 38 import java.nio.charset.StandardCharsets; 39 import java.util.Map; 40 import java.util.concurrent.TimeUnit; 41 42 public class AtraceHostTestBase extends DeviceTestCase implements IBuildReceiver { 43 private static final String TEST_RUNNER = "androidx.test.runner.AndroidJUnitRunner"; 44 private static final String TEST_APK = "CtsAtraceTestApp.apk"; 45 // TODO: Make private 46 protected static final String TEST_PKG = "com.android.cts.atracetestapp"; 47 private static final String TEST_CLASS = "com.android.cts.atracetestapp.AtraceDeviceTests"; 48 49 private static final String START_TRACE_CMD = "atrace --async_start -a \\* -c -b 16000"; 50 private static final String START_TRACE_NO_APP_CMD = "atrace --async_start -c -b 16000"; 51 private static final String STOP_TRACE_CMD = "atrace --async_stop"; 52 53 private IBuildInfo mCtsBuild; 54 private boolean mIsInstalled = false; 55 56 /** 57 * {@inheritDoc} 58 */ 59 @Override setBuild(IBuildInfo buildInfo)60 public void setBuild(IBuildInfo buildInfo) { 61 mCtsBuild = buildInfo; 62 } 63 64 /** 65 * Install a device side test package. 66 * 67 * @param appFileName Apk file name, such as "CtsNetStatsApp.apk". 68 * @param grantPermissions whether to give runtime permissions. 69 */ installPackage(String appFileName, boolean grantPermissions)70 private void installPackage(String appFileName, boolean grantPermissions) 71 throws FileNotFoundException, DeviceNotAvailableException { 72 LogUtil.CLog.d("Installing app " + appFileName); 73 CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild); 74 final String result = getDevice().installPackage( 75 buildHelper.getTestFile(appFileName), true, grantPermissions); 76 assertNull("Failed to install " + appFileName + ": " + result, result); 77 } 78 requireApk()79 private final void requireApk() { 80 if (mIsInstalled) return; 81 try { 82 System.out.println("Installing APK"); 83 installPackage(TEST_APK, true); 84 mIsInstalled = true; 85 } catch (FileNotFoundException | DeviceNotAvailableException e) { 86 e.printStackTrace(); 87 throw new RuntimeException(e); 88 } 89 } 90 turnScreenOn()91 protected final void turnScreenOn() { 92 shell("input keyevent KEYCODE_WAKEUP"); 93 shell("wm dismiss-keyguard"); 94 } 95 96 @Override run(junit.framework.TestResult result)97 public void run(junit.framework.TestResult result) { 98 try { 99 super.run(result); 100 } finally { 101 try { 102 // We don't have the equivalent of @AfterClass, but this basically does that 103 System.out.println("Uninstalling APK"); 104 getDevice().uninstallPackage(TEST_PKG); 105 mIsInstalled = false; 106 } catch (DeviceNotAvailableException e) {} 107 } 108 } 109 110 @Override setUp()111 protected void setUp() throws Exception { 112 try { 113 shell("atrace --async_stop"); 114 } finally { 115 super.setUp(); 116 } 117 } 118 119 @Override tearDown()120 protected void tearDown() throws Exception { 121 try { 122 shell("atrace --async_stop"); 123 } finally { 124 super.tearDown(); 125 } 126 } 127 128 /** 129 * Run a device side test. 130 * 131 * @param pkgName Test package name, such as "com.android.server.cts.netstats". 132 * @param testClassName Test class name; either a fully qualified name, or "." + a class name. 133 * @param testMethodName Test method name. 134 * @throws DeviceNotAvailableException 135 */ runDeviceTests(String pkgName, String testClassName, String testMethodName)136 private PidTidPair runDeviceTests(String pkgName, 137 String testClassName, String testMethodName) 138 throws DeviceNotAvailableException { 139 if (testClassName != null && testClassName.startsWith(".")) { 140 testClassName = pkgName + testClassName; 141 } 142 143 RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner( 144 pkgName, TEST_RUNNER, getDevice().getIDevice()); 145 testRunner.setMaxTimeout(60, TimeUnit.SECONDS); 146 if (testClassName != null && testMethodName != null) { 147 testRunner.setMethodName(testClassName, testMethodName); 148 } else if (testClassName != null) { 149 testRunner.setClassName(testClassName); 150 } 151 152 CollectingTestListener listener = new CollectingTestListener(); 153 assertTrue(getDevice().runInstrumentationTests(testRunner, listener)); 154 155 final TestRunResult result = listener.getCurrentRunResults(); 156 if (result.isRunFailure()) { 157 throw new AssertionError("Failed to successfully run device tests for " 158 + result.getName() + ": " + result.getRunFailureMessage()); 159 } 160 if (result.getNumTests() == 0) { 161 throw new AssertionError("No tests were run on the device"); 162 } 163 164 if (result.hasFailedTests()) { 165 // build a meaningful error message 166 StringBuilder errorBuilder = new StringBuilder("On-device tests failed:\n"); 167 for (Map.Entry<TestDescription, TestResult> resultEntry : 168 result.getTestResults().entrySet()) { 169 if (!resultEntry.getValue().getStatus().equals( 170 com.android.ddmlib.testrunner.TestResult.TestStatus.PASSED)) { 171 errorBuilder.append(resultEntry.getKey().toString()); 172 errorBuilder.append(":\n"); 173 errorBuilder.append(resultEntry.getValue().getStackTrace()); 174 } 175 } 176 throw new AssertionError(errorBuilder.toString()); 177 } 178 return new PidTidPair(result); 179 } 180 runAppTest(String testname)181 private final PidTidPair runAppTest(String testname) { 182 requireApk(); 183 try { 184 return runDeviceTests(TEST_PKG, TEST_CLASS, testname); 185 } catch (DeviceNotAvailableException e) { 186 throw new RuntimeException(e); 187 } 188 } 189 shell(String command, String... args)190 protected final String shell(String command, String... args) { 191 if (args != null && args.length > 0) { 192 command += " " + String.join(" ", args); 193 } 194 try { 195 return getDevice().executeShellCommand(command); 196 } catch (DeviceNotAvailableException ex) { 197 throw new RuntimeException(ex); 198 } 199 } 200 201 private static class StringAdapter implements BufferProducer { 202 private byte[] data; 203 private boolean hasRead = false; 204 StringAdapter(String str)205 StringAdapter(String str) { 206 this.data = str.getBytes(StandardCharsets.UTF_8); 207 } 208 209 @Override next()210 public DataSlice next() { 211 if (!hasRead) { 212 hasRead = true; 213 return new DataSlice(data); 214 } 215 return null; 216 } 217 218 @Override close()219 public void close() { 220 hasRead = true; 221 } 222 } 223 parse(String traceOutput)224 private Model parse(String traceOutput) { 225 ImportTask importTask = new ImportTask(new PrintlnImportFeedback()); 226 return importTask.importTrace(new StringAdapter(traceOutput)); 227 } 228 runSingleAppTest(AtraceDeviceTestList test)229 protected final PidTidPair runSingleAppTest(AtraceDeviceTestList test) { 230 return runAppTest(test.toString()); 231 } 232 traceSingleTest(AtraceDeviceTestList test, boolean withAppTracing, String... categories)233 protected final TraceResult traceSingleTest(AtraceDeviceTestList test, boolean withAppTracing, 234 String... categories) { 235 requireApk(); 236 shell(withAppTracing ? START_TRACE_CMD : START_TRACE_NO_APP_CMD, categories); 237 PidTidPair pidTid = runSingleAppTest(test); 238 String traceOutput = shell("atrace --async_stop", categories); 239 assertNotNull("unable to capture atrace output", traceOutput); 240 return new TraceResult(pidTid, parse(traceOutput)); 241 } 242 traceSingleTest(AtraceDeviceTestList test, String... categories)243 protected final TraceResult traceSingleTest(AtraceDeviceTestList test, String... categories) { 244 return traceSingleTest(test, true, categories); 245 } 246 } 247