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