Writing good test cases ¶
General tips ¶
A good test case should have the following properties:
- portable
- fast
- understandable & clear
- deterministic
Portable ¶
A test should work in DumpRenderTree (of course!) and in a WebKit-base browser. This means that anything that is specific to DumpRenderTree should not make the test fail if it is not available.
Bad test:
<script> layoutTestController.dumpAsText(); </script>
This will throw an exception in any WebKit-based browser.
Good test:
<script> if (window.layoutTestController) layoutTestController.dumpAsText() </script>
Note: You get more points if your test works in ANY browsers.
Fast ¶
WebKit has several thousands layout tests that are run in parallel. If your layout test takes a lot time, it will slow down everyone's testing. Also DumpRenderTree has a default timer of several seconds before it considers that a test timed out. This is true even if you are using waitUntilDone / notifyDone.
Here are some advices to avoid having a slow test case:
- Do not make unnecessary use of window.setTimeout and similar functions which create a lower bound on the time it takes the test to complete. For separating events in time, you can use eventSender.leapForward; for waiting on sub-resources, you can use load events (e.g. iframe.onload = doNextStep;)
- Do not make unnecessary use of external resources: use inline JavaScript and style elements instead of links to external stylesheets. You can also use data: URLs sometimes for things like frames' src attribute. The exceptions are stylesheets and JavaScript libraries that are shared by multiple tests, and cases that test the loading of external resources. (There are various data: url generators on the net.)
Understandable & clear ¶
A good reading on how to write awesome test cases is the CSS2.1 Test Case Authoring Guidelines. Especially read the section: 4. Writing ideal tests
Now in the context of WebKit, a test should at least indicate:
- which bug it belongs to. That means the bug number or the bugzilla URL (the latter is better as you can paste it in a browser) but also the bug title to see what is tested.
- what is the condition for success or failure.
The rule to understand what you need to include is to position yourself as someone who has to investigate why your test is failing. He need to kickly understand if the new baseline is a progression, a regression or just a small change unrelated to what is tested.
Bad test:
<style> .block { color: red; } </style> <p class="block"></p>
This is bad because it misses all the WebKit information but also because it uses red when it means passing. Red should be reserved for failures.
Note: This test is badly formatted and if this is not something your test needs, it is better to avoid it.
Better test:
<!DOCTYPE html> <html> <head> <style> .block { color: green; } </style> </head> <body> <p> Bug <a href="http://webkit.org/b/XXXX">XXXX</a>: WebKit does not apply class-selector</p> <p> For this test to pass, you should see a green rectangle below. </p> <p class="block"></p> </body> </html>
This test is a huge improvement over the previous one but we can actually go one step further! (see the next section about writing pixel tests on that)
One last comment (FIXME: move it to a better section):
- Tests should not access the Internet. Avoid http: URLs in src and href attributes, in CSS properties and in XMLHttpRequest. Testing WebKit's network layer should be done using the HTTP test facility, to be described below.
Pixel test tips ¶
Do you really need a pixel test? ¶
That should be your first question. Pixel tests are a burden on every ports as any small change in the engine could lead to your test needing a new pixel result. That does not mean that pixel tests are bad, they are invaluable for catching visual regressions but there may be a way to avoid dumping the pixel while checking the behavior you need!
Let's take the example from the previous section and make it better.
Even better test:
<!DOCTYPE html> <html> <head> <style> .block { color: green; } </style> <script> if (window.layoutTestController) layoutTestController.dumpAsText(); function log(message) { var console = document.getElementById('console'); console.appendChild(document.createTextNode(message)); console.appendChild(document.createElement('br')); } function checkBlockColor() { var block = document.getElementsByClassName('block')[0]; var color = document.defaultView.getComputedStyle(block, null).getPropertyValue('color'); if (color === 'rgb(0, 255, 0)') log('PASSED'); else log('FAILED: color was not green but ' + color); } window.addEventListener('load', checkBlockColor, true); </script> </head> <body> <p> Bug <a href="http://webkit.org/b/XXXX">XXXX</a>: WebKit does not apply class-selector</p> <p> For this test to pass, you should see a green rectangle below and / or you should see PASS below. </p> <p id="console"></p> <p class="block"></p> </html>
As you can see, we did not need the pixels to get the information. The idea is that in this case, we were testing CSS and not the painting part of WebKit so querying the property directly would give us the result we need without having to dump the pixels.
How to write portable pixel tests ¶
The problem with a pixel dump is that it can be highly depend on the platform on which you run it. To be avoided to have more portable tests cases are the following:
- fonts
- native controls
Fonts ¶
Fonts are very dependent on the platform and the source of a lot of differences in our tests' output. That's because not all platform share the same fonts and the fallback mechanism is pretty much defined per machine as it depends on the OS and the available fonts!
The golden rule for writing pixel tests is that if you can avoid text, you should.
This seems against the previous section about writing understandable test cases and it is. However there are ways to put the information you need while keeping the portability of the no-text mantra:
- put your information as comments in the HTML code (preferably somewhere prominent like the beginning)
- put your text outside of the dumped area: by default DumpRenderTree dumps a viewport of 600 * 800 pixels (FIXME: not 100% sure about the exact size). This means that anything outside this range would not be dumped in DRT but will appear in a browser opening the test if the resolution is sufficient.
- add a 0 opacity to your text.
Important note: For the 2 last points, the text will still be in the text render tree dump! You can however disable the text dump if you don't need it to achieve maximum portability, see below.
If you test really needs to use some fonts, make sure you use the Ahem font as it is a very portable font. Make sure to specify the font-height too not to depend on the platform for that (FIXME: not sure if the font-size is really needed but better safe than sorry).
Native controls ¶
Native controls cover anything that is painted by the OS. In WebKit, this means buttons, scroll bars, media players, ...
You can use the previous tips for text here to hide your button if you don't need to show it.
For scroll bars, you just have to use the following rule in your CSS to disable them:
overflow: hidden;
How to disable render tree dumps but keep the pixels ¶
If you want to just dump the pixels but don't care about DRT's text dump. You can use the following trick:
if (window.layoutTestController) layoutTestController.dumpAsText(true);
This is not a typo. It is an extremely confusing syntax but this disables the text dump (you basically dump an empty text file).
How to disable the pixel dumps but keep the render tree dump ¶
There is currently no way for you to do that.
Writing JavaScript-based DOM-only Test Cases ¶
When writing test cases that only test the DOM it is still preferred to use an .html file since it only requires one test file instead of two.
When writing these tests it is often useful to include LayoutTests/fast/js/resources/js-test-pre.js and LayoutTests/fast/js/resources/js-test-post.js.
To see what sort of special js functions are exposed to js-only tests, see LayoutTests/fast/js/resources/js-test-pre.js
DumpRenderTree JavaScript Environment ¶
DumpRenderTree exposes a number of additional JavaScript objects in the testing environment.
These can be used to perform additional debugging-related tasks.
window.layoutTestController ¶
dumpAsText() ¶
Call this method to make your test output plain text instead of a render tree. This is useful if your test prints messages rather than testing fancy layout. For an example of how to print to a console in a test, check out LayoutTests/fast/dom/Element/attribute-uppercase.html.
waitUntilDone() and notifyDone() ¶
By default, DumpRenderTree dumps each test file immediately after the document has loaded and the load event handlers have executed. If your test needs to do further processing after loading -- for example, waiting for a timer to fire -- call layoutTestController.waitUntilDone() to tell DumpRenderTree to delay its dump, and then call notifyDone when your results are ready.
overridePreference(key, value) ¶
Changes a preference with name key (for example "WebKitEnableCaretBrowsing") with value (for example "1") for the duration of the test. The preference is reset when the test ends.
setCanOpenWindows() ¶
If your layout test needs to open pop-up windows, call this method before it does.
clearBackForwardList() ¶
Clears the back/forward list (i.e. history).
window.eventSender ¶
mouseMoveTo(x, y) ¶
Used to change the current mouse position.
leapForward(ms) ¶
Jumps the current event time forward by a specified number of miliseconds.
mouseDown([buttonNumber [, modifiers]]) ¶
Sends a mouseDown event to the WebView at the current mouse position.
buttonNumber; 0:left button, 1:middle button, 2:right button.
modifiers; See keyDown().
mouseUp([buttonNumber [, modifiers]]) ¶
Sends a mouseUp event to the WebView at the current mouse position.
keyDown(character [, modifiers]]) ¶
Sends a keyDown event to the WebView.
modifiers: An array of strings. A string should be a modifier key name in the followings:
- "ctrlKey"
- "shiftKey"
- "altKey"
- "metaKey" (Command key in Mac)
- "addSelectionKey" (equivalent to metaKey in Mac, ctrlKey in Windows)
- "rangeSelectionKey" (equivalent to shiftKey in Mac and Windows)
enableDOMUIEventLogging ¶
fireKeyboardEventsToElement ¶
setDragMode ¶
window.GCController ¶
collect() ¶
Performs JavaScript garbage collection.
collectOnAlternateThread(wait) ¶
Performs JavaScript garbage collection on an alternate thread. The wait argument specifies whether script execution waits for garbage collection to finish.
window.textInputController ¶
Needs to be filled in.
insertText ¶
doCommand ¶
setMarkedText ¶
substringFromRange ¶
attributedSubstringFromRange ¶
firstRectForCharacterRange ¶
characterIndexForPoint ¶
makeAttributedString ¶
window.appleScriptController ¶
doJavaScript() ¶
Needs to be filled in.
window.navigationController ¶
The navigation controller is currently broken. http://bugs.webkit.org/show_bug.cgi?id=11042
evalAfterBackForwardNavigation(script [, destination]) ¶
To test a bug having to do with the loader or the back/forward cache, call this method to run a script after executing a back/forward navigation. The first argument is the script to run, and the second argument is the page to load during the navigation. The second argument is optional. It defaults to about:blank.
window.internals ¶
createShadowContentElement(Document) ¶
Creates a <content> element for use in shadow DOM. These elements can not be created from JavaScript, hence this constructor in the test harness.
elementRenderTreeAsText(Element) ¶
Gets and returns the element’s renderer’s description of its tree as a String.
ensureShadowRoot(Element) ¶
Given a host element, returns its shadow root node. If the element doesn’t have a shadow root one is created and attached to the element. If you want to just retrieve a shadow root without creating one, use shadowRoot. ensureShadowRoot only inspects DOM shadows; not SVG shadows.
isPreloaded(Document, String url) ¶
Gets whether the specified document’s cached resource loader has the specified URL preloaded.
removeShadowRoot(Element) ¶
Given a host element, removes its shadow root if it has one. removeShadowRoot only inspects DOM shadows; not SVG shadows.
shadowPseudoId(Element) ¶
Gets the specified element’s CSS pseudo-id for styling when in shadow DOM.
shadowRoot(Element) ¶
Given a host element gets its shadow root, if it has one; otherwise shadowRoot returns null.
lastSpellCheckRequestSequence(Document) ¶
Gets the last sequence number used in SpellChecker.
lastSpellCheckProcessedSequence(Document) ¶
Gets the last sequence number already processed in SpellChecker.
Writing tests which require network access ¶
run-webkit-tests (the script which runs DumpRenderTree) also launches a local Apache daemon (httpd) to allow real local-only network based testing (for incremental loads, XMLHttpRequest, etc.) ap needs to document how best to use this.
Debugging DumpRenderTree ¶
Debugging DumpRenderTree on Windows
Debugging chromium DumpRenderTree