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

Commit 555fddb0 authored by Guang Zhu's avatar Guang Zhu Committed by Android (Google) Code Review
Browse files

Merge "change how app launch is tested" into jb-mr2-dev

parents 8fb6f418 32abd66e
Loading
Loading
Loading
Loading
+99 −38
Original line number Original line 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");
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with 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.test.InstrumentationTestRunner;
import android.util.Log;
import android.util.Log;


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


    private static final int JOIN_TIMEOUT = 10000;
    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_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, Intent> mNameToIntent;
    private Map<String, String> mNameToProcess;
    private Map<String, String> mNameToProcess;
    private Map<String, String> mNameToResultKey;
    private Map<String, String> mNameToResultKey;

    private Map<String, Long> mNameToLaunchTime;
    private IActivityManager mAm;
    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 instrumentation =
                (InstrumentationTestRunner)getInstrumentation();
                (InstrumentationTestRunner)getInstrumentation();
        Bundle args = instrumentation.getArguments();
        Bundle args = instrumentation.getArguments();
@@ -66,25 +73,59 @@ public class AppLaunch extends InstrumentationTestCase {
        createMappings();
        createMappings();
        parseArgs(args);
        parseArgs(args);


        Bundle results = new Bundle();
        // do initial app launch, without force stopping
        for (String app : mNameToResultKey.keySet()) {
        for (String app : mNameToResultKey.keySet()) {
            try {
            long launchTime = startApp(app, false);
                startApp(app, results);
            if (launchTime <=0 ) {
                sleep(750);
                mNameToLaunchTime.put(app, -1L);
                closeApp(app);
                // simply pass the app if launch isn't successful
                sleep(2000);
                // error should have already been logged by startApp
            } catch (NameNotFoundException e) {
                continue;
                Log.i(TAG, "Application " + app + " not found");
            }
            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) {
    private void parseArgs(Bundle args) {
        mNameToResultKey = new LinkedHashMap<String, String>();
        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);
        String appList = args.getString(KEY_APPS);

        if (appList == null)
        if (appList == null)
            return;
            return;


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


            mNameToResultKey.put(parts[0], parts[1]);
            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);
                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
                startIntent.setClassName(ri.activityInfo.packageName,
                startIntent.setClassName(ri.activityInfo.packageName,
                        ri.activityInfo.name);
                        ri.activityInfo.name);
                mNameToIntent.put(ri.loadLabel(pm).toString(), startIntent);
                String appName = ri.loadLabel(pm).toString();
                mNameToProcess.put(ri.loadLabel(pm).toString(),
                if (appName != null) {
                        ri.activityInfo.processName);
                    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 {
            throws NameNotFoundException, RemoteException {
        Log.i(TAG, "Starting " + appName);
        Log.i(TAG, "Starting " + appName);


        Intent startIntent = mNameToIntent.get(appName);
        Intent startIntent = mNameToIntent.get(appName);
        if (startIntent == null) {
        if (startIntent == null) {
            Log.w(TAG, "App does not exist: " + appName);
            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);
        Thread t = new Thread(runnable);
        t.start();
        t.start();
        try {
        try {
@@ -143,20 +188,30 @@ public class AppLaunch extends InstrumentationTestCase {
            // ignore
            // ignore
        }
        }
        WaitResult result = runnable.getResult();
        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.");
            Log.w(TAG, "Assuming app " + appName + " crashed.");
            reportError(appName, mNameToProcess.get(appName), results);
            reportError(appName, mNameToProcess.get(appName));
            return;
            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);
        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
        homeIntent.addCategory(Intent.CATEGORY_HOME);
        homeIntent.addCategory(Intent.CATEGORY_HOME);
        homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
        homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        getInstrumentation().getContext().startActivity(homeIntent);
        getInstrumentation().getContext().startActivity(homeIntent);
        sleep(POST_LAUNCH_IDLE_TIMEOUT);
        if (forceStopApp) {
            Intent startIntent = mNameToIntent.get(appName);
            Intent startIntent = mNameToIntent.get(appName);
            if (startIntent != null) {
            if (startIntent != null) {
                String packageName = startIntent.getComponent().getPackageName();
                String packageName = startIntent.getComponent().getPackageName();
@@ -167,6 +222,7 @@ public class AppLaunch extends InstrumentationTestCase {
                }
                }
            }
            }
        }
        }
    }


    private void sleep(int time) {
    private void sleep(int time) {
        try {
        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()
        ActivityManager am = (ActivityManager) getInstrumentation()
                .getContext().getSystemService(Context.ACTIVITY_SERVICE);
                .getContext().getSystemService(Context.ACTIVITY_SERVICE);
        List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState();
        List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState();
@@ -186,12 +242,12 @@ public class AppLaunch extends InstrumentationTestCase {
                    continue;
                    continue;


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


        results.putString(mNameToResultKey.get(appName),
        mResult.putString(mNameToResultKey.get(appName),
                "Crashed for unknown reason");
                "Crashed for unknown reason");
        Log.w(TAG, appName
        Log.w(TAG, appName
                + " not found in process list, most likely it is crashed");
                + " 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 class AppLaunchRunnable implements Runnable {
        private Intent mLaunchIntent;
        private Intent mLaunchIntent;
        private IActivityManager.WaitResult mResult;
        private IActivityManager.WaitResult mResult;
        public AppLaunchRunnable(Intent intent) {
        private boolean mForceStopBeforeLaunch;

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


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