blob: 3618d66b7daa2c79fdd10e371c2acd1adfe1c20a [file] [log] [blame] [view]
andybons3322f762015-08-24 21:37:091# Extending the Layout Framework
2
3# Introduction
4The Layout Test Framework that Blink uses is a regression testing tool that is multi-platform and it has a large amount of tools that help test varying types of regression, such as pixel diffs, text diffs, etc. The framework is mainly used by Blink, however it was made to be extensible so that other projects can use it test different parts of chrome (such as Print Preview). This is a guide to help people who want to actually the framework to test whatever they want.
5
6# Background
7Before you can start actually extending the framework, you should be familiar with how to use it. This wiki is basically all you need to learn how to use it
8http://www.chromium.org/developers/testing/webkit-layout-tests
9
10# How to Extend the Framework
11There are two parts to actually extending framework to test a piece of software.
12The first part is extending certain files in:
13src/third\_party/Webkit/Tools/Scripts/webkitpy/layout\_tests/
14The code in webkitpy/layout\_tests is the layout test framework itself
15
16The second part is creating a driver (program) to actually communicate the layout test framework. This part is significantly more tricky and dependant on what exactly exactly is being tested.
17
18## Part 1:
19This part isnt too difficult. There are basically two classes that need to be extended (ideally, just inherited from). These classes are:
20Driver
21Located in layout\_tests/port/driver.py
22Each instance of this is the class that will actually an instance of the program that produces the test data (program in Part 2).
23Port
24Located in layout\_tests/port/base.py
25This class is responsible creating drivers with the correct settings, giving access to certain OS functionality to access expected files, etc.
26
27
28
29
30
31### Extending Driver:
32As said, Driver launches the program from Part 2. Said program will communicate with the driver class to receive instructions and send back data. All of the work for driver gets done in Driver.run\_test. Everything else is a helper or initialization function.
33run\_test() steps:
34 1. On the very first call of this function, it will actually run the test program. On every subsequent call to this function, at the beginning it will verify that the process doesnt need to be restarted, and if it does, it will create a new instance of the test program.
35 1. It will then create a command to send the program
36 * This command generally consists of an html file path for the test program to navigate to.
37 * After creating it, the command is sent
38 1. After the command has been sent, it will then wait for data from the program.
39 * It will actually wait for 2 blocks of data.
40 * The first part being text or audio data. This part is required (the program will always send something, even an empty string)
41 * The second block is optional and is image data and an image hash (md5) this block of data is used for pixel tests
42 1. After it has received all the data, it will proceed to check if the program has timed out or crashed, and if so fail this instance of the test (it can be retried later if need be).
43
44Luckily, run\_test() most likely doesnt need to be overridden unless extra blocks of data need to be sent to/read from the test program. However, you do need to know how it works because it will influence what functions you need to override. Here are the ones youre probably going to need to override
45cmd\_line
46This function creates a set of command line arguments to run the test program, so the function will almost certainly need to be overridden.
47It creates the command line to run the program. Driver uses subprocess.popen to create the process, which takes the name of the test program and any options it might need.
48The first item in the list of arguments should be the path to test program using this function:
49self._port._path\_to\_driver()
50This is an absolute path to the test program.
51This is the bare minimum you need to get the driver to launch the test program, however if you have options you need to append, just append them to the list.
52start
53If your program has any special startup needs, then this will be the place to put it.
54
55Thats mostly it. The Driver class has almost all the functionality you could want, so there isnt much to override here. If extra data needs to be read or sent, extra data members should be added to ContentBlock.
56
57### Extending Port:
58This class is responsible for providing functionality such as where to look for tests, where to store test results, what driver to run, what timeout to use, what kind of files can be run, etc. It provides a lot of functionality, however it isnt really sufficient because it doesnt account of platform specific problems, therefore port itself shouldnt be extend. Instead LinuxPort, WinPort, and MacPort (and maybe the android port class) should be extended as they provide platform specific overrides/extensions that implement most of the important functionality. While there are many functions in Port, overriding one function will affect most of the other ones to get the desired behavior. For example, if layout\_tests\_dir() is overriden, not only will the code look for tests in that directory, but it will find the correct TestExpectations file, the platform specific expected files, etc.
59
60Here are some of the functions that most likely need to be overridden.
61 * driver\_class
62 * This should be overridden to allow the testing program to actually run. By default the code will run content\_shell, which might or might not be what you want.
63 * It should be overridden to return the driver extension class created earlier.This function doesnt return an instance on the driver, just the class itself.
64 * driver\_name
65 * This should return the name of the program test p. By default it returns content\_shell’, but you want to have it return the program you want to run, such as chrome or browser\_tests.
66 * layout\_tests\_dir
67 * This tells the port where to look for all the and everything associated with them such as resources files.
68 * By default it returns absolute path to the webkit tests.
69 * If you are planning on running something in the chromium src/ directory, there are helper functions to allow you to return a path relative to the base of the chromium src directory.
70
71The rest of the functions can definitely be overridden for your projects specific needs, however these are the bare minimum needed to get it running. There are also functions you can override to make certain actions that arent on by default always take place. For example, the layout test framework always checks for system dependencies unless you pass in a switch. If you want them disabled for your project, just override check\_sys\_deps to always return OK. This way you dont need to pass in so many switches.
72
73As said earlier, you should override LinuxPort, MacPort, and/or WinPort. You should create a class that implements the platform independent overrides (such as driver\_class) and then create a separate class for each platform specific port of your program that inherits from the class with the independent overrides and the platform port you want. For example, you might want to have a different timeout for your project, but on Windows the timeout needs to be vastly different than the others. In this case you can just create a default override that every class uses except your Windows port. In that port you can just override the function again to provide the specific timeout you need. This way you dont need to maintain the same function on each platform if they all do the same thing.
74
75For Driver and Port thats basically it unless you need to make many odd modifications. Lots of functionality is already there so you shouldnt really need to do much.
76
77## Part 2:
78This is the part where you create the program that your driver class launches. This part is very application dependent, so it will not be a guide on how implement certain features, just what should be implemented and the order in which events should occur and some guidelines about what to do/not do. For a good example of how to implement your test program, look at MockDRT in mock\_drt.pyin the same directory as base.py and driver.py. It goes through all the steps described below and is very clear and concise. It is written in python, but your driver can be anything that can be run by subprocess.popen and has stdout, stdin, stderr.
79
80### Goals
81Your goal for this part of the project is to create a program (or extend a program) to interface with the layout test framework. The layout test framework will communicate with this program to tell it what to do and it will accept data from this program to perform the regression testing or create new base line files.
82
83### Structure
84This is how your code should be laid out.
85 1. Initialization
86 * The creation of any directories or the launching of any programs should be done here and should be done once.
87 * After the program is initialized, “#READY\n should be sent to progress the run\_test() in the driver.
88 1. Infinite Loop (!)
89 * After initialization, your program needs to actually wait for input, then process that input to carry out the test. In the context of layout testing, the content\_shell needs to wait for an html file to navigate to, render it, then convert that rendering to a PNG. It does this constantly, until a signal/message is sent to indicate that no more tests should be processed
90 * Details:
91 * The first thing you need is your test file path and any other additional information about the test that is required (this is sent during the write() step in run\_tests() is driver.py. This information will be passed through stdin and is just one large string, with each part of the command being split with apostrophes (ex: “/pathfoo is path to the test file, then foo is some setting that your program might need).
92 * After that, your program should act on this input, how it does this is dependent on your program, however in content\_shell, this would be the part where it navigates to the test file, then renders it. After the program acts on the input, it needs to send some text to the driver code to indicate that it has acted on the input. This text will indicate something that you want to test. For example, if you want to make sure you program always prints foo you should send it to the driver. If the program every prints bar (or anything else), that would indicate a failure and the test will fail.
93 * Then you need to send any image data in the same manner as you did for step ii.
94 * Cleanup everything related to processing the input from step i, then go back to step i.
95 * This is where the infinite loop part comes in, your program should constantly accept input from the driver until the driver indicates that there are no more tests to run. The driver does this by closing stdin, which will cause std::cin to go into a bad state. However, you can also modify the driver to send a special string such as QUIT to exit the while loop.
96
97Thats basically what the skeleton of your program should be.
98
99### Details:
100This is information about how to do some specific things, such as sending data to the layout test framework.
101 * Content Blocks
102 * The layout test framework accepts output from your program in blocks of data through stdout. Therefore, printing to stdout is really sending data to the layout test framework.
103 * Structure of block
104 * Header: Data\n
105 * Header indicates what type of data will be sent through. A list of valid headers is listed in Driver.py.
106 * Data is the data that you actually want to send. For pixel tests, you want to send the actual PNG data here.
107 * The newline is needed to indicate the end of a header.
108 * End of a content block
109 * To indicate the end of a a content block and cause the driver to progress, you need to write “#EOF\n to stdout (mandatory) and to stderr for certain types of content, such as image data.
110 * Multiple headers per block
111 * Some blocks require different sets of data. For PNGs, not only is the PNG needed, but so is a hash of the bitmap used to create the PNG.
112 * In this case this is how your output should look.
113 * Content-type: image/png\n
114 * ActualHash: hashData\n
115 * Content-Length: lengthOfPng\n
116 * pngdata
117 * This part doesnt need a header specifying that you are sending png data, just send it
118 * “#EOF\n on both stdout and stderr
119 * To see the structure of the data required, look at the read\_block functions in Driver.py
120
121
122
123
124
125