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