 |
1 Layout Engine Visual Tests (reftest)
2 L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
3 July 19, 2006
4
5 This code is designed to run tests of Mozilla's layout engine. These
6 tests consist of an HTML (or other format) file along with a reference
7 in the same format. The tests are run based on a manifest file, and for
8 each test, PASS or FAIL is reported, and UNEXPECTED is reported if the
9 result (PASS or FAIL) was not the expected result noted in the manifest.
10
11 Why this way?
12 =============
13
14 Writing HTML tests where the reference rendering is also in HTML is
15 harder than simply writing bits of HTML that can be regression-tested by
16 comparing the rendering of an older build to that of a newer build
17 (perhaps using stored reference images from the older build). However,
18 comparing across time has major disadvantages:
19
20 * Comparisons across time either require two runs for every test, or
21 they require stored reference images appropriate for the platform and
22 configuration (often limiting testing to a very specific
23 configuration).
24
25 * Comparisons across time may fail due to expected changes, for
26 example, changes in the default style sheet for HTML, changes in the
27 appearance of form controls, or changes in default preferences like
28 default font size or default colors.
29
30 Using tests for which the pass criteria were explicitly chosen allows
31 running tests at any time to see whether they still pass.
32
33 Manifest Format
34 ===============
35
36 The test manifest format is a plain text file. A line starting with a
37 "#" is a comment. Lines may be commented using whitespace followed by
38 a "#" and the comment. Each non-blank line (after removal of comments)
39 must be one of the following:
40
41 1. Inclusion of another manifest
42
43 include <relative_path>
44
45 2. A test item
46
47 <failure-type>* [<http>] <type> <url> <url_ref>
48
49 where
50
51 a. <failure-type> (optional) is one of the following:
52
53 fails The test passes if the images of the two renderings DO NOT
54 meet the conditions specified in the <type>.
55
56 fails-if(condition) If the condition is met, the test passes if the
57 images of the two renderings DO NOT meet the
58 conditions of <type>. If the condition is not met,
59 the test passes if the conditions of <type> are met.
60
61 random The results of the test are random and therefore not to be
62 considered in the output.
63
64 random-if(condition) The results of the test are random if a given
65 condition is met.
66
67 skip This test should not be run. This is useful when a test fails in a
68 catastrophic way, such as crashing or hanging the browser. Using
69 'skip' is preferred to simply commenting out the test because we
70 want to report the test failure at the end of the test run.
71
72 skip-if(condition) If the condition is met, the test is not run. This is
73 useful if, for example, the test crashes only on a
74 particular platform (i.e. it allows us to get test
75 coverage on the other platforms).
76
77 Examples of using conditions:
78 fails-if(MOZ_WIDGET_TOOLKIT=="windows") ...
79 fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") ...
80 fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") ...
81
82 b. <http>, if present, is the string "HTTP" (sans quotes), indicating that
83 the test should be run over an HTTP server because it requires certain
84 HTTP headers or a particular HTTP status. (Don't use this if your test
85 doesn't require this functionality, because it unnecessarily slows down
86 the test.)
87
88 HTTP tests have the restriction that any resource an HTTP test accesses
89 must be accessed using a relative URL, and the test and the resource must
90 be within the directory containing the reftest manifest that describes
91 the test (or within a descendant directory).
92
93 To modify the HTTP status or headers of a resource named FOO, create a
94 sibling file named FOO^headers^ with the following contents:
95
96 [<http-status>]
97 <http-header>*
98
99 <http-status> A line of the form "HTTP ###[ <description>]", where
100 ### indicates the desired HTTP status and <description>
101 indicates a desired HTTP status description, if any.
102 If this line is omitted, the default is "HTTP 200 OK".
103 <http-header> A line in standard HTTP header line format, i.e.
104 "Field-Name: field-value". You may not repeat the use
105 of a Field-Name and must coalesce such headers together,
106 and each header must be specified on a single line, but
107 otherwise the format exactly matches that from HTTP
108 itself.
109
110 HTTP tests may also incorporate SJS files. SJS files provide similar
111 functionality to CGI scripts, in that the response they produce can be
112 dependent on properties of the incoming request. Currently these
113 properties are restricted to method type and headers, but eventually
114 it should be possible to examine data in the body of the request as
115 well when computing the generated response. An SJS file is a JavaScript
116 file with a .sjs extension which defines a global |handleRequest|
117 function (called every time that file is loaded during reftests) in this
118 format:
119
120 function handleRequest(request, response)
121 {
122 response.setStatusLine(request.httpVersion, 200, "OK");
123
124 // You *probably* want this, or else you'll get bitten if you run
125 // reftest multiple times with the same profile.
126 response.setHeader("Cache-Control", "no-cache");
127
128 response.write("any ASCII data you want");
129
130 var outputStream = response.bodyOutputStream;
131 // ...anything else you want to do, synchronously...
132 }
133
134 For more details on exactly which functions and properties are available
135 on request/response in handleRequest, see the nsIHttpRe(quest|sponse)
136 definitions in <netwerk/test/httpserver/nsIHttpServer.idl>.
137
138 c. <type> is one of the following:
139
140 == The test passes if the images of the two renderings are the
141 SAME.
142 != The test passes if the images of the two renderings are
143 DIFFERENT.
144 load The test passes unconditionally if the page loads. url_ref
145 must be omitted, and the test cannot be marked as fails or
146 random. (Used to test for crashes, hangs, assertions, and
147 leaks.)
148
149 d. <url> is either a relative file path or an absolute URL for the
150 test page
151
152 e. <url_ref> is either a relative file path or an absolute URL for
153 the reference page
154
155 The only difference between <url> and <url_ref> is that results of
156 the test are reported using <url> only.
157
158 This test manifest format could be used by other harnesses, such as ones
159 that do not depend on XUL, or even ones testing other layout engines.
160
161 Running Tests
162 =============
163
164 At some point in the future there will hopefully be a cleaner way to do
165 this. For now, go to your object directory, and run (perhaps using
166 MOZ_NO_REMOTE=1 or the -profile <directory> option)
167
168 ./firefox -reftest /path/to/srcdir/mozilla/layout/reftests/reftest.list > reftest.out
169
170 and then search/grep reftest.out for "UNEXPECTED".
171
172 There are two scripts provided to convert the reftest.out to HTML.
173 clean-reftest-output.pl converts reftest.out into simple HTML, stripping
174 lines from the log that aren't relevant. reftest-to-html.pl converts
175 the output into html that makes it easier to visually check for
176 failures.
177
178 Testable Areas
179 ==============
180
181 This framework is capable of testing many areas of the layout engine.
182 It is particularly well-suited to testing dynamic change handling (by
183 comparison to the static end-result as a reference) and incremental
184 layout (comparison of a script-interrupted layout to one that was not).
185 However, it is also possible to write tests for many other things that
186 can be described in terms of equivalence, for example:
187
188 * CSS cascading could be tested by comparing the result of a
189 complicated set of style rules that makes a word green to <span
190 style="color:green">word</span>.
191
192 * <canvas> compositing operators could be tested by comparing the
193 result of drawing using canvas to a block-level element with the
194 desired color as a CSS background-color.
195
196 * CSS counters could be tested by comparing the text output by counters
197 with a page containing the text written out
198
199 * complex margin collapsing could be tested by comparing the complex
200 case to a case where the margin is written out, or where the margin
201 space is created by an element with 'height' and transparent
202 background
203
204 When it is not possible to test by equivalence, it may be possible to
205 test by non-equivalence. For example, testing justification in cases
206 with more than two words, or more than three different words, is
207 difficult. However, it is simple to test that justified text is at
208 least displayed differently from left-, center-, or right-aligned text.
209
210 Writing Tests
211 =============
212
213 When writing tests for this framework, it is important for the test to
214 depend only on behaviors that are known to be correct and permanent.
215 For example, tests should not depend on default font sizes, default
216 margins of the body element, the default style sheet used for HTML, the
217 default appearance of form controls, or anything else that can be
218 avoided.
219
220 In general, the best way to achieve this is to make the test and the
221 reference identical in as many aspects as possible. For example:
222
223 Good test markup:
224 <div style="color:green"><table><tr><td><span>green
225 </span></td></tr></table></div>
226
227 Good reference markup:
228 <div><table><tr><td><span style="color:green">green
229 </span></td></tr></table></div>
230
231 BAD reference markup:
232 <!-- 3px matches the default cellspacing and cellpadding -->
233 <div style="color:green; padding: 3px">green
234 </div>
235
236 BAD test markup:
237 <!-- span doesn't change the positioning, so skip it -->
238 <div style="color:green"><table><tr><td>green
239 </td></tr></table></div>
240
241 Asynchronous Tests
242 ==================
243
244 Normally reftest takes a snapshot of the given markup's rendering right
245 after the load event fires for content. If your test needs to postpone
246 the moment the snapshot is taken, it should make sure a class
247 'reftest-wait' is on the root element by the moment the load event
248 fires. The easiest way to do this is to put it in the markup, e.g.:
249 <html class="reftest-wait">
250
251 When your test is ready, you should remove this class from the root
252 element, for example using this code:
253 document.documentElement.className = "";
254
255
256 Note that in layout tests it is often enough to trigger layout using
257 document.body.offsetWidth // HTML example
258
259 When possible, you should use this technique instead of making your
260 test async.
261
262 Printing Tests
263 ==============
264 Now that the patch for bug 374050 has landed
265 (https://bugzilla.mozilla.org/show_bug.cgi?id=374050), it is possible to
266 create reftests that run in a paginated context.
267
268 The page size used is 5in wide and 3in tall (with the default half-inch
269 margins). This is to allow tests to have less text and to make the
270 entire test fit on the screen.
271
272 There is a layout/reftests/printing directory for printing reftests; however,
273 there is nothing special about this directory. You can put printing reftests
274 anywhere that is appropriate.
275
276 The suggested first lines for any printing test is
277 <!DOCTYPE html><html class="reftest-print">
278 <style>html{font-size:12pt}</style>
279
280 The reftest-print class on the root element triggers the reftest to
281 switch into page mode on load. Fixing the font size is suggested,
282 although not required, because the pages are a fixed size in inches.
283
284 The underlying layout support for this mode isn't really complete; it
285 doesn't use exactly the same codepath as real print preview/print. In
286 particular, scripting and frames are likely to cause problems; it is untested,
287 though. That said, it should be sufficient for testing layout issues related
288 to pagination.