Home | History | Annotate | Download | only in cts
      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 
     92         protected AutoActivityLaunch() {
     93             super(sDefaultUiBot);
     94         }
     95 
     96         @Override
     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          */
    108         protected abstract @NonNull AutofillActivityTestRule<A> getActivityRule();
    109 
    110         protected @NonNull A launchActivity(@NonNull Intent intent) {
    111             return getActivityRule().launchActivity(intent);
    112         }
    113 
    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 
    129         protected ManualActivityLaunch() {
    130             this(sDefaultUiBot);
    131         }
    132 
    133         protected ManualActivityLaunch(@NonNull UiBot uiBot) {
    134             super(uiBot);
    135         }
    136 
    137         @Override
    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