mirror of
https://github.com/open-goal/jak-project
synced 2026-08-06 18:03:30 -04:00
5d5e35fb9b
## 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)
207 lines
7.9 KiB
Markdown
Vendored
Generated
207 lines
7.9 KiB
Markdown
Vendored
Generated
<!--
|
|
Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
|
|
|
SPDX-License-Identifier: curl
|
|
-->
|
|
|
|
# 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.
|
|
|
|
```sh
|
|
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.
|
|
|
|
```sh
|
|
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:
|
|
|
|
1. a recent Python, `pytest` and the other modules listed in
|
|
`tests/http/requirements.txt`
|
|
2. Apache httpd and its development files. On Debian/Ubuntu, the packages
|
|
`apache2-bin` and `apache2-dev` have these.
|
|
3. the Apache `mod_ssl`, `mod_http2` and `mod_proxy` modules. On Debian/Ubuntu, these
|
|
modules are part of the `apache2-bin` package, but other distributions may
|
|
package them separately.
|
|
4. a local `curl` project build
|
|
5. optionally, `nghttpx` with 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/bin` and `/usr/sbin` to 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 have `dante-server` installed
|
|
|
|
## 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:
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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`:
|
|
|
|
```sh
|
|
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](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.
|
|
|
|
### Servers
|
|
|
|
In `conftest.py` 3 "fixtures" are defined that are used by all test cases:
|
|
|
|
1. `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.
|
|
2. `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).
|
|
3. `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 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 code `nnn`.
|
|
* `chunks=n`: generate `n` chunks of data in the response body, defaults to 3.
|
|
* `chunk_size=nnn`: each chunk should contain `nnn` bytes of data. Maximum is 16KB right now.
|
|
* `chunkd_delay=duration`: wait `duration` time between writing chunks
|
|
* `delay=duration`: wait `duration` time to send the response headers
|
|
* `body_error=(timeout|reset)`: produce an error after the first chunk in the response body
|
|
* `id=str`: add `str` in the response header `request-id`
|
|
|
|
`duration` values are integers, optionally followed by a unit. Units are:
|
|
|
|
* `d`: days (probably not useful here)
|
|
* `h`: hours
|
|
* `mi`: minutes
|
|
* `s`: 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.
|