• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
17 package android.autofillservice.cts;
18 
19 import static android.autofillservice.cts.Helper.getContext;
20 import static android.autofillservice.cts.InstrumentedAutoFillService.SERVICE_NAME;
21 import static android.content.Context.CLIPBOARD_SERVICE;
22 
23 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
24 
25 import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
26 
27 import android.autofillservice.cts.InstrumentedAutoFillService.Replier;
28 import android.content.ClipboardManager;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.pm.PackageManager;
32 import android.provider.DeviceConfig;
33 import android.provider.Settings;
34 import android.util.Log;
35 import android.view.autofill.AutofillManager;
36 import android.widget.RemoteViews;
37 
38 import androidx.annotation.NonNull;
39 import androidx.test.ext.junit.runners.AndroidJUnit4;
40 
41 import com.android.compatibility.common.util.DeviceConfigStateChangerRule;
42 import com.android.compatibility.common.util.RequiredFeatureRule;
43 import com.android.compatibility.common.util.RetryRule;
44 import com.android.compatibility.common.util.SafeCleanerRule;
45 import com.android.compatibility.common.util.SettingsStateKeeperRule;
46 import com.android.compatibility.common.util.TestNameUtils;
47 import com.android.cts.mockime.MockImeSessionRule;
48 
49 import org.junit.AfterClass;
50 import org.junit.Before;
51 import org.junit.BeforeClass;
52 import org.junit.ClassRule;
53 import org.junit.Rule;
54 import org.junit.rules.RuleChain;
55 import org.junit.rules.TestRule;
56 import org.junit.rules.TestWatcher;
57 import org.junit.runner.Description;
58 import org.junit.runner.RunWith;
59 import org.junit.runners.model.Statement;
60 
61 /**
62  * Placeholder for the base class for all integration tests:
63  *
64  * <ul>
65  *   <li>{@link AutoActivityLaunch}
66  *   <li>{@link ManualActivityLaunch}
67  * </ul>
68  *
69  * <p>These classes provide the common infrastructure such as:
70  *
71  * <ul>
72  *   <li>Preserving the autofill service settings.
73  *   <li>Cleaning up test state.
74  *   <li>Wrapping the test under autofill-specific test rules.
75  *   <li>Launching the activity used by the test.
76  * </ul>
77  */
78 public final class AutoFillServiceTestCase {
79 
80     /**
81      * Base class for all test cases that use an {@link AutofillActivityTestRule} to
82      * launch the activity.
83      */
84     // Must be public because of @ClassRule
85     public abstract static class AutoActivityLaunch<A extends AbstractAutoFillActivity>
86             extends BaseTestCase {
87 
88         @ClassRule
89         public static final SettingsStateKeeperRule sPublicServiceSettingsKeeper =
90                 sTheRealServiceSettingsKeeper;
91 
AutoActivityLaunch()92         protected AutoActivityLaunch() {
93             super(sDefaultUiBot);
94         }
95 
96         @Override
getMainTestRule()97         protected TestRule getMainTestRule() {
98             return getActivityRule();
99         }
100 
101         /**
102          * Gets the rule to launch the main activity for this test.
103          *
104          * <p><b>Note: </b>the rule must be either lazily generated or a static singleton, otherwise
105          * this method could return {@code null} when the rule chain that uses it is constructed.
106          *
107          */
getActivityRule()108         protected abstract @NonNull AutofillActivityTestRule<A> getActivityRule();
109 
launchActivity(@onNull Intent intent)110         protected @NonNull A launchActivity(@NonNull Intent intent) {
111             return getActivityRule().launchActivity(intent);
112         }
113 
getActivity()114         protected @NonNull A getActivity() {
115             return getActivityRule().getActivity();
116         }
117     }
118 
119     /**
120      * Base class for all test cases that don't require an {@link AutofillActivityTestRule}.
121      */
122     // Must be public because of @ClassRule
123     public abstract static class ManualActivityLaunch extends BaseTestCase {
124 
125         @ClassRule
126         public static final SettingsStateKeeperRule sPublicServiceSettingsKeeper =
127                 sTheRealServiceSettingsKeeper;
128 
ManualActivityLaunch()129         protected ManualActivityLaunch() {
130             this(sDefaultUiBot);
131         }
132 
ManualActivityLaunch(@onNull UiBot uiBot)133         protected ManualActivityLaunch(@NonNull UiBot uiBot) {
134             super(uiBot);
135         }
136 
137         @Override
getMainTestRule()138         protected TestRule getMainTestRule() {
139             // TODO: create a NoOpTestRule on common code
140             return new TestRule() {
141 
142                 @Override
143                 public Statement apply(Statement base, Description description) {
144                     // Returns a no-op statements
145                     return new Statement() {
146                         @Override
147                         public void evaluate() throws Throwable {
148                             base.evaluate();
149                         }
150                     };
151                 }
152             };
153         }
154 
155         protected SimpleSaveActivity startSimpleSaveActivity() throws Exception {
156             final Intent intent = new Intent(mContext, SimpleSaveActivity.class)
157                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
158             mContext.startActivity(intent);
159             mUiBot.assertShownByRelativeId(SimpleSaveActivity.ID_LABEL);
160             return SimpleSaveActivity.getInstance();
161         }
162 
163         protected PreSimpleSaveActivity startPreSimpleSaveActivity() throws Exception {
164             final Intent intent = new Intent(mContext, PreSimpleSaveActivity.class)
165                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
166             mContext.startActivity(intent);
167             mUiBot.assertShownByRelativeId(PreSimpleSaveActivity.ID_PRE_LABEL);
168             return PreSimpleSaveActivity.getInstance();
169         }
170     }
171 
172     @RunWith(AndroidJUnit4.class)
173     // Must be public because of @ClassRule
174     public abstract static class BaseTestCase {
175 
176         private static final String TAG = "AutoFillServiceTestCase";
177 
178         protected static final Replier sReplier = InstrumentedAutoFillService.getReplier();
179 
180         protected static final Context sContext = getInstrumentation().getTargetContext();
181 
182         // Hack because JUnit requires that @ClassRule instance belong to a public class.
183         protected static final SettingsStateKeeperRule sTheRealServiceSettingsKeeper =
184                 new SettingsStateKeeperRule(sContext, Settings.Secure.AUTOFILL_SERVICE) {
185             @Override
186             protected void preEvaluate(Description description) {
187                 TestNameUtils.setCurrentTestClass(description.getClassName());
188             }
189 
190             @Override
191             protected void postEvaluate(Description description) {
192                 TestNameUtils.setCurrentTestClass(null);
193             }
194         };
195 
196         @ClassRule
197         public static final MockImeSessionRule sMockImeSessionRule = new MockImeSessionRule();
198 
199         private final TestWatcher mTestWatcher = new AutofillTestWatcher();
200 
201         private final RetryRule mRetryRule = new RetryRule(getNumberRetries());
202 
203         private final AutofillLoggingTestRule mLoggingRule = new AutofillLoggingTestRule(TAG);
204 
205         private final RequiredFeatureRule mRequiredFeatureRule =
206                 new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
207 
208         protected final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
209                 .setDumper(mLoggingRule)
210                 .run(() -> sReplier.assertNoUnhandledFillRequests())
211                 .run(() -> sReplier.assertNoUnhandledSaveRequests())
212                 .add(() -> { return sReplier.getExceptions(); });
213 
214         @Rule
215         public final RuleChain mLookAllTheseRules = RuleChain
216                 //
217                 // mRequiredFeatureRule should be first so the test can be skipped right away
218                 .outerRule(mRequiredFeatureRule)
219                 //
220                 // mTestWatcher should always be one the first rules, as it defines the name of the
221                 // test being ran and finishes dangling activities at the end
222                 .around(mTestWatcher)
223                 //
224                 // mLoggingRule wraps the test but doesn't interfere with it
225                 .around(mLoggingRule)
226                 //
227                 // mSafeCleanerRule will catch errors
228                 .around(mSafeCleanerRule)
229                 //
230                 // mRetryRule should be closest to the main test as possible
231                 .around(mRetryRule)
232                 //
233                 // Augmented Autofill should be disabled by default
234                 .around(new DeviceConfigStateChangerRule(sContext, DeviceConfig.NAMESPACE_AUTOFILL,
235                         AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES,
236                         Integer.toString(getSmartSuggestionMode())))
237                 //
238                 // Finally, let subclasses add their own rules (like ActivityTestRule)
239                 .around(getMainTestRule());
240 
241 
242         protected final Context mContext = sContext;
243         protected final String mPackageName;
244         protected final UiBot mUiBot;
245 
246         private BaseTestCase(@NonNull UiBot uiBot) {
247             mPackageName = mContext.getPackageName();
248             mUiBot = uiBot;
249             mUiBot.reset();
250         }
251 
252         protected int getSmartSuggestionMode() {
253             return AutofillManager.FLAG_SMART_SUGGESTION_OFF;
254         }
255 
256         /**
257          * Gets how many times a test should be retried.
258          *
259          * @return {@code 1} by default, unless overridden by subclasses or by a global settings
260          * named {@code CLASS_NAME + #getNumberRetries} or
261          * {@code CtsAutoFillServiceTestCases#getNumberRetries} (the former having a higher
262          * priority).
263          */
264         protected int getNumberRetries() {
265             final String localProp = getClass().getName() + "#getNumberRetries";
266             final Integer localValue = getNumberRetries(localProp);
267             if (localValue != null) return localValue.intValue();
268 
269             final String globalProp = "CtsAutoFillServiceTestCases#getNumberRetries";
270             final Integer globalValue = getNumberRetries(globalProp);
271             if (globalValue != null) return globalValue.intValue();
272 
273             return 1;
274         }
275 
276         private Integer getNumberRetries(String prop) {
277             final String value = Settings.Global.getString(sContext.getContentResolver(), prop);
278             if (value != null) {
279                 Log.i(TAG, "getNumberRetries(): overriding to " + value + " because of '" + prop
280                         + "' global setting");
281                 try {
282                     return Integer.parseInt(value);
283                 } catch (Exception e) {
284                     Log.w(TAG, "error parsing property '" + prop + "'='" + value + "'", e);
285                 }
286             }
287             return null;
288         }
289 
290         /**
291          * Gets the test-specific {@link Rule @Rule}.
292          *
293          * <p>Sub-class <b>MUST</b> override this method instead of annotation their own rules,
294          * so the order is preserved.
295          *
296          */
297         @NonNull
298         protected abstract TestRule getMainTestRule();
299 
300         @BeforeClass
301         public static void disableDefaultAugmentedService() {
302             Log.v(TAG, "@BeforeClass: disableDefaultAugmentedService()");
303             Helper.setDefaultAugmentedAutofillServiceEnabled(false);
304         }
305 
306         @AfterClass
307         public static void enableDefaultAugmentedService() {
308             Log.v(TAG, "@AfterClass: enableDefaultAugmentedService()");
309             Helper.setDefaultAugmentedAutofillServiceEnabled(true);
310         }
311 
312         @Before
313         public void prepareDevice() throws Exception {
314             Log.v(TAG, "@Before: prepareDevice()");
315 
316             // Unlock screen.
317             runShellCommand("input keyevent KEYCODE_WAKEUP");
318 
319             // Dismiss keyguard, in case it's set as "Swipe to unlock".
320             runShellCommand("wm dismiss-keyguard");
321 
322             // Collapse notifications.
323             runShellCommand("cmd statusbar collapse");
324 
325             // Set orientation as portrait, otherwise some tests might fail due to elements not
326             // fitting in, IME orientation, etc...
327             mUiBot.setScreenOrientation(UiBot.PORTRAIT);
328 
329             // Wait until device is idle to avoid flakiness
330             mUiBot.waitForIdle();
331 
332             // Clear Clipboard
333             // TODO(b/117768051): remove try/catch once fixed
334             try {
335                 ((ClipboardManager) mContext.getSystemService(CLIPBOARD_SERVICE))
336                     .clearPrimaryClip();
337             } catch (Exception e) {
338                 Log.e(TAG, "Ignoring exception clearing clipboard", e);
339             }
340         }
341 
342         @Before
343         public void preTestCleanup() {
344             Log.v(TAG, "@Before: preTestCleanup()");
345 
346             prepareServicePreTest();
347 
348             InstrumentedAutoFillService.resetStaticState();
349             AuthenticationActivity.resetStaticState();
350             sReplier.reset();
351         }
352 
353         /**
354          * Prepares the service before each test - by default, disables it
355          */
356         protected void prepareServicePreTest() {
357             Log.v(TAG, "prepareServicePreTest(): calling disableService()");
358             disableService();
359         }
360 
361         /**
362          * Enables the {@link InstrumentedAutoFillService} for autofill for the current user.
363          */
364         protected void enableService() {
365             Helper.enableAutofillService(getContext(), SERVICE_NAME);
366         }
367 
368         /**
369          * Disables the {@link InstrumentedAutoFillService} for autofill for the current user.
370          */
371         protected void disableService() {
372             Helper.disableAutofillService(getContext());
373         }
374 
375         /**
376          * Asserts that the {@link InstrumentedAutoFillService} is enabled for the default user.
377          */
378         protected void assertServiceEnabled() {
379             Helper.assertAutofillServiceStatus(SERVICE_NAME, true);
380         }
381 
382         /**
383          * Asserts that the {@link InstrumentedAutoFillService} is disabled for the default user.
384          */
385         protected void assertServiceDisabled() {
386             Helper.assertAutofillServiceStatus(SERVICE_NAME, false);
387         }
388 
389         protected RemoteViews createPresentation(String message) {
390             final RemoteViews presentation = new RemoteViews(getContext()
391                     .getPackageName(), R.layout.list_item);
392             presentation.setTextViewText(R.id.text1, message);
393             return presentation;
394         }
395 
396         @NonNull
397         protected AutofillManager getAutofillManager() {
398             return mContext.getSystemService(AutofillManager.class);
399         }
400     }
401 
402     protected static final UiBot sDefaultUiBot = new UiBot();
403 
404     private AutoFillServiceTestCase() {
405         throw new UnsupportedOperationException("Contain static stuff only");
406     }
407 }
408