1 /* 2 * Copyright (C) 2021 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.csuite.tests; 18 19 import com.android.csuite.core.ApkInstaller; 20 import com.android.csuite.core.ApkInstaller.ApkInstallerException; 21 import com.android.csuite.core.AppCrawlTester; 22 import com.android.csuite.core.DeviceUtils; 23 import com.android.csuite.core.TestUtils; 24 import com.android.tradefed.config.Option; 25 import com.android.tradefed.device.DeviceNotAvailableException; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 28 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData; 29 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 30 31 import com.google.common.base.Preconditions; 32 33 import org.junit.After; 34 import org.junit.Before; 35 import org.junit.Rule; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 39 import java.io.File; 40 import java.io.IOException; 41 import java.nio.file.Path; 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.List; 45 import java.util.stream.Collectors; 46 47 import javax.annotation.Nullable; 48 49 /** A test that verifies that a single app can be successfully launched. */ 50 @RunWith(DeviceJUnit4ClassRunner.class) 51 public class AppCrawlTest extends BaseHostJUnit4Test { 52 private static final String COLLECT_APP_VERSION = "collect-app-version"; 53 private static final String COLLECT_GMS_VERSION = "collect-gms-version"; 54 private static final String RECORD_SCREEN = "record-screen"; 55 56 @Rule public TestLogData mLogData = new TestLogData(); 57 private boolean mIsLastTestPass; 58 private boolean mIsApkSaved = false; 59 60 private ApkInstaller mApkInstaller; 61 private AppCrawlTester mCrawler; 62 63 @Option(name = RECORD_SCREEN, description = "Whether to record screen during test.") 64 private boolean mRecordScreen; 65 66 @Option( 67 name = COLLECT_APP_VERSION, 68 description = 69 "Whether to collect package version information and store the information in" 70 + " test log files.") 71 private boolean mCollectAppVersion; 72 73 @Option( 74 name = COLLECT_GMS_VERSION, 75 description = 76 "Whether to collect GMS core version information and store the information in" 77 + " test log files.") 78 private boolean mCollectGmsVersion; 79 80 @Option( 81 name = "repack-apk", 82 mandatory = false, 83 description = 84 "Path to an apk file or a directory containing apk files of a single package " 85 + "to repack and install in Espresso mode") 86 private File mRepackApk; 87 88 @Option( 89 name = "install-apk", 90 mandatory = false, 91 description = 92 "The path to an apk file or a directory of apk files to be installed on the" 93 + " device. In Ui-automator mode, this includes both the target apk to" 94 + " install and any dependencies. In Espresso mode this can include" 95 + " additional libraries or dependencies.") 96 private final List<File> mInstallApkPaths = new ArrayList<>(); 97 98 @Option( 99 name = "install-arg", 100 description = 101 "Arguments for the 'adb install-multiple' package installation command for" 102 + " UI-automator mode.") 103 private final List<String> mInstallArgs = new ArrayList<>(); 104 105 @Option(name = "package-name", mandatory = true, description = "Package name of testing app.") 106 private String mPackageName; 107 108 @Option( 109 name = "crawl-controller-endpoint", 110 mandatory = false, 111 description = "The crawl controller endpoint to target.") 112 private String mCrawlControllerEndpoint; 113 114 @Option( 115 name = "ui-automator-mode", 116 mandatory = false, 117 description = 118 "Run the crawler with UIAutomator mode. Apk option is not required in this" 119 + " mode.") 120 private boolean mUiAutomatorMode = false; 121 122 @Option( 123 name = "timeout-sec", 124 mandatory = false, 125 description = "The timeout for the crawl test.") 126 private int mTimeoutSec = 60; 127 128 @Option( 129 name = "robo-script-file", 130 description = "A Roboscript file to be executed by the crawler.") 131 private File mRoboscriptFile; 132 133 // TODO(b/234512223): add support for contextual roboscript files 134 135 @Option( 136 name = "crawl-guidance-proto-file", 137 description = "A CrawlGuidance file to be executed by the crawler.") 138 private File mCrawlGuidanceProtoFile; 139 140 @Option( 141 name = "login-config-dir", 142 description = 143 "A directory containing Roboscript and CrawlGuidance files with login" 144 + " credentials that are passed to the crawler. There should be one config" 145 + " file per package name. If both Roboscript and CrawlGuidance files are" 146 + " present, only the Roboscript file will be used.") 147 private File mLoginConfigDir; 148 149 @Option( 150 name = "save-apk-when", 151 description = "When to save apk files to the test result artifacts.") 152 private TestUtils.TakeEffectWhen mSaveApkWhen = TestUtils.TakeEffectWhen.NEVER; 153 154 @Option( 155 name = "grant-external-storage", 156 mandatory = false, 157 description = "After an apks are installed, grant MANAGE_EXTERNAL_STORAGE permissions.") 158 private boolean mGrantExternalStoragePermission = false; 159 160 @Before setUp()161 public void setUp() 162 throws ApkInstaller.ApkInstallerException, IOException, DeviceNotAvailableException { 163 DeviceUtils deviceUtils = DeviceUtils.getInstance(getDevice()); 164 mIsLastTestPass = false; 165 mCrawler = AppCrawlTester.newInstance(mPackageName, getTestInformation(), mLogData); 166 if (!mUiAutomatorMode) { 167 setApkForEspressoMode(); 168 } 169 mCrawler.setCrawlControllerEndpoint(mCrawlControllerEndpoint); 170 mCrawler.setRecordScreen(mRecordScreen); 171 mCrawler.setCollectGmsVersion(mCollectGmsVersion); 172 mCrawler.setCollectAppVersion(mCollectAppVersion); 173 mCrawler.setUiAutomatorMode(mUiAutomatorMode); 174 mCrawler.setRoboscriptFile(toPathOrNull(mRoboscriptFile)); 175 mCrawler.setCrawlGuidanceProtoFile(toPathOrNull(mCrawlGuidanceProtoFile)); 176 mCrawler.setLoginConfigDir(toPathOrNull(mLoginConfigDir)); 177 mCrawler.setTimeoutSec(mTimeoutSec); 178 179 mApkInstaller = ApkInstaller.getInstance(getDevice()); 180 mApkInstaller.install( 181 mInstallApkPaths.stream().map(File::toPath).collect(Collectors.toList()), 182 mInstallArgs); 183 if (mGrantExternalStoragePermission) { 184 deviceUtils.grantExternalStoragePermissions(mPackageName); 185 } 186 } 187 188 /** Helper method to fetch the path of optional File variables. */ toPathOrNull(@ullable File f)189 private static Path toPathOrNull(@Nullable File f) { 190 return f == null ? null : f.toPath(); 191 } 192 193 /** 194 * For Espresso mode, checks that a path with the location of the apk to repackage was provided 195 */ setApkForEspressoMode()196 private void setApkForEspressoMode() { 197 Preconditions.checkNotNull( 198 mRepackApk, "Apk file path is required when not running in UIAutomator mode"); 199 // set the root path of the target apk for Espresso mode 200 mCrawler.setApkPath(mRepackApk.toPath()); 201 } 202 203 @Test testAppCrash()204 public void testAppCrash() throws DeviceNotAvailableException { 205 mCrawler.startAndAssertAppNoCrash(); 206 mIsLastTestPass = true; 207 } 208 209 @After tearDown()210 public void tearDown() throws DeviceNotAvailableException { 211 TestUtils testUtils = TestUtils.getInstance(getTestInformation(), mLogData); 212 213 if (!mIsApkSaved) { 214 mIsApkSaved = 215 testUtils.saveApks( 216 mSaveApkWhen, mIsLastTestPass, mPackageName, mInstallApkPaths); 217 if (mRepackApk != null) { 218 mIsApkSaved &= 219 testUtils.saveApks( 220 mSaveApkWhen, 221 mIsLastTestPass, 222 mPackageName, 223 Arrays.asList(mRepackApk)); 224 } 225 } 226 227 try { 228 mApkInstaller.uninstallAllInstalledPackages(); 229 } catch (ApkInstallerException e) { 230 CLog.w("Uninstallation of installed apps failed during teardown: %s", e.getMessage()); 231 } 232 if (!mUiAutomatorMode) { 233 getDevice().uninstallPackage(mPackageName); 234 } 235 236 mCrawler.cleanUp(); 237 } 238 } 239