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