blob: 5354b02da7ee4d5bdb7671d2b0b5f4a1a03a32d8 [file] [log] [blame]
/*
* 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);
}
}