Open-Source Wikis

/

curl

/

How to contribute

/

Testing

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:

  1. Starts the small servers needed by the test (tests/server/ — sws.c, ftpd.c, smtpd.c, sshserver.pl, etc.) on dynamic ports
  2. Substitutes the ports into <command>
  3. Runs curl (or the named libtest program) against those servers
  4. 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)           # verbose

Common 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 test

CI

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

  1. Pick the next free test number — typically the highest-numbered file in tests/data/ plus one.
  2. Copy a similar test, edit <command>, <protocol>, <reply>, and <verify>.
  3. Add the new number to tests/data/Makefile.inc (the harness reads tests from there).
  4. Run ( cd tests && ./runtests.pl <N> ) until it passes.
  5. 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.

Testing – curl wiki | Factory