1 /* 2 * Copyright (C) 2022 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.webview.tests; 18 19 import com.android.csuite.core.ApkInstaller; 20 import com.android.csuite.core.ApkInstaller.ApkInstallerException; 21 import com.android.csuite.core.DeviceUtils; 22 import com.android.csuite.core.DeviceUtils.DeviceTimestamp; 23 import com.android.csuite.core.DeviceUtils.DeviceUtilsException; 24 import com.android.csuite.core.TestUtils; 25 import com.android.tradefed.config.Option; 26 import com.android.tradefed.device.DeviceNotAvailableException; 27 import com.android.tradefed.log.LogUtil.CLog; 28 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 29 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData; 30 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 31 import com.android.tradefed.util.RunUtil; 32 33 import org.junit.After; 34 import org.junit.Assert; 35 import org.junit.Before; 36 import org.junit.Rule; 37 import org.junit.Test; 38 import org.junit.runner.RunWith; 39 40 import java.io.File; 41 import java.io.IOException; 42 import java.util.ArrayList; 43 import java.util.List; 44 45 46 /** A test that verifies that a single app can be successfully launched. */ 47 @RunWith(DeviceJUnit4ClassRunner.class) 48 public class WebviewAppLaunchTest extends BaseHostJUnit4Test { 49 @Rule public TestLogData mLogData = new TestLogData(); 50 51 private static final long COMMAND_TIMEOUT_MILLIS = 5 * 60 * 1000; 52 private WebviewUtils mWebviewUtils; 53 private WebviewPackage mPreInstalledWebview; 54 private ApkInstaller mApkInstaller; 55 56 @Option(name = "record-screen", description = "Whether to record screen during test.") 57 private boolean mRecordScreen; 58 59 @Option(name = "webview-version-to-test", description = "Version of Webview to test.") 60 private String mWebviewVersionToTest; 61 62 @Option( 63 name = "release-channel", 64 description = "Release channel to fetch Webview from, i.e. stable.") 65 private String mReleaseChannel; 66 67 @Option(name = "package-name", description = "Package name of testing app.") 68 private String mPackageName; 69 70 @Option( 71 name = "install-apk", 72 description = 73 "The path to an apk file or a directory of apk files of a singe package to be" 74 + " installed on device. Can be repeated.") 75 private List<File> mApkPaths = new ArrayList<>(); 76 77 @Option( 78 name = "install-arg", 79 description = "Arguments for the 'adb install-multiple' package installation command.") 80 private final List<String> mInstallArgs = new ArrayList<>(); 81 82 @Option( 83 name = "app-launch-timeout-ms", 84 description = "Time to wait for an app to launch in msecs.") 85 private int mAppLaunchTimeoutMs = 20000; 86 87 @Before setUp()88 public void setUp() throws DeviceNotAvailableException, ApkInstallerException, IOException { 89 Assert.assertNotNull("Package name cannot be null", mPackageName); 90 Assert.assertTrue( 91 "Either the --release-channel or --webview-version-to-test arguments " 92 + "must be used", 93 mWebviewVersionToTest != null || mReleaseChannel != null); 94 95 mApkInstaller = ApkInstaller.getInstance(getDevice()); 96 mWebviewUtils = new WebviewUtils(getTestInformation()); 97 mPreInstalledWebview = mWebviewUtils.getCurrentWebviewPackage(); 98 99 for (File apkPath : mApkPaths) { 100 CLog.d("Installing " + apkPath); 101 mApkInstaller.install(apkPath.toPath(), mInstallArgs); 102 } 103 104 DeviceUtils.getInstance(getDevice()).freezeRotation(); 105 mWebviewUtils.printWebviewVersion(); 106 } 107 108 @Test testAppLaunch()109 public void testAppLaunch() 110 throws DeviceNotAvailableException, InterruptedException, ApkInstallerException, 111 IOException { 112 AssertionError lastError = null; 113 WebviewPackage lastWebviewInstalled = 114 mWebviewUtils.installWebview(mWebviewVersionToTest, mReleaseChannel); 115 116 try { 117 assertAppLaunchNoCrash(); 118 } catch (AssertionError e) { 119 lastError = e; 120 } finally { 121 mWebviewUtils.uninstallWebview(lastWebviewInstalled, mPreInstalledWebview); 122 } 123 124 // If the app doesn't crash, complete the test. 125 if (lastError == null) { 126 return; 127 } 128 129 // If the app crashes, try the app with the original webview version that comes with the 130 // device. 131 try { 132 assertAppLaunchNoCrash(); 133 } catch (AssertionError newError) { 134 CLog.w( 135 "The app %s crashed both with and without the webview installation," 136 + " ignoring the failure...", 137 mPackageName); 138 return; 139 } 140 throw new AssertionError( 141 String.format( 142 "Package %s crashed since webview version %s", 143 mPackageName, lastWebviewInstalled.getVersion()), 144 lastError); 145 } 146 147 @After tearDown()148 public void tearDown() throws DeviceNotAvailableException, ApkInstallerException { 149 TestUtils testUtils = TestUtils.getInstance(getTestInformation(), mLogData); 150 testUtils.collectScreenshot(mPackageName); 151 152 DeviceUtils deviceUtils = DeviceUtils.getInstance(getDevice()); 153 deviceUtils.stopPackage(mPackageName); 154 deviceUtils.unfreezeRotation(); 155 156 mApkInstaller.uninstallAllInstalledPackages(); 157 mWebviewUtils.printWebviewVersion(); 158 } 159 assertAppLaunchNoCrash()160 private void assertAppLaunchNoCrash() throws DeviceNotAvailableException { 161 DeviceUtils.getInstance(getDevice()).resetPackage(mPackageName); 162 TestUtils testUtils = TestUtils.getInstance(getTestInformation(), mLogData); 163 164 if (mRecordScreen) { 165 testUtils.collectScreenRecord( 166 () -> { 167 launchPackageAndCheckForCrash(); 168 }, 169 mPackageName); 170 } else { 171 launchPackageAndCheckForCrash(); 172 } 173 } 174 launchPackageAndCheckForCrash()175 private void launchPackageAndCheckForCrash() throws DeviceNotAvailableException { 176 CLog.d("Launching package: %s.", mPackageName); 177 178 TestUtils testUtils = TestUtils.getInstance(getTestInformation(), mLogData); 179 DeviceUtils deviceUtils = DeviceUtils.getInstance(getDevice()); 180 DeviceTimestamp startTime = deviceUtils.currentTimeMillis(); 181 try { 182 deviceUtils.launchPackage(mPackageName); 183 } catch (DeviceUtilsException e) { 184 Assert.fail(e.getMessage()); 185 } 186 187 CLog.d("Waiting %s milliseconds for the app to launch fully.", mAppLaunchTimeoutMs); 188 RunUtil.getDefault().sleep(mAppLaunchTimeoutMs); 189 190 CLog.d("Completed launching package: %s", mPackageName); 191 192 try { 193 String crashLog = testUtils.getDropboxPackageCrashLog(mPackageName, startTime, true); 194 if (crashLog != null) { 195 Assert.fail(crashLog); 196 } 197 } catch (IOException e) { 198 Assert.fail("Error while getting dropbox crash log: " + e); 199 } 200 } 201 } 202