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