Chromium Code Reviews| 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 <string> | 6 #include <string> |
| 6 #include <vector> | 7 #include <vector> |
| 7 | 8 |
| 8 #include "base/logging.h" | 9 #include "base/logging.h" |
| 9 #include "base/path_service.h" | 10 #include "base/path_service.h" |
| 11 #include "base/string_split.h" | |
| 10 #include "base/string_util.h" | 12 #include "base/string_util.h" |
| 11 #include "base/string16.h" | 13 #include "base/string16.h" |
| 12 #include "base/utf_string_conversions.h" | 14 #include "base/utf_string_conversions.h" |
| 13 #include "content/browser/accessibility/browser_accessibility.h" | 15 #include "content/browser/accessibility/browser_accessibility.h" |
| 14 #include "content/browser/accessibility/browser_accessibility_manager.h" | 16 #include "content/browser/accessibility/browser_accessibility_manager.h" |
| 15 #include "content/browser/accessibility/dump_accessibility_tree_helper.h" | 17 #include "content/browser/accessibility/dump_accessibility_tree_helper.h" |
| 16 #include "content/browser/renderer_host/render_view_host_impl.h" | 18 #include "content/browser/renderer_host/render_view_host_impl.h" |
| 17 #include "content/port/browser/render_widget_host_view_port.h" | 19 #include "content/port/browser/render_widget_host_view_port.h" |
| 18 #include "content/public/browser/notification_service.h" | 20 #include "content/public/browser/notification_service.h" |
| 19 #include "content/public/browser/notification_types.h" | 21 #include "content/public/browser/notification_types.h" |
| 20 #include "content/public/browser/web_contents.h" | 22 #include "content/public/browser/web_contents.h" |
| 21 #include "content/public/common/content_paths.h" | 23 #include "content/public/common/content_paths.h" |
| 22 #include "content/public/test/test_utils.h" | 24 #include "content/public/test/test_utils.h" |
| 23 #include "content/test/content_browser_test.h" | 25 #include "content/test/content_browser_test.h" |
| 24 #include "content/test/content_browser_test_utils.h" | 26 #include "content/test/content_browser_test_utils.h" |
| 25 #include "content/shell/shell.h" | 27 #include "content/shell/shell.h" |
| 26 #include "testing/gtest/include/gtest/gtest.h" | 28 #include "testing/gtest/include/gtest/gtest.h" |
| 27 | 29 |
| 28 namespace { | 30 namespace { |
| 29 // Required to enter html content into a url. | |
| 30 static const std::string kUrlPreamble = "data:text/html,\n<!doctype html>"; | |
| 31 static const char kCommentToken = '#'; | 31 static const char kCommentToken = '#'; |
| 32 static const char* kMarkSkipFile = "#<skip"; | 32 static const char* kMarkSkipFile = "#<skip"; |
| 33 static const char* kMarkEndOfFile = "<-- End-of-file -->"; | 33 static const char* kMarkEndOfFile = "<-- End-of-file -->"; |
| 34 static const char* kSignalDiff = "*"; | 34 static const char* kSignalDiff = "*"; |
| 35 } // namespace | 35 } // namespace |
| 36 | 36 |
| 37 namespace content { | 37 namespace content { |
| 38 | 38 |
| 39 // This test takes a snapshot of the platform BrowserAccessibility tree and | 39 // This test takes a snapshot of the platform BrowserAccessibility tree and |
| 40 // tests it against an expected baseline. | 40 // tests it against an expected baseline. |
| 41 // | 41 // |
| 42 // The flow of the test is as outlined below. | 42 // The flow of the test is as outlined below. |
| 43 // 1. Load an html file from chrome/test/data/accessibility. | 43 // 1. Load an html file from chrome/test/data/accessibility. |
| 44 // 2. Read the expectation. | 44 // 2. Read the expectation. |
| 45 // 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 |
| 46 // readable string. | 46 // readable string. |
| 47 // 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 |
| 48 // exactly match. | 48 // exactly match. |
| 49 class DumpAccessibilityTreeTest : public ContentBrowserTest { | 49 class DumpAccessibilityTreeTest : public ContentBrowserTest { |
| 50 public: | 50 public: |
| 51 // Utility helper that does a comment aware equality check. | 51 // Utility helper that does a comment aware equality check. |
| 52 // Returns array of lines from expected file which are different. | 52 // Returns array of lines from expected file which are different. |
| 53 std::vector<int> DiffLines(std::vector<std::string>& expected_lines, | 53 std::vector<int> DiffLines(std::vector<std::string>& expected_lines, |
| 54 std::vector<std::string>& actual_lines) { | 54 std::vector<std::string>& actual_lines) { |
| 55 int actual_lines_count = actual_lines.size(); | 55 int actual_lines_count = actual_lines.size(); |
| 56 int expected_lines_count = expected_lines.size(); | 56 int expected_lines_count = expected_lines.size(); |
| 57 std::vector<int> diff_lines; | 57 std::vector<int> diff_lines; |
| 58 int i = 0, j = 0; | 58 int i = 0, j = 0; |
| 59 while (i < actual_lines_count && j < expected_lines_count) { | 59 while (i < actual_lines_count && j < expected_lines_count) { |
| 60 if (expected_lines[j].size() == 0 || | 60 if (expected_lines[j].size() == 0 || |
| 61 expected_lines[j][0] == kCommentToken) { | 61 expected_lines[j][0] == kCommentToken) { |
| 62 // Skip comment lines and blank lines in expected output. | 62 // Skip comment lines and blank lines in expected output. |
| 63 ++j; | 63 ++j; |
| 64 continue; | 64 continue; |
| 65 } | 65 } |
| 66 | 66 |
| 67 if (actual_lines[i] != expected_lines[j]) | 67 if (actual_lines[i] != expected_lines[j]) |
| 68 diff_lines.push_back(j); | 68 diff_lines.push_back(j); |
| 69 ++i; | 69 ++i; |
| 70 ++j; | 70 ++j; |
| 71 } | 71 } |
| 72 | 72 |
| 73 // Actual file has been fully checked. | 73 // Actual file has been fully checked. |
| 74 return diff_lines; | 74 return diff_lines; |
| 75 } | 75 } |
| 76 | 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 | |
| 77 DumpAccessibilityTreeHelper helper_; | 103 DumpAccessibilityTreeHelper helper_; |
| 78 }; | 104 }; |
| 79 | 105 |
| 80 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, | 106 void DumpAccessibilityTreeTest::RunTest(const FilePath::CharType* file_path) { |
| 81 DISABLED_PlatformTreeDifferenceTest) { | 107 NavigateToURL(shell(), GURL("about:blank")); |
| 82 RenderWidgetHostViewPort* host_view = static_cast<RenderWidgetHostViewPort*>( | 108 RenderWidgetHostViewPort* host_view = static_cast<RenderWidgetHostViewPort*>( |
| 83 shell()->web_contents()->GetRenderWidgetHostView()); | 109 shell()->web_contents()->GetRenderWidgetHostView()); |
| 84 RenderWidgetHost* host = host_view->GetRenderWidgetHost(); | 110 RenderWidgetHostImpl* host = |
| 85 RenderViewHostImpl* view_host = | 111 RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost()); |
| 86 static_cast<RenderViewHostImpl*>(RenderWidgetHostImpl::From(host)); | 112 RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(host); |
| 87 view_host->set_save_accessibility_tree_for_testing(true); | 113 view_host->set_save_accessibility_tree_for_testing(true); |
| 88 view_host->SetAccessibilityMode(AccessibilityModeComplete); | 114 view_host->SetAccessibilityMode(AccessibilityModeComplete); |
| 89 | 115 |
| 90 // Setup test paths. | 116 // Setup test paths. |
| 91 FilePath dir_test_data; | 117 FilePath dir_test_data; |
| 92 EXPECT_TRUE(PathService::Get(DIR_TEST_DATA, &dir_test_data)); | 118 EXPECT_TRUE(PathService::Get(DIR_TEST_DATA, &dir_test_data)); |
| 93 FilePath test_path(dir_test_data.Append(FILE_PATH_LITERAL("accessibility"))); | 119 FilePath test_path(dir_test_data.Append(FILE_PATH_LITERAL("accessibility"))); |
| 94 EXPECT_TRUE(file_util::PathExists(test_path)) | 120 EXPECT_TRUE(file_util::PathExists(test_path)) |
| 95 << test_path.LossyDisplayName(); | 121 << test_path.LossyDisplayName(); |
| 96 | 122 |
| 123 FilePath html_file = test_path.Append(FilePath(file_path)); | |
| 97 // Output the test path to help anyone who encounters a failure and needs | 124 // Output the test path to help anyone who encounters a failure and needs |
| 98 // to know where to look. | 125 // to know where to look. |
| 99 printf("Path to test files: %s\n", test_path.MaybeAsASCII().c_str()); | 126 printf("Testing: %s\n", html_file.MaybeAsASCII().c_str()); |
| 100 | 127 |
| 101 // Grab all HTML files. | 128 std::string html_contents; |
| 102 file_util::FileEnumerator file_enumerator(test_path, | 129 file_util::ReadFileToString(html_file, &html_contents); |
| 103 false, | |
| 104 file_util::FileEnumerator::FILES, | |
| 105 FILE_PATH_LITERAL("*.html")); | |
| 106 | 130 |
| 107 // TODO(dtseng): Make each of these a gtest with script. | 131 // Parse filters in the test file. |
| 108 FilePath html_file(file_enumerator.Next()); | 132 std::set<string16> allow_filters; |
| 109 ASSERT_FALSE(html_file.empty()); | 133 std::set<string16> deny_filters; |
| 110 do { | 134 AddDefaultFilters(&allow_filters, &deny_filters); |
| 111 std::string html_contents; | 135 ParseFilters(html_contents, &allow_filters, &deny_filters); |
| 112 file_util::ReadFileToString(html_file, &html_contents); | 136 helper_.SetFilters(allow_filters, deny_filters); |
| 113 | 137 |
| 114 // Read the expected file. | 138 // Read the expected file. |
| 115 std::string expected_contents_raw; | 139 std::string expected_contents_raw; |
| 116 FilePath expected_file = | 140 FilePath expected_file = |
| 117 FilePath(html_file.RemoveExtension().value() + | 141 FilePath(html_file.RemoveExtension().value() + |
| 118 helper_.GetExpectedFileSuffix()); | 142 helper_.GetExpectedFileSuffix()); |
| 119 file_util::ReadFileToString( | 143 file_util::ReadFileToString( |
| 120 expected_file, | 144 expected_file, |
| 121 &expected_contents_raw); | 145 &expected_contents_raw); |
| 122 | 146 |
| 123 // Tolerate Windows-style line endings (\r\n) in the expected file: | 147 // Tolerate Windows-style line endings (\r\n) in the expected file: |
| 124 // normalize by deleting all \r from the file (if any) to leave only \n. | 148 // normalize by deleting all \r from the file (if any) to leave only \n. |
| 125 std::string expected_contents; | 149 std::string expected_contents; |
| 126 RemoveChars(expected_contents_raw, "\r", &expected_contents); | 150 RemoveChars(expected_contents_raw, "\r", &expected_contents); |
| 127 | 151 |
| 128 if (!expected_contents.compare(0, strlen(kMarkSkipFile), kMarkSkipFile)) { | 152 if (!expected_contents.compare(0, strlen(kMarkSkipFile), kMarkSkipFile)) { |
| 129 printf("Skipping %s\n", html_file.BaseName().MaybeAsASCII().c_str()); | 153 printf("Skipping this test on this platform.\n"); |
| 130 continue; | 154 return; |
| 155 } | |
| 156 | |
| 157 // Load the page. | |
| 158 WindowedNotificationObserver tree_updated_observer( | |
| 159 NOTIFICATION_RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED, | |
| 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()); | |
| 131 } | 202 } |
| 203 printf("\nActual\n"); | |
| 204 printf("------\n"); | |
| 205 printf("%s\n", actual_contents.c_str()); | |
| 206 } | |
| 132 | 207 |
| 133 printf("Testing %s\n", html_file.BaseName().MaybeAsASCII().c_str()); | 208 if (!file_util::PathExists(expected_file)) { |
| 209 FilePath actual_file = | |
| 210 FilePath(html_file.RemoveExtension().value() + | |
| 211 helper_.GetActualFileSuffix()); | |
| 134 | 212 |
| 135 // Load the page. | 213 EXPECT_TRUE(file_util::WriteFile( |
| 136 WindowedNotificationObserver tree_updated_observer( | 214 actual_file, actual_contents.c_str(), actual_contents.size())); |
| 137 NOTIFICATION_RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED, | |
| 138 NotificationService::AllSources()); | |
| 139 string16 html_contents16; | |
| 140 html_contents16 = UTF8ToUTF16(html_contents); | |
| 141 GURL url(UTF8ToUTF16(kUrlPreamble) + html_contents16); | |
| 142 NavigateToURL(shell(), url); | |
| 143 | 215 |
| 144 // Wait for the tree. | 216 ADD_FAILURE() << "No expectation found. Create it by doing:\n" |
| 145 tree_updated_observer.Wait(); | 217 << "mv " << actual_file.LossyDisplayName() << " " |
| 218 << expected_file.LossyDisplayName(); | |
| 219 } | |
| 220 } | |
| 146 | 221 |
| 147 // Perform a diff (or write the initial baseline). | 222 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityA) { |
|
David Tseng
2012/07/31 17:04:17
nit: Do we really want to add a test per file? Gte
| |
| 148 string16 actual_contents_utf16; | 223 RunTest(FILE_PATH_LITERAL("a.html")); |
| 149 helper_.DumpAccessibilityTree( | 224 } |
| 150 host_view->GetBrowserAccessibilityManager()->GetRoot(), | |
| 151 &actual_contents_utf16); | |
| 152 std::string actual_contents = UTF16ToUTF8(actual_contents_utf16); | |
| 153 std::vector<std::string> actual_lines, expected_lines; | |
| 154 Tokenize(actual_contents, "\n", &actual_lines); | |
| 155 Tokenize(expected_contents, "\n", &expected_lines); | |
| 156 // Marking the end of the file with a line of text ensures that | |
| 157 // file length differences are found. | |
| 158 expected_lines.push_back(kMarkEndOfFile); | |
| 159 actual_lines.push_back(kMarkEndOfFile); | |
| 160 | 225 |
| 161 std::vector<int> diff_lines = DiffLines(expected_lines, actual_lines); | 226 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAName) { |
| 162 bool is_different = diff_lines.size() > 0; | 227 RunTest(FILE_PATH_LITERAL("a-name.html")); |
| 163 EXPECT_FALSE(is_different); | 228 } |
| 164 if (is_different) { | |
| 165 // Mark the expected lines which did not match actual output with a *. | |
| 166 printf("* Line Expected\n"); | |
| 167 printf("- ---- --------\n"); | |
| 168 for (int line = 0, diff_index = 0; | |
| 169 line < static_cast<int>(expected_lines.size()); | |
| 170 ++line) { | |
| 171 bool is_diff = false; | |
| 172 if (diff_index < static_cast<int>(diff_lines.size()) && | |
| 173 diff_lines[diff_index] == line) { | |
| 174 is_diff = true; | |
| 175 ++ diff_index; | |
| 176 } | |
| 177 printf("%1s %4d %s\n", is_diff? kSignalDiff : "", line + 1, | |
| 178 expected_lines[line].c_str()); | |
| 179 } | |
| 180 printf("\nActual\n"); | |
| 181 printf("------\n"); | |
| 182 printf("%s\n", actual_contents.c_str()); | |
| 183 } | |
| 184 | 229 |
| 185 if (!file_util::PathExists(expected_file)) { | 230 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAOnclick) { |
| 186 FilePath actual_file = | 231 RunTest(FILE_PATH_LITERAL("a-onclick.html")); |
| 187 FilePath(html_file.RemoveExtension().value() + | 232 } |
| 188 helper_.GetActualFileSuffix()); | |
| 189 | 233 |
| 190 EXPECT_TRUE(file_util::WriteFile( | 234 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, |
| 191 actual_file, actual_contents.c_str(), actual_contents.size())); | 235 AccessibilityAriaApplication) { |
| 236 RunTest(FILE_PATH_LITERAL("aria-application.html")); | |
| 237 } | |
| 192 | 238 |
| 193 ADD_FAILURE() << "No expectation found. Create it by doing:\n" | 239 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAWithImg) { |
| 194 << "mv " << actual_file.LossyDisplayName() << " " | 240 RunTest(FILE_PATH_LITERAL("a-with-img.html")); |
| 195 << expected_file.LossyDisplayName(); | 241 } |
| 196 } | 242 |
| 197 } while (!(html_file = file_enumerator.Next()).empty()); | 243 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, |
| 244 AccessibilityContenteditableDescendants) { | |
| 245 RunTest(FILE_PATH_LITERAL("contenteditable-descendants.html")); | |
| 246 } | |
| 247 | |
| 248 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityFooter) { | |
| 249 RunTest(FILE_PATH_LITERAL("footer.html")); | |
| 250 } | |
| 251 | |
| 252 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityListMarkers) { | |
| 253 RunTest(FILE_PATH_LITERAL("list-markers.html")); | |
| 254 } | |
| 255 | |
| 256 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityUl) { | |
| 257 RunTest(FILE_PATH_LITERAL("ul.html")); | |
| 198 } | 258 } |
| 199 | 259 |
| 200 } // namespace content | 260 } // namespace content |
| OLD | NEW |