1 /*
2  * Copyright (C) 2020 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 com.android.tools.idea.validator;
18 
19 import com.android.ide.common.rendering.api.SessionParams;
20 import com.android.layoutlib.bridge.intensive.RenderTestBase;
21 import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
22 import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
23 import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
24 import com.android.tools.idea.validator.ValidatorData.Issue;
25 import com.android.tools.idea.validator.ValidatorData.Level;
26 import com.android.tools.idea.validator.ValidatorData.Type;
27 
28 import org.junit.Test;
29 
30 import android.view.View;
31 
32 import java.util.EnumSet;
33 import java.util.Set;
34 import java.util.stream.Collectors;
35 
36 import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset;
37 import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
38 
39 import static org.junit.Assert.assertEquals;
40 import static org.junit.Assert.assertTrue;
41 
42 public class LayoutValidatorTests extends RenderTestBase {
43 
44     @Test
testRenderAndVerify()45     public void testRenderAndVerify() throws Exception {
46         LayoutPullParser parser = createParserFromPath("a11y_test1.xml");
47         LayoutLibTestCallback layoutLibCallback =
48                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
49         layoutLibCallback.initResources();
50         SessionParams params = getSessionParamsBuilder()
51                 .setParser(parser)
52                 .setConfigGenerator(ConfigGenerator.NEXUS_5)
53                 .setCallback(layoutLibCallback)
54                 .disableDecoration()
55                 .enableLayoutValidation()
56                 .build();
57 
58         renderAndVerify(params, "a11y_test1.png");
59     }
60 
61     @Test
testValidation()62     public void testValidation() throws Exception {
63         render(sBridge, generateParams(), -1, session -> {
64             ValidatorResult result = LayoutValidator
65                     .validate(((View) session.getRootViews().get(0).getViewObject()), null);
66             assertEquals(3, result.getIssues().size());
67             for (Issue issue : result.getIssues()) {
68                 assertEquals(Type.ACCESSIBILITY, issue.mType);
69                 assertEquals(Level.ERROR, issue.mLevel);
70             }
71 
72             Issue first = result.getIssues().get(0);
73             assertEquals("This item may not have a label readable by screen readers.",
74                          first.mMsg);
75             assertEquals("https://support.google.com/accessibility/android/answer/7158690",
76                          first.mHelpfulUrl);
77             assertEquals("SpeakableTextPresentCheck", first.mSourceClass);
78 
79             Issue second = result.getIssues().get(1);
80             assertEquals("This item's size is 10dp x 10dp. Consider making this touch target " +
81                             "48dp wide and 48dp high or larger.",
82                          second.mMsg);
83             assertEquals("https://support.google.com/accessibility/android/answer/7101858",
84                          second.mHelpfulUrl);
85             assertEquals("TouchTargetSizeCheck", second.mSourceClass);
86 
87             Issue third = result.getIssues().get(2);
88             assertEquals("The item's text contrast ratio is 1.00. This ratio is based on a text color " +
89                             "of #000000 and background color of #000000. Consider increasing this item's" +
90                             " text contrast ratio to 4.50 or greater.",
91                          third.mMsg);
92             assertEquals("https://support.google.com/accessibility/android/answer/7158390",
93                          third.mHelpfulUrl);
94             assertEquals("TextContrastCheck", third.mSourceClass);
95         });
96     }
97 
98     @Test
testValidationPolicyType()99     public void testValidationPolicyType() throws Exception {
100         try {
101             ValidatorData.Policy newPolicy = new ValidatorData.Policy(
102                     EnumSet.of(Type.RENDER),
103                     EnumSet.of(Level.ERROR, Level.WARNING));
104             LayoutValidator.updatePolicy(newPolicy);
105 
106             render(sBridge, generateParams(), -1, session -> {
107                 ValidatorResult result = LayoutValidator.validate(
108                         ((View) session.getRootViews().get(0).getViewObject()), null);
109                 assertTrue(result.getIssues().isEmpty());
110             });
111         } finally {
112             LayoutValidator.updatePolicy(LayoutValidator.DEFAULT_POLICY);
113         }
114     }
115 
116     @Test
testValidationPolicyLevel()117     public void testValidationPolicyLevel() throws Exception {
118         try {
119             ValidatorData.Policy newPolicy = new ValidatorData.Policy(
120                     EnumSet.of(Type.ACCESSIBILITY, Type.RENDER),
121                     EnumSet.of(Level.VERBOSE));
122             LayoutValidator.updatePolicy(newPolicy);
123 
124             render(sBridge, generateParams(), -1, session -> {
125                 ValidatorResult result = LayoutValidator.validate(
126                         ((View) session.getRootViews().get(0).getViewObject()), null);
127                 assertEquals(27, result.getIssues().size());
128                 result.getIssues().forEach(issue ->assertEquals(Level.VERBOSE, issue.mLevel));
129             });
130         } finally {
131             LayoutValidator.updatePolicy(LayoutValidator.DEFAULT_POLICY);
132         }
133     }
134 
135     @Test
testValidationPolicyChecks()136     public void testValidationPolicyChecks() throws Exception {
137         Set<AccessibilityHierarchyCheck> allChecks =
138                 AccessibilityCheckPreset.getAccessibilityHierarchyChecksForPreset(
139                         AccessibilityCheckPreset.LATEST);
140         Set<AccessibilityHierarchyCheck> filtered =allChecks
141                 .stream()
142                 .filter(it -> it.getClass().getSimpleName().equals("TextContrastCheck"))
143                 .collect(Collectors.toSet());
144         try {
145             ValidatorData.Policy newPolicy = new ValidatorData.Policy(
146                     EnumSet.of(Type.ACCESSIBILITY, Type.RENDER),
147                     EnumSet.of(Level.ERROR));
148             newPolicy.mChecks.addAll(filtered);
149             LayoutValidator.updatePolicy(newPolicy);
150 
151             render(sBridge, generateParams(), -1, session -> {
152                 ValidatorResult result = LayoutValidator.validate(
153                         ((View) session.getRootViews().get(0).getViewObject()), null);
154                 assertEquals(1, result.getIssues().size());
155                 Issue textCheck = result.getIssues().get(0);
156                 assertEquals("The item's text contrast ratio is 1.00. This ratio is based on a text color " +
157                                 "of #000000 and background color of #000000. Consider increasing this item's" +
158                                 " text contrast ratio to 4.50 or greater.",
159                         textCheck.mMsg);
160                 assertEquals("https://support.google.com/accessibility/android/answer/7158390",
161                         textCheck.mHelpfulUrl);
162                 assertEquals("TextContrastCheck", textCheck.mSourceClass);
163             });
164         } finally {
165             LayoutValidator.updatePolicy(LayoutValidator.DEFAULT_POLICY);
166         }
167     }
168 
generateParams()169     private SessionParams generateParams() throws Exception {
170         LayoutPullParser parser = createParserFromPath("a11y_test1.xml");
171         LayoutLibTestCallback layoutLibCallback =
172                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
173         layoutLibCallback.initResources();
174         return getSessionParamsBuilder()
175                 .setParser(parser)
176                 .setConfigGenerator(ConfigGenerator.NEXUS_5)
177                 .setCallback(layoutLibCallback)
178                 .disableDecoration()
179                 .enableLayoutValidation()
180                 .build();
181     }
182 }
183