| /* |
| * Copyright (C) 2014 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 com.android.cts.devicepolicy; |
| |
| import com.android.cts.tradefed.build.CtsBuildHelper; |
| import com.android.ddmlib.Log.LogLevel; |
| import com.android.ddmlib.testrunner.InstrumentationResultParser; |
| import com.android.ddmlib.testrunner.RemoteAndroidTestRunner; |
| import com.android.ddmlib.testrunner.TestIdentifier; |
| import com.android.tradefed.build.IBuildInfo; |
| import com.android.tradefed.device.DeviceNotAvailableException; |
| import com.android.tradefed.device.ITestDevice; |
| import com.android.tradefed.log.LogUtil.CLog; |
| import com.android.tradefed.result.CollectingTestListener; |
| import com.android.tradefed.result.TestResult; |
| import com.android.tradefed.result.TestResult.TestStatus; |
| 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.HashSet; |
| import java.util.Map; |
| |
| import javax.annotation.Nullable; |
| |
| /** |
| * Set of tests for Profile Owner use cases. |
| */ |
| public class ProfileOwnerTest extends DeviceTestCase implements IBuildReceiver { |
| |
| private static final String RUNNER = "android.test.InstrumentationTestRunner"; |
| |
| private static final String PROFILE_OWNER_PKG = "com.android.cts.profileowner"; |
| private static final String PROFILE_OWNER_APK = "CtsProfileOwnerApp.apk"; |
| |
| private static final String ADMIN_RECEIVER_TEST_CLASS = |
| PROFILE_OWNER_PKG + ".BaseProfileOwnerTest$BasicAdminReceiver"; |
| |
| private static final String[] REQUIRED_DEVICE_FEATURES = new String[] { |
| "android.software.managed_users", |
| "android.software.device_admin" }; |
| |
| private CtsBuildHelper mCtsBuild; |
| private int mUserId; |
| private boolean mHasFeature; |
| |
| @Override |
| public void setBuild(IBuildInfo buildInfo) { |
| mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo); |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| assertNotNull(mCtsBuild); // ensure build has been set before test is run. |
| mHasFeature = hasDeviceFeatures(REQUIRED_DEVICE_FEATURES); |
| |
| if (mHasFeature) { |
| mUserId = createUser(); |
| installApp(PROFILE_OWNER_APK); |
| setProfileOwner(PROFILE_OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS); |
| } |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| if (mHasFeature) { |
| // Remove the user that we created on setUp(), and the app that we installed. |
| String removeUserCommand = "pm remove-user " + mUserId; |
| CLog.logAndDisplay(LogLevel.INFO, "Output for command " + removeUserCommand + ": " |
| + getDevice().executeShellCommand(removeUserCommand)); |
| getDevice().uninstallPackage(PROFILE_OWNER_PKG); |
| } |
| |
| super.tearDown(); |
| } |
| |
| public void testProfileOwner() throws Exception { |
| if (!mHasFeature) { |
| return; |
| } |
| // Runs all tests classes from the package, as the profile user. |
| assertTrue(runDeviceTestsAsUser(PROFILE_OWNER_PKG, null /*testClassName*/, mUserId)); |
| } |
| |
| private boolean hasDeviceFeatures(String[] requiredFeatures) |
| throws DeviceNotAvailableException { |
| // TODO: Move this logic to ITestDevice. |
| String command = "pm list features"; |
| String commandOutput = getDevice().executeShellCommand(command); |
| |
| // Extract the id of the new user. |
| HashSet<String> availableFeatures = new HashSet<String>(); |
| for (String feature: commandOutput.split("\\s+")) { |
| // Each line in the output of the command has the format "feature:{FEATURE_VALUE}". |
| String[] tokens = feature.split(":"); |
| assertTrue(tokens.length > 1); |
| assertEquals("feature", tokens[0]); |
| availableFeatures.add(tokens[1]); |
| } |
| |
| for (String requiredFeature : requiredFeatures) { |
| if(!availableFeatures.contains(requiredFeature)) { |
| CLog.logAndDisplay(LogLevel.INFO, "Device doesn't have required feature " |
| + requiredFeature + ". Tests won't run."); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private void installApp(String fileName) |
| throws FileNotFoundException, DeviceNotAvailableException { |
| String installResult = getDevice().installPackage(mCtsBuild.getTestApp(fileName), true); |
| assertNull(String.format("Failed to install %s, Reason: %s", fileName, installResult), |
| installResult); |
| } |
| |
| private int createUser() throws DeviceNotAvailableException { |
| String command = |
| "pm create-user --profileOf 0 --managed TestProfile_" + System.currentTimeMillis(); |
| String commandOutput = getDevice().executeShellCommand(command); |
| CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput); |
| |
| // Extract the id of the new user. |
| String[] tokens = commandOutput.split("\\s+"); |
| assertTrue(tokens.length > 0); |
| assertEquals("Success:", tokens[0]); |
| return Integer.parseInt(tokens[tokens.length-1]); |
| } |
| |
| private void setProfileOwner(String componentName) throws DeviceNotAvailableException { |
| String command = "dpm set-profile-owner '" + componentName + "' " + mUserId; |
| String commandOutput = getDevice().executeShellCommand(command); |
| CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput); |
| assertTrue(commandOutput.startsWith("Success:")); |
| } |
| |
| /** Returns true if the specified tests passed. Tests are run as user owner. */ |
| private boolean runDeviceTests(String pkgName, @Nullable String testClassName) |
| throws DeviceNotAvailableException { |
| return runDeviceTests(pkgName, testClassName, null /*testMethodName*/, null /*userId*/); |
| } |
| |
| /** Returns true if the specified tests passed. Tests are run as given user. */ |
| private boolean runDeviceTestsAsUser(String pkgName, @Nullable String testClassName, int userId) |
| throws DeviceNotAvailableException { |
| return runDeviceTests(pkgName, testClassName, null /*testMethodName*/, userId); |
| } |
| |
| private boolean runDeviceTests(String pkgName, @Nullable String testClassName, |
| @Nullable String testMethodName, @Nullable Integer userId) |
| throws DeviceNotAvailableException { |
| TestRunResult runResult = (userId == null) |
| ? doRunTests(pkgName, testClassName, testMethodName) |
| : doRunTestsAsUser(pkgName, testClassName, testMethodName, userId); |
| printTestResult(runResult); |
| return !runResult.hasFailedTests() && runResult.getNumPassedTests() > 0; |
| } |
| |
| /** Helper method to run tests and return the listener that collected the results. */ |
| private TestRunResult doRunTests( |
| String pkgName, @Nullable String testClassName, @Nullable String testMethodName) |
| throws DeviceNotAvailableException { |
| RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner( |
| pkgName, RUNNER, getDevice().getIDevice()); |
| if (testClassName != null && testMethodName != null) { |
| testRunner.setMethodName(testClassName, testMethodName); |
| } else if (testClassName != null) { |
| testRunner.setClassName(testClassName); |
| } |
| |
| CollectingTestListener listener = new CollectingTestListener(); |
| assertTrue(getDevice().runInstrumentationTests(testRunner, listener)); |
| return listener.getCurrentRunResults(); |
| } |
| |
| private TestRunResult doRunTestsAsUser(String pkgName, @Nullable String testClassName, |
| @Nullable String testMethodName, int userId) |
| throws DeviceNotAvailableException { |
| // TODO: move this to RemoteAndroidTestRunner once it supports users. Should be straight |
| // forward to add a RemoteAndroidTestRunner.setUser(userId) method. Then we can merge both |
| // doRunTests* methods. |
| StringBuilder testsToRun = new StringBuilder(); |
| if (testClassName != null) { |
| testsToRun.append("-e class " + testClassName); |
| if (testMethodName != null) { |
| testsToRun.append("#" + testMethodName); |
| } |
| } |
| String command = "am instrument --user " + userId + " -w -r " + testsToRun + " " |
| + pkgName + "/" + RUNNER; |
| CLog.i("Running " + command); |
| |
| CollectingTestListener listener = new CollectingTestListener(); |
| InstrumentationResultParser parser = new InstrumentationResultParser(pkgName, listener); |
| getDevice().executeShellCommand(command, parser); |
| return listener.getCurrentRunResults(); |
| } |
| |
| private void printTestResult(TestRunResult runResult) { |
| for (Map.Entry<TestIdentifier, TestResult> testEntry : |
| runResult.getTestResults().entrySet()) { |
| TestResult testResult = testEntry.getValue(); |
| CLog.logAndDisplay(LogLevel.INFO, |
| "Test " + testEntry.getKey() + ": " + testResult.getStatus()); |
| if (testResult.getStatus() != TestStatus.PASSED) { |
| CLog.logAndDisplay(LogLevel.WARN, testResult.getStackTrace()); |
| } |
| } |
| } |
| } |