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