OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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 "content/browser/accessibility/dump_accessibility_browsertest_base.h" |
| 6 |
| 7 #include <set> |
| 8 #include <string> |
| 9 #include <vector> |
| 10 |
| 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/web_contents/web_contents_impl.h" |
| 20 #include "content/public/browser/web_contents.h" |
| 21 #include "content/public/common/content_paths.h" |
| 22 #include "content/public/common/url_constants.h" |
| 23 #include "content/public/test/content_browser_test.h" |
| 24 #include "content/public/test/content_browser_test_utils.h" |
| 25 #include "content/shell/browser/shell.h" |
| 26 #include "content/test/accessibility_browser_test_utils.h" |
| 27 |
| 28 namespace content { |
| 29 |
| 30 namespace { |
| 31 |
| 32 const char kCommentToken = '#'; |
| 33 const char kMarkSkipFile[] = "#<skip"; |
| 34 const char kMarkEndOfFile[] = "<-- End-of-file -->"; |
| 35 const char kSignalDiff[] = "*"; |
| 36 |
| 37 } // namespace |
| 38 |
| 39 typedef AccessibilityTreeFormatter::Filter Filter; |
| 40 |
| 41 DumpAccessibilityTestBase::DumpAccessibilityTestBase() { |
| 42 } |
| 43 |
| 44 DumpAccessibilityTestBase::~DumpAccessibilityTestBase() { |
| 45 } |
| 46 |
| 47 base::string16 |
| 48 DumpAccessibilityTestBase::DumpUnfilteredAccessibilityTreeAsString() { |
| 49 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( |
| 50 shell()->web_contents()); |
| 51 AccessibilityTreeFormatter formatter( |
| 52 web_contents->GetRootBrowserAccessibilityManager()->GetRoot()); |
| 53 std::vector<Filter> filters; |
| 54 filters.push_back(Filter(base::ASCIIToUTF16("*"), Filter::ALLOW)); |
| 55 formatter.SetFilters(filters); |
| 56 base::string16 ax_tree_dump; |
| 57 formatter.FormatAccessibilityTree(&ax_tree_dump); |
| 58 return ax_tree_dump; |
| 59 } |
| 60 |
| 61 std::vector<int> DumpAccessibilityTestBase::DiffLines( |
| 62 const std::vector<std::string>& expected_lines, |
| 63 const std::vector<std::string>& actual_lines) { |
| 64 int actual_lines_count = actual_lines.size(); |
| 65 int expected_lines_count = expected_lines.size(); |
| 66 std::vector<int> diff_lines; |
| 67 int i = 0, j = 0; |
| 68 while (i < actual_lines_count && j < expected_lines_count) { |
| 69 if (expected_lines[j].size() == 0 || |
| 70 expected_lines[j][0] == kCommentToken) { |
| 71 // Skip comment lines and blank lines in expected output. |
| 72 ++j; |
| 73 continue; |
| 74 } |
| 75 |
| 76 if (actual_lines[i] != expected_lines[j]) |
| 77 diff_lines.push_back(j); |
| 78 ++i; |
| 79 ++j; |
| 80 } |
| 81 |
| 82 // Actual file has been fully checked. |
| 83 return diff_lines; |
| 84 } |
| 85 |
| 86 void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives( |
| 87 const std::string& test_html, |
| 88 std::vector<Filter>* filters, |
| 89 std::string* wait_for) { |
| 90 std::vector<std::string> lines; |
| 91 base::SplitString(test_html, '\n', &lines); |
| 92 for (std::vector<std::string>::const_iterator iter = lines.begin(); |
| 93 iter != lines.end(); |
| 94 ++iter) { |
| 95 const std::string& line = *iter; |
| 96 const std::string& allow_empty_str = |
| 97 AccessibilityTreeFormatter::GetAllowEmptyString(); |
| 98 const std::string& allow_str = |
| 99 AccessibilityTreeFormatter::GetAllowString(); |
| 100 const std::string& deny_str = |
| 101 AccessibilityTreeFormatter::GetDenyString(); |
| 102 const std::string& wait_str = "@WAIT-FOR:"; |
| 103 if (StartsWithASCII(line, allow_empty_str, true)) { |
| 104 filters->push_back( |
| 105 Filter(base::UTF8ToUTF16(line.substr(allow_empty_str.size())), |
| 106 Filter::ALLOW_EMPTY)); |
| 107 } else if (StartsWithASCII(line, allow_str, true)) { |
| 108 filters->push_back(Filter(base::UTF8ToUTF16( |
| 109 line.substr(allow_str.size())), |
| 110 Filter::ALLOW)); |
| 111 } else if (StartsWithASCII(line, deny_str, true)) { |
| 112 filters->push_back(Filter(base::UTF8ToUTF16( |
| 113 line.substr(deny_str.size())), |
| 114 Filter::DENY)); |
| 115 } else if (StartsWithASCII(line, wait_str, true)) { |
| 116 *wait_for = line.substr(wait_str.size()); |
| 117 } |
| 118 } |
| 119 } |
| 120 |
| 121 void DumpAccessibilityTestBase::RunTest( |
| 122 const base::FilePath::CharType* file_path) { |
| 123 NavigateToURL(shell(), GURL(url::kAboutBlankURL)); |
| 124 |
| 125 // Setup test paths. |
| 126 base::FilePath dir_test_data; |
| 127 ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &dir_test_data)); |
| 128 base::FilePath test_path( |
| 129 dir_test_data.Append(FILE_PATH_LITERAL("accessibility"))); |
| 130 ASSERT_TRUE(base::PathExists(test_path)) |
| 131 << test_path.LossyDisplayName(); |
| 132 |
| 133 base::FilePath html_file = test_path.Append(base::FilePath(file_path)); |
| 134 // Output the test path to help anyone who encounters a failure and needs |
| 135 // to know where to look. |
| 136 printf("Testing: %s\n", html_file.MaybeAsASCII().c_str()); |
| 137 |
| 138 std::string html_contents; |
| 139 base::ReadFileToString(html_file, &html_contents); |
| 140 |
| 141 // Read the expected file. |
| 142 std::string expected_contents_raw; |
| 143 base::FilePath expected_file = |
| 144 base::FilePath(html_file.RemoveExtension().value() + |
| 145 AccessibilityTreeFormatter::GetExpectedFileSuffix()); |
| 146 base::ReadFileToString(expected_file, &expected_contents_raw); |
| 147 |
| 148 // Tolerate Windows-style line endings (\r\n) in the expected file: |
| 149 // normalize by deleting all \r from the file (if any) to leave only \n. |
| 150 std::string expected_contents; |
| 151 base::RemoveChars(expected_contents_raw, "\r", &expected_contents); |
| 152 |
| 153 if (!expected_contents.compare(0, strlen(kMarkSkipFile), kMarkSkipFile)) { |
| 154 printf("Skipping this test on this platform.\n"); |
| 155 return; |
| 156 } |
| 157 |
| 158 // Parse filters and other directives in the test file. |
| 159 std::string wait_for; |
| 160 AddDefaultFilters(&filters_); |
| 161 ParseHtmlForExtraDirectives(html_contents, &filters_, &wait_for); |
| 162 |
| 163 // Load the page. |
| 164 base::string16 html_contents16; |
| 165 html_contents16 = base::UTF8ToUTF16(html_contents); |
| 166 GURL url = GetTestUrl("accessibility", |
| 167 html_file.BaseName().MaybeAsASCII().c_str()); |
| 168 |
| 169 // If there's a @WAIT-FOR directive, set up an accessibility notification |
| 170 // waiter that returns on any event; we'll stop when we get the text we're |
| 171 // waiting for, or time out. Otherwise just wait specifically for |
| 172 // the "load complete" event. |
| 173 scoped_ptr<AccessibilityNotificationWaiter> waiter; |
| 174 if (!wait_for.empty()) { |
| 175 waiter.reset(new AccessibilityNotificationWaiter( |
| 176 shell(), AccessibilityModeComplete, ui::AX_EVENT_NONE)); |
| 177 } else { |
| 178 waiter.reset(new AccessibilityNotificationWaiter( |
| 179 shell(), AccessibilityModeComplete, ui::AX_EVENT_LOAD_COMPLETE)); |
| 180 } |
| 181 |
| 182 // Load the test html. |
| 183 NavigateToURL(shell(), url); |
| 184 |
| 185 // Wait for notifications. If there's a @WAIT-FOR directive, break when |
| 186 // the text we're waiting for appears in the dump, otherwise break after |
| 187 // the first notification, which will be a load complete. |
| 188 do { |
| 189 waiter->WaitForNotification(); |
| 190 if (!wait_for.empty()) { |
| 191 base::string16 tree_dump = DumpUnfilteredAccessibilityTreeAsString(); |
| 192 if (base::UTF16ToUTF8(tree_dump).find(wait_for) != std::string::npos) |
| 193 wait_for.clear(); |
| 194 } |
| 195 } while (!wait_for.empty()); |
| 196 |
| 197 // Call the subclass to dump the output. |
| 198 std::vector<std::string> actual_lines = Dump(); |
| 199 |
| 200 // Perform a diff (or write the initial baseline). |
| 201 std::vector<std::string> expected_lines; |
| 202 Tokenize(expected_contents, "\n", &expected_lines); |
| 203 // Marking the end of the file with a line of text ensures that |
| 204 // file length differences are found. |
| 205 expected_lines.push_back(kMarkEndOfFile); |
| 206 actual_lines.push_back(kMarkEndOfFile); |
| 207 std::string actual_contents = JoinString(actual_lines, "\n"); |
| 208 |
| 209 std::vector<int> diff_lines = DiffLines(expected_lines, actual_lines); |
| 210 bool is_different = diff_lines.size() > 0; |
| 211 EXPECT_FALSE(is_different); |
| 212 if (is_different) { |
| 213 OnDiffFailed(); |
| 214 |
| 215 // Mark the expected lines which did not match actual output with a *. |
| 216 printf("* Line Expected\n"); |
| 217 printf("- ---- --------\n"); |
| 218 for (int line = 0, diff_index = 0; |
| 219 line < static_cast<int>(expected_lines.size()); |
| 220 ++line) { |
| 221 bool is_diff = false; |
| 222 if (diff_index < static_cast<int>(diff_lines.size()) && |
| 223 diff_lines[diff_index] == line) { |
| 224 is_diff = true; |
| 225 ++diff_index; |
| 226 } |
| 227 printf("%1s %4d %s\n", is_diff? kSignalDiff : "", line + 1, |
| 228 expected_lines[line].c_str()); |
| 229 } |
| 230 printf("\nActual\n"); |
| 231 printf("------\n"); |
| 232 printf("%s\n", actual_contents.c_str()); |
| 233 } |
| 234 |
| 235 if (!base::PathExists(expected_file)) { |
| 236 base::FilePath actual_file = |
| 237 base::FilePath(html_file.RemoveExtension().value() + |
| 238 AccessibilityTreeFormatter::GetActualFileSuffix()); |
| 239 |
| 240 EXPECT_TRUE(base::WriteFile( |
| 241 actual_file, actual_contents.c_str(), actual_contents.size())); |
| 242 |
| 243 ADD_FAILURE() << "No expectation found. Create it by doing:\n" |
| 244 << "mv " << actual_file.LossyDisplayName() << " " |
| 245 << expected_file.LossyDisplayName(); |
| 246 } |
| 247 } |
| 248 |
| 249 } // namespace content |
OLD | NEW |