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