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.
5 #include "content/browser/accessibility/dump_accessibility_browsertest_base.h"
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/accessibility/browser_accessibility_state_impl.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/common/content_paths.h"
23 #include "content/public/common/url_constants.h"
24 #include "content/public/test/content_browser_test.h"
25 #include "content/public/test/content_browser_test_utils.h"
26 #include "content/shell/browser/shell.h"
27 #include "content/test/accessibility_browser_test_utils.h"
33 const char kCommentToken
= '#';
34 const char kMarkSkipFile
[] = "#<skip";
35 const char kMarkEndOfFile
[] = "<-- End-of-file -->";
36 const char kSignalDiff
[] = "*";
40 typedef AccessibilityTreeFormatter::Filter Filter
;
42 DumpAccessibilityTestBase::DumpAccessibilityTestBase() {
45 DumpAccessibilityTestBase::~DumpAccessibilityTestBase() {
49 DumpAccessibilityTestBase::DumpUnfilteredAccessibilityTreeAsString() {
50 WebContentsImpl
* web_contents
= static_cast<WebContentsImpl
*>(
51 shell()->web_contents());
52 AccessibilityTreeFormatter
formatter(
53 web_contents
->GetRootBrowserAccessibilityManager()->GetRoot());
54 std::vector
<Filter
> filters
;
55 filters
.push_back(Filter(base::ASCIIToUTF16("*"), Filter::ALLOW
));
56 formatter
.SetFilters(filters
);
57 formatter
.set_show_ids(true);
58 base::string16 ax_tree_dump
;
59 formatter
.FormatAccessibilityTree(&ax_tree_dump
);
63 std::vector
<int> DumpAccessibilityTestBase::DiffLines(
64 const std::vector
<std::string
>& expected_lines
,
65 const std::vector
<std::string
>& actual_lines
) {
66 int actual_lines_count
= actual_lines
.size();
67 int expected_lines_count
= expected_lines
.size();
68 std::vector
<int> diff_lines
;
70 while (i
< actual_lines_count
&& j
< expected_lines_count
) {
71 if (expected_lines
[j
].size() == 0 ||
72 expected_lines
[j
][0] == kCommentToken
) {
73 // Skip comment lines and blank lines in expected output.
78 if (actual_lines
[i
] != expected_lines
[j
])
79 diff_lines
.push_back(j
);
84 // Actual file has been fully checked.
88 void DumpAccessibilityTestBase::ParseHtmlForExtraDirectives(
89 const std::string
& test_html
,
90 std::vector
<Filter
>* filters
,
91 std::string
* wait_for
) {
92 for (const std::string
& line
:
93 base::SplitString(test_html
, "\n", base::TRIM_WHITESPACE
,
94 base::SPLIT_WANT_ALL
)) {
95 const std::string
& allow_empty_str
=
96 AccessibilityTreeFormatter::GetAllowEmptyString();
97 const std::string
& allow_str
=
98 AccessibilityTreeFormatter::GetAllowString();
99 const std::string
& deny_str
=
100 AccessibilityTreeFormatter::GetDenyString();
101 const std::string
& wait_str
= "@WAIT-FOR:";
102 if (base::StartsWith(line
, allow_empty_str
,
103 base::CompareCase::SENSITIVE
)) {
105 Filter(base::UTF8ToUTF16(line
.substr(allow_empty_str
.size())),
106 Filter::ALLOW_EMPTY
));
107 } else if (base::StartsWith(line
, allow_str
,
108 base::CompareCase::SENSITIVE
)) {
109 filters
->push_back(Filter(base::UTF8ToUTF16(
110 line
.substr(allow_str
.size())),
112 } else if (base::StartsWith(line
, deny_str
,
113 base::CompareCase::SENSITIVE
)) {
114 filters
->push_back(Filter(base::UTF8ToUTF16(
115 line
.substr(deny_str
.size())),
117 } else if (base::StartsWith(line
, wait_str
,
118 base::CompareCase::SENSITIVE
)) {
119 *wait_for
= line
.substr(wait_str
.size());
124 void DumpAccessibilityTestBase::RunTest(
125 const base::FilePath file_path
, const char* file_dir
) {
126 // Disable the "hot tracked" state (set when the mouse is hovering over
127 // an object) because it makes test output change based on the mouse position.
128 BrowserAccessibilityStateImpl::GetInstance()->
129 set_disable_hot_tracking_for_testing(true);
131 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
133 // Output the test path to help anyone who encounters a failure and needs
134 // to know where to look.
135 printf("Testing: %s\n", file_path
.MaybeAsASCII().c_str());
137 std::string html_contents
;
138 base::ReadFileToString(file_path
, &html_contents
);
140 // Read the expected file.
141 std::string expected_contents_raw
;
142 base::FilePath expected_file
=
143 base::FilePath(file_path
.RemoveExtension().value() +
144 AccessibilityTreeFormatter::GetExpectedFileSuffix());
145 base::ReadFileToString(expected_file
, &expected_contents_raw
);
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 base::RemoveChars(expected_contents_raw
, "\r", &expected_contents
);
152 if (!expected_contents
.compare(0, strlen(kMarkSkipFile
), kMarkSkipFile
)) {
153 printf("Skipping this test on this platform.\n");
157 // Parse filters and other directives in the test file.
158 std::string wait_for
;
159 AddDefaultFilters(&filters_
);
160 ParseHtmlForExtraDirectives(html_contents
, &filters_
, &wait_for
);
163 base::string16 html_contents16
;
164 html_contents16
= base::UTF8ToUTF16(html_contents
);
165 GURL url
= GetTestUrl(file_dir
, file_path
.BaseName().MaybeAsASCII().c_str());
167 // If there's a @WAIT-FOR directive, set up an accessibility notification
168 // waiter that returns on any event; we'll stop when we get the text we're
169 // waiting for, or time out. Otherwise just wait specifically for
170 // the "load complete" event.
171 scoped_ptr
<AccessibilityNotificationWaiter
> waiter
;
172 if (!wait_for
.empty()) {
173 waiter
.reset(new AccessibilityNotificationWaiter(
174 shell(), AccessibilityModeComplete
, ui::AX_EVENT_NONE
));
176 waiter
.reset(new AccessibilityNotificationWaiter(
177 shell(), AccessibilityModeComplete
, ui::AX_EVENT_LOAD_COMPLETE
));
180 // Load the test html.
181 NavigateToURL(shell(), url
);
183 // Wait for notifications. If there's a @WAIT-FOR directive, break when
184 // the text we're waiting for appears in the dump, otherwise break after
185 // the first notification, which will be a load complete.
187 waiter
->WaitForNotification();
188 if (!wait_for
.empty()) {
189 base::string16 tree_dump
= DumpUnfilteredAccessibilityTreeAsString();
190 if (base::UTF16ToUTF8(tree_dump
).find(wait_for
) != std::string::npos
)
193 } while (!wait_for
.empty());
195 // Call the subclass to dump the output.
196 std::vector
<std::string
> actual_lines
= Dump();
198 // Perform a diff (or write the initial baseline).
199 std::vector
<std::string
> expected_lines
= base::SplitString(
200 expected_contents
, "\n", base::KEEP_WHITESPACE
,
201 base::SPLIT_WANT_NONEMPTY
);
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 std::string actual_contents
= base::JoinString(actual_lines
, "\n");
208 std::vector
<int> diff_lines
= DiffLines(expected_lines
, actual_lines
);
209 bool is_different
= diff_lines
.size() > 0;
210 EXPECT_FALSE(is_different
);
214 // Mark the expected lines which did not match actual output with a *.
215 printf("* Line Expected\n");
216 printf("- ---- --------\n");
217 for (int line
= 0, diff_index
= 0;
218 line
< static_cast<int>(expected_lines
.size());
220 bool is_diff
= false;
221 if (diff_index
< static_cast<int>(diff_lines
.size()) &&
222 diff_lines
[diff_index
] == line
) {
226 printf("%1s %4d %s\n", is_diff
? kSignalDiff
: "", line
+ 1,
227 expected_lines
[line
].c_str());
229 printf("\nActual\n");
231 printf("%s\n", actual_contents
.c_str());
234 if (!base::PathExists(expected_file
)) {
235 base::FilePath actual_file
=
236 base::FilePath(file_path
.RemoveExtension().value() +
237 AccessibilityTreeFormatter::GetActualFileSuffix());
239 EXPECT_TRUE(base::WriteFile(
240 actual_file
, actual_contents
.c_str(), actual_contents
.size()));
242 ADD_FAILURE() << "No expectation found. Create it by doing:\n"
243 << "mv " << actual_file
.LossyDisplayName() << " "
244 << expected_file
.LossyDisplayName();
248 } // namespace content