1*6236dae4SAndroid Build Coastguard Worker<!-- 2*6236dae4SAndroid Build Coastguard WorkerCopyright (C) Daniel Stenberg, <[email protected]>, et al. 3*6236dae4SAndroid Build Coastguard Worker 4*6236dae4SAndroid Build Coastguard WorkerSPDX-License-Identifier: curl 5*6236dae4SAndroid Build Coastguard Worker--> 6*6236dae4SAndroid Build Coastguard Worker 7*6236dae4SAndroid Build Coastguard Worker# The curl HTTP Test Suite 8*6236dae4SAndroid Build Coastguard Worker 9*6236dae4SAndroid Build Coastguard WorkerThis is an additional test suite using a combination of Apache httpd and nghttpx servers to perform various tests beyond the capabilities of the standard curl test suite. 10*6236dae4SAndroid Build Coastguard Worker 11*6236dae4SAndroid Build Coastguard Worker# Usage 12*6236dae4SAndroid Build Coastguard Worker 13*6236dae4SAndroid Build Coastguard WorkerThe test cases and necessary files are in `tests/http`. You can invoke `pytest` from there or from the top level curl checkout and it will find all tests. 14*6236dae4SAndroid Build Coastguard Worker 15*6236dae4SAndroid Build Coastguard Worker``` 16*6236dae4SAndroid Build Coastguard Workercurl> pytest test/http 17*6236dae4SAndroid Build Coastguard Workerplatform darwin -- Python 3.9.15, pytest-6.2.0, py-1.10.0, pluggy-0.13.1 18*6236dae4SAndroid Build Coastguard Workerrootdir: /Users/sei/projects/curl 19*6236dae4SAndroid Build Coastguard Workercollected 5 items 20*6236dae4SAndroid Build Coastguard Worker 21*6236dae4SAndroid Build Coastguard Workertests/http/test_01_basic.py ..... 22*6236dae4SAndroid Build Coastguard Worker``` 23*6236dae4SAndroid Build Coastguard Worker 24*6236dae4SAndroid Build Coastguard WorkerPytest takes arguments. `-v` increases its verbosity and can be used several times. `-k <expr>` can be used to run only matching test cases. The `expr` can be something resembling a python test or just a string that needs to match test cases in their names. 25*6236dae4SAndroid Build Coastguard Worker 26*6236dae4SAndroid Build Coastguard Worker``` 27*6236dae4SAndroid Build Coastguard Workercurl/tests/http> pytest -vv -k test_01_02 28*6236dae4SAndroid Build Coastguard Worker``` 29*6236dae4SAndroid Build Coastguard Worker 30*6236dae4SAndroid Build Coastguard Workerruns all test cases that have `test_01_02` in their name. This does not have to be the start of the name. 31*6236dae4SAndroid Build Coastguard Worker 32*6236dae4SAndroid Build Coastguard WorkerDepending on your setup, some test cases may be skipped and appear as `s` in the output. If you run pytest verbose, it will also give you the reason for skipping. 33*6236dae4SAndroid Build Coastguard Worker 34*6236dae4SAndroid Build Coastguard Worker# Prerequisites 35*6236dae4SAndroid Build Coastguard Worker 36*6236dae4SAndroid Build Coastguard WorkerYou will need: 37*6236dae4SAndroid Build Coastguard Worker 38*6236dae4SAndroid Build Coastguard Worker1. a recent Python, the `cryptography` module and, of course, `pytest` 39*6236dae4SAndroid Build Coastguard Worker2. an apache httpd development version. On Debian/Ubuntu, the package `apache2-dev` has this. 40*6236dae4SAndroid Build Coastguard Worker3. a local `curl` project build 41*6236dae4SAndroid Build Coastguard Worker3. optionally, a `nghttpx` with HTTP/3 enabled or h3 test cases will be skipped. 42*6236dae4SAndroid Build Coastguard Worker 43*6236dae4SAndroid Build Coastguard Worker### Configuration 44*6236dae4SAndroid Build Coastguard Worker 45*6236dae4SAndroid Build Coastguard WorkerVia curl's `configure` script you may specify: 46*6236dae4SAndroid Build Coastguard Worker 47*6236dae4SAndroid Build Coastguard Worker * `--with-test-nghttpx=<path-of-nghttpx>` if you have nghttpx to use somewhere outside your `$PATH`. 48*6236dae4SAndroid Build Coastguard Worker * `--with-test-httpd=<httpd-install-path>` if you have an Apache httpd installed somewhere else. On Debian/Ubuntu it will otherwise look into `/usr/bin` and `/usr/sbin` to find those. 49*6236dae4SAndroid Build Coastguard Worker * `--with-test-caddy=<caddy-install-path>` if you have a Caddy web server installed somewhere else. 50*6236dae4SAndroid Build Coastguard Worker * `--with-test-vsftpd=<vsftpd-install-path>` if you have a vsftpd ftp server installed somewhere else. 51*6236dae4SAndroid Build Coastguard Worker 52*6236dae4SAndroid Build Coastguard Worker## Usage Tips 53*6236dae4SAndroid Build Coastguard Worker 54*6236dae4SAndroid Build Coastguard WorkerSeveral test cases are parameterized, for example with the HTTP version to use. If you want to run a test with a particular protocol only, use a command line like: 55*6236dae4SAndroid Build Coastguard Worker 56*6236dae4SAndroid Build Coastguard Worker``` 57*6236dae4SAndroid Build Coastguard Workercurl/tests/http> pytest -k "test_02_06 and h2" 58*6236dae4SAndroid Build Coastguard Worker``` 59*6236dae4SAndroid Build Coastguard Worker 60*6236dae4SAndroid Build Coastguard WorkerTest cases can be repeated, with the `pytest-repeat` module (`pip install pytest-repeat`). Like in: 61*6236dae4SAndroid Build Coastguard Worker 62*6236dae4SAndroid Build Coastguard Worker``` 63*6236dae4SAndroid Build Coastguard Workercurl/tests/http> pytest -k "test_02_06 and h2" --count=100 64*6236dae4SAndroid Build Coastguard Worker``` 65*6236dae4SAndroid Build Coastguard Worker 66*6236dae4SAndroid Build Coastguard Workerwhich then runs this test case a hundred times. In case of flaky tests, you can make pytest stop on the first one with: 67*6236dae4SAndroid Build Coastguard Worker 68*6236dae4SAndroid Build Coastguard Worker``` 69*6236dae4SAndroid Build Coastguard Workercurl/tests/http> pytest -k "test_02_06 and h2" --count=100 --maxfail=1 70*6236dae4SAndroid Build Coastguard Worker``` 71*6236dae4SAndroid Build Coastguard Worker 72*6236dae4SAndroid Build Coastguard Workerwhich allow you to inspect output and log files for the failed run. Speaking of log files, the verbosity of pytest is also used to collect curl trace output. If you specify `-v` three times, the `curl` command is started with `--trace`: 73*6236dae4SAndroid Build Coastguard Worker 74*6236dae4SAndroid Build Coastguard Worker``` 75*6236dae4SAndroid Build Coastguard Workercurl/tests/http> pytest -vvv -k "test_02_06 and h2" --count=100 --maxfail=1 76*6236dae4SAndroid Build Coastguard Worker``` 77*6236dae4SAndroid Build Coastguard Worker 78*6236dae4SAndroid Build Coastguard Workerall of curl's output and trace file are found in `tests/http/gen/curl`. 79*6236dae4SAndroid Build Coastguard Worker 80*6236dae4SAndroid Build Coastguard Worker## Writing Tests 81*6236dae4SAndroid Build Coastguard Worker 82*6236dae4SAndroid Build Coastguard WorkerThere is a lot of [`pytest` documentation](https://docs.pytest.org/) with examples. No use in repeating that here. Assuming you are somewhat familiar with it, it is useful how *this* general test suite is setup. Especially if you want to add test cases. 83*6236dae4SAndroid Build Coastguard Worker 84*6236dae4SAndroid Build Coastguard Worker### Servers 85*6236dae4SAndroid Build Coastguard Worker 86*6236dae4SAndroid Build Coastguard WorkerIn `conftest.py` 3 "fixtures" are defined that are used by all test cases: 87*6236dae4SAndroid Build Coastguard Worker 88*6236dae4SAndroid Build Coastguard Worker1. `env`: the test environment. It is an instance of class `testenv/env.py:Env`. It holds all information about paths, availability of features (HTTP/3), port numbers to use, domains and SSL certificates for those. 89*6236dae4SAndroid Build Coastguard Worker2. `httpd`: the Apache httpd instance, configured and started, then stopped at the end of the test suite. It has sites configured for the domains from `env`. It also loads a local module `mod_curltest?` and makes it available in certain locations. (more on mod_curltest below). 90*6236dae4SAndroid Build Coastguard Worker3. `nghttpx`: an instance of nghttpx that provides HTTP/3 support. `nghttpx` proxies those requests to the `httpd` server. In a direct mapping, so you may access all the resources under the same path as with HTTP/2. Only the port number used for HTTP/3 requests will be different. 91*6236dae4SAndroid Build Coastguard Worker 92*6236dae4SAndroid Build Coastguard Worker`pytest` manages these fixture so that they are created once and terminated before exit. This means you can `Ctrl-C` a running pytest and the server will shutdown. Only when you brutally chop its head off, might there be servers left 93*6236dae4SAndroid Build Coastguard Workerbehind. 94*6236dae4SAndroid Build Coastguard Worker 95*6236dae4SAndroid Build Coastguard Worker### Test Cases 96*6236dae4SAndroid Build Coastguard Worker 97*6236dae4SAndroid Build Coastguard WorkerTests making use of these fixtures have them in their parameter list. This tells pytest that a particular test needs them, so it has to create them. Since one can invoke pytest for just a single test, it is important that a test references the ones it needs. 98*6236dae4SAndroid Build Coastguard Worker 99*6236dae4SAndroid Build Coastguard WorkerAll test cases start with `test_` in their name. We use a double number scheme to group them. This makes it ease to run only specific tests and also give a short mnemonic to communicate trouble with others in the project. Otherwise you are free to name test cases as you think fitting. 100*6236dae4SAndroid Build Coastguard Worker 101*6236dae4SAndroid Build Coastguard WorkerTests are grouped thematically in a file with a single Python test class. This is convenient if you need a special "fixture" for several tests. "fixtures" can have "class" scope. 102*6236dae4SAndroid Build Coastguard Worker 103*6236dae4SAndroid Build Coastguard WorkerThere is a curl helper class that knows how to invoke curl and interpret its output. Among other things, it does add the local CA to the command line, so that SSL connections to the test servers are verified. Nothing prevents anyone from running curl directly, for specific uses not covered by the `CurlClient` class. 104*6236dae4SAndroid Build Coastguard Worker 105*6236dae4SAndroid Build Coastguard Worker### mod_curltest 106*6236dae4SAndroid Build Coastguard Worker 107*6236dae4SAndroid Build Coastguard WorkerThe module source code is found in `testenv/mod_curltest`. It is compiled using the `apxs` command, commonly provided via the `apache2-dev` package. Compilation is quick and done once at the start of a test run. 108*6236dae4SAndroid Build Coastguard Worker 109*6236dae4SAndroid Build Coastguard WorkerThe module adds 2 "handlers" to the Apache server (right now). Handler are pieces of code that receive HTTP requests and generate the response. Those handlers are: 110*6236dae4SAndroid Build Coastguard Worker 111*6236dae4SAndroid Build Coastguard Worker* `curltest-echo`: hooked up on the path `/curltest/echo`. This one echoes a request and copies all data from the request body to the response body. Useful for simulating upload and checking that the data arrived as intended. 112*6236dae4SAndroid Build Coastguard Worker* `curltest-tweak`: hooked up on the path `/curltest/tweak`. This handler is more of a Swiss army knife. It interprets parameters from the URL query string to drive its behavior. 113*6236dae4SAndroid Build Coastguard Worker * `status=nnn`: generate a response with HTTP status code `nnn`. 114*6236dae4SAndroid Build Coastguard Worker * `chunks=n`: generate `n` chunks of data in the response body, defaults to 3. 115*6236dae4SAndroid Build Coastguard Worker * `chunk_size=nnn`: each chunk should contain `nnn` bytes of data. Maximum is 16KB right now. 116*6236dae4SAndroid Build Coastguard Worker * `chunkd_delay=duration`: wait `duration` time between writing chunks 117*6236dae4SAndroid Build Coastguard Worker * `delay=duration`: wait `duration` time to send the response headers 118*6236dae4SAndroid Build Coastguard Worker * `body_error=(timeout|reset)`: produce an error after the first chunk in the response body 119*6236dae4SAndroid Build Coastguard Worker * `id=str`: add `str` in the response header `request-id` 120*6236dae4SAndroid Build Coastguard Worker 121*6236dae4SAndroid Build Coastguard Worker`duration` values are integers, optionally followed by a unit. Units are: 122*6236dae4SAndroid Build Coastguard Worker 123*6236dae4SAndroid Build Coastguard Worker * `d`: days (probably not useful here) 124*6236dae4SAndroid Build Coastguard Worker * `h`: hours 125*6236dae4SAndroid Build Coastguard Worker * `mi`: minutes 126*6236dae4SAndroid Build Coastguard Worker * `s`: seconds (the default) 127*6236dae4SAndroid Build Coastguard Worker * `ms`: milliseconds 128*6236dae4SAndroid Build Coastguard Worker 129*6236dae4SAndroid Build Coastguard WorkerAs you can see, `mod_curltest`'s tweak handler allow to simulate many kinds of responses. An example of its use is `test_03_01` where responses are delayed using `chunk_delay`. This gives the response a defined duration and the test uses that to reload `httpd` in the middle of the first request. A graceful reload in httpd lets ongoing requests finish, but will close the connection afterwards and tear down the serving process. The following request need then to open a new connection. This is verified by the test case. 130