## Problem Windows builds break with a current local toolchain (Scoop LLVM 22.1.8, CMake 4.4.0, VS 2026), in two independent ways: 1. The build stops at curl's deliberate guard: `#error "no non-blocking method was found/used/set"` in `third-party/curl/lib/nonblock.c`. 2. From the second configure onward, `cmake --build` re-runs CMake in an endless loop (observed 42 consecutive reconfigure cycles in a single build). Likely the same mechanism behind the "endlessly building" VS 2026 note in `docs/setup/dev/vs.md`. ## Root cause 1. `third-party/curl/CMake/CurlTests.c` passes `int *` to `ioctlsocket()`, whose third parameter is `u_long *`. Clang 22 promotes `-Wincompatible-pointer-types` to a hard error in C, so the `HAVE_IOCTLSOCKET_FIONBIO` try_compile silently fails and `curl_config.h` never defines it. Upstream CI does not see this because the windows-2022 runner image ships an older LLVM. GCC 14 promotes the same warning to a hard error, which is very likely the `CurlTests.c.obj` failure reported from MSYS2 in open-goal/jak-project#3551. Upstream curl hit the identical problem with GCC 14 and fixed the probe in curl 8.8.0 (curl/curl#13578). 2. The root CMakeLists copies the build tree's `compile_commands.json` into `<src>/build/` for clangd using `configure_file()`, which registers its input as a configure dependency. CMake rewrites `compile_commands.json` late in every generation, after `CTestTestfile.cmake` and `cmake_install.cmake` (outputs of the same Ninja regen rule), so once the dependency is registered the rule is deterministically dirty and every `ninja` invocation re-runs CMake. A pristine first configure is safe (the file does not exist yet, so the `if(EXISTS ...)` guard skips the copy), which is why the loop looks machine- or IDE-specific. ## Fix 1. Per review, re-vendor `third-party/curl` at the `curl-8_21_0` tag (previously `curl-8_3_0`), which carries the upstream probe fix plus two years of upstream development; `vendor.yaml` updated to match. Adjustments the version jump forced: - curl 8.15 removed the native macOS Secure Transport backend (`CURL_USE_SECTRANSP`), so macOS now builds curl against OpenSSL like Linux. The two macOS workflows install Homebrew `openssl@3` and export `OPENSSL_ROOT_DIR` (keg-only), and the macOS setup docs gained the same two lines. - `CURL_BROTLI` / `CURL_ZSTD` switched to AUTO-detection in curl 8.10; pinned OFF to keep the previous no-compression behavior and avoid silently linking whatever the CI images happen to have. - curl's new top-level `BUILD_EXAMPLES` cache option (default ON) leaked into discord-rpc's identically named option and broke configure at a nonexistent `examples/send-presence` directory; pinned OFF ahead of the third-party subdirectories. The diff is dominated by the mechanical tag-tree swap under `third-party/curl` (linguist-vendored, collapsed in review). The hand-written changes are `CMakeLists.txt`, the two macOS workflows, `docs/setup/system/macos.md`, and `vendor.yaml`. 2. Swap `configure_file()` for `file(COPY ...)`: the same clangd copy with no configure dependency registered. (`file(COPY_FILE ... ONLY_IF_DIFFERENT)` would be cleaner still but requires CMake 3.21, above the declared `cmake_minimum_required(VERSION 3.10)`.) ## Test plan - [x] Fresh `cmake --preset Release-windows-clang` (LLVM 22, no cache seeding) completes and logs `Enabled SSL backends: Schannel`; the FIONBIO probe passes without the previous `#error` - [x] Full Windows Release build from scratch in the branch worktree (all 1422 targets) - [x] goalc-test suite: 1509 passed, 0 failed - [x] Second consecutive configure with `compile_commands.json` present: the regen rule in `build.ninja` has no `compile_commands.json` input; `<src>/build/compile_commands.json` is still refreshed for clangd - [x] Repeated `ninja` invocations after a full build no longer re-run CMake - [x] macOS Intel and ARM CI green (first exercise of the OpenSSL backend switch) --- I work off a self-hosted forge, so this GitHub account is quiet; the configure logs and ninja dirty-node traces from the investigation are available if anyone wants the raw data. (AI-assisted)
7.9 KiB
Vendored
Generated
The curl HTTP Test Suite
This 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.
Usage
The test cases and necessary files are in tests/http. You can invoke
pytest from there or from the top level curl checkout and it finds all
tests.
curl> pytest tests/http
platform darwin -- Python 3.9.15, pytest-6.2.0, py-1.10.0, pluggy-0.13.1
rootdir: /Users/sei/projects/curl
collected 5 items
tests/http/test_01_basic.py .....
Pytest 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 a string that needs to match test
cases in their names.
curl/tests/http> pytest -vv -k test_01_02
runs all test cases that have test_01_02 in their name. This does not have
to be the start of the name.
Depending on your setup, some test cases may be skipped and appear as s in
the output. If you run pytest verbose, it also gives you the reason for
skipping.
Prerequisites
You need:
- a recent Python,
pytestand the other modules listed intests/http/requirements.txt - Apache httpd and its development files. On Debian/Ubuntu, the packages
apache2-binandapache2-devhave these. - the Apache
mod_ssl,mod_http2andmod_proxymodules. On Debian/Ubuntu, these modules are part of theapache2-binpackage, but other distributions may package them separately. - a local
curlproject build - optionally,
nghttpxwith HTTP/3 enabled or h3 test cases are skipped
Configuration
Via curl's configure script you may specify:
-
--with-test-nghttpx=<path-of-nghttpx>if you have nghttpx to use somewhere outside your$PATH. -
--with-test-h2o=<path-of-h2o>if you have h2o to use somewhere outside your$PATH. -
--with-test-httpd=<httpd-install-path>if you have an Apache httpd installed somewhere else. On Debian/Ubuntu it otherwise looks into/usr/binand/usr/sbinto find those. -
--with-test-caddy=<caddy-install-path>if you have a Caddy web server installed somewhere else. -
--with-test-vsftpd=<vsftpd-install-path>if you have a vsftpd ftp server installed somewhere else. -
--with-test-danted=<danted-path>if you havedante-serverinstalled
Usage Tips
Several 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:
curl/tests/http> pytest -k "test_02_06 and h2"
Test cases can be repeated, with the pytest-repeat module (pip install pytest-repeat). Like in:
curl/tests/http> pytest -k "test_02_06 and h2" --count=100
which then runs this test case a hundred times. In case of flaky tests, you can make pytest stop on the first one with:
curl/tests/http> pytest -k "test_02_06 and h2" --count=100 --maxfail=1
which 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:
curl/tests/http> pytest -vvv -k "test_02_06 and h2" --count=100 --maxfail=1
all of curl's output and trace file are found in tests/http/gen/curl.
Writing Tests
There is a lot of pytest documentation 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.
Servers
In conftest.py 3 "fixtures" are defined that are used by all test cases:
env: the test environment. It is an instance of classtestenv/env.py:Env. It holds all information about paths, availability of features (HTTP/3), port numbers to use, domains and SSL certificates for those.httpd: the Apache httpd instance, configured and started, then stopped at the end of the test suite. It has sites configured for the domains fromenv. It also loads a local modulemod_curltest?and makes it available in certain locations. (more on mod_curltest below).nghttpx: an instance of nghttpx that provides HTTP/3 support.nghttpxproxies those requests to thehttpdserver. 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 are different.
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 then
shutdowns. Only when you brutally chop its head off, might there be servers
left behind.
Test Cases
Tests 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 a single test, it is important that a test references the ones it needs.
All 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.
Tests 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.
There 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.
mod_curltest
The 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.
The 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:
-
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. -
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.status=nnn: generate a response with HTTP status codennn.chunks=n: generatenchunks of data in the response body, defaults to 3.chunk_size=nnn: each chunk should containnnnbytes of data. Maximum is 16KB right now.chunkd_delay=duration: waitdurationtime between writing chunksdelay=duration: waitdurationtime to send the response headersbody_error=(timeout|reset): produce an error after the first chunk in the response bodyid=str: addstrin the response headerrequest-id
duration values are integers, optionally followed by a unit. Units are:
d: days (probably not useful here)h: hoursmi: minutess: seconds (the default)ms: milliseconds
As you can see, mod_curltest's tweak handler allows Apache 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 closes the connection
afterwards and tears down the serving process. The following request then needs
to open a new connection. This is verified by the test case.