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