Stephen Warren | 10e5063 | 2016-01-15 11:15:24 -0700 | [diff] [blame^] | 1 | # U-Boot pytest suite |
| 2 | |
| 3 | ## Introduction |
| 4 | |
| 5 | This tool aims to test U-Boot by executing U-Boot shell commands using the |
| 6 | console interface. A single top-level script exists to execute or attach to the |
| 7 | U-Boot console, run the entire script of tests against it, and summarize the |
| 8 | results. Advantages of this approach are: |
| 9 | |
| 10 | - Testing is performed in the same way a user or script would interact with |
| 11 | U-Boot; there can be no disconnect. |
| 12 | - There is no need to write or embed test-related code into U-Boot itself. |
| 13 | It is asserted that writing test-related code in Python is simpler and more |
| 14 | flexible that writing it all in C. |
| 15 | - It is reasonably simple to interact with U-Boot in this way. |
| 16 | |
| 17 | ## Requirements |
| 18 | |
| 19 | The test suite is implemented using pytest. Interaction with the U-Boot console |
| 20 | involves executing some binary and interacting with its stdin/stdout. You will |
| 21 | need to implement various "hook" scripts that are called by the test suite at |
| 22 | the appropriate time. |
| 23 | |
| 24 | On Debian or Debian-like distributions, the following packages are required. |
| 25 | Similar package names should exist in other distributions. |
| 26 | |
| 27 | | Package | Version tested (Ubuntu 14.04) | |
| 28 | | -------------- | ----------------------------- | |
| 29 | | python | 2.7.5-5ubuntu3 | |
| 30 | | python-pytest | 2.5.1-1 | |
| 31 | |
| 32 | The test script supports either: |
| 33 | |
| 34 | - Executing a sandbox port of U-Boot on the local machine as a sub-process, |
| 35 | and interacting with it over stdin/stdout. |
| 36 | - Executing an external "hook" scripts to flash a U-Boot binary onto a |
| 37 | physical board, attach to the board's console stream, and reset the board. |
| 38 | Further details are described later. |
| 39 | |
| 40 | ### Using `virtualenv` to provide requirements |
| 41 | |
| 42 | Older distributions (e.g. Ubuntu 10.04) may not provide all the required |
| 43 | packages, or may provide versions that are too old to run the test suite. One |
| 44 | can use the Python `virtualenv` script to locally install more up-to-date |
| 45 | versions of the required packages without interfering with the OS installation. |
| 46 | For example: |
| 47 | |
| 48 | ```bash |
| 49 | $ cd /path/to/u-boot |
| 50 | $ sudo apt-get install python python-virtualenv |
| 51 | $ virtualenv venv |
| 52 | $ . ./venv/bin/activate |
| 53 | $ pip install pytest |
| 54 | ``` |
| 55 | |
| 56 | ## Testing sandbox |
| 57 | |
| 58 | To run the testsuite on the sandbox port (U-Boot built as a native user-space |
| 59 | application), simply execute: |
| 60 | |
| 61 | ``` |
| 62 | ./test/py/test.py --bd sandbox --build |
| 63 | ``` |
| 64 | |
| 65 | The `--bd` option tells the test suite which board type is being tested. This |
| 66 | lets the test suite know which features the board has, and hence exactly what |
| 67 | can be tested. |
| 68 | |
| 69 | The `--build` option tells U-Boot to compile U-Boot. Alternatively, you may |
| 70 | omit this option and build U-Boot yourself, in whatever way you choose, before |
| 71 | running the test script. |
| 72 | |
| 73 | The test script will attach to U-Boot, execute all valid tests for the board, |
| 74 | then print a summary of the test process. A complete log of the test session |
| 75 | will be written to `${build_dir}/test-log.html`. This is best viewed in a web |
| 76 | browser, but may be read directly as plain text, perhaps with the aid of the |
| 77 | `html2text` utility. |
| 78 | |
| 79 | ## Command-line options |
| 80 | |
| 81 | - `--board-type`, `--bd`, `-B` set the type of the board to be tested. For |
| 82 | example, `sandbox` or `seaboard`. |
| 83 | - `--board-identity`, `--id` set the identity of the board to be tested. |
| 84 | This allows differentiation between multiple instances of the same type of |
| 85 | physical board that are attached to the same host machine. This parameter is |
| 86 | not interpreted by the test script in any way, but rather is simply passed |
| 87 | to the hook scripts described below, and may be used in any site-specific |
| 88 | way deemed necessary. |
| 89 | - `--build` indicates that the test script should compile U-Boot itself |
| 90 | before running the tests. If using this option, make sure that any |
| 91 | environment variables required by the build process are already set, such as |
| 92 | `$CROSS_COMPILE`. |
| 93 | - `--build-dir` sets the directory containing the compiled U-Boot binaries. |
| 94 | If omitted, this is `${source_dir}/build-${board_type}`. |
| 95 | - `--result-dir` sets the directory to write results, such as log files, |
| 96 | into. If omitted, the build directory is used. |
| 97 | - `--persistent-data-dir` sets the directory used to store persistent test |
| 98 | data. This is test data that may be re-used across test runs, such as file- |
| 99 | system images. |
| 100 | |
| 101 | `pytest` also implements a number of its own command-line options. Please see |
| 102 | `pytest` documentation for complete details. Execute `py.test --version` for |
| 103 | a brief summary. Note that U-Boot's test.py script passes all command-line |
| 104 | arguments directly to `pytest` for processing. |
| 105 | |
| 106 | ## Testing real hardware |
| 107 | |
| 108 | The tools and techniques used to interact with real hardware will vary |
| 109 | radically between different host and target systems, and the whims of the user. |
| 110 | For this reason, the test suite does not attempt to directly interact with real |
| 111 | hardware in any way. Rather, it executes a standardized set of "hook" scripts |
| 112 | via `$PATH`. These scripts implement certain actions on behalf of the test |
| 113 | suite. This keeps the test suite simple and isolated from system variances |
| 114 | unrelated to U-Boot features. |
| 115 | |
| 116 | ### Hook scripts |
| 117 | |
| 118 | #### Environment variables |
| 119 | |
| 120 | The following environment variables are set when running hook scripts: |
| 121 | |
| 122 | - `UBOOT_BOARD_TYPE` the board type being tested. |
| 123 | - `UBOOT_BOARD_IDENTITY` the board identity being tested, or `na` if none was |
| 124 | specified. |
| 125 | - `UBOOT_SOURCE_DIR` the U-Boot source directory. |
| 126 | - `UBOOT_TEST_PY_DIR` the full path to `test/py/` in the source directory. |
| 127 | - `UBOOT_BUILD_DIR` the U-Boot build directory. |
| 128 | - `UBOOT_RESULT_DIR` the test result directory. |
| 129 | - `UBOOT_PERSISTENT_DATA_DIR` the test peristent data directory. |
| 130 | |
| 131 | #### `u-boot-test-console` |
| 132 | |
| 133 | This script provides access to the U-Boot console. The script's stdin/stdout |
| 134 | should be connected to the board's console. This process should continue to run |
| 135 | indefinitely, until killed. The test suite will run this script in parallel |
| 136 | with all other hooks. |
| 137 | |
| 138 | This script may be implemented e.g. by exec()ing `cu`, `kermit`, `conmux`, etc. |
| 139 | |
| 140 | If you are able to run U-Boot under a hardware simulator such as qemu, then |
| 141 | you would likely spawn that simulator from this script. However, note that |
| 142 | `u-boot-test-reset` may be called multiple times per test script run, and must |
| 143 | cause U-Boot to start execution from scratch each time. Hopefully your |
| 144 | simulator includes a virtual reset button! If not, you can launch the |
| 145 | simulator from `u-boot-test-reset` instead, while arranging for this console |
| 146 | process to always communicate with the current simulator instance. |
| 147 | |
| 148 | #### `u-boot-test-flash` |
| 149 | |
| 150 | Prior to running the test suite against a board, some arrangement must be made |
| 151 | so that the board executes the particular U-Boot binary to be tested. Often, |
| 152 | this involves writing the U-Boot binary to the board's flash ROM. The test |
| 153 | suite calls this hook script for that purpose. |
| 154 | |
| 155 | This script should perform the entire flashing process synchronously; the |
| 156 | script should only exit once flashing is complete, and a board reset will |
| 157 | cause the newly flashed U-Boot binary to be executed. |
| 158 | |
| 159 | It is conceivable that this script will do nothing. This might be useful in |
| 160 | the following cases: |
| 161 | |
| 162 | - Some other process has already written the desired U-Boot binary into the |
| 163 | board's flash prior to running the test suite. |
| 164 | - The board allows U-Boot to be downloaded directly into RAM, and executed |
| 165 | from there. Use of this feature will reduce wear on the board's flash, so |
| 166 | may be preferable if available, and if cold boot testing of U-Boot is not |
| 167 | required. If this feature is used, the `u-boot-test-reset` script should |
| 168 | peform this download, since the board could conceivably be reset multiple |
| 169 | times in a single test run. |
| 170 | |
| 171 | It is up to the user to determine if those situations exist, and to code this |
| 172 | hook script appropriately. |
| 173 | |
| 174 | This script will typically be implemented by calling out to some SoC- or |
| 175 | board-specific vendor flashing utility. |
| 176 | |
| 177 | #### `u-boot-test-reset` |
| 178 | |
| 179 | Whenever the test suite needs to reset the target board, this script is |
| 180 | executed. This is guaranteed to happen at least once, prior to executing the |
| 181 | first test function. If any test fails, the test infra-structure will execute |
| 182 | this script again to restore U-Boot to an operational state before running the |
| 183 | next test function. |
| 184 | |
| 185 | This script will likely be implemented by communicating with some form of |
| 186 | relay or electronic switch attached to the board's reset signal. |
| 187 | |
| 188 | The semantics of this script require that when it is executed, U-Boot will |
| 189 | start running from scratch. If the U-Boot binary to be tested has been written |
| 190 | to flash, pulsing the board's reset signal is likely all this script need do. |
| 191 | However, in some scenarios, this script may perform other actions. For |
| 192 | example, it may call out to some SoC- or board-specific vendor utility in order |
| 193 | to download the U-Boot binary directly into RAM and execute it. This would |
| 194 | avoid the need for `u-boot-test-flash` to actually write U-Boot to flash, thus |
| 195 | saving wear on the flash chip(s). |
| 196 | |
| 197 | ### Board-type-specific configuration |
| 198 | |
| 199 | Each board has a different configuration and behaviour. Many of these |
| 200 | differences can be automatically detected by parsing the `.config` file in the |
| 201 | build directory. However, some differences can't yet be handled automatically. |
| 202 | |
| 203 | For each board, an optional Python module `u_boot_board_${board_type}` may exist |
| 204 | to provide board-specific information to the test script. Any global value |
| 205 | defined in these modules is available for use by any test function. The data |
| 206 | contained in these scripts must be purely derived from U-Boot source code. |
| 207 | Hence, these configuration files are part of the U-Boot source tree too. |
| 208 | |
| 209 | ### Execution environment configuration |
| 210 | |
| 211 | Each user's hardware setup may enable testing different subsets of the features |
| 212 | implemented by a particular board's configuration of U-Boot. For example, a |
| 213 | U-Boot configuration may support USB device mode and USB Mass Storage, but this |
| 214 | can only be tested if a USB cable is connected between the board and the host |
| 215 | machine running the test script. |
| 216 | |
| 217 | For each board, optional Python modules `u_boot_boardenv_${board_type}` and |
| 218 | `u_boot_boardenv_${board_type}_${board_identity}` may exist to provide |
| 219 | board-specific and board-identity-specific information to the test script. Any |
| 220 | global value defined in these modules is available for use by any test |
| 221 | function. The data contained in these is specific to a particular user's |
| 222 | hardware configuration. Hence, these configuration files are not part of the |
| 223 | U-Boot source tree, and should be installed outside of the source tree. Users |
| 224 | should set `$PYTHONPATH` prior to running the test script to allow these |
| 225 | modules to be loaded. |
| 226 | |
| 227 | ### Board module parameter usage |
| 228 | |
| 229 | The test scripts rely on the following variables being defined by the board |
| 230 | module: |
| 231 | |
| 232 | - None at present. |
| 233 | |
| 234 | ### U-Boot `.config` feature usage |
| 235 | |
| 236 | The test scripts rely on various U-Boot `.config` features, either directly in |
| 237 | order to test those features, or indirectly in order to query information from |
| 238 | the running U-Boot instance in order to test other features. |
| 239 | |
| 240 | One example is that testing of the `md` command requires knowledge of a RAM |
| 241 | address to use for the test. This data is parsed from the output of the |
| 242 | `bdinfo` command, and hence relies on CONFIG_CMD_BDI being enabled. |
| 243 | |
| 244 | For a complete list of dependencies, please search the test scripts for |
| 245 | instances of: |
| 246 | |
| 247 | - `buildconfig.get(...` |
| 248 | - `@pytest.mark.buildconfigspec(...` |
| 249 | |
| 250 | ### Complete invocation example |
| 251 | |
| 252 | Assuming that you have installed the hook scripts into $HOME/ubtest/bin, and |
| 253 | any required environment configuration Python modules into $HOME/ubtest/py, |
| 254 | then you would likely invoke the test script as follows: |
| 255 | |
| 256 | If U-Boot has already been built: |
| 257 | |
| 258 | ```bash |
| 259 | PATH=$HOME/ubtest/bin:$PATH \ |
| 260 | PYTHONPATH=${HOME}/ubtest/py:${PYTHONPATH} \ |
| 261 | ./test/py/test.py --bd seaboard |
| 262 | ``` |
| 263 | |
| 264 | If you want the test script to compile U-Boot for you too, then you likely |
| 265 | need to set `$CROSS_COMPILE` to allow this, and invoke the test script as |
| 266 | follow: |
| 267 | |
| 268 | ```bash |
| 269 | CROSS_COMPILE=arm-none-eabi- \ |
| 270 | PATH=$HOME/ubtest/bin:$PATH \ |
| 271 | PYTHONPATH=${HOME}/ubtest/py:${PYTHONPATH} \ |
| 272 | ./test/py/test.py --bd seaboard --build |
| 273 | ``` |
| 274 | |
| 275 | ## Writing tests |
| 276 | |
| 277 | Please refer to the pytest documentation for details of writing pytest tests. |
| 278 | Details specific to the U-Boot test suite are described below. |
| 279 | |
| 280 | A test fixture named `u_boot_console` should be used by each test function. This |
| 281 | provides the means to interact with the U-Boot console, and retrieve board and |
| 282 | environment configuration information. |
| 283 | |
| 284 | The function `u_boot_console.run_command()` executes a shell command on the |
| 285 | U-Boot console, and returns all output from that command. This allows |
| 286 | validation or interpretation of the command output. This function validates |
| 287 | that certain strings are not seen on the U-Boot console. These include shell |
| 288 | error messages and the U-Boot sign-on message (in order to detect unexpected |
| 289 | board resets). See the source of `u_boot_console_base.py` for a complete list of |
| 290 | "bad" strings. Some test scenarios are expected to trigger these strings. Use |
| 291 | `u_boot_console.disable_check()` to temporarily disable checking for specific |
| 292 | strings. See `test_unknown_cmd.py` for an example. |
| 293 | |
| 294 | Board- and board-environment configuration values may be accessed as sub-fields |
| 295 | of the `u_boot_console.config` object, for example |
| 296 | `u_boot_console.config.ram_base`. |
| 297 | |
| 298 | Build configuration values (from `.config`) may be accessed via the dictionary |
| 299 | `u_boot_console.config.buildconfig`, with keys equal to the Kconfig variable |
| 300 | names. |