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