dump_accessibility_tree_browsertest.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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
3// found in the LICENSE file.
4
5#include <set>
6#include <string>
7#include <vector>
8
9#include "base/file_util.h"
10#include "base/logging.h"
11#include "base/path_service.h"
12#include "base/strings/string16.h"
13#include "base/strings/string_split.h"
14#include "base/strings/string_util.h"
15#include "base/strings/utf_string_conversions.h"
16#include "content/browser/accessibility/accessibility_tree_formatter.h"
17#include "content/browser/accessibility/browser_accessibility.h"
18#include "content/browser/accessibility/browser_accessibility_manager.h"
19#include "content/browser/renderer_host/render_view_host_impl.h"
20#include "content/port/browser/render_widget_host_view_port.h"
21#include "content/public/browser/web_contents.h"
22#include "content/public/common/content_paths.h"
23#include "content/public/common/url_constants.h"
24#include "content/shell/browser/shell.h"
25#include "content/test/accessibility_browser_test_utils.h"
26#include "content/test/content_browser_test.h"
27#include "content/test/content_browser_test_utils.h"
28#include "testing/gtest/include/gtest/gtest.h"
29
30// TODO(dmazzoni): Disabled accessibility tests on Win64. crbug.com/179717
31#if defined(OS_WIN) && defined(ARCH_CPU_X86_64)
32#define MAYBE(x) DISABLED_##x
33#else
34#define MAYBE(x) x
35#endif
36
37namespace content {
38
39namespace {
40
41const char kCommentToken = '#';
42const char kMarkSkipFile[] = "#<skip";
43const char kMarkEndOfFile[] = "<-- End-of-file -->";
44const char kSignalDiff[] = "*";
45
46}  // namespace
47
48typedef AccessibilityTreeFormatter::Filter Filter;
49
50// This test takes a snapshot of the platform BrowserAccessibility tree and
51// tests it against an expected baseline.
52//
53// The flow of the test is as outlined below.
54// 1. Load an html file from chrome/test/data/accessibility.
55// 2. Read the expectation.
56// 3. Browse to the page and serialize the platform specific tree into a human
57//    readable string.
58// 4. Perform a comparison between actual and expected and fail if they do not
59//    exactly match.
60class DumpAccessibilityTreeTest : public ContentBrowserTest {
61 public:
62  // Utility helper that does a comment aware equality check.
63  // Returns array of lines from expected file which are different.
64  std::vector<int> DiffLines(const std::vector<std::string>& expected_lines,
65                             const std::vector<std::string>& actual_lines) {
66    int actual_lines_count = actual_lines.size();
67    int expected_lines_count = expected_lines.size();
68    std::vector<int> diff_lines;
69    int i = 0, j = 0;
70    while (i < actual_lines_count && j < expected_lines_count) {
71      if (expected_lines[j].size() == 0 ||
72          expected_lines[j][0] == kCommentToken) {
73        // Skip comment lines and blank lines in expected output.
74        ++j;
75        continue;
76      }
77
78      if (actual_lines[i] != expected_lines[j])
79        diff_lines.push_back(j);
80      ++i;
81      ++j;
82    }
83
84    // Actual file has been fully checked.
85    return diff_lines;
86  }
87
88  void AddDefaultFilters(std::vector<Filter>* filters) {
89    filters->push_back(Filter(ASCIIToUTF16("FOCUSABLE"), Filter::ALLOW));
90    filters->push_back(Filter(ASCIIToUTF16("READONLY"), Filter::ALLOW));
91    filters->push_back(Filter(ASCIIToUTF16("*=''"), Filter::DENY));
92  }
93
94  void ParseFilters(const std::string& test_html,
95                    std::vector<Filter>* filters) {
96    std::vector<std::string> lines;
97    base::SplitString(test_html, '\n', &lines);
98    for (std::vector<std::string>::const_iterator iter = lines.begin();
99         iter != lines.end();
100         ++iter) {
101      const std::string& line = *iter;
102      const std::string& allow_empty_str =
103          AccessibilityTreeFormatter::GetAllowEmptyString();
104      const std::string& allow_str =
105          AccessibilityTreeFormatter::GetAllowString();
106      const std::string& deny_str =
107          AccessibilityTreeFormatter::GetDenyString();
108      if (StartsWithASCII(line, allow_empty_str, true)) {
109        filters->push_back(
110          Filter(UTF8ToUTF16(line.substr(allow_empty_str.size())),
111                 Filter::ALLOW_EMPTY));
112      } else if (StartsWithASCII(line, allow_str, true)) {
113        filters->push_back(Filter(UTF8ToUTF16(line.substr(allow_str.size())),
114                                  Filter::ALLOW));
115      } else if (StartsWithASCII(line, deny_str, true)) {
116        filters->push_back(Filter(UTF8ToUTF16(line.substr(deny_str.size())),
117                                  Filter::DENY));
118      }
119    }
120  }
121
122  void RunTest(const base::FilePath::CharType* file_path);
123};
124
125void DumpAccessibilityTreeTest::RunTest(
126    const base::FilePath::CharType* file_path) {
127  NavigateToURL(shell(), GURL(kAboutBlankURL));
128
129  // Setup test paths.
130  base::FilePath dir_test_data;
131  ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &dir_test_data));
132  base::FilePath test_path(
133      dir_test_data.Append(FILE_PATH_LITERAL("accessibility")));
134  ASSERT_TRUE(base::PathExists(test_path))
135      << test_path.LossyDisplayName();
136
137  base::FilePath html_file = test_path.Append(base::FilePath(file_path));
138  // Output the test path to help anyone who encounters a failure and needs
139  // to know where to look.
140  printf("Testing: %s\n", html_file.MaybeAsASCII().c_str());
141
142  std::string html_contents;
143  base::ReadFileToString(html_file, &html_contents);
144
145  // Read the expected file.
146  std::string expected_contents_raw;
147  base::FilePath expected_file =
148    base::FilePath(html_file.RemoveExtension().value() +
149                   AccessibilityTreeFormatter::GetExpectedFileSuffix());
150  base::ReadFileToString(expected_file, &expected_contents_raw);
151
152  // Tolerate Windows-style line endings (\r\n) in the expected file:
153  // normalize by deleting all \r from the file (if any) to leave only \n.
154  std::string expected_contents;
155  RemoveChars(expected_contents_raw, "\r", &expected_contents);
156
157  if (!expected_contents.compare(0, strlen(kMarkSkipFile), kMarkSkipFile)) {
158    printf("Skipping this test on this platform.\n");
159    return;
160  }
161
162  // Load the page.
163  string16 html_contents16;
164  html_contents16 = UTF8ToUTF16(html_contents);
165  GURL url = GetTestUrl("accessibility",
166                        html_file.BaseName().MaybeAsASCII().c_str());
167  AccessibilityNotificationWaiter waiter(
168      shell(), AccessibilityModeComplete,
169      WebKit::WebAXEventLoadComplete);
170  NavigateToURL(shell(), url);
171  waiter.WaitForNotification();
172
173  RenderWidgetHostViewPort* host_view = RenderWidgetHostViewPort::FromRWHV(
174      shell()->web_contents()->GetRenderWidgetHostView());
175  AccessibilityTreeFormatter formatter(
176      host_view->GetBrowserAccessibilityManager()->GetRoot());
177
178  // Parse filters in the test file.
179  std::vector<Filter> filters;
180  AddDefaultFilters(&filters);
181  ParseFilters(html_contents, &filters);
182  formatter.SetFilters(filters);
183
184  // Perform a diff (or write the initial baseline).
185  string16 actual_contents_utf16;
186  formatter.FormatAccessibilityTree(&actual_contents_utf16);
187  std::string actual_contents = UTF16ToUTF8(actual_contents_utf16);
188  std::vector<std::string> actual_lines, expected_lines;
189  Tokenize(actual_contents, "\n", &actual_lines);
190  Tokenize(expected_contents, "\n", &expected_lines);
191  // Marking the end of the file with a line of text ensures that
192  // file length differences are found.
193  expected_lines.push_back(kMarkEndOfFile);
194  actual_lines.push_back(kMarkEndOfFile);
195
196  std::vector<int> diff_lines = DiffLines(expected_lines, actual_lines);
197  bool is_different = diff_lines.size() > 0;
198  EXPECT_FALSE(is_different);
199  if (is_different) {
200    // Mark the expected lines which did not match actual output with a *.
201    printf("* Line Expected\n");
202    printf("- ---- --------\n");
203    for (int line = 0, diff_index = 0;
204         line < static_cast<int>(expected_lines.size());
205         ++line) {
206      bool is_diff = false;
207      if (diff_index < static_cast<int>(diff_lines.size()) &&
208          diff_lines[diff_index] == line) {
209        is_diff = true;
210        ++diff_index;
211      }
212      printf("%1s %4d %s\n", is_diff? kSignalDiff : "", line + 1,
213             expected_lines[line].c_str());
214    }
215    printf("\nActual\n");
216    printf("------\n");
217    printf("%s\n", actual_contents.c_str());
218  }
219
220  if (!base::PathExists(expected_file)) {
221    base::FilePath actual_file =
222        base::FilePath(html_file.RemoveExtension().value() +
223                       AccessibilityTreeFormatter::GetActualFileSuffix());
224
225    EXPECT_TRUE(file_util::WriteFile(
226        actual_file, actual_contents.c_str(), actual_contents.size()));
227
228    ADD_FAILURE() << "No expectation found. Create it by doing:\n"
229                  << "mv " << actual_file.LossyDisplayName() << " "
230                  << expected_file.LossyDisplayName();
231  }
232}
233
234IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityA) {
235  RunTest(FILE_PATH_LITERAL("a.html"));
236}
237
238IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAddress) {
239  RunTest(FILE_PATH_LITERAL("address.html"));
240}
241
242IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAName) {
243  RunTest(FILE_PATH_LITERAL("a-name.html"));
244}
245
246IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAOnclick) {
247  RunTest(FILE_PATH_LITERAL("a-onclick.html"));
248}
249
250IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
251                       AccessibilityAriaApplication) {
252  RunTest(FILE_PATH_LITERAL("aria-application.html"));
253}
254
255IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
256                       AccessibilityAriaAutocomplete) {
257  RunTest(FILE_PATH_LITERAL("aria-autocomplete.html"));
258}
259
260IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaCombobox) {
261  RunTest(FILE_PATH_LITERAL("aria-combobox.html"));
262}
263
264IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaInvalid) {
265  RunTest(FILE_PATH_LITERAL("aria-invalid.html"));
266}
267
268IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaLevel) {
269  RunTest(FILE_PATH_LITERAL("aria-level.html"));
270}
271
272IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaMenu) {
273  RunTest(FILE_PATH_LITERAL("aria-menu.html"));
274}
275
276IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
277                       AccessibilityAriaMenuitemradio) {
278  RunTest(FILE_PATH_LITERAL("aria-menuitemradio.html"));
279}
280
281IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
282                       AccessibilityAriaPressed) {
283  RunTest(FILE_PATH_LITERAL("aria-pressed.html"));
284}
285
286IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
287                       AccessibilityAriaProgressbar) {
288  RunTest(FILE_PATH_LITERAL("aria-progressbar.html"));
289}
290
291IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
292                       AccessibilityAriaToolbar) {
293  RunTest(FILE_PATH_LITERAL("toolbar.html"));
294}
295
296IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
297                       AccessibilityAriaValueMin) {
298  RunTest(FILE_PATH_LITERAL("aria-valuemin.html"));
299}
300
301IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
302                       AccessibilityAriaValueMax) {
303  RunTest(FILE_PATH_LITERAL("aria-valuemax.html"));
304}
305
306IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityArticle) {
307  RunTest(FILE_PATH_LITERAL("article.html"));
308}
309
310IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAWithImg) {
311  RunTest(FILE_PATH_LITERAL("a-with-img.html"));
312}
313
314IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityBdo) {
315  RunTest(FILE_PATH_LITERAL("bdo.html"));
316}
317
318IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityBR) {
319  RunTest(FILE_PATH_LITERAL("br.html"));
320}
321
322IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityButtonNameCalc) {
323  RunTest(FILE_PATH_LITERAL("button-name-calc.html"));
324}
325
326IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityCanvas) {
327  RunTest(FILE_PATH_LITERAL("canvas.html"));
328}
329
330IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
331                       AccessibilityCheckboxNameCalc) {
332  RunTest(FILE_PATH_LITERAL("checkbox-name-calc.html"));
333}
334
335IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityDiv) {
336  RunTest(FILE_PATH_LITERAL("div.html"));
337}
338
339IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityDl) {
340  RunTest(FILE_PATH_LITERAL("dl.html"));
341}
342
343IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
344                       AccessibilityContenteditableDescendants) {
345  RunTest(FILE_PATH_LITERAL("contenteditable-descendants.html"));
346}
347
348IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityEm) {
349  RunTest(FILE_PATH_LITERAL("em.html"));
350}
351
352IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityFooter) {
353  RunTest(FILE_PATH_LITERAL("footer.html"));
354}
355
356IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityForm) {
357  RunTest(FILE_PATH_LITERAL("form.html"));
358}
359
360IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityHeading) {
361  RunTest(FILE_PATH_LITERAL("heading.html"));
362}
363
364IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityHR) {
365  RunTest(FILE_PATH_LITERAL("hr.html"));
366}
367
368// crbug.com/179717 and crbug.com/224659
369IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
370                       DISABLED_AccessibilityIframeCoordinates) {
371  RunTest(FILE_PATH_LITERAL("iframe-coordinates.html"));
372}
373
374IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityInputButton) {
375  RunTest(FILE_PATH_LITERAL("input-button.html"));
376}
377
378IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
379                       AccessibilityInputButtonInMenu) {
380  RunTest(FILE_PATH_LITERAL("input-button-in-menu.html"));
381}
382
383IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityInputColor) {
384  RunTest(FILE_PATH_LITERAL("input-color.html"));
385}
386
387IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
388                       AccessibilityInputImageButtonInMenu) {
389  RunTest(FILE_PATH_LITERAL("input-image-button-in-menu.html"));
390}
391
392IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityInputRange) {
393  RunTest(FILE_PATH_LITERAL("input-range.html"));
394}
395
396IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
397                       AccessibilityInputTextNameCalc) {
398  RunTest(FILE_PATH_LITERAL("input-text-name-calc.html"));
399}
400
401IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityLabel) {
402  RunTest(FILE_PATH_LITERAL("label.html"));
403}
404
405IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityListMarkers) {
406  RunTest(FILE_PATH_LITERAL("list-markers.html"));
407}
408
409IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityP) {
410  RunTest(FILE_PATH_LITERAL("p.html"));
411}
412
413IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilitySelect) {
414  RunTest(FILE_PATH_LITERAL("select.html"));
415}
416
417IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilitySpan) {
418  RunTest(FILE_PATH_LITERAL("span.html"));
419}
420
421IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilitySpinButton) {
422  RunTest(FILE_PATH_LITERAL("spinbutton.html"));
423}
424
425// TODO(dmazzoni): Rebaseline this test after Blink rolls past r155083.
426// See http://crbug.com/265619
427IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, DISABLED_AccessibilitySvg) {
428  RunTest(FILE_PATH_LITERAL("svg.html"));
429}
430
431IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityTab) {
432  RunTest(FILE_PATH_LITERAL("tab.html"));
433}
434
435IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityTableSimple) {
436  RunTest(FILE_PATH_LITERAL("table-simple.html"));
437}
438
439IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityTableSpans) {
440  RunTest(FILE_PATH_LITERAL("table-spans.html"));
441}
442
443IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
444                       AccessibilityToggleButton) {
445  RunTest(FILE_PATH_LITERAL("togglebutton.html"));
446}
447
448IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityUl) {
449  RunTest(FILE_PATH_LITERAL("ul.html"));
450}
451
452IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityWbr) {
453  RunTest(FILE_PATH_LITERAL("wbr.html"));
454}
455
456}  // namespace content
457