1704d59a9e57b28939ba5c709cf4b50d72aa072e6Christian Williamspackage org.robolectric.android;
255e6fdf6aef81359df48acc92714f89111b7613bErich Douglass
3e3260798aeb2a5a4440966a270f859847f25254aChristian Williamsimport android.view.View;
455e6fdf6aef81359df48acc92714f89111b7613bErich Douglassimport com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset;
532f582faf0b5e6658a19046afb44d33f712d121ePhil Weaverimport com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult;
6510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaverimport com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult.AccessibilityCheckResultType;
755e6fdf6aef81359df48acc92714f89111b7613bErich Douglassimport com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils;
855e6fdf6aef81359df48acc92714f89111b7613bErich Douglassimport com.google.android.apps.common.testing.accessibility.framework.AccessibilityViewCheckResult;
932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaverimport com.google.android.apps.common.testing.accessibility.framework.DuplicateClickableBoundsViewCheck;
108c34621d8da4bc313229be97b4ef30cdeb0ad852Phil Weaverimport com.google.android.apps.common.testing.accessibility.framework.TouchTargetSizeViewCheck;
1132f582faf0b5e6658a19046afb44d33f712d121ePhil Weaverimport com.google.android.apps.common.testing.accessibility.framework.integrations.espresso.AccessibilityValidator;
12e3260798aeb2a5a4440966a270f859847f25254aChristian Williamsimport java.lang.annotation.Annotation;
1355e6fdf6aef81359df48acc92714f89111b7613bErich Douglassimport java.lang.reflect.Method;
14510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaverimport java.util.Collections;
1555e6fdf6aef81359df48acc92714f89111b7613bErich Douglassimport java.util.List;
16851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport org.hamcrest.Matcher;
17851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport org.hamcrest.Matchers;
18851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport org.robolectric.annotation.AccessibilityChecks;
19851f2a9519be23c73a9e2929128179b405e2e7a6Christian Williamsimport org.robolectric.annotation.AccessibilityChecks.ForRobolectricVersion;
2055e6fdf6aef81359df48acc92714f89111b7613bErich Douglass
2155e6fdf6aef81359df48acc92714f89111b7613bErich Douglass/**
22510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver * Utility class for checking Views for accessibility.
23510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver *
24510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver * This class is used by {@code ShadowView.checkedPerformClick} to check for accessibility problems.
25510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver * There is some subtlety to checking a UI for accessibility when it hasn't been rendered. The
26510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver * better initialized the View, the more accurate the checking will be. At a minimum, the view
27510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver * should be attached to a proper view hierarchy similar to what's checked for in:q
28510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver * {@code ShadowView.checkedPerformClick}.
2955e6fdf6aef81359df48acc92714f89111b7613bErich Douglass */
3055e6fdf6aef81359df48acc92714f89111b7613bErich Douglasspublic class AccessibilityUtil {
3132f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  private static final String COMPAT_V4_CLASS_NAME = "android.support.v4.view.ViewCompat";
3232f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  /* The validator that this class configures and uses to run the checks */
3332f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  private static AccessibilityValidator validator;
3432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver
3532f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  /*
3632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * Slightly hacky way to deal with the legacy of allowing the annotation to configure the
3732f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * subset of checks to run from the annotation. {@code true} when a version set is
3832f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * specified by setRunChecksForRobolectricVersion.
3932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   */
4032f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  private static boolean forVersionSet = false;
4132f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver
4232f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  /* Flag indicating if the support library's presence has been verified */
4332f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  private static boolean v4SupportPresenceVerified = false;
4432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver
45bd8b4b4b73ddcce4b22e5fd9213eeca1218f4756Christian Williams  protected AccessibilityUtil() {}
4655e6fdf6aef81359df48acc92714f89111b7613bErich Douglass
4755e6fdf6aef81359df48acc92714f89111b7613bErich Douglass  /**
48510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * Check a hierarchy of {@code View}s for accessibility. Only performs checks if (in decreasing
49510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * priority order) accessibility checking is enabled using an {@link AccessibilityChecks}
50510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * annotation, if the system property {@code robolectric.accessibility.enablechecks} is set to
51510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * {@code true}, or if the environment variable {@code robolectric.accessibility.enablechecks}
52510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * is set to {@code true}.
5332f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   *
5432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * @param view The {@code View} to examine
55510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   *
56510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * @return A list of results from the check. If there are no results or checking is disabled,
57510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * the list is empty.
5832f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   */
59510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  public static List<AccessibilityViewCheckResult> checkViewIfCheckingEnabled(View view) {
60510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    AccessibilityChecks classChecksAnnotation = getAnnotation();
61510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    if (!isAccessibilityCheckingEnabled(classChecksAnnotation)) {
62510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver      return Collections.emptyList();
63510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    }
64510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver
65510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    return checkView(view);
6632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  }
67510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver
68510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  /**
69510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * Check a hierarchy of {@code View}s for accessibility, based on currently set options.
70510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   *
71510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * @param view The {@code View} to examine
72510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   *
73510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * @return A list of results from the check. If there are no results, the list is empty.
74510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   */
75510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  public static List<AccessibilityViewCheckResult> checkView(View view) {
76510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    return checkView(view, getAnnotation());
77510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  }
78510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver
79510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  /**
80510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * Check a hierarchy of {@code View}s for accessibility. Only performs checks if (in decreasing
81510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * priority order) accessibility checking is enabled using an {@link AccessibilityChecks}
82510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * annotation, if the system property {@code robolectric.accessibility.enablechecks} is set to
83510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * {@code true}, or if the environment variable {@code robolectric.accessibility.enablechecks}
84510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * is set to {@code true}.
85510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   *
86510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * Implicitly calls {code setThrowExceptionForErrors(false)} to disable exception throwing. This
87510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * method is deprecated, both because of this side effect and because the other methods offer
88510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * more control over execution.
89510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   *
90510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * @param view The {@code View} to examine
91510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   *
92510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * @return A list of results from the check. If there are no results or checking is disabled,
93510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   * the list is empty.
94510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver   */
95510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  @Deprecated
96510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  public static boolean passesAccessibilityChecksIfEnabled(View view) {
97510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    setThrowExceptionForErrors(false);
98510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    List<AccessibilityViewCheckResult> results = checkViewIfCheckingEnabled(view);
99510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    List<AccessibilityViewCheckResult> errors = AccessibilityCheckResultUtils.getResultsForType(
100510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver        results, AccessibilityCheckResultType.ERROR);
101510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    return (errors.size() == 0);
102510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  }
103510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver
10432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  /**
10532f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * Specify that a specific subset of accessibility checks be run. The subsets are specified based
10632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * on which Robolectric version particular checks were released with. By default, all checks are
10782049a7b7c53d5a208921940bda298535697d0b7Christian Williams   * run {@link ForRobolectricVersion}.
10882049a7b7c53d5a208921940bda298535697d0b7Christian Williams   *
10932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * If you call this method, the value you pass will take precedence over any value in any
11032f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * annotations.
11132f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   *
11232f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * @param forVersion The version of checks to run for. If {@code null}, throws away the current
11332f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * value and falls back on the annotation or default.
11432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   */
11532f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  public static void setRunChecksForRobolectricVersion(ForRobolectricVersion forVersion) {
11632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    initializeValidator();
11732f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    if (forVersion != null) {
11832f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      validator.setCheckPreset(convertRoboVersionToA11yTestVersion(forVersion));
11932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      forVersionSet = true;
12032f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    } else {
12132f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      forVersionSet = false;
12232f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    }
12332f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  }
12432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver
12532f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  /**
12632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * Specify that accessibility checks should be run for all views in the hierarchy whenever a
12732f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * single view's accessibility is asserted.
12832f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   *
12932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * @param runChecksFromRootView {@code true} if all views in the hierarchy should be checked.
13032f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   */
13132f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  public static void setRunChecksFromRootView(boolean runChecksFromRootView) {
13232f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    initializeValidator();
13332f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    validator.setRunChecksFromRootView(runChecksFromRootView);
13432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  }
13532f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver
13632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  /**
13732f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * Suppress all results that match the given matcher. Suppressed results will not be included
13832f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * in any logs or cause any {@code Exception} to be thrown. This capability is useful if there
13932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * are known issues, but checks should still look for regressions.
14032f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   *
14132f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * @param matcher A matcher to match a {@link AccessibilityViewCheckResult}. {@code null}
14232f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * disables suppression and is the default.
14332f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   */
14432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  @SuppressWarnings("unchecked") // The generic passed to anyOf
14532f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  public static void setSuppressingResultMatcher(
14632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      final Matcher<? super AccessibilityViewCheckResult> matcher) {
14732f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    initializeValidator();
14832f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    /* Suppress all touch target results, since views all report size as 0x0 */
14932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    Matcher<AccessibilityCheckResult> touchTargetResultMatcher =
15032f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver        AccessibilityCheckResultUtils.matchesChecks(
15132f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver            Matchers.equalTo(TouchTargetSizeViewCheck.class));
15232f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    Matcher<AccessibilityCheckResult> duplicateBoundsResultMatcher =
15332f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver        AccessibilityCheckResultUtils.matchesChecks(
15432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver            Matchers.equalTo(DuplicateClickableBoundsViewCheck.class));
15532f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    if (matcher == null) {
15632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      validator.setSuppressingResultMatcher(
15732f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver          Matchers.anyOf(touchTargetResultMatcher, duplicateBoundsResultMatcher));
15832f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    } else {
15932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      validator.setSuppressingResultMatcher(
16032f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver          Matchers.anyOf(matcher, touchTargetResultMatcher, duplicateBoundsResultMatcher));
16132f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    }
16232f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  }
16332f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver
16432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  /**
16532f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * Control whether or not to throw exceptions when accessibility errors are found.
16632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   *
16732f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * @param throwExceptionForErrors {@code true} to throw an {@code AccessibilityViewCheckException}
16832f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   * when there is at least one error result. Default: {@code true}.
16932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver   */
17032f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  public static void setThrowExceptionForErrors(boolean throwExceptionForErrors) {
17132f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    initializeValidator();
17232f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    validator.setThrowExceptionForErrors(throwExceptionForErrors);
17332f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  }
17432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver
175510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  private static List<AccessibilityViewCheckResult> checkView(View view,
176510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver      AccessibilityChecks classChecksAnnotation) {
177510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    /*
178510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver     * Accessibility Checking requires the v4 support library. If the support library isn't present,
179510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver     * throw a descriptive exception now.
180510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver     */
181510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    if (!v4SupportPresenceVerified) {
182510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver      try {
183510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver        View.class.getClassLoader().loadClass(COMPAT_V4_CLASS_NAME);
184510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver      } catch (ClassNotFoundException e) {
185510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver        throw new RuntimeException(
186510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver            "Accessibility Checking requires the Android support library (v4).\n"
187510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver            + "Either include it in the project or disable accessibility checking.");
18832f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      }
189510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver      v4SupportPresenceVerified = true;
190510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    }
19155e6fdf6aef81359df48acc92714f89111b7613bErich Douglass
192510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    initializeValidator();
193510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    if (!forVersionSet) {
194510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver      if (classChecksAnnotation != null) {
195510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver        validator.setCheckPreset(
196510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver            convertRoboVersionToA11yTestVersion(classChecksAnnotation.forRobolectricVersion()));
197510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver      } else {
198510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver        validator.setCheckPreset(AccessibilityCheckPreset.LATEST);
19932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      }
20032f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    }
201510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    return validator.checkAndReturnResults(view);
202510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  }
203510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver
204510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  private static boolean isAccessibilityCheckingEnabled(AccessibilityChecks classChecksAnnotation) {
205510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    boolean checksEnabled = false;
206510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver
207510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    String checksEnabledString = System.getenv("robolectric.accessibility.enablechecks");
208510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    if (checksEnabledString != null) {
209510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver      checksEnabled = checksEnabledString.equals("true");
210510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    }
211510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver
212510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    /* Allow test arg to enable checking (and override environment variables) */
213510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    checksEnabledString = System.getProperty("robolectric.accessibility.enablechecks");
214510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    if (checksEnabledString != null) {
215510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver      checksEnabled = checksEnabledString.equals("true");
216510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    }
217510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver
218510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    if (classChecksAnnotation != null) {
219510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver      checksEnabled = classChecksAnnotation.enabled();
220510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    }
221510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver
222510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    return checksEnabled;
223510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  }
224510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver
225510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver  private static AccessibilityChecks getAnnotation() {
22632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    AccessibilityChecks classChecksAnnotation = null;
227510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    StackTraceElement[] stack = new Throwable().fillInStackTrace().getStackTrace();
22855e6fdf6aef81359df48acc92714f89111b7613bErich Douglass    for (StackTraceElement element : stack) {
22955e6fdf6aef81359df48acc92714f89111b7613bErich Douglass      /* Look for annotations on the method or the class */
23055e6fdf6aef81359df48acc92714f89111b7613bErich Douglass      Class<?> clazz;
23155e6fdf6aef81359df48acc92714f89111b7613bErich Douglass      try {
23255e6fdf6aef81359df48acc92714f89111b7613bErich Douglass        clazz = Class.forName(element.getClassName());
23355e6fdf6aef81359df48acc92714f89111b7613bErich Douglass        Method method;
23455e6fdf6aef81359df48acc92714f89111b7613bErich Douglass        method = clazz.getMethod(element.getMethodName());
23555e6fdf6aef81359df48acc92714f89111b7613bErich Douglass        /* Assume the method is void, as that is the case for tests */
23632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver        classChecksAnnotation = method.getAnnotation(AccessibilityChecks.class);
23755e6fdf6aef81359df48acc92714f89111b7613bErich Douglass        if (classChecksAnnotation == null) {
23855e6fdf6aef81359df48acc92714f89111b7613bErich Douglass          classChecksAnnotation = clazz.getAnnotation(AccessibilityChecks.class);
23955e6fdf6aef81359df48acc92714f89111b7613bErich Douglass        }
240510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver        /* Stop looking when we find an annotation */
24155e6fdf6aef81359df48acc92714f89111b7613bErich Douglass        if (classChecksAnnotation != null) {
24255e6fdf6aef81359df48acc92714f89111b7613bErich Douglass          break;
24355e6fdf6aef81359df48acc92714f89111b7613bErich Douglass        }
244e3260798aeb2a5a4440966a270f859847f25254aChristian Williams
24555e6fdf6aef81359df48acc92714f89111b7613bErich Douglass        /* If we've crawled up the stack far enough to find the test, stop looking */
246e3260798aeb2a5a4440966a270f859847f25254aChristian Williams        for (Annotation annotation : clazz.getAnnotations()) {
2475cb217a0f521dc390fdaa3050889b03a7da4d1cfJonathan Gerrish          if (annotation.annotationType().getName().equals("org.junit.Test")) {
248e3260798aeb2a5a4440966a270f859847f25254aChristian Williams            break;
249e3260798aeb2a5a4440966a270f859847f25254aChristian Williams          }
25055e6fdf6aef81359df48acc92714f89111b7613bErich Douglass        }
25155e6fdf6aef81359df48acc92714f89111b7613bErich Douglass      }
25255e6fdf6aef81359df48acc92714f89111b7613bErich Douglass      /*
25355e6fdf6aef81359df48acc92714f89111b7613bErich Douglass       * The reflective calls may throw exceptions if the stack trace elements
25455e6fdf6aef81359df48acc92714f89111b7613bErich Douglass       * don't look like junit test methods. In that case we simply go on
25555e6fdf6aef81359df48acc92714f89111b7613bErich Douglass       * to the next element
25655e6fdf6aef81359df48acc92714f89111b7613bErich Douglass       */
25777ae4054c6fd36bd2efa66068c63c7a4635a1593Erich Douglass      catch (ClassNotFoundException | SecurityException | NoSuchMethodException e) {}
25855e6fdf6aef81359df48acc92714f89111b7613bErich Douglass    }
259510eddc53293fa1ff6a672660e3c2eb904746d00Phil Weaver    return classChecksAnnotation;
26032f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  }
26155e6fdf6aef81359df48acc92714f89111b7613bErich Douglass
26232f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  private static void initializeValidator() {
26332f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    if (validator == null) {
26432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      validator = new AccessibilityValidator();
26532f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      setSuppressingResultMatcher(null);
26655e6fdf6aef81359df48acc92714f89111b7613bErich Douglass    }
26732f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  }
26855e6fdf6aef81359df48acc92714f89111b7613bErich Douglass
26932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  private static AccessibilityCheckPreset convertRoboVersionToA11yTestVersion(
27032f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      ForRobolectricVersion robolectricVersion) {
27132f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    if (robolectricVersion == ForRobolectricVersion.LATEST) {
27232f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      return AccessibilityCheckPreset.LATEST;
27355e6fdf6aef81359df48acc92714f89111b7613bErich Douglass    }
27432f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    AccessibilityCheckPreset preset = AccessibilityCheckPreset.VERSION_1_0_CHECKS;
27532f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    if (robolectricVersion.ordinal() >= ForRobolectricVersion.VERSION_3_1.ordinal()) {
27632f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver      preset = AccessibilityCheckPreset.VERSION_2_0_CHECKS;
27732f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    }
27832f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver    return preset;
27932f582faf0b5e6658a19046afb44d33f712d121ePhil Weaver  }
28005a44e0f3250a52784027324d41be41858a67ef8Roger Hu}
281