Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 32abd66e authored by Guang Zhu's avatar Guang Zhu
Browse files

change how app launch is tested

new approach:
  * for each app
    * initial launch
    * sleep 7.5s
    * do 10 iterations: launch app with force stop + sleep in between
    * report average of 10 launches

Change-Id: I9e68975325aa83af35620d727823f5c072aac488
parent b1fa4f92
Loading
Loading
Loading
Loading
+99 −38
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 The Android Open Source Project
 * Copyright (C) 2013 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.
@@ -32,6 +32,7 @@ import android.test.InstrumentationTestCase;
import android.test.InstrumentationTestRunner;
import android.util.Log;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -48,16 +49,22 @@ import java.util.Map;
public class AppLaunch extends InstrumentationTestCase {

    private static final int JOIN_TIMEOUT = 10000;
    private static final String TAG = "AppLaunch";
    private static final String TAG = AppLaunch.class.getSimpleName();
    private static final String KEY_APPS = "apps";
    private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
    private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 7500; //7.5s to allow app to idle
    private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
    private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 2000; //2s between launching apps

    private Map<String, Intent> mNameToIntent;
    private Map<String, String> mNameToProcess;
    private Map<String, String> mNameToResultKey;

    private Map<String, Long> mNameToLaunchTime;
    private IActivityManager mAm;
    private int mLaunchIterations = 10;
    private Bundle mResult = new Bundle();

    public void testMeasureStartUpTime() throws RemoteException {
    public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException {
        InstrumentationTestRunner instrumentation =
                (InstrumentationTestRunner)getInstrumentation();
        Bundle args = instrumentation.getArguments();
@@ -66,25 +73,59 @@ public class AppLaunch extends InstrumentationTestCase {
        createMappings();
        parseArgs(args);

        Bundle results = new Bundle();
        // do initial app launch, without force stopping
        for (String app : mNameToResultKey.keySet()) {
            try {
                startApp(app, results);
                sleep(750);
                closeApp(app);
                sleep(2000);
            } catch (NameNotFoundException e) {
                Log.i(TAG, "Application " + app + " not found");
            long launchTime = startApp(app, false);
            if (launchTime <=0 ) {
                mNameToLaunchTime.put(app, -1L);
                // simply pass the app if launch isn't successful
                // error should have already been logged by startApp
                continue;
            }
            sleep(INITIAL_LAUNCH_IDLE_TIMEOUT);
            closeApp(app, false);
            sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
        }
        // do the real app launch now
        for (int i = 0; i < mLaunchIterations; i++) {
            for (String app : mNameToResultKey.keySet()) {
                long totalLaunchTime = mNameToLaunchTime.get(app);
                long launchTime = 0;
                if (totalLaunchTime < 0) {
                    // skip if the app has previous failures
                    continue;
                }
                launchTime = startApp(app, true);
                if (launchTime <= 0) {
                    // if it fails once, skip the rest of the launches
                    mNameToLaunchTime.put(app, -1L);
                    continue;
                }
                totalLaunchTime += launchTime;
                mNameToLaunchTime.put(app, totalLaunchTime);
                sleep(POST_LAUNCH_IDLE_TIMEOUT);
                closeApp(app, true);
                sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
            }

        }
        instrumentation.sendStatus(0, results);
        for (String app : mNameToResultKey.keySet()) {
            long totalLaunchTime = mNameToLaunchTime.get(app);
            if (totalLaunchTime != -1) {
                mResult.putDouble(mNameToResultKey.get(app),
                        ((double) totalLaunchTime) / mLaunchIterations);
            }
        }
        instrumentation.sendStatus(0, mResult);
    }

    private void parseArgs(Bundle args) {
        mNameToResultKey = new LinkedHashMap<String, String>();
        mNameToLaunchTime = new HashMap<String, Long>();
        String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS);
        if (launchIterations != null) {
            mLaunchIterations = Integer.parseInt(launchIterations);
        }
        String appList = args.getString(KEY_APPS);

        if (appList == null)
            return;

@@ -97,6 +138,7 @@ public class AppLaunch extends InstrumentationTestCase {
            }

            mNameToResultKey.put(parts[0], parts[1]);
            mNameToLaunchTime.put(parts[0], 0L);
        }
    }

@@ -118,23 +160,26 @@ public class AppLaunch extends InstrumentationTestCase {
                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
                startIntent.setClassName(ri.activityInfo.packageName,
                        ri.activityInfo.name);
                mNameToIntent.put(ri.loadLabel(pm).toString(), startIntent);
                mNameToProcess.put(ri.loadLabel(pm).toString(),
                        ri.activityInfo.processName);
                String appName = ri.loadLabel(pm).toString();
                if (appName != null) {
                    mNameToIntent.put(appName, startIntent);
                    mNameToProcess.put(appName, ri.activityInfo.processName);
                }
            }
        }
    }

    private void startApp(String appName, Bundle results)
    private long startApp(String appName, boolean forceStopBeforeLaunch)
            throws NameNotFoundException, RemoteException {
        Log.i(TAG, "Starting " + appName);

        Intent startIntent = mNameToIntent.get(appName);
        if (startIntent == null) {
            Log.w(TAG, "App does not exist: " + appName);
            return;
            mResult.putString(mNameToResultKey.get(appName), "App does not exist");
            return -1;
        }
        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent);
        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch);
        Thread t = new Thread(runnable);
        t.start();
        try {
@@ -143,20 +188,30 @@ public class AppLaunch extends InstrumentationTestCase {
            // ignore
        }
        WaitResult result = runnable.getResult();
        if(t.isAlive() || (result != null && result.result != ActivityManager.START_SUCCESS)) {
        // report error if any of the following is true:
        // * launch thread is alive
        // * result is not null, but:
        //   * result is not START_SUCESS
        //   * or in case of no force stop, result is not TASK_TO_FRONT either
        if (t.isAlive() || (result != null
                && ((result.result != ActivityManager.START_SUCCESS)
                        && (!forceStopBeforeLaunch
                                && result.result != ActivityManager.START_TASK_TO_FRONT)))) {
            Log.w(TAG, "Assuming app " + appName + " crashed.");
            reportError(appName, mNameToProcess.get(appName), results);
            return;
            reportError(appName, mNameToProcess.get(appName));
            return -1;
        }
        results.putString(mNameToResultKey.get(appName), String.valueOf(result.thisTime));
        return result.thisTime;
    }

    private void closeApp(String appName) {
    private void closeApp(String appName, boolean forceStopApp) {
        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
        homeIntent.addCategory(Intent.CATEGORY_HOME);
        homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        getInstrumentation().getContext().startActivity(homeIntent);
        sleep(POST_LAUNCH_IDLE_TIMEOUT);
        if (forceStopApp) {
            Intent startIntent = mNameToIntent.get(appName);
            if (startIntent != null) {
                String packageName = startIntent.getComponent().getPackageName();
@@ -167,6 +222,7 @@ public class AppLaunch extends InstrumentationTestCase {
                }
            }
        }
    }

    private void sleep(int time) {
        try {
@@ -176,7 +232,7 @@ public class AppLaunch extends InstrumentationTestCase {
        }
    }

    private void reportError(String appName, String processName, Bundle results) {
    private void reportError(String appName, String processName) {
        ActivityManager am = (ActivityManager) getInstrumentation()
                .getContext().getSystemService(Context.ACTIVITY_SERVICE);
        List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState();
@@ -186,12 +242,12 @@ public class AppLaunch extends InstrumentationTestCase {
                    continue;

                Log.w(TAG, appName + " crashed: " + crash.shortMsg);
                results.putString(mNameToResultKey.get(appName), crash.shortMsg);
                mResult.putString(mNameToResultKey.get(appName), crash.shortMsg);
                return;
            }
        }

        results.putString(mNameToResultKey.get(appName),
        mResult.putString(mNameToResultKey.get(appName),
                "Crashed for unknown reason");
        Log.w(TAG, appName
                + " not found in process list, most likely it is crashed");
@@ -200,8 +256,11 @@ public class AppLaunch extends InstrumentationTestCase {
    private class AppLaunchRunnable implements Runnable {
        private Intent mLaunchIntent;
        private IActivityManager.WaitResult mResult;
        public AppLaunchRunnable(Intent intent) {
        private boolean mForceStopBeforeLaunch;

        public AppLaunchRunnable(Intent intent, boolean forceStopBeforeLaunch) {
            mLaunchIntent = intent;
            mForceStopBeforeLaunch = forceStopBeforeLaunch;
        }

        public IActivityManager.WaitResult getResult() {
@@ -211,7 +270,9 @@ public class AppLaunch extends InstrumentationTestCase {
        public void run() {
            try {
                String packageName = mLaunchIntent.getComponent().getPackageName();
                if (mForceStopBeforeLaunch) {
                    mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
                }
                String mimeType = mLaunchIntent.getType();
                if (mimeType == null && mLaunchIntent.getData() != null
                        && "content".equals(mLaunchIntent.getData().getScheme())) {