• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base.test;
6 
7 import static com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesCheckNames;
8 
9 import static org.hamcrest.Matchers.anyOf;
10 import static org.hamcrest.Matchers.is;
11 
12 import android.app.Activity;
13 import android.content.Intent;
14 import android.text.TextUtils;
15 
16 import androidx.annotation.CallSuper;
17 import androidx.annotation.Nullable;
18 import androidx.test.espresso.contrib.AccessibilityChecks;
19 import androidx.test.runner.lifecycle.Stage;
20 
21 import com.google.android.apps.common.testing.accessibility.framework.ClickableSpanViewCheck;
22 import com.google.android.apps.common.testing.accessibility.framework.DuplicateClickableBoundsViewCheck;
23 import com.google.android.apps.common.testing.accessibility.framework.EditableContentDescViewCheck;
24 import com.google.android.apps.common.testing.accessibility.framework.SpeakableTextPresentInfoCheck;
25 import com.google.android.apps.common.testing.accessibility.framework.SpeakableTextPresentViewCheck;
26 import com.google.android.apps.common.testing.accessibility.framework.TouchTargetSizeViewCheck;
27 
28 import org.junit.Assert;
29 import org.junit.rules.TestRule;
30 import org.junit.runner.Description;
31 import org.junit.runners.model.Statement;
32 
33 import org.chromium.base.ContextUtils;
34 import org.chromium.base.Log;
35 import org.chromium.base.test.util.ApplicationTestUtils;
36 
37 /**
38  * A replacement for ActivityTestRule, designed for use in Chromium. This implementation supports
39  * launching the target activity through a launcher or redirect from another Activity.
40  *
41  * @param <T> The type of Activity this Rule will use.
42  */
43 public class BaseActivityTestRule<T extends Activity> implements TestRule {
44     private static final String TAG = "BaseActivityTestRule";
45 
46     private final Class<T> mActivityClass;
47     private boolean mFinishActivity = true;
48     private T mActivity;
49 
50     /**
51      * @param activityClass The Class of the Activity the TestRule will use.
52      */
BaseActivityTestRule(Class<T> activityClass)53     public BaseActivityTestRule(Class<T> activityClass) {
54         mActivityClass = activityClass;
55 
56         // Enable accessibility checks, but suppress checks that fit into the following:
57         //
58         //   TouchTargetSize checks - Many views in Chrome give false positives for the minimum
59         //                            target size of 48dp. 100s of tests fail, leave disabled
60         //                            until a complete audit can be done.
61         //
62         //   ClickableSpan checks - Chrome uses ClickableSpan's throughout for in-line links,
63         //                          but a URLSpan is considered more accessible, except in the
64         //                          case of relative links. Disable until after an audit.
65         //
66         //   EditableContentDesc checks - Editable TextViews (EditText's) should not have a
67         //                                content description and instead have a hint or label.
68         //                                Various Autofill tests fail because of this, leave
69         //                                disabled until after an audit.
70         //
71         //   DuplicateClickableBounds checks - Some containers are marked clickable when they do not
72         //                                     process click events. Two views with the same bounds
73         //                                     should not both be clickable. Some examples in:
74         //                                     PageInfoRowView and TabModal.
75         //
76         //   SpeakableTextPresent* checks - Some views are failing this test on certain try bots,
77         //                                  so disable this check to reduce churn for sheriffs
78         //                                  until issue can be found. Some examples in:
79         //                                  AccessibilitySettings, ReaderMode, and Feedv2 tests.
80         //
81         // TODO(AccessibilityChecks): Complete above audits and ideally suppress no checks.
82         try {
83             AccessibilityChecks.enable()
84                     .setSuppressingResultMatcher(
85                             anyOf(
86                                     matchesCheckNames(
87                                             is(TouchTargetSizeViewCheck.class.getSimpleName())),
88                                     matchesCheckNames(
89                                             is(ClickableSpanViewCheck.class.getSimpleName())),
90                                     matchesCheckNames(
91                                             is(EditableContentDescViewCheck.class.getSimpleName())),
92                                     matchesCheckNames(
93                                             is(
94                                                     DuplicateClickableBoundsViewCheck.class
95                                                             .getSimpleName())),
96                                     matchesCheckNames(
97                                             is(
98                                                     SpeakableTextPresentInfoCheck.class
99                                                             .getSimpleName())),
100                                     matchesCheckNames(
101                                             is(
102                                                     SpeakableTextPresentViewCheck.class
103                                                             .getSimpleName()))));
104         } catch (IllegalStateException e) {
105             // Suppress IllegalStateException for AccessibilityChecks already enabled.
106         }
107     }
108 
109     @Override
110     @CallSuper
apply(final Statement base, final Description desc)111     public Statement apply(final Statement base, final Description desc) {
112         return new Statement() {
113             @Override
114             public void evaluate() throws Throwable {
115                 try {
116                     base.evaluate();
117                 } finally {
118                     if (mFinishActivity && mActivity != null) {
119                         ApplicationTestUtils.finishActivity(mActivity);
120                     }
121                 }
122             }
123         };
124     }
125 
126     /**
127      * @param finishActivity Whether to finish the Activity between tests. This is only meaningful
128      *     in the context of {@link Batch} tests. Non-batched tests will always finish Activities
129      *     between tests.
130      */
131     public void setFinishActivity(boolean finishActivity) {
132         mFinishActivity = finishActivity;
133     }
134 
135     /**
136      * @return The activity under test.
137      */
138     public T getActivity() {
139         return mActivity;
140     }
141 
142     /** Set the Activity to be used by this TestRule. */
143     public void setActivity(T activity) {
144         mActivity = activity;
145     }
146 
147     protected Intent getActivityIntent() {
148         return new Intent(ContextUtils.getApplicationContext(), mActivityClass);
149     }
150 
151     /**
152      * Launches the Activity under test using the provided intent. If the provided intent is null,
153      * an explicit intent targeting the Activity is created and used.
154      */
155     public void launchActivity(@Nullable Intent startIntent) {
156         if (startIntent == null) {
157             startIntent = getActivityIntent();
158         } else {
159             String packageName = ContextUtils.getApplicationContext().getPackageName();
160             Assert.assertTrue(
161                     TextUtils.equals(startIntent.getPackage(), packageName)
162                             || (startIntent.getComponent() != null
163                                     && TextUtils.equals(
164                                             startIntent.getComponent().getPackageName(),
165                                             packageName)));
166         }
167 
168         startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
169 
170         Log.d(TAG, String.format("Launching activity %s", mActivityClass.getName()));
171 
172         final Intent intent = startIntent;
173         mActivity =
174                 ApplicationTestUtils.waitForActivityWithClass(
175                         mActivityClass,
176                         Stage.CREATED,
177                         () -> ContextUtils.getApplicationContext().startActivity(intent));
178     }
179 
180     /**
181      * Recreates the Activity, blocking until finished.
182      * After calling this, getActivity() returns the new Activity.
183      */
184     public void recreateActivity() {
185         setActivity(ApplicationTestUtils.recreateActivity(getActivity()));
186     }
187 }
188