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