| # How to Extend the Web Test Framework | 
 |  | 
 | The Web 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. | 
 |  | 
 | [TOC] | 
 |  | 
 | ## Background | 
 |  | 
 | Before you can start actually extending the framework, you should be familiar | 
 | with how to use it. See the [web tests documentation](testing/web_tests.md). | 
 |  | 
 | ## How to Extend the Framework | 
 |  | 
 | There are two parts to actually extending framework to test a piece of software. | 
 | The first part is extending certain files in: | 
 | [/third_party/blink/tools/blinkpy/web_tests/](/third_party/blink/tools/blinkpy/web_tests/) | 
 | The code in `blinkpy/web_tests` is the web test framework itself | 
 |  | 
 | The second part is creating a driver (program) to actually communicate the | 
 | web test framework. This part is significantly more tricky and dependent on | 
 | what exactly exactly is being tested. | 
 |  | 
 | ### Part 1 | 
 |  | 
 | This part isn’t too difficult. There are basically two classes that need to be | 
 | extended (ideally, just inherited from). These classes are: | 
 |  | 
 | *   `Driver`. Located in `web_tests/port/driver.py`. Each instance of this is | 
 |     the class that will actually an instance of the program that produces the | 
 |     test data (program in Part 2). | 
 | *   `Port`. Located in `web_tests/port/base.py`. This class is responsible | 
 |     creating drivers with the correct settings, giving access to certain OS | 
 |     functionality to access expected files, etc. | 
 |  | 
 | #### Extending Driver | 
 |  | 
 | As 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. | 
 |  | 
 | `run_test()` steps: | 
 |  | 
 | 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 doesn’t need to be restarted, and if it does, it | 
 |     will create a new instance of the test program. | 
 | 1.  It will then create a command to send the program | 
 |     *   This command generally consists of an html file path for the test | 
 |         program to navigate to. | 
 |     *   After creating it, the command is sent | 
 | 1.  After the command has been sent, it will then wait for data from the | 
 |     program. | 
 |     *   It will actually wait for 2 blocks of data. | 
 |         *   The first part being text or audio data. This part is required (the | 
 |             program will always send something, even an empty string) | 
 |         *   The second block is optional and is image data and an image hash | 
 |             (md5) this block of data is used for pixel tests | 
 | 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). | 
 |  | 
 | Luckily, `run_test()` most likely doesn’t 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 you’re probably going to need to override | 
 |  | 
 |     cmd_line | 
 |  | 
 | This function creates a set of command line arguments to run the test program, | 
 | so the function will almost certainly need to be overridden. | 
 |  | 
 | It 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. | 
 |  | 
 | The first item in the list of arguments should be the path to test program using | 
 | this function: | 
 |  | 
 |     self._port._path_to_driver() | 
 |  | 
 | This is an absolute path to the test program. This 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. | 
 |  | 
 |     start | 
 |  | 
 | If your program has any special startup needs, then this will be the place to | 
 | put it. | 
 |  | 
 | That’s mostly it. The Driver class has almost all the functionality you could | 
 | want, so there isn’t much to override here. If extra data needs to be read or | 
 | sent, extra data members should be added to `ContentBlock`. | 
 |  | 
 | #### Extending Port | 
 |  | 
 | This 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 isn’t really sufficient because it doesn’t account of platform specific | 
 | problems, therefore port itself shouldn’t 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 `web_tests_dir()` is overridden, 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. | 
 |  | 
 | Here are some of the functions that most likely need to be overridden. | 
 |  | 
 | *   `driver_class` | 
 |     *   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. | 
 |     *   It should be overridden to return the driver extension class created | 
 |         earlier. This function doesn’t return an instance on the driver, just | 
 |         the class itself. | 
 | *   `driver_name` | 
 |     *   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`. | 
 | *   `web_tests_dir` | 
 |     *   This tells the port where to look for all the and everything associated | 
 |         with them such as resources files. | 
 |     *   By default it returns the absolute path to the web tests directory. | 
 |     *   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. | 
 |  | 
 | The 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 aren’t on | 
 | by default always take place. For example, the web 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 don’t need to pass in so many switches. | 
 |  | 
 | As 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 don’t need to maintain the same function on each platform if they all do the | 
 | same thing. | 
 |  | 
 | For `Driver` and `Port` that’s basically it unless you need to make many odd | 
 | modifications. Lots of functionality is already there so you shouldn’t really | 
 | need to do much. | 
 |  | 
 | ### Part 2 | 
 |  | 
 | This 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. | 
 |  | 
 | #### Goals | 
 |  | 
 | Your goal for this part of the project is to create a program (or extend a | 
 | program) to interface with the web test framework. The web 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. | 
 |  | 
 | #### Structure | 
 |  | 
 | This is how your code should be laid out. | 
 |  | 
 | 1.  Initialization | 
 |     *   The creation of any directories or the launching of any programs should | 
 |         be done here and should be done once. | 
 |     *   After the program is initialized, “#READY\n” should be sent to progress | 
 |         the `run_test()` in the driver. | 
 | 1.  Infinite Loop (!) | 
 |     *   After initialization, your program needs to actually wait for input, | 
 |         then process that input to carry out the test. In the context of web | 
 |         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 | 
 |     *   Details: | 
 |         *   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: “/path’foo” is path to the test file, then foo is some setting | 
 |             that your program might need). | 
 |         *   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. | 
 |         *   Then you need to send any image data in the same manner as you did | 
 |             for step 2. | 
 |         *   Cleanup everything related to processing the input from step i, then | 
 |             go back to step 1. | 
 |             *   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. | 
 |  | 
 | That’s basically what the skeleton of your program should be. | 
 |  | 
 | ### Details | 
 |  | 
 | This is information about how to do some specific things, such as sending data | 
 | to the web test framework. | 
 |  | 
 | *   Content Blocks | 
 |     *   The web test framework accepts output from your program in blocks of | 
 |         data through stdout. Therefore, printing to stdout is really sending | 
 |         data to the web test framework. | 
 |     *   Structure of block | 
 |         *   “Header: Data\n” | 
 |             *   Header indicates what type of data will be sent through. A list | 
 |                 of valid headers is listed in `Driver.py`. | 
 |             *   Data is the data that you actually want to send. For pixel | 
 |                 tests, you want to send the actual PNG data here. | 
 |             *   The newline is needed to indicate the end of a header. | 
 |         * End of a content block | 
 |             *   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. | 
 |         * Multiple headers per block | 
 |             *   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. | 
 |             *   In this case this is how your output should look. | 
 |                 *   “Content-type: image/png\n” | 
 |                 *   “ActualHash: hashData\n” | 
 |                 *   “Content-Length: lengthOfPng\n” | 
 |                 *   “pngdata” | 
 |                     *   This part doesn’t need a header specifying that you are | 
 |                         sending png data, just send it | 
 |                 *   “#EOF\n” on both stdout and stderr | 
 |             *   To see the structure of the data required, look at the | 
 |                 `read_block` functions in Driver.py |