1 /* 2 * Copyright (C) 2016 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.performance.tests; 18 19 import com.android.tradefed.config.Option; 20 import com.android.tradefed.config.OptionClass; 21 import com.android.tradefed.device.DeviceNotAvailableException; 22 import com.android.tradefed.device.ITestDevice; 23 import com.android.tradefed.log.LogUtil.CLog; 24 import com.android.tradefed.result.ITestInvocationListener; 25 import com.android.tradefed.testtype.IDeviceTest; 26 import com.android.tradefed.testtype.IRemoteTest; 27 import com.android.tradefed.util.AaptParser; 28 import com.android.tradefed.util.proto.TfMetricProtoUtil; 29 30 import org.junit.Assert; 31 32 import java.io.File; 33 import java.util.HashMap; 34 import java.util.Map; 35 36 @OptionClass(alias = "app-install-perf") 37 // Test framework that measures the install time for all apk files located under a given directory. 38 // The test needs aapt to be in its path in order to determine the package name of the apk. The 39 // package name is needed to clean up after the test is done. 40 public class AppInstallTest implements IDeviceTest, IRemoteTest { 41 42 @Option(name = "test-apk-dir", description = "Directory that contains the test apks.", 43 importance= Option.Importance.ALWAYS) 44 private String mTestApkPath; 45 46 @Option(name = "test-label", description = "Unique test identifier label.") 47 private String mTestLabel = "AppInstallPerformance"; 48 49 @Option(name = "test-start-delay", 50 description = "Delay in ms to wait for before starting the install test.") 51 private long mTestStartDelay = 60000; 52 53 private ITestDevice mDevice; 54 55 /* 56 * {@inheritDoc} 57 */ 58 @Override 59 public void setDevice(ITestDevice device) { 60 mDevice = device; 61 } 62 63 /* 64 * {@inheritDoc} 65 */ 66 @Override 67 public ITestDevice getDevice() { 68 return mDevice; 69 } 70 71 /* 72 * {@inheritDoc} 73 */ 74 @Override 75 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 76 // Delay test start time to give the background processes to finish. 77 if (mTestStartDelay > 0) { 78 try { 79 Thread.sleep(mTestStartDelay); 80 } catch (InterruptedException e) { 81 CLog.e("Failed to delay test: %s", e.toString()); 82 } 83 } 84 Assert.assertFalse(mTestApkPath.isEmpty()); 85 File apkDir = new File(mTestApkPath); 86 Assert.assertTrue(apkDir.isDirectory()); 87 // Find all apks in directory. 88 String[] files = apkDir.list(); 89 Map<String, String> metrics = new HashMap<String, String> (); 90 try { 91 for (String fileName : files) { 92 if (!fileName.endsWith(".apk")) { 93 CLog.d("Skipping non-apk %s", fileName); 94 continue; 95 } 96 File file = new File(apkDir, fileName); 97 // Install app and measure time. 98 String installTime = Long.toString(installAndTime(file)); 99 metrics.put(fileName, installTime); 100 } 101 } finally { 102 reportMetrics(listener, mTestLabel, metrics); 103 } 104 } 105 106 /** 107 * Install file and time its install time. Cleans up after itself. 108 * @param packageFile apk file to install 109 * @return install time in msecs. 110 * @throws DeviceNotAvailableException 111 */ 112 long installAndTime(File packageFile) throws DeviceNotAvailableException { 113 AaptParser parser = AaptParser.parse(packageFile); 114 String packageName = parser.getPackageName(); 115 116 String remotePath = "/data/local/tmp/" + packageFile.getName(); 117 if (!mDevice.pushFile(packageFile, remotePath)) { 118 throw new RuntimeException("Failed to push " + packageFile.getAbsolutePath()); 119 } 120 long start = System.currentTimeMillis(); 121 String output = mDevice.executeShellCommand( 122 String.format("pm install -r \"%s\"", remotePath)); 123 long end = System.currentTimeMillis(); 124 if (output == null || output.indexOf("Success") == -1) { 125 CLog.e("Failed to install package %s with error %s", packageFile, output); 126 return -1; 127 } 128 mDevice.executeShellCommand(String.format("rm \"%s\"", remotePath)); 129 if (packageName != null) { 130 CLog.d("Uninstalling: %s", packageName); 131 mDevice.uninstallPackage(packageName); 132 } 133 return end - start; 134 } 135 136 /** 137 * Report run metrics by creating an empty test run to stick them in 138 * 139 * @param listener the {@link ITestInvocationListener} of test results 140 * @param runName the test name 141 * @param metrics the {@link Map} that contains metrics for the given test 142 */ 143 void reportMetrics(ITestInvocationListener listener, String runName, 144 Map<String, String> metrics) { 145 // Create an empty testRun to report the parsed runMetrics 146 CLog.d("About to report metrics: %s", metrics); 147 listener.testRunStarted(runName, 0); 148 listener.testRunEnded(0, TfMetricProtoUtil.upgradeConvert(metrics)); 149 } 150 } 151