1 /* 2 * Copyright (C) 2016 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 package android.content.pm.cts.shortcuthost; 17 18 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 19 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; 20 import com.android.ddmlib.testrunner.TestIdentifier; 21 import com.android.ddmlib.testrunner.TestResult; 22 import com.android.ddmlib.testrunner.TestResult.TestStatus; 23 import com.android.ddmlib.testrunner.TestRunResult; 24 import com.android.tradefed.build.IBuildInfo; 25 import com.android.tradefed.device.DeviceNotAvailableException; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.result.CollectingTestListener; 28 import com.android.tradefed.testtype.DeviceTestCase; 29 import com.android.tradefed.testtype.IBuildReceiver; 30 31 import java.io.FileNotFoundException; 32 import java.util.ArrayList; 33 import java.util.Collections; 34 import java.util.Map; 35 import java.util.regex.MatchResult; 36 import java.util.regex.Matcher; 37 import java.util.regex.Pattern; 38 39 import javax.annotation.Nullable; 40 41 abstract public class BaseShortcutManagerHostTest extends DeviceTestCase implements IBuildReceiver { 42 protected static final boolean DUMPSYS_IN_TEARDOWN = false; // DO NOT SUBMIT WITH TRUE 43 44 private static final String RUNNER = "android.support.test.runner.AndroidJUnitRunner"; 45 46 private IBuildInfo mCtsBuild; 47 48 protected boolean mIsMultiuserSupported; 49 protected boolean mIsManagedUserSupported; 50 51 private ArrayList<Integer> mOriginalUsers; 52 53 @Override setBuild(IBuildInfo buildInfo)54 public void setBuild(IBuildInfo buildInfo) { 55 mCtsBuild = buildInfo; 56 } 57 58 @Override setUp()59 protected void setUp() throws Exception { 60 super.setUp(); 61 assertNotNull(mCtsBuild); // ensure build has been set before test is run. 62 63 mIsMultiuserSupported = getDevice().isMultiUserSupported(); 64 if (!mIsMultiuserSupported) { 65 CLog.w("Multi user not supporeted"); 66 } 67 mIsManagedUserSupported = getDevice().hasFeature("android.software.managed_users"); 68 if (!mIsManagedUserSupported) { 69 CLog.w("Managed users not supporeted"); 70 } 71 72 if (mIsMultiuserSupported) { 73 mOriginalUsers = new ArrayList<>(getDevice().listUsers()); 74 } 75 } 76 77 @Override tearDown()78 protected void tearDown() throws Exception { 79 removeTestUsers(); 80 super.tearDown(); 81 } 82 dumpsys(String label)83 protected void dumpsys(String label) throws DeviceNotAvailableException { 84 CLog.w("dumpsys shortcuts #" + label); 85 86 CLog.w(getDevice().executeShellCommand("dumpsys shortcut")); 87 } 88 executeShellCommandWithLog(String command)89 protected String executeShellCommandWithLog(String command) throws DeviceNotAvailableException { 90 CLog.i("Executing command: " + command); 91 final String output = getDevice().executeShellCommand(command); 92 CLog.i(output); 93 return output; 94 } 95 clearShortcuts(String packageName, int userId)96 protected void clearShortcuts(String packageName, int userId) throws Exception { 97 assertContainsRegex("Success", 98 getDevice().executeShellCommand("cmd shortcut clear-shortcuts --user " + userId 99 + " " + packageName)); 100 } 101 installAppAsUser(String appFileName, int userId)102 protected void installAppAsUser(String appFileName, int userId) throws FileNotFoundException, 103 DeviceNotAvailableException { 104 CLog.i("Installing app " + appFileName + " for user " + userId); 105 CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild); 106 String result = getDevice().installPackageForUser( 107 buildHelper.getTestFile(appFileName), true, true, userId, "-t"); 108 assertNull("Failed to install " + appFileName + " for user " + userId + ": " + result, 109 result); 110 } 111 getPrimaryUserId()112 protected int getPrimaryUserId() throws DeviceNotAvailableException { 113 return getDevice().getPrimaryUserId(); 114 } 115 116 /** Returns true if the specified tests passed. Tests are run as given user. */ runDeviceTestsAsUser( String pkgName, @Nullable String testClassName, int userId)117 protected void runDeviceTestsAsUser( 118 String pkgName, @Nullable String testClassName, int userId) 119 throws DeviceNotAvailableException { 120 runDeviceTestsAsUser(pkgName, testClassName, null /*testMethodName*/, userId); 121 } 122 123 /** Returns true if the specified tests passed. Tests are run as given user. */ runDeviceTestsAsUser( String pkgName, @Nullable String testClassName, String testMethodName, int userId)124 protected void runDeviceTestsAsUser( 125 String pkgName, @Nullable String testClassName, String testMethodName, int userId) 126 throws DeviceNotAvailableException { 127 Map<String, String> params = Collections.emptyMap(); 128 runDeviceTestsAsUser(pkgName, testClassName, testMethodName, userId, params); 129 } 130 runDeviceTestsAsUser(String pkgName, @Nullable String testClassName, @Nullable String testMethodName, int userId, Map<String, String> params)131 protected void runDeviceTestsAsUser(String pkgName, @Nullable String testClassName, 132 @Nullable String testMethodName, int userId, 133 Map<String, String> params) throws DeviceNotAvailableException { 134 if (testClassName != null && testClassName.startsWith(".")) { 135 testClassName = pkgName + testClassName; 136 } 137 138 RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner( 139 pkgName, RUNNER, getDevice().getIDevice()); 140 if (testClassName != null && testMethodName != null) { 141 testRunner.setMethodName(testClassName, testMethodName); 142 } else if (testClassName != null) { 143 testRunner.setClassName(testClassName); 144 } 145 146 for (Map.Entry<String, String> param : params.entrySet()) { 147 testRunner.addInstrumentationArg(param.getKey(), param.getValue()); 148 } 149 150 CollectingTestListener listener = new CollectingTestListener(); 151 assertTrue(getDevice().runInstrumentationTestsAsUser(testRunner, userId, listener)); 152 153 TestRunResult runResult = listener.getCurrentRunResults(); 154 if (runResult.getTestResults().size() == 0) { 155 fail("No tests have been executed."); 156 return; 157 } 158 159 printTestResult(runResult); 160 if (runResult.hasFailedTests() || runResult.getNumTestsInState(TestStatus.PASSED) == 0) { 161 fail("Some tests have been failed."); 162 } 163 } 164 printTestResult(TestRunResult runResult)165 private void printTestResult(TestRunResult runResult) { 166 for (Map.Entry<TestIdentifier, TestResult> testEntry : 167 runResult.getTestResults().entrySet()) { 168 TestResult testResult = testEntry.getValue(); 169 170 final String message = "Test " + testEntry.getKey() + ": " + testResult.getStatus(); 171 if (testResult.getStatus() == TestStatus.PASSED) { 172 CLog.i(message); 173 } else { 174 CLog.e(message); 175 CLog.e(testResult.getStackTrace()); 176 } 177 } 178 } 179 removeTestUsers()180 private void removeTestUsers() throws Exception { 181 if (!mIsMultiuserSupported) { 182 return; 183 } 184 getDevice().switchUser(getPrimaryUserId()); 185 for (int userId : getDevice().listUsers()) { 186 if (!mOriginalUsers.contains(userId)) { 187 getDevice().removeUser(userId); 188 } 189 } 190 } 191 createUser()192 protected int createUser() throws Exception{ 193 return getDevice().createUser("TestUser_" + System.currentTimeMillis()); 194 } 195 createProfile(int parentUserId)196 protected int createProfile(int parentUserId) throws Exception{ 197 final String command = "pm create-user --profileOf " + parentUserId 198 + " --managed TestUser_" + System.currentTimeMillis(); 199 CLog.d("Starting command: " + command); 200 final String output = getDevice().executeShellCommand(command); 201 CLog.d("Output for command " + command + ": " + output); 202 203 if (output.startsWith("Success")) { 204 try { 205 return Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim()); 206 } catch (NumberFormatException e) { 207 CLog.e("Failed to parse result: %s", output); 208 } 209 } else { 210 CLog.e("Failed to create user: %s", output); 211 } 212 throw new IllegalStateException(); 213 } 214 215 /** 216 * Variant of {@link #assertContainsRegex(String,String,String)} using a 217 * generic message. 218 */ assertContainsRegex( String expectedRegex, String actual)219 public MatchResult assertContainsRegex( 220 String expectedRegex, String actual) { 221 return assertContainsRegex(null, expectedRegex, actual); 222 } 223 224 /** 225 * Asserts that {@code expectedRegex} matches any substring of {@code actual} 226 * and fails with {@code message} if it does not. The Matcher is returned in 227 * case the test needs access to any captured groups. Note that you can also 228 * use this for a literal string, by wrapping your expected string in 229 * {@link Pattern#quote}. 230 */ assertContainsRegex( String message, String expectedRegex, String actual)231 public MatchResult assertContainsRegex( 232 String message, String expectedRegex, String actual) { 233 if (actual == null) { 234 failNotContains(message, expectedRegex, actual); 235 } 236 Matcher matcher = getMatcher(expectedRegex, actual); 237 if (!matcher.find()) { 238 failNotContains(message, expectedRegex, actual); 239 } 240 return matcher; 241 } 242 243 /** 244 * Asserts that {@code expectedRegex} does not exactly match {@code actual}, 245 * and fails with {@code message} if it does. Note that you can also use 246 * this for a literal string, by wrapping your expected string in 247 * {@link Pattern#quote}. 248 */ assertNotMatchesRegex( String message, String expectedRegex, String actual)249 public void assertNotMatchesRegex( 250 String message, String expectedRegex, String actual) { 251 Matcher matcher = getMatcher(expectedRegex, actual); 252 if (matcher.matches()) { 253 failMatch(message, expectedRegex, actual); 254 } 255 } 256 getMatcher(String expectedRegex, String actual)257 private Matcher getMatcher(String expectedRegex, String actual) { 258 Pattern pattern = Pattern.compile(expectedRegex); 259 return pattern.matcher(actual); 260 } 261 failMatch( String message, String expectedRegex, String actual)262 private void failMatch( 263 String message, String expectedRegex, String actual) { 264 failWithMessage(message, "expected not to match regex:<" + expectedRegex 265 + "> but was:<" + actual + '>'); 266 } 267 failWithMessage(String userMessage, String ourMessage)268 private void failWithMessage(String userMessage, String ourMessage) { 269 fail((userMessage == null) 270 ? ourMessage 271 : userMessage + ' ' + ourMessage); 272 } 273 failNotContains( String message, String expectedRegex, String actual)274 private void failNotContains( 275 String message, String expectedRegex, String actual) { 276 String actualDesc = (actual == null) ? "null" : ('<' + actual + '>'); 277 failWithMessage(message, "expected to contain regex:<" + expectedRegex 278 + "> but was:" + actualDesc); 279 } 280 } 281