Add AppInstallTest for testing app install performance
Support measure install time for a list of apps under the test-apk-dir
Bug: 33777746
Change-Id: I7b656ddcc080d55cc5db251d0ec6cfbb65436f0d
diff --git a/prod-tests/src/com/android/performance/tests/AppInstallTest.java b/prod-tests/src/com/android/performance/tests/AppInstallTest.java
new file mode 100644
index 0000000..afdaa0b
--- /dev/null
+++ b/prod-tests/src/com/android/performance/tests/AppInstallTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.performance.tests;
+
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.AaptParser;
+
+import junit.framework.Assert;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+@OptionClass(alias = "app-install-perf")
+// Test framework that measures the install time for all apk files located under a given directory.
+// The test needs aapt to be in its path in order to determine the package name of the apk. The
+// package name is needed to clean up after the test is done.
+public class AppInstallTest implements IDeviceTest, IRemoteTest {
+
+ @Option(name = "test-apk-dir", description = "Directory that contains the test apks.",
+ importance= Option.Importance.ALWAYS)
+ private String mTestApkPath;
+
+ @Option(name = "test-label", description = "Unique test identifier label.")
+ private String mTestLabel = "AppInstallPerformance";
+
+ @Option(name = "test-start-delay",
+ description = "Delay in ms to wait for before starting the install test.")
+ private long mTestStartDelay = 60000;
+
+ private ITestDevice mDevice;
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public void setDevice(ITestDevice device) {
+ mDevice = device;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public ITestDevice getDevice() {
+ return mDevice;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+ // Delay test start time to give the background processes to finish.
+ if (mTestStartDelay > 0) {
+ try {
+ Thread.sleep(mTestStartDelay);
+ } catch (InterruptedException e) {
+ CLog.e("Failed to delay test: %s", e.toString());
+ }
+ }
+ Assert.assertFalse(mTestApkPath.isEmpty());
+ File apkDir = new File(mTestApkPath);
+ Assert.assertTrue(apkDir.isDirectory());
+ // Find all apks in directory.
+ String[] files = apkDir.list();
+ Map<String, String> metrics = new HashMap<String, String> ();
+ try {
+ for (String fileName : files) {
+ if (!fileName.endsWith(".apk")) {
+ CLog.d("Skipping non-apk %s", fileName);
+ continue;
+ }
+ File file = new File(apkDir, fileName);
+ // Install app and measure time.
+ String installTime = Long.toString(installAndTime(file));
+ metrics.put(fileName, installTime);
+ }
+ } finally {
+ reportMetrics(listener, mTestLabel, metrics);
+ }
+ }
+
+ /**
+ * Install file and time its install time. Cleans up after itself.
+ * @param packageFile apk file to install
+ * @return install time in msecs.
+ * @throws DeviceNotAvailableException
+ */
+ long installAndTime(File packageFile) throws DeviceNotAvailableException {
+ AaptParser parser = AaptParser.parse(packageFile);
+ String packageName = parser.getPackageName();
+
+ String remotePath = "/data/local/tmp/" + packageFile.getName();
+ if (!mDevice.pushFile(packageFile, remotePath)) {
+ throw new RuntimeException("Failed to push " + packageFile.getAbsolutePath());
+ }
+ long start = System.currentTimeMillis();
+ String output = mDevice.executeShellCommand(
+ String.format("pm install -r \"%s\"", remotePath));
+ long end = System.currentTimeMillis();
+ if (output == null || output.indexOf("Success") == -1) {
+ CLog.e("Failed to install package %s with error %s", packageFile, output);
+ return -1;
+ }
+ mDevice.executeShellCommand(String.format("rm \"%s\"", remotePath));
+ if (packageName != null) {
+ CLog.d("Uninstalling: %s", packageName);
+ mDevice.uninstallPackage(packageName);
+ }
+ return end - start;
+ }
+
+ /**
+ * Report run metrics by creating an empty test run to stick them in
+ *
+ * @param listener the {@link ITestInvocationListener} of test results
+ * @param runName the test name
+ * @param metrics the {@link Map} that contains metrics for the given test
+ */
+ void reportMetrics(ITestInvocationListener listener, String runName,
+ Map<String, String> metrics) {
+ // Create an empty testRun to report the parsed runMetrics
+ CLog.d("About to report metrics: %s", metrics);
+ listener.testRunStarted(runName, 0);
+ listener.testRunEnded(0, metrics);
+ }
+}