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 static org.junit.Assert.assertNotNull;
19 import static org.junit.Assert.assertNull;
20 import static org.junit.Assert.assertTrue;
21 import static org.junit.Assert.fail;
22 
23 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
24 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
25 import com.android.ddmlib.testrunner.TestResult.TestStatus;
26 import com.android.tradefed.device.DeviceNotAvailableException;
27 import com.android.tradefed.log.LogUtil.CLog;
28 import com.android.tradefed.result.CollectingTestListener;
29 import com.android.tradefed.result.TestDescription;
30 import com.android.tradefed.result.TestResult;
31 import com.android.tradefed.result.TestRunResult;
32 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
33 import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
34 
35 import org.junit.After;
36 import org.junit.Before;
37 
38 import java.io.FileNotFoundException;
39 import java.util.ArrayList;
40 import java.util.Collections;
41 import java.util.Map;
42 import java.util.regex.MatchResult;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
45 
46 import javax.annotation.Nullable;
47 
48 abstract public class BaseShortcutManagerHostTest extends BaseHostJUnit4Test {
49     protected static final boolean DUMPSYS_IN_TEARDOWN = false; // DO NOT SUBMIT WITH TRUE
50 
51     protected static final boolean NO_UNINSTALL_IN_TEARDOWN = false; // DO NOT SUBMIT WITH TRUE
52 
53     private static final String RUNNER = "androidx.test.runner.AndroidJUnitRunner";
54 
55     protected boolean mIsMultiuserSupported;
56     protected boolean mIsManagedUserSupported;
57 
58     private int mInitialUserId;
59     private ArrayList<Integer> mOriginalUsers;
60 
61     @Before
setUp()62     public void setUp() throws Exception {
63         assertNotNull(getBuild());  // ensure build has been set before test is run.
64 
65         mIsMultiuserSupported = getDevice().isMultiUserSupported();
66         if (!mIsMultiuserSupported) {
67             CLog.w("Multi user not supporeted");
68         }
69         mIsManagedUserSupported = getDevice().hasFeature("android.software.managed_users");
70         if (!mIsManagedUserSupported) {
71             CLog.w("Managed users not supporeted");
72         }
73 
74         if (mIsMultiuserSupported) {
75             mInitialUserId = getDevice().getCurrentUser();
76             mOriginalUsers = new ArrayList<>(getDevice().listUsers());
77         }
78     }
79 
80     @After
tearDown()81     public void tearDown() throws Exception {
82         removeTestUsers();
83     }
84 
dumpsys(String label)85     protected void dumpsys(String label) throws DeviceNotAvailableException {
86         CLog.w("dumpsys shortcuts #" + label);
87 
88         CLog.w(getDevice().executeShellCommand("dumpsys shortcut"));
89     }
90 
executeShellCommandWithLog(String command)91     protected String executeShellCommandWithLog(String command) throws DeviceNotAvailableException {
92         CLog.i("Executing command: " + command);
93         final String output = getDevice().executeShellCommand(command);
94         CLog.i(output);
95         return output;
96     }
97 
clearShortcuts(String packageName, int userId)98     protected void clearShortcuts(String packageName, int userId) throws Exception {
99         assertContainsRegex("Success",
100                 getDevice().executeShellCommand("cmd shortcut clear-shortcuts --user " + userId
101                         + " " + packageName));
102     }
103 
installAppAsUser(String appFileName, int userId)104     protected void installAppAsUser(String appFileName, int userId) throws FileNotFoundException,
105             DeviceNotAvailableException {
106         CLog.i("Installing app " + appFileName + " for user " + userId);
107         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
108         String result = getDevice().installPackageForUser(
109                 buildHelper.getTestFile(appFileName), true, true, userId, "-t");
110         assertNull("Failed to install " + appFileName + " for user " + userId + ": " + result,
111                 result);
112     }
113 
getPrimaryUserId()114     protected int getPrimaryUserId() throws DeviceNotAvailableException {
115         return getDevice().getPrimaryUserId();
116     }
117 
118     /** Returns true if the specified tests passed. Tests are run as given user. */
runDeviceTestsAsUser( String pkgName, @Nullable String testClassName, int userId)119     protected void runDeviceTestsAsUser(
120             String pkgName, @Nullable String testClassName, int userId)
121             throws DeviceNotAvailableException {
122         runDeviceTestsAsUser(pkgName, testClassName, null /*testMethodName*/, userId);
123     }
124 
125     /** Returns true if the specified tests passed. Tests are run as given user. */
runDeviceTestsAsUser( String pkgName, @Nullable String testClassName, String testMethodName, int userId)126     protected void runDeviceTestsAsUser(
127             String pkgName, @Nullable String testClassName, String testMethodName, int userId)
128             throws DeviceNotAvailableException {
129 
130         final DeviceTestRunOptions opts = new DeviceTestRunOptions(pkgName);
131         if (testClassName != null) {
132             if (testClassName.startsWith(".")) {
133                 testClassName = pkgName + testClassName;
134             }
135             opts.setTestClassName(testClassName);
136         }
137         if (testMethodName != null) {
138             opts.setTestMethodName(testMethodName);
139         }
140         opts.setUserId(userId);
141 
142         runDeviceTests(opts);
143     }
144 
removeTestUsers()145     private void removeTestUsers() throws Exception {
146         if (!mIsMultiuserSupported) {
147             return;
148         }
149         getDevice().switchUser(mInitialUserId);
150         for (int userId : getDevice().listUsers()) {
151             if (!mOriginalUsers.contains(userId)) {
152                 getDevice().removeUser(userId);
153             }
154         }
155     }
156 
getOrCreateSecondaryUser()157     protected int getOrCreateSecondaryUser() throws Exception {
158         if (getDevice().isUserSecondary(mInitialUserId)) {
159             return mInitialUserId;
160         }
161         for (int userId : getDevice().listUsers()) {
162             if (getDevice().isUserSecondary(userId)) {
163                 return userId;
164             }
165         }
166         return createUser();
167     }
168 
createUser()169     protected int createUser() throws Exception{
170         return getDevice().createUser("TestUser_" + System.currentTimeMillis());
171     }
172 
createProfile(int parentUserId)173     protected int createProfile(int parentUserId) throws Exception{
174         final String command = "pm create-user --profileOf " + parentUserId
175                 + " --managed TestUser_" + System.currentTimeMillis();
176         CLog.d("Starting command: " + command);
177         final String output = getDevice().executeShellCommand(command);
178         CLog.d("Output for command " + command + ": " + output);
179 
180         if (output.startsWith("Success")) {
181             try {
182                 return Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim());
183             } catch (NumberFormatException e) {
184                 CLog.e("Failed to parse result: %s", output);
185             }
186         } else {
187             CLog.e("Failed to create user: %s", output);
188         }
189         throw new IllegalStateException();
190     }
191 
192     /**
193      * Variant of {@link #assertContainsRegex(String,String,String)} using a
194      * generic message.
195      */
assertContainsRegex( String expectedRegex, String actual)196     public MatchResult assertContainsRegex(
197             String expectedRegex, String actual) {
198         return assertContainsRegex(null, expectedRegex, actual);
199     }
200 
201     /**
202      * Asserts that {@code expectedRegex} matches any substring of {@code actual}
203      * and fails with {@code message} if it does not.  The Matcher is returned in
204      * case the test needs access to any captured groups.  Note that you can also
205      * use this for a literal string, by wrapping your expected string in
206      * {@link Pattern#quote}.
207      */
assertContainsRegex( String message, String expectedRegex, String actual)208     public MatchResult assertContainsRegex(
209             String message, String expectedRegex, String actual) {
210         if (actual == null) {
211             failNotContains(message, expectedRegex, actual);
212         }
213         Matcher matcher = getMatcher(expectedRegex, actual);
214         if (!matcher.find()) {
215             failNotContains(message, expectedRegex, actual);
216         }
217         return matcher;
218     }
219 
220     /**
221      * Asserts that {@code expectedRegex} does not exactly match {@code actual},
222      * and fails with {@code message} if it does. Note that you can also use
223      * this for a literal string, by wrapping your expected string in
224      * {@link Pattern#quote}.
225      */
assertNotMatchesRegex( String message, String expectedRegex, String actual)226     public void assertNotMatchesRegex(
227             String message, String expectedRegex, String actual) {
228         Matcher matcher = getMatcher(expectedRegex, actual);
229         if (matcher.matches()) {
230             failMatch(message, expectedRegex, actual);
231         }
232     }
233 
getMatcher(String expectedRegex, String actual)234     private Matcher getMatcher(String expectedRegex, String actual) {
235         Pattern pattern = Pattern.compile(expectedRegex);
236         return pattern.matcher(actual);
237     }
238 
failMatch( String message, String expectedRegex, String actual)239     private void failMatch(
240             String message, String expectedRegex, String actual) {
241         failWithMessage(message, "expected not to match regex:<" + expectedRegex
242                 + "> but was:<" + actual + '>');
243     }
244 
failWithMessage(String userMessage, String ourMessage)245     private void failWithMessage(String userMessage, String ourMessage) {
246         fail((userMessage == null)
247                 ? ourMessage
248                 : userMessage + ' ' + ourMessage);
249     }
250 
failNotContains( String message, String expectedRegex, String actual)251     private void failNotContains(
252             String message, String expectedRegex, String actual) {
253         String actualDesc = (actual == null) ? "null" : ('<' + actual + '>');
254         failWithMessage(message, "expected to contain regex:<" + expectedRegex
255                 + "> but was:" + actualDesc);
256     }
257 }
258