| /* |
| * 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 com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; |
| import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; |
| import com.android.ddmlib.testrunner.TestResult.TestStatus; |
| import com.android.tradefed.build.IBuildInfo; |
| 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.DeviceTestCase; |
| import com.android.tradefed.testtype.IBuildReceiver; |
| |
| 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 DeviceTestCase implements IBuildReceiver { |
| 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 = "android.support.test.runner.AndroidJUnitRunner"; |
| |
| private IBuildInfo mCtsBuild; |
| |
| protected boolean mIsMultiuserSupported; |
| protected boolean mIsManagedUserSupported; |
| |
| private ArrayList<Integer> mOriginalUsers; |
| |
| @Override |
| public void setBuild(IBuildInfo buildInfo) { |
| mCtsBuild = buildInfo; |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| assertNotNull(mCtsBuild); // 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) { |
| mOriginalUsers = new ArrayList<>(getDevice().listUsers()); |
| } |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| removeTestUsers(); |
| super.tearDown(); |
| } |
| |
| 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(mCtsBuild); |
| 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 { |
| Map<String, String> params = Collections.emptyMap(); |
| runDeviceTestsAsUser(pkgName, testClassName, testMethodName, userId, params); |
| } |
| |
| protected void runDeviceTestsAsUser(String pkgName, @Nullable String testClassName, |
| @Nullable String testMethodName, int userId, |
| Map<String, String> params) throws DeviceNotAvailableException { |
| if (testClassName != null && testClassName.startsWith(".")) { |
| testClassName = pkgName + testClassName; |
| } |
| |
| RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner( |
| pkgName, RUNNER, getDevice().getIDevice()); |
| if (testClassName != null && testMethodName != null) { |
| testRunner.setMethodName(testClassName, testMethodName); |
| } else if (testClassName != null) { |
| testRunner.setClassName(testClassName); |
| } |
| |
| for (Map.Entry<String, String> param : params.entrySet()) { |
| testRunner.addInstrumentationArg(param.getKey(), param.getValue()); |
| } |
| |
| CollectingTestListener listener = new CollectingTestListener(); |
| assertTrue(getDevice().runInstrumentationTestsAsUser(testRunner, userId, listener)); |
| |
| TestRunResult runResult = listener.getCurrentRunResults(); |
| if (runResult.getTestResults().size() == 0) { |
| fail("No tests have been executed."); |
| return; |
| } |
| |
| printTestResult(runResult); |
| if (runResult.hasFailedTests() || runResult.getNumTestsInState(TestStatus.PASSED) == 0) { |
| fail("Some tests have been failed."); |
| } |
| } |
| |
| private void printTestResult(TestRunResult runResult) { |
| for (Map.Entry<TestDescription, TestResult> testEntry : |
| runResult.getTestResults().entrySet()) { |
| TestResult testResult = testEntry.getValue(); |
| |
| final String message = "Test " + testEntry.getKey() + ": " + testResult.getStatus(); |
| if (testResult.getStatus() == TestStatus.PASSED) { |
| CLog.i(message); |
| } else { |
| CLog.e(message); |
| CLog.e(testResult.getStackTrace()); |
| } |
| } |
| } |
| |
| private void removeTestUsers() throws Exception { |
| if (!mIsMultiuserSupported) { |
| return; |
| } |
| getDevice().switchUser(getPrimaryUserId()); |
| for (int userId : getDevice().listUsers()) { |
| if (!mOriginalUsers.contains(userId)) { |
| getDevice().removeUser(userId); |
| } |
| } |
| } |
| |
| 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(); |
| } |
| |
| /** Starts user {@code userId} and waits until it is in state RUNNING_UNLOCKED. */ |
| protected void startUserAndWait(int userId) throws Exception { |
| getDevice().startUser(userId); |
| |
| final String desiredState = "RUNNING_UNLOCKED"; |
| final long USER_STATE_TIMEOUT_MS = 60_0000; // 1 minute |
| final long timeout = System.currentTimeMillis() + USER_STATE_TIMEOUT_MS; |
| final String command = String.format("am get-started-user-state %d", userId); |
| String output = ""; |
| while (System.currentTimeMillis() <= timeout) { |
| output = getDevice().executeShellCommand(command); |
| if (output.contains(desiredState)) { |
| return; |
| } |
| try { |
| Thread.sleep(100); |
| } catch (InterruptedException e) { |
| // Do nothing. |
| } |
| } |
| fail("User state of " + userId + " was '" + output + "' rather than " + desiredState); |
| } |
| |
| /** |
| * 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); |
| } |
| } |