dump_accessibility_tree_browsertest.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/logging.h" 10#include "base/path_service.h" 11#include "base/string_split.h" 12#include "base/string_util.h" 13#include "base/string16.h" 14#include "base/utf_string_conversions.h" 15#include "content/browser/accessibility/browser_accessibility.h" 16#include "content/browser/accessibility/browser_accessibility_manager.h" 17#include "content/browser/accessibility/dump_accessibility_tree_helper.h" 18#include "content/browser/renderer_host/render_view_host_impl.h" 19#include "content/port/browser/render_widget_host_view_port.h" 20#include "content/public/browser/notification_service.h" 21#include "content/public/browser/notification_types.h" 22#include "content/public/browser/web_contents.h" 23#include "content/public/common/content_paths.h" 24#include "content/public/test/test_utils.h" 25#include "content/test/content_browser_test.h" 26#include "content/test/content_browser_test_utils.h" 27#include "content/shell/shell.h" 28#include "testing/gtest/include/gtest/gtest.h" 29 30namespace { 31 static const char kCommentToken = '#'; 32 static const char* kMarkSkipFile = "#<skip"; 33 static const char* kMarkEndOfFile = "<-- End-of-file -->"; 34 static const char* kSignalDiff = "*"; 35} // namespace 36 37namespace content { 38 39// This test takes a snapshot of the platform BrowserAccessibility tree and 40// tests it against an expected baseline. 41// 42// The flow of the test is as outlined below. 43// 1. Load an html file from chrome/test/data/accessibility. 44// 2. Read the expectation. 45// 3. Browse to the page and serialize the platform specific tree into a human 46// readable string. 47// 4. Perform a comparison between actual and expected and fail if they do not 48// exactly match. 49class DumpAccessibilityTreeTest : public ContentBrowserTest { 50 public: 51 // Utility helper that does a comment aware equality check. 52 // Returns array of lines from expected file which are different. 53 std::vector<int> DiffLines(std::vector<std::string>& expected_lines, 54 std::vector<std::string>& actual_lines) { 55 int actual_lines_count = actual_lines.size(); 56 int expected_lines_count = expected_lines.size(); 57 std::vector<int> diff_lines; 58 int i = 0, j = 0; 59 while (i < actual_lines_count && j < expected_lines_count) { 60 if (expected_lines[j].size() == 0 || 61 expected_lines[j][0] == kCommentToken) { 62 // Skip comment lines and blank lines in expected output. 63 ++j; 64 continue; 65 } 66 67 if (actual_lines[i] != expected_lines[j]) 68 diff_lines.push_back(j); 69 ++i; 70 ++j; 71 } 72 73 // Actual file has been fully checked. 74 return diff_lines; 75 } 76 77 void AddDefaultFilters(std::set<string16>* allow_filters, 78 std::set<string16>* deny_filters) { 79 allow_filters->insert(ASCIIToUTF16("FOCUSABLE")); 80 allow_filters->insert(ASCIIToUTF16("READONLY")); 81 } 82 83 void ParseFilters(const std::string& test_html, 84 std::set<string16>* allow_filters, 85 std::set<string16>* deny_filters) { 86 std::vector<std::string> lines; 87 base::SplitString(test_html, '\n', &lines); 88 for (std::vector<std::string>::const_iterator iter = lines.begin(); 89 iter != lines.end(); 90 ++iter) { 91 const std::string& line = *iter; 92 const std::string& allow_str = helper_.GetAllowString(); 93 const std::string& deny_str = helper_.GetDenyString(); 94 if (StartsWithASCII(line, allow_str, true)) 95 allow_filters->insert(UTF8ToUTF16(line.substr(allow_str.size()))); 96 else if (StartsWithASCII(line, deny_str, true)) 97 deny_filters->insert(UTF8ToUTF16(line.substr(deny_str.size()))); 98 } 99 } 100 101 void RunTest(const FilePath::CharType* file_path); 102 103 DumpAccessibilityTreeHelper helper_; 104}; 105 106void DumpAccessibilityTreeTest::RunTest(const FilePath::CharType* file_path) { 107 NavigateToURL(shell(), GURL("about:blank")); 108 RenderWidgetHostViewPort* host_view = static_cast<RenderWidgetHostViewPort*>( 109 shell()->web_contents()->GetRenderWidgetHostView()); 110 RenderWidgetHostImpl* host = 111 RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost()); 112 RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(host); 113 view_host->set_save_accessibility_tree_for_testing(true); 114 view_host->SetAccessibilityMode(AccessibilityModeComplete); 115 116 // Setup test paths. 117 FilePath dir_test_data; 118 EXPECT_TRUE(PathService::Get(DIR_TEST_DATA, &dir_test_data)); 119 FilePath test_path(dir_test_data.Append(FILE_PATH_LITERAL("accessibility"))); 120 EXPECT_TRUE(file_util::PathExists(test_path)) 121 << test_path.LossyDisplayName(); 122 123 FilePath html_file = test_path.Append(FilePath(file_path)); 124 // Output the test path to help anyone who encounters a failure and needs 125 // to know where to look. 126 printf("Testing: %s\n", html_file.MaybeAsASCII().c_str()); 127 128 std::string html_contents; 129 file_util::ReadFileToString(html_file, &html_contents); 130 131 // Parse filters in the test file. 132 std::set<string16> allow_filters; 133 std::set<string16> deny_filters; 134 AddDefaultFilters(&allow_filters, &deny_filters); 135 ParseFilters(html_contents, &allow_filters, &deny_filters); 136 helper_.SetFilters(allow_filters, deny_filters); 137 138 // Read the expected file. 139 std::string expected_contents_raw; 140 FilePath expected_file = 141 FilePath(html_file.RemoveExtension().value() + 142 helper_.GetExpectedFileSuffix()); 143 file_util::ReadFileToString( 144 expected_file, 145 &expected_contents_raw); 146 147 // Tolerate Windows-style line endings (\r\n) in the expected file: 148 // normalize by deleting all \r from the file (if any) to leave only \n. 149 std::string expected_contents; 150 RemoveChars(expected_contents_raw, "\r", &expected_contents); 151 152 if (!expected_contents.compare(0, strlen(kMarkSkipFile), kMarkSkipFile)) { 153 printf("Skipping this test on this platform.\n"); 154 return; 155 } 156 157 // Load the page. 158 WindowedNotificationObserver tree_updated_observer( 159 NOTIFICATION_ACCESSIBILITY_LOAD_COMPLETE, 160 NotificationService::AllSources()); 161 string16 html_contents16; 162 html_contents16 = UTF8ToUTF16(html_contents); 163 GURL url = GetTestUrl("accessibility", 164 html_file.BaseName().MaybeAsASCII().c_str()); 165 NavigateToURL(shell(), url); 166 167 // Wait for the tree. 168 tree_updated_observer.Wait(); 169 170 // Perform a diff (or write the initial baseline). 171 string16 actual_contents_utf16; 172 helper_.DumpAccessibilityTree( 173 host_view->GetBrowserAccessibilityManager()->GetRoot(), 174 &actual_contents_utf16); 175 std::string actual_contents = UTF16ToUTF8(actual_contents_utf16); 176 std::vector<std::string> actual_lines, expected_lines; 177 Tokenize(actual_contents, "\n", &actual_lines); 178 Tokenize(expected_contents, "\n", &expected_lines); 179 // Marking the end of the file with a line of text ensures that 180 // file length differences are found. 181 expected_lines.push_back(kMarkEndOfFile); 182 actual_lines.push_back(kMarkEndOfFile); 183 184 std::vector<int> diff_lines = DiffLines(expected_lines, actual_lines); 185 bool is_different = diff_lines.size() > 0; 186 EXPECT_FALSE(is_different); 187 if (is_different) { 188 // Mark the expected lines which did not match actual output with a *. 189 printf("* Line Expected\n"); 190 printf("- ---- --------\n"); 191 for (int line = 0, diff_index = 0; 192 line < static_cast<int>(expected_lines.size()); 193 ++line) { 194 bool is_diff = false; 195 if (diff_index < static_cast<int>(diff_lines.size()) && 196 diff_lines[diff_index] == line) { 197 is_diff = true; 198 ++ diff_index; 199 } 200 printf("%1s %4d %s\n", is_diff? kSignalDiff : "", line + 1, 201 expected_lines[line].c_str()); 202 } 203 printf("\nActual\n"); 204 printf("------\n"); 205 printf("%s\n", actual_contents.c_str()); 206 } 207 208 if (!file_util::PathExists(expected_file)) { 209 FilePath actual_file = 210 FilePath(html_file.RemoveExtension().value() + 211 helper_.GetActualFileSuffix()); 212 213 EXPECT_TRUE(file_util::WriteFile( 214 actual_file, actual_contents.c_str(), actual_contents.size())); 215 216 ADD_FAILURE() << "No expectation found. Create it by doing:\n" 217 << "mv " << actual_file.LossyDisplayName() << " " 218 << expected_file.LossyDisplayName(); 219 } 220} 221 222IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityA) { 223 RunTest(FILE_PATH_LITERAL("a.html")); 224} 225 226IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAName) { 227 RunTest(FILE_PATH_LITERAL("a-name.html")); 228} 229 230IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAOnclick) { 231 RunTest(FILE_PATH_LITERAL("a-onclick.html")); 232} 233 234IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, 235 AccessibilityAriaApplication) { 236 RunTest(FILE_PATH_LITERAL("aria-application.html")); 237} 238 239IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaCombobox) { 240 RunTest(FILE_PATH_LITERAL("aria-combobox.html")); 241} 242 243IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaMenu) { 244 RunTest(FILE_PATH_LITERAL("aria-menu.html")); 245} 246 247IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAWithImg) { 248 RunTest(FILE_PATH_LITERAL("a-with-img.html")); 249} 250 251IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityButtonNameCalc) { 252 RunTest(FILE_PATH_LITERAL("button-name-calc.html")); 253} 254 255// TODO(dmazzoni): rebaseline and enable after this WebKit change is rolled: 256// https://bugs.webkit.org/show_bug.cgi?id=96323 257IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, 258 DISABLED_AccessibilityCanvas) { 259 RunTest(FILE_PATH_LITERAL("canvas.html")); 260} 261 262IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, 263 AccessibilityCheckboxNameCalc) { 264 RunTest(FILE_PATH_LITERAL("checkbox-name-calc.html")); 265} 266 267// TODO(dimich): Started to fail in Chrome r149732 (crbug 140397) 268#if defined(OS_WIN) 269#define MAYBE_AccessibilityContenteditableDescendants \ 270 DISABLED_AccessibilityContenteditableDescendants 271#else 272#define MAYBE_AccessibilityContenteditableDescendants \ 273 AccessibilityContenteditableDescendants 274#endif 275 276IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityDiv) { 277 RunTest(FILE_PATH_LITERAL("div.html")); 278} 279 280IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, 281 MAYBE_AccessibilityContenteditableDescendants) { 282 RunTest(FILE_PATH_LITERAL("contenteditable-descendants.html")); 283} 284 285IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityFooter) { 286 RunTest(FILE_PATH_LITERAL("footer.html")); 287} 288 289IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityForm) { 290 RunTest(FILE_PATH_LITERAL("form.html")); 291} 292 293IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityHR) { 294 RunTest(FILE_PATH_LITERAL("hr.html")); 295} 296 297IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityInputRange) { 298 RunTest(FILE_PATH_LITERAL("input-range.html")); 299} 300 301IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, 302 AccessibilityInputTextNameCalc) { 303 RunTest(FILE_PATH_LITERAL("input-text-name-calc.html")); 304} 305 306IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityLabel) { 307 RunTest(FILE_PATH_LITERAL("label.html")); 308} 309 310IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityListMarkers) { 311 RunTest(FILE_PATH_LITERAL("list-markers.html")); 312} 313 314IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityP) { 315 RunTest(FILE_PATH_LITERAL("p.html")); 316} 317 318IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilitySpinButton) { 319 RunTest(FILE_PATH_LITERAL("spinbutton.html")); 320} 321 322// TODO(dmazzoni): rebaseline and enable after this WebKit change is rolled: 323// https://bugs.webkit.org/show_bug.cgi?id=96323 324IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, 325 DISABLED_AccessibilityToggleButton) { 326 RunTest(FILE_PATH_LITERAL("togglebutton.html")); 327} 328 329IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityUl) { 330 RunTest(FILE_PATH_LITERAL("ul.html")); 331} 332 333} // namespace content 334