Home | History | Annotate | Download | only in compatibilitytest
      1 /*
      2  * Copyright (C) 2012 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.compatibilitytest;
     18 
     19 import android.app.ActivityManager;
     20 import android.app.ActivityManager.ProcessErrorStateInfo;
     21 import android.app.ActivityManager.RunningAppProcessInfo;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.pm.PackageInfo;
     25 import android.content.pm.PackageManager;
     26 import android.content.pm.PackageManager.NameNotFoundException;
     27 import android.os.Bundle;
     28 import android.test.InstrumentationTestCase;
     29 import android.util.Log;
     30 
     31 import junit.framework.Assert;
     32 
     33 import java.util.Collection;
     34 import java.util.List;
     35 
     36 /**
     37  * Application Compatibility Test that launches an application and detects
     38  * crashes.
     39  */
     40 public class AppCompatibility extends InstrumentationTestCase {
     41 
     42     private static final String TAG = "AppCompability";
     43     private static final String PACKAGE_TO_LAUNCH = "package_to_launch";
     44     private static final String APP_LAUNCH_TIMEOUT_MSECS = "app_launch_timeout_ms";
     45     private static final String WORKSPACE_LAUNCH_TIMEOUT_MSECS = "workspace_launch_timeout_ms";
     46 
     47     private int mAppLaunchTimeout = 7000;
     48     private int mWorkspaceLaunchTimeout = 2000;
     49 
     50     private Context mContext;
     51     private ActivityManager mActivityManager;
     52     private PackageManager mPackageManager;
     53     private AppCompatibilityRunner mRunner;
     54     private Bundle mArgs;
     55 
     56     @Override
     57     public void setUp() throws Exception {
     58         super.setUp();
     59         mRunner = (AppCompatibilityRunner) getInstrumentation();
     60         assertNotNull("Could not fetch InstrumentationTestRunner.", mRunner);
     61 
     62         mContext = mRunner.getTargetContext();
     63         Assert.assertNotNull("Could not get the Context", mContext);
     64 
     65         mActivityManager = (ActivityManager)
     66                 mContext.getSystemService(Context.ACTIVITY_SERVICE);
     67         Assert.assertNotNull("Could not get Activity Manager", mActivityManager);
     68 
     69         mPackageManager = mContext.getPackageManager();
     70         Assert.assertNotNull("Missing Package Manager", mPackageManager);
     71 
     72         mArgs = mRunner.getBundle();
     73 
     74         // Parse optional inputs.
     75         String appLaunchTimeoutMsecs = mArgs.getString(APP_LAUNCH_TIMEOUT_MSECS);
     76         if (appLaunchTimeoutMsecs != null) {
     77             mAppLaunchTimeout = Integer.parseInt(appLaunchTimeoutMsecs);
     78         }
     79         String workspaceLaunchTimeoutMsecs = mArgs.getString(WORKSPACE_LAUNCH_TIMEOUT_MSECS);
     80         if (workspaceLaunchTimeoutMsecs != null) {
     81             mWorkspaceLaunchTimeout = Integer.parseInt(workspaceLaunchTimeoutMsecs);
     82         }
     83     }
     84 
     85     @Override
     86     protected void tearDown() throws Exception {
     87         super.tearDown();
     88     }
     89 
     90     /**
     91      * Actual test case that launches the package and throws an exception on the
     92      * first error.
     93      *
     94      * @throws Exception
     95      */
     96     public void testAppStability() throws Exception {
     97         String packageName = mArgs.getString(PACKAGE_TO_LAUNCH);
     98         if (packageName != null) {
     99             Log.d(TAG, "Launching app " + packageName);
    100             Collection<ProcessErrorStateInfo> err = launchActivity(packageName);
    101             // Make sure there are no errors when launching the application,
    102             // otherwise raise an
    103             // exception with the first error encountered.
    104             assertNull(getFirstError(err), err);
    105             assertTrue("App crashed after launch.", processStillUp(packageName));
    106         } else {
    107             Log.d(TAG, "Missing argument, use " + PACKAGE_TO_LAUNCH +
    108                     " to specify the package to launch");
    109         }
    110     }
    111 
    112     /**
    113      * Gets the first error in collection and return the long message for it.
    114      *
    115      * @param in {@link Collection} of {@link ProcessErrorStateInfo} to parse.
    116      * @return {@link String} the long message of the error.
    117      */
    118     private String getFirstError(Collection<ProcessErrorStateInfo> in) {
    119         if (in == null) {
    120             return null;
    121         }
    122         ProcessErrorStateInfo err = in.iterator().next();
    123         if (err != null) {
    124             return err.stackTrace;
    125         }
    126         return null;
    127     }
    128 
    129     /**
    130      * Launches and activity and queries for errors.
    131      *
    132      * @param packageName {@link String} the package name of the application to
    133      *            launch.
    134      * @return {@link Collection} of {@link ProcessErrorStateInfo} detected
    135      *         during the app launch.
    136      */
    137     private Collection<ProcessErrorStateInfo> launchActivity(String packageName) {
    138         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
    139         homeIntent.addCategory(Intent.CATEGORY_HOME);
    140         homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    141 
    142         Intent intent = mPackageManager.getLaunchIntentForPackage(packageName);
    143         // Skip if the apk does not have a launch intent.
    144         if (intent == null) {
    145             Log.d(TAG, "Skipping " + packageName + "; missing launch intent");
    146             return null;
    147         }
    148 
    149         // We check for any Crash or ANR dialogs that are already up, and we
    150         // ignore them. This is
    151         // so that we don't report crashes that were caused by prior apps (which
    152         // those particular
    153         // tests should have caught and reported already). Otherwise, test
    154         // failures would cascade
    155         // from the initial broken app to many/all of the tests following that
    156         // app's launch.
    157         final Collection<ProcessErrorStateInfo> preErr =
    158                 mActivityManager.getProcessesInErrorState();
    159 
    160         // Launch Activity
    161         mContext.startActivity(intent);
    162 
    163         try {
    164             Thread.sleep(mAppLaunchTimeout);
    165         } catch (InterruptedException e) {
    166             // ignore
    167         }
    168 
    169         // Send the "home" intent and wait 2 seconds for us to get there
    170         mContext.startActivity(homeIntent);
    171         try {
    172             Thread.sleep(mWorkspaceLaunchTimeout);
    173         } catch (InterruptedException e) {
    174             // ignore
    175         }
    176 
    177         // See if there are any errors. We wait until down here to give ANRs as
    178         // much time as
    179         // possible to occur.
    180         final Collection<ProcessErrorStateInfo> postErr =
    181                 mActivityManager.getProcessesInErrorState();
    182         // Take the difference between the error processes we see now, and the
    183         // ones that were
    184         // present when we started
    185         if (preErr != null && postErr != null) {
    186             postErr.removeAll(preErr);
    187         }
    188         return postErr;
    189     }
    190 
    191     /**
    192      * Determine if a given package is still running.
    193      *
    194      * @param packageName {@link String} package to look for
    195      * @return True if package is running, false otherwise.
    196      */
    197     private boolean processStillUp(String packageName) {
    198         try {
    199             PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, 0);
    200             String processName = packageInfo.applicationInfo.processName;
    201             List<RunningAppProcessInfo> runningApps = mActivityManager.getRunningAppProcesses();
    202             for (RunningAppProcessInfo app : runningApps) {
    203                 if (app.processName.equalsIgnoreCase(processName)) {
    204                     Log.d(TAG, "Found process " + app.processName);
    205                     return true;
    206                 }
    207             }
    208             Log.d(TAG, "Failed to find process " + processName + " with package name "
    209                     + packageName);
    210         } catch (NameNotFoundException e) {
    211             Log.w(TAG, "Failed to find package " + packageName);
    212             return false;
    213         }
    214         return false;
    215     }
    216 }
    217