| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package android.content.pm.cts.shortcuthost; |
| |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; |
| import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; |
| import com.android.ddmlib.testrunner.TestResult.TestStatus; |
| import com.android.tradefed.device.DeviceNotAvailableException; |
| import com.android.tradefed.log.LogUtil.CLog; |
| import com.android.tradefed.result.CollectingTestListener; |
| import com.android.tradefed.result.TestDescription; |
| import com.android.tradefed.result.TestResult; |
| import com.android.tradefed.result.TestRunResult; |
| import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; |
| import com.android.tradefed.testtype.junit4.DeviceTestRunOptions; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| |
| import java.io.FileNotFoundException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Map; |
| import java.util.regex.MatchResult; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import javax.annotation.Nullable; |
| |
| abstract public class BaseShortcutManagerHostTest extends BaseHostJUnit4Test { |
| protected static final boolean DUMPSYS_IN_TEARDOWN = false; // DO NOT SUBMIT WITH TRUE |
| |
| protected static final boolean NO_UNINSTALL_IN_TEARDOWN = false; // DO NOT SUBMIT WITH TRUE |
| |
| private static final String RUNNER = "androidx.test.runner.AndroidJUnitRunner"; |
| |
| protected boolean mIsMultiuserSupported; |
| protected boolean mIsManagedUserSupported; |
| |
| private int mInitialUserId; |
| private ArrayList<Integer> mOriginalUsers; |
| |
| @Before |
| public void setUp() throws Exception { |
| assertNotNull(getBuild()); // ensure build has been set before test is run. |
| |
| mIsMultiuserSupported = getDevice().isMultiUserSupported(); |
| if (!mIsMultiuserSupported) { |
| CLog.w("Multi user not supporeted"); |
| } |
| mIsManagedUserSupported = getDevice().hasFeature("android.software.managed_users"); |
| if (!mIsManagedUserSupported) { |
| CLog.w("Managed users not supporeted"); |
| } |
| |
| if (mIsMultiuserSupported) { |
| mInitialUserId = getDevice().getCurrentUser(); |
| mOriginalUsers = new ArrayList<>(getDevice().listUsers()); |
| } |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| removeTestUsers(); |
| } |
| |
| protected void dumpsys(String label) throws DeviceNotAvailableException { |
| CLog.w("dumpsys shortcuts #" + label); |
| |
| CLog.w(getDevice().executeShellCommand("dumpsys shortcut")); |
| } |
| |
| protected String executeShellCommandWithLog(String command) throws DeviceNotAvailableException { |
| CLog.i("Executing command: " + command); |
| final String output = getDevice().executeShellCommand(command); |
| CLog.i(output); |
| return output; |
| } |
| |
| protected void clearShortcuts(String packageName, int userId) throws Exception { |
| assertContainsRegex("Success", |
| getDevice().executeShellCommand("cmd shortcut clear-shortcuts --user " + userId |
| + " " + packageName)); |
| } |
| |
| protected void installAppAsUser(String appFileName, int userId) throws FileNotFoundException, |
| DeviceNotAvailableException { |
| CLog.i("Installing app " + appFileName + " for user " + userId); |
| CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); |
| String result = getDevice().installPackageForUser( |
| buildHelper.getTestFile(appFileName), true, true, userId, "-t"); |
| assertNull("Failed to install " + appFileName + " for user " + userId + ": " + result, |
| result); |
| } |
| |
| protected int getPrimaryUserId() throws DeviceNotAvailableException { |
| return getDevice().getPrimaryUserId(); |
| } |
| |
| /** Returns true if the specified tests passed. Tests are run as given user. */ |
| protected void runDeviceTestsAsUser( |
| String pkgName, @Nullable String testClassName, int userId) |
| throws DeviceNotAvailableException { |
| runDeviceTestsAsUser(pkgName, testClassName, null /*testMethodName*/, userId); |
| } |
| |
| /** Returns true if the specified tests passed. Tests are run as given user. */ |
| protected void runDeviceTestsAsUser( |
| String pkgName, @Nullable String testClassName, String testMethodName, int userId) |
| throws DeviceNotAvailableException { |
| |
| final DeviceTestRunOptions opts = new DeviceTestRunOptions(pkgName); |
| if (testClassName != null) { |
| if (testClassName.startsWith(".")) { |
| testClassName = pkgName + testClassName; |
| } |
| opts.setTestClassName(testClassName); |
| } |
| if (testMethodName != null) { |
| opts.setTestMethodName(testMethodName); |
| } |
| opts.setUserId(userId); |
| |
| runDeviceTests(opts); |
| } |
| |
| private void removeTestUsers() throws Exception { |
| if (!mIsMultiuserSupported) { |
| return; |
| } |
| getDevice().switchUser(mInitialUserId); |
| for (int userId : getDevice().listUsers()) { |
| if (!mOriginalUsers.contains(userId)) { |
| getDevice().removeUser(userId); |
| } |
| } |
| } |
| |
| protected int getOrCreateSecondaryUser() throws Exception { |
| if (getDevice().isUserSecondary(mInitialUserId)) { |
| return mInitialUserId; |
| } |
| for (int userId : getDevice().listUsers()) { |
| if (getDevice().isUserSecondary(userId)) { |
| return userId; |
| } |
| } |
| return createUser(); |
| } |
| |
| protected int createUser() throws Exception{ |
| return getDevice().createUser("TestUser_" + System.currentTimeMillis()); |
| } |
| |
| protected int createProfile(int parentUserId) throws Exception{ |
| final String command = "pm create-user --profileOf " + parentUserId |
| + " --managed TestUser_" + System.currentTimeMillis(); |
| CLog.d("Starting command: " + command); |
| final String output = getDevice().executeShellCommand(command); |
| CLog.d("Output for command " + command + ": " + output); |
| |
| if (output.startsWith("Success")) { |
| try { |
| return Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim()); |
| } catch (NumberFormatException e) { |
| CLog.e("Failed to parse result: %s", output); |
| } |
| } else { |
| CLog.e("Failed to create user: %s", output); |
| } |
| throw new IllegalStateException(); |
| } |
| |
| /** |
| * Variant of {@link #assertContainsRegex(String,String,String)} using a |
| * generic message. |
| */ |
| public MatchResult assertContainsRegex( |
| String expectedRegex, String actual) { |
| return assertContainsRegex(null, expectedRegex, actual); |
| } |
| |
| /** |
| * Asserts that {@code expectedRegex} matches any substring of {@code actual} |
| * and fails with {@code message} if it does not. The Matcher is returned in |
| * case the test needs access to any captured groups. Note that you can also |
| * use this for a literal string, by wrapping your expected string in |
| * {@link Pattern#quote}. |
| */ |
| public MatchResult assertContainsRegex( |
| String message, String expectedRegex, String actual) { |
| if (actual == null) { |
| failNotContains(message, expectedRegex, actual); |
| } |
| Matcher matcher = getMatcher(expectedRegex, actual); |
| if (!matcher.find()) { |
| failNotContains(message, expectedRegex, actual); |
| } |
| return matcher; |
| } |
| |
| /** |
| * Asserts that {@code expectedRegex} does not exactly match {@code actual}, |
| * and fails with {@code message} if it does. Note that you can also use |
| * this for a literal string, by wrapping your expected string in |
| * {@link Pattern#quote}. |
| */ |
| public void assertNotMatchesRegex( |
| String message, String expectedRegex, String actual) { |
| Matcher matcher = getMatcher(expectedRegex, actual); |
| if (matcher.matches()) { |
| failMatch(message, expectedRegex, actual); |
| } |
| } |
| |
| private Matcher getMatcher(String expectedRegex, String actual) { |
| Pattern pattern = Pattern.compile(expectedRegex); |
| return pattern.matcher(actual); |
| } |
| |
| private void failMatch( |
| String message, String expectedRegex, String actual) { |
| failWithMessage(message, "expected not to match regex:<" + expectedRegex |
| + "> but was:<" + actual + '>'); |
| } |
| |
| private void failWithMessage(String userMessage, String ourMessage) { |
| fail((userMessage == null) |
| ? ourMessage |
| : userMessage + ' ' + ourMessage); |
| } |
| |
| private void failNotContains( |
| String message, String expectedRegex, String actual) { |
| String actualDesc = (actual == null) ? "null" : ('<' + actual + '>'); |
| failWithMessage(message, "expected to contain regex:<" + expectedRegex |
| + "> but was:" + actualDesc); |
| } |
| |
| protected void waitForBroadcastIdle() throws Exception { |
| runCommand("am wait-for-broadcast-idle"); |
| } |
| |
| private String runCommand(String command) throws Exception { |
| return runCommand(command, "", true); |
| } |
| |
| private String runCommand(String command, String expectedOutputPattern) throws Exception { |
| return runCommand(command, expectedOutputPattern, true); |
| } |
| |
| private String runCommandAndNotMatch(String command, String expectedOutputPattern) |
| throws Exception { |
| return runCommand(command, expectedOutputPattern, false); |
| } |
| |
| private String runCommand(String command, String expectedOutputPattern, |
| boolean shouldMatch) throws Exception { |
| CLog.d("Executing command: " + command); |
| final String output = getDevice().executeShellCommand(command); |
| |
| CLog.d("Output:\n" |
| + "====================\n" |
| + output |
| + "===================="); |
| |
| final Pattern pat = Pattern.compile( |
| expectedOutputPattern, Pattern.MULTILINE | Pattern.COMMENTS); |
| if (pat.matcher(output.trim()).find() != shouldMatch) { |
| fail("Output from \"" + command + "\" " |
| + (shouldMatch ? "didn't match" : "unexpectedly matched") |
| + " \"" + expectedOutputPattern + "\""); |
| } |
| return output; |
| } |
| } |