OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <set> | 5 #include <set> |
6 #include <string> | 6 #include <string> |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/files/file_util.h" | |
11 #include "base/logging.h" | |
12 #include "base/path_service.h" | |
13 #include "base/strings/string16.h" | 10 #include "base/strings/string16.h" |
14 #include "base/strings/string_split.h" | |
15 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
16 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
17 #include "content/browser/accessibility/accessibility_tree_formatter.h" | 13 #include "content/browser/accessibility/accessibility_tree_formatter.h" |
18 #include "content/browser/accessibility/browser_accessibility.h" | 14 #include "content/browser/accessibility/browser_accessibility.h" |
19 #include "content/browser/accessibility/browser_accessibility_manager.h" | 15 #include "content/browser/accessibility/browser_accessibility_manager.h" |
20 #include "content/browser/renderer_host/render_view_host_impl.h" | 16 #include "content/browser/accessibility/dump_accessibility_browsertest_base.h" |
21 #include "content/browser/renderer_host/render_widget_host_view_base.h" | |
22 #include "content/browser/web_contents/web_contents_impl.h" | 17 #include "content/browser/web_contents/web_contents_impl.h" |
23 #include "content/public/browser/web_contents.h" | 18 #include "content/public/browser/web_contents.h" |
24 #include "content/public/common/content_paths.h" | 19 #include "content/public/common/content_paths.h" |
25 #include "content/public/common/content_switches.h" | 20 #include "content/public/common/content_switches.h" |
26 #include "content/public/common/url_constants.h" | |
27 #include "content/public/test/content_browser_test.h" | |
28 #include "content/public/test/content_browser_test_utils.h" | |
29 #include "content/shell/browser/shell.h" | 21 #include "content/shell/browser/shell.h" |
30 #include "content/test/accessibility_browser_test_utils.h" | 22 #include "content/test/accessibility_browser_test_utils.h" |
31 #include "testing/gtest/include/gtest/gtest.h" | |
32 | 23 |
33 #if defined(OS_MACOSX) | 24 #if defined(OS_MACOSX) |
34 #include "base/mac/mac_util.h" | 25 #include "base/mac/mac_util.h" |
35 #endif | 26 #endif |
36 | 27 |
37 // TODO(aboxhall): Create expectations on Android for these | 28 // TODO(aboxhall): Create expectations on Android for these |
38 #if defined(OS_ANDROID) | 29 #if defined(OS_ANDROID) |
39 #define MAYBE(x) DISABLED_##x | 30 #define MAYBE(x) DISABLED_##x |
40 #else | 31 #else |
41 #define MAYBE(x) x | 32 #define MAYBE(x) x |
42 #endif | 33 #endif |
43 | 34 |
44 namespace content { | 35 namespace content { |
45 | 36 |
46 namespace { | |
47 | |
48 const char kCommentToken = '#'; | |
49 const char kMarkSkipFile[] = "#<skip"; | |
50 const char kMarkEndOfFile[] = "<-- End-of-file -->"; | |
51 const char kSignalDiff[] = "*"; | |
52 | |
53 } // namespace | |
54 | |
55 typedef AccessibilityTreeFormatter::Filter Filter; | 37 typedef AccessibilityTreeFormatter::Filter Filter; |
56 | 38 |
57 // This test takes a snapshot of the platform BrowserAccessibility tree and | 39 // This test takes a snapshot of the platform BrowserAccessibility tree and |
58 // tests it against an expected baseline. | 40 // tests it against an expected baseline. |
59 // | 41 // |
60 // The flow of the test is as outlined below. | 42 // The flow of the test is as outlined below. |
61 // 1. Load an html file from chrome/test/data/accessibility. | 43 // 1. Load an html file from chrome/test/data/accessibility. |
62 // 2. Read the expectation. | 44 // 2. Read the expectation. |
63 // 3. Browse to the page and serialize the platform specific tree into a human | 45 // 3. Browse to the page and serialize the platform specific tree into a human |
64 // readable string. | 46 // readable string. |
65 // 4. Perform a comparison between actual and expected and fail if they do not | 47 // 4. Perform a comparison between actual and expected and fail if they do not |
66 // exactly match. | 48 // exactly match. |
67 class DumpAccessibilityTreeTest : public ContentBrowserTest { | 49 class DumpAccessibilityTreeTest : public DumpAccessibilityTestBase { |
68 public: | 50 public: |
69 // Utility helper that does a comment aware equality check. | 51 void AddDefaultFilters(std::vector<Filter>* filters) override { |
70 // Returns array of lines from expected file which are different. | |
71 std::vector<int> DiffLines(const std::vector<std::string>& expected_lines, | |
72 const std::vector<std::string>& actual_lines) { | |
73 int actual_lines_count = actual_lines.size(); | |
74 int expected_lines_count = expected_lines.size(); | |
75 std::vector<int> diff_lines; | |
76 int i = 0, j = 0; | |
77 while (i < actual_lines_count && j < expected_lines_count) { | |
78 if (expected_lines[j].size() == 0 || | |
79 expected_lines[j][0] == kCommentToken) { | |
80 // Skip comment lines and blank lines in expected output. | |
81 ++j; | |
82 continue; | |
83 } | |
84 | |
85 if (actual_lines[i] != expected_lines[j]) | |
86 diff_lines.push_back(j); | |
87 ++i; | |
88 ++j; | |
89 } | |
90 | |
91 // Actual file has been fully checked. | |
92 return diff_lines; | |
93 } | |
94 | |
95 void AddDefaultFilters(std::vector<Filter>* filters) { | |
96 filters->push_back(Filter(base::ASCIIToUTF16("FOCUSABLE"), Filter::ALLOW)); | 52 filters->push_back(Filter(base::ASCIIToUTF16("FOCUSABLE"), Filter::ALLOW)); |
97 filters->push_back(Filter(base::ASCIIToUTF16("READONLY"), Filter::ALLOW)); | 53 filters->push_back(Filter(base::ASCIIToUTF16("READONLY"), Filter::ALLOW)); |
98 filters->push_back(Filter(base::ASCIIToUTF16("name*"), Filter::ALLOW)); | 54 filters->push_back(Filter(base::ASCIIToUTF16("name*"), Filter::ALLOW)); |
99 filters->push_back(Filter(base::ASCIIToUTF16("*=''"), Filter::DENY)); | 55 filters->push_back(Filter(base::ASCIIToUTF16("*=''"), Filter::DENY)); |
100 } | 56 } |
101 | 57 |
102 // Parse the test html file and parse special directives, usually | |
103 // beginning with an '@' and inside an HTML comment, that control how the | |
104 // test is run and how the results are interpreted. | |
105 // | |
106 // When the accessibility tree is dumped as text, each attribute is | |
107 // run through filters before being appended to the string. An "allow" | |
108 // filter specifies attribute strings that should be dumped, and a "deny" | |
109 // filter specifies strings that should be suppressed. As an example, | |
110 // @MAC-ALLOW:AXSubrole=* means that the AXSubrole attribute should be | |
111 // printed, while @MAC-ALLOW:AXSubrole=AXList* means that any subrole | |
112 // beginning with the text "AXList" should be printed. | |
113 // | |
114 // The @WAIT-FOR:text directive allows the test to specify that the document | |
115 // may dynamically change after initial load, and the test is to wait | |
116 // until the given string (e.g., "text") appears in the resulting dump. | |
117 // A test can make some changes to the document, then append a magic string | |
118 // indicating that the test is done, and this framework will wait for that | |
119 // string to appear before comparing the results. | |
120 void ParseHtmlForExtraDirectives(const std::string& test_html, | |
121 std::vector<Filter>* filters, | |
122 std::string* wait_for) { | |
123 std::vector<std::string> lines; | |
124 base::SplitString(test_html, '\n', &lines); | |
125 for (std::vector<std::string>::const_iterator iter = lines.begin(); | |
126 iter != lines.end(); | |
127 ++iter) { | |
128 const std::string& line = *iter; | |
129 const std::string& allow_empty_str = | |
130 AccessibilityTreeFormatter::GetAllowEmptyString(); | |
131 const std::string& allow_str = | |
132 AccessibilityTreeFormatter::GetAllowString(); | |
133 const std::string& deny_str = | |
134 AccessibilityTreeFormatter::GetDenyString(); | |
135 const std::string& wait_str = "@WAIT-FOR:"; | |
136 if (StartsWithASCII(line, allow_empty_str, true)) { | |
137 filters->push_back( | |
138 Filter(base::UTF8ToUTF16(line.substr(allow_empty_str.size())), | |
139 Filter::ALLOW_EMPTY)); | |
140 } else if (StartsWithASCII(line, allow_str, true)) { | |
141 filters->push_back(Filter(base::UTF8ToUTF16( | |
142 line.substr(allow_str.size())), | |
143 Filter::ALLOW)); | |
144 } else if (StartsWithASCII(line, deny_str, true)) { | |
145 filters->push_back(Filter(base::UTF8ToUTF16( | |
146 line.substr(deny_str.size())), | |
147 Filter::DENY)); | |
148 } else if (StartsWithASCII(line, wait_str, true)) { | |
149 *wait_for = line.substr(wait_str.size()); | |
150 } | |
151 } | |
152 } | |
153 | |
154 void SetUpCommandLine(base::CommandLine* command_line) override { | 58 void SetUpCommandLine(base::CommandLine* command_line) override { |
155 ContentBrowserTest::SetUpCommandLine(command_line); | 59 ContentBrowserTest::SetUpCommandLine(command_line); |
156 // Enable <dialog>, which is used in some tests. | 60 // Enable <dialog>, which is used in some tests. |
157 base::CommandLine::ForCurrentProcess()->AppendSwitch( | 61 base::CommandLine::ForCurrentProcess()->AppendSwitch( |
158 switches::kEnableExperimentalWebPlatformFeatures); | 62 switches::kEnableExperimentalWebPlatformFeatures); |
159 } | 63 } |
160 | 64 |
161 void RunTest(const base::FilePath::CharType* file_path); | 65 std::vector<std::string> Dump() override { |
162 }; | 66 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( |
163 | 67 shell()->web_contents()); |
164 void DumpAccessibilityTreeTest::RunTest( | |
165 const base::FilePath::CharType* file_path) { | |
166 NavigateToURL(shell(), GURL(url::kAboutBlankURL)); | |
167 | |
168 // Setup test paths. | |
169 base::FilePath dir_test_data; | |
170 ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &dir_test_data)); | |
171 base::FilePath test_path( | |
172 dir_test_data.Append(FILE_PATH_LITERAL("accessibility"))); | |
173 ASSERT_TRUE(base::PathExists(test_path)) | |
174 << test_path.LossyDisplayName(); | |
175 | |
176 base::FilePath html_file = test_path.Append(base::FilePath(file_path)); | |
177 // Output the test path to help anyone who encounters a failure and needs | |
178 // to know where to look. | |
179 printf("Testing: %s\n", html_file.MaybeAsASCII().c_str()); | |
180 | |
181 std::string html_contents; | |
182 base::ReadFileToString(html_file, &html_contents); | |
183 | |
184 // Read the expected file. | |
185 std::string expected_contents_raw; | |
186 base::FilePath expected_file = | |
187 base::FilePath(html_file.RemoveExtension().value() + | |
188 AccessibilityTreeFormatter::GetExpectedFileSuffix()); | |
189 base::ReadFileToString(expected_file, &expected_contents_raw); | |
190 | |
191 // Tolerate Windows-style line endings (\r\n) in the expected file: | |
192 // normalize by deleting all \r from the file (if any) to leave only \n. | |
193 std::string expected_contents; | |
194 base::RemoveChars(expected_contents_raw, "\r", &expected_contents); | |
195 | |
196 if (!expected_contents.compare(0, strlen(kMarkSkipFile), kMarkSkipFile)) { | |
197 printf("Skipping this test on this platform.\n"); | |
198 return; | |
199 } | |
200 | |
201 // Parse filters and other directives in the test file. | |
202 std::vector<Filter> filters; | |
203 std::string wait_for; | |
204 AddDefaultFilters(&filters); | |
205 ParseHtmlForExtraDirectives(html_contents, &filters, &wait_for); | |
206 | |
207 // Load the page. | |
208 base::string16 html_contents16; | |
209 html_contents16 = base::UTF8ToUTF16(html_contents); | |
210 GURL url = GetTestUrl("accessibility", | |
211 html_file.BaseName().MaybeAsASCII().c_str()); | |
212 | |
213 // If there's a @WAIT-FOR directive, set up an accessibility notification | |
214 // waiter that returns on any event; we'll stop when we get the text we're | |
215 // waiting for, or time out. Otherwise just wait specifically for | |
216 // the "load complete" event. | |
217 scoped_ptr<AccessibilityNotificationWaiter> waiter; | |
218 if (!wait_for.empty()) { | |
219 waiter.reset(new AccessibilityNotificationWaiter( | |
220 shell(), AccessibilityModeComplete, ui::AX_EVENT_NONE)); | |
221 } else { | |
222 waiter.reset(new AccessibilityNotificationWaiter( | |
223 shell(), AccessibilityModeComplete, ui::AX_EVENT_LOAD_COMPLETE)); | |
224 } | |
225 | |
226 // Load the test html. | |
227 NavigateToURL(shell(), url); | |
228 | |
229 // Wait for notifications. If there's a @WAIT-FOR directive, break when | |
230 // the text we're waiting for appears in the dump, otherwise break after | |
231 // the first notification, which will be a load complete. | |
232 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( | |
233 shell()->web_contents()); | |
234 std::string actual_contents; | |
235 do { | |
236 waiter->WaitForNotification(); | |
237 base::string16 actual_contents_utf16; | |
238 AccessibilityTreeFormatter formatter( | 68 AccessibilityTreeFormatter formatter( |
239 web_contents->GetRootBrowserAccessibilityManager()->GetRoot()); | 69 web_contents->GetRootBrowserAccessibilityManager()->GetRoot()); |
240 formatter.SetFilters(filters); | 70 formatter.SetFilters(filters_); |
| 71 base::string16 actual_contents_utf16; |
241 formatter.FormatAccessibilityTree(&actual_contents_utf16); | 72 formatter.FormatAccessibilityTree(&actual_contents_utf16); |
242 actual_contents = base::UTF16ToUTF8(actual_contents_utf16); | 73 std::string actual_contents = base::UTF16ToUTF8(actual_contents_utf16); |
243 } while (!wait_for.empty() && | 74 std::vector<std::string> actual_lines; |
244 actual_contents.find(wait_for) == std::string::npos); | 75 Tokenize(actual_contents, "\n", &actual_lines); |
245 | 76 return actual_lines; |
246 // Perform a diff (or write the initial baseline). | |
247 std::vector<std::string> actual_lines, expected_lines; | |
248 Tokenize(actual_contents, "\n", &actual_lines); | |
249 Tokenize(expected_contents, "\n", &expected_lines); | |
250 // Marking the end of the file with a line of text ensures that | |
251 // file length differences are found. | |
252 expected_lines.push_back(kMarkEndOfFile); | |
253 actual_lines.push_back(kMarkEndOfFile); | |
254 | |
255 std::vector<int> diff_lines = DiffLines(expected_lines, actual_lines); | |
256 bool is_different = diff_lines.size() > 0; | |
257 EXPECT_FALSE(is_different); | |
258 if (is_different) { | |
259 // Mark the expected lines which did not match actual output with a *. | |
260 printf("* Line Expected\n"); | |
261 printf("- ---- --------\n"); | |
262 for (int line = 0, diff_index = 0; | |
263 line < static_cast<int>(expected_lines.size()); | |
264 ++line) { | |
265 bool is_diff = false; | |
266 if (diff_index < static_cast<int>(diff_lines.size()) && | |
267 diff_lines[diff_index] == line) { | |
268 is_diff = true; | |
269 ++diff_index; | |
270 } | |
271 printf("%1s %4d %s\n", is_diff? kSignalDiff : "", line + 1, | |
272 expected_lines[line].c_str()); | |
273 } | |
274 printf("\nActual\n"); | |
275 printf("------\n"); | |
276 printf("%s\n", actual_contents.c_str()); | |
277 } | 77 } |
278 | 78 }; |
279 if (!base::PathExists(expected_file)) { | |
280 base::FilePath actual_file = | |
281 base::FilePath(html_file.RemoveExtension().value() + | |
282 AccessibilityTreeFormatter::GetActualFileSuffix()); | |
283 | |
284 EXPECT_TRUE(base::WriteFile( | |
285 actual_file, actual_contents.c_str(), actual_contents.size())); | |
286 | |
287 ADD_FAILURE() << "No expectation found. Create it by doing:\n" | |
288 << "mv " << actual_file.LossyDisplayName() << " " | |
289 << expected_file.LossyDisplayName(); | |
290 } | |
291 } | |
292 | 79 |
293 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityA) { | 80 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityA) { |
294 RunTest(FILE_PATH_LITERAL("a.html")); | 81 RunTest(FILE_PATH_LITERAL("a.html")); |
295 } | 82 } |
296 | 83 |
297 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAbbr) { | 84 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAbbr) { |
298 RunTest(FILE_PATH_LITERAL("abbr.html")); | 85 RunTest(FILE_PATH_LITERAL("abbr.html")); |
299 } | 86 } |
300 | 87 |
301 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAddress) { | 88 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAddress) { |
(...skipping 785 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1087 | 874 |
1088 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityVar) { | 875 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityVar) { |
1089 RunTest(FILE_PATH_LITERAL("var.html")); | 876 RunTest(FILE_PATH_LITERAL("var.html")); |
1090 } | 877 } |
1091 | 878 |
1092 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityWbr) { | 879 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityWbr) { |
1093 RunTest(FILE_PATH_LITERAL("wbr.html")); | 880 RunTest(FILE_PATH_LITERAL("wbr.html")); |
1094 } | 881 } |
1095 | 882 |
1096 } // namespace content | 883 } // namespace content |
OLD | NEW |