curl/curl
Testing
curl's test suite is unusually large for a C project: ~1995 functional tests, ~300 unit tests, plus fuzz harnesses, torture mode, and a CI matrix that exercises dozens of platform/library combinations. The full driver is tests/runtests.pl. Reference docs: docs/runtests.md, docs/internals/TESTS.md, docs/internals/UNITTEST.md.
The test taxonomy
| Kind | Location | Scope |
|---|---|---|
| Functional | tests/data/test<N> |
End-to-end: the curl binary or a libtest program against a fake server |
| Unit | tests/unit/unit<N>.c |
A single internal function or struct |
| Tunit | tests/tunit/ |
Type-checking tests for the public headers |
| libtest | tests/libtest/lib<N>.c |
Compiled programs that drive libcurl directly |
| Fuzzer | tests/fuzz/ |
OSS-Fuzz-driven libFuzzer harnesses |
| Torture | runtests.pl -t |
Re-run failing memory allocations to expose error paths |
| Disabled | runtests.pl --disabled |
Tests known to fail in specific configurations |
How a functional test works
Each numbered test is a single tests/data/test<N> file with sections like <command>, <protocol>, <reply>, <verify>. The harness:
- Starts the small servers needed by the test (
tests/server/— sws.c, ftpd.c, smtpd.c, sshserver.pl, etc.) on dynamic ports - Substitutes the ports into
<command> - Runs
curl(or the named libtest program) against those servers - Compares the actual stdout, stderr, and protocol traffic against
<reply>and the expected log
Tests are filtered by tags — keywords, features, protocols. runtests.pl skips tests whose required features are not present (HTTP/2, mbedtls, IPv6, ...).
graph LR
A[runtests.pl] --> B[start servers]
A --> C[parse test data]
B --> D[curl process]
C --> D
D --> E[capture stdout/stderr]
E --> F[diff vs expected]
F --> G[report]Running tests
make test # everything
( cd tests && ./runtests.pl 1) # one test by number
( cd tests && ./runtests.pl "1..200") # a range
( cd tests && ./runtests.pl !FTP) # exclude a keyword
( cd tests && ./runtests.pl -t) # torture mode (slow)
( cd tests && ./runtests.pl -p) # print test output on failure
( cd tests && ./runtests.pl -v) # verboseCommon keywords: HTTP, HTTPS, FTP, IMAP, IPv6, verbose, cookies. The full list is in the test files themselves — the harness compiles a keyword index on startup.
Unit tests
Unit tests live in tests/unit/ and are linked statically against the libcurl object files so they can call private symbols. Each test is a small program that follows the UNITTEST_START/UNITTEST_STOP pattern (see tests/unit/curlcheck.h). The Makefile builds and runs them as part of make test.
Memory tracking
Building with --enable-debug activates lib/memdebug.c, which wraps every allocation in a tracked call. The test harness sets CURL_MEMDEBUG=<file> and analyses the resulting log with tests/memanalyze.pl to catch leaks, double frees, and out-of-order frees. Torture mode exploits this to inject a failure at every allocation point.
Fuzzing
OSS-Fuzz has run libcurl since July 2017. The harnesses in tests/fuzz/ (curl_fuzzer, curl_fuzzer_url, ...) are driven by libFuzzer. The fuzz.yml workflow runs an in-CI smoke fuzz on every PR.
Sanitizers
The Linux CI matrix runs builds with AddressSanitizer (-fsanitize=address), UndefinedBehaviorSanitizer (-fsanitize=undefined), and MemorySanitizer (where supported) — see .github/workflows/linux.yml. Locally:
CFLAGS="-fsanitize=address,undefined -fno-sanitize-recover" \
./configure --enable-debug --with-openssl
make
make testCI
Every PR runs through:
| Workflow | Coverage |
|---|---|
.github/workflows/linux.yml |
The biggest matrix: dozens of TLS and resolver combos |
.github/workflows/macos.yml |
macOS / Apple SecTrust / SecureTransport(legacy) |
.github/workflows/windows.yml |
MSVC, MinGW, Schannel, Cygwin |
.github/workflows/non-native.yml |
Cross-compiled targets (Cygwin, qemu) |
.github/workflows/http3-linux.yml |
HTTP/3 with ngtcp2/quiche |
.github/workflows/configure-vs-cmake.yml |
Catches drift between the two build systems |
.github/workflows/distcheck.yml |
make distcheck: tarball builds and self-tests |
.github/workflows/codeql.yml |
GitHub static analysis |
.github/workflows/checksrc.yml |
scripts/checksrc.pl style enforcement |
.github/workflows/checkdocs.yml |
Doc lint (curldown, link checking, options completeness) |
.github/workflows/fuzz.yml |
Short fuzz run |
Adding a test
- Pick the next free test number — typically the highest-numbered file in
tests/data/plus one. - Copy a similar test, edit
<command>,<protocol>,<reply>, and<verify>. - Add the new number to
tests/data/Makefile.inc(the harness reads tests from there). - Run
( cd tests && ./runtests.pl <N> )until it passes. - Run
( cd tests && ./runtests.pl <N> -t )to ensure error paths are clean.
For library-internal coverage (parser unit tests, hash-table behaviour), prefer a unit test in tests/unit/. See Patterns and conventions for guidance on writing testable C.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.