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