Skip to content

Testing

aeronet's tests use GoogleTest and run with CTest. Unit tests live next to their module, in aeronet/<module>/test/, and cross-module integration tests, which start real servers on loopback sockets, live in tests/. Shared helpers (test clients, temporary files, TLS certificates, fault injection) are in aeronet/test_support/.

cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Debug -D AERONET_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build --output-on-failure --timeout 300

Test coverage matrix

Summary of the automated test coverage; the files named are in tests/ unless a module path is given. Legend: ✅ covered by explicit tests, ⚠ partial or indirect.

Area Feature Status Representative test files
Parsing Request line (method, target, version), header parsing and lookup ✅ http-core_test.cpp, aeronet/server/test/http-parser_test.cpp
Parsing Percent-decoding of the path ✅ http-core_test.cpp, aeronet/tech/test/url-encode-decode_test.cpp
Parsing Unsupported HTTP version (505), unsupported method (501), malformed request line (400) ✅ http-errors_test.cpp, http-core_test.cpp
Limits Maximum header size (431) ✅ http-core_test.cpp, http-additional_test.cpp
Limits Maximum body size (413), Content-Length and chunked growth ✅ http-additional_test.cpp, http-request-decompression_test.cpp
Bodies Content-Length bodies, chunked decoding ✅ http-core_test.cpp, http-errors_test.cpp, aeronet/server/test/http-parser_test.cpp
Bodies Trailers (incoming and outgoing) ✅ http-core_test.cpp, aeronet/http/test/http-response_test.cpp
Expect 100-continue for a non-empty body, none for an empty one ✅ http-errors_test.cpp, http-additional_test.cpp
Keep-alive Persistence, maximum requests per connection ✅ http-core_test.cpp, http-server-lifecycle_test.cpp
Keep-alive Idle timeout close, active connections outliving the timeout ✅ http-core_test.cpp
Connection close Lingering close after an early response or before pipelined requests (client close, reset, timeout, configuration update, TLS close_notify, shutdown failure) ✅ http-core_test.cpp, http-tls-io_test.cpp, http-errors_test.cpp
Pipelining Sequential pipelines, malformed second request, bodies and split requests behind served ones, TRACE, CONNECT, Expect, async handlers and WebSocket upgrades (with coalesced frames) behind served requests, batched responses ✅ http-core_test.cpp, http-errors_test.cpp, http-additional_test.cpp, http-connect_test.cpp, http-async_test.cpp, websocket-integration_test.cpp
Methods HEAD semantics (no body) ✅ http-core_test.cpp, aeronet/http/test/router_test.cpp
Date RFC 9110 format, same-second caching, second-boundary refresh ✅ aeronet/server/test/date-header-cache_test.cpp, http-core_test.cpp
Diagnostics Parser error callback (version, bad line, limits) ✅ http-errors_test.cpp
Concurrency SO_REUSEPORT distribution ✅ multi-http-server_test.cpp, http-server-lifecycle_test.cpp
Lifecycle Move and copy semantics, restart, graceful stop and drain, start() ✅ http-server-lifecycle_test.cpp
Performance writev header and body path ⚠ Exercised indirectly (http-errors_test.cpp), no direct assertion
TLS Handshake, rejection behavior, I/O, OCSP stapling ✅ http-tls-handshake_test.cpp, http-tls-io_test.cpp, aeronet/tls/test/tls-components_test.cpp
Streaming Streaming responses and incremental flush ✅ http-core_test.cpp
Routing Path and method matching ✅ http-routing_test.cpp, aeronet/http/test/router_test.cpp
Protocol parity Request pipeline over HTTP/1.1 and h2c: middleware and its metrics, CORS, trailing slash and HTTPS redirects, handler errors, streaming and async handlers ✅ http-protocol-parity_test.cpp
Compression Negotiation, outbound insertion, inbound decompression ✅ http-compression_test.cpp, http-request-decompression_test.cpp
OpenTelemetry Integration and end-to-end export ✅ opentelemetry-e2e_test.cpp, aeronet/objects/test/opentelemetry-integration_test.cpp
Probes and statistics Built-in probes, server statistics ✅ http-server-lifecycle_test.cpp, http-core_test.cpp

Protocol parity

HTTP/1.1 and HTTP/2 share the steps of the request pipeline that follow the parsing of a request (RequestPipeline, in aeronet/http-request-dispatch.hpp): the request middleware, the CORS origin check, the handler call, the fallback responses, and the response middleware. Each protocol still drives them from its own code, around its protocol-specific parts (HTTP/1.1 Expect, body framing, WebSocket upgrades; HTTP/2 streams and flow control). A behavior of the pipeline is tested once for both protocols in tests/http-protocol-parity_test.cpp, whose tests run with an HttpClient speaking HTTP/1.1, then cleartext HTTP/2 with prior knowledge: add the scenario there when changing the pipeline.

Network fault injection

Real networks deliver partial reads and writes, return EAGAIN at arbitrary points, and reset connections. Loopback tests rarely exercise these paths, so aeronet provides deterministic fault injection at the transport layer.

The hooks are compiled when AERONET_ENABLE_TEST_HOOKS is ON, which is the default for test builds that are not Release. They cost nothing when disabled.

Component Header Role
FaultPolicy aeronet/fault-policy.hpp Describes the faults: maximum bytes per read or write, periodic EAGAIN every N reads or writes, connection reset after a byte count or on the next operation, and an optional seed to randomize partial sizes.
TestPipe aeronet/test-pipe.hpp In-memory bidirectional byte channel, for tests without sockets or event loop.
TestTransport aeronet/test-transport.hpp In-memory Transport over a TestPipe, with a FaultPolicy applied, for unit tests of protocol handlers.
FaultInjectingTransport aeronet/fault-injecting-transport.hpp Decorator applying a FaultPolicy to a real transport, such as a plain socket.
ScopedTransportDecorator aeronet/transport-test-hook.hpp Installs a decorator that the server applies to every transport it accepts, for integration tests with real sockets and the full event loop.

Unit tests of the components are in test-transport_test.cpp. Integration tests in tests/network-fault-injection_test.cpp exercise partial reads and writes, simulated EAGAIN, connection resets, combined faults, TLS read-ahead, and pipelining under faults, against the full server.

Simulated EAGAIN on reads does not combine with edge-triggered epoll in integration tests: when the transport reports no data although the socket still holds some, no new edge arrives to wake the server. Use it in unit tests without event loop, or simulate EAGAIN on writes, which works in both.