Home | History | Annotate | Download | only in tests
      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