Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/test-standards/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Back every claim with a command: a "checked and clean" statement, a mutation res
## No test seams in production

- `src/` and `include/` acquire no friends, test-only hooks, widened visibility, extra template parameters, injectable clocks or transports, or fixture-aware naming for a test. The fix is extraction, a test-side technique, or a named gap.
- Allowed test-side techniques: tests include private headers from `src/` and construct internal classes directly (`HostView`, `GroupAudio`, `Session`); a test file that reaches private members compiles with `-fno-access-control`, set on that file in `tests/CMakeLists.txt` beside a comment naming each member it reaches and why. A test that reaches a private member the comment does not name is a finding.
- Allowed test-side techniques: tests include private headers from `src/` and construct internal classes directly (`HostView`, `GroupAudio`, `Session`); a test file that reaches private members compiles with `-fno-access-control`, set on that file in `tests/CMakeLists.txt` beside a comment naming each member it reaches and why. A test that reaches a private member the comment does not name is a finding, and so is a test that needs no private member in a file compiled with the flag when the class has a file without it.

## Mutation survival

Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ script/clang-tidy.sh - clang-tidy over src/ using build/compile_commands.js
- Library target `sendspin_server_cpp` (alias `sendspin_server_cpp::sendspin_server_cpp`); CMake options are prefixed `SENDSPIN_SERVER_`. `SENDSPIN_SERVER_ENABLE_WERROR` (default off) makes warnings in this project's targets errors; CI builds with it on, so local builds should too. `SENDSPIN_SERVER_EMBEDDED_PROFILE` (default off) builds with `src/profile.h`'s embedded limits, as the ESP-IDF component (`cmake/esp_idf.cmake`) always does.
- Plain: `cmake -B build && cmake --build build`, which builds `examples/` too (`SENDSPIN_SERVER_BUILD_EXAMPLES`, default on when top-level). Uses ccache when installed (`SENDSPIN_SERVER_USE_CCACHE`, default on when top-level). `SENDSPIN_SERVER_ENABLE_DISCOVERY` (default on) builds the mDNS backend: dns_sd on macOS, avahi-client through pkg-config on Linux (`libavahi-client-dev`). `SENDSPIN_SERVER_ENABLE_OPUS` (default on) builds the Opus encoder and the source role's Opus decoder; off compiles them out (`src/frame_encoder.cpp` and `src/source_decoder.cpp` are the places it is guarded) and is refused with the tests on.
- Tests never use the network's mDNS: `tests/test_recorder.h` `local_config()` turns advertising and browsing off and listens on a port the system picks, and `test_server_discovery.cpp` supplies its own `DiscoveryProvider` through `ServerConfig::discovery`.
- Tests: `cmake -B build-asan -DSENDSPIN_SERVER_ENABLE_WERROR=ON -DSENDSPIN_SERVER_BUILD_TESTS=ON -DSENDSPIN_SERVER_ENABLE_SANITIZERS=ON`, then `cmake --build build-asan` and `ctest --test-dir build-asan --output-on-failure`. The suites whose assertions depend on players keeping up with real time carry the CTest label `realtime` (`tests/CMakeLists.txt`); CI runs them on their own and the rest in parallel (`-LE realtime -j`). The tests also build `server_cli`'s WAV and FLAC readers, feeders, `--one-group`, timing figures and log and `--web` dashboard; `tests/temp_dir.h` gives a test a temporary directory, and `tests/job_view.h` a copy of what a group's pipelines read of a host view. `tests/test_ports.h` hands out loopback ports for test peers, and holds one that nothing listens on for a dial that must be refused; `tests/raw_client.h` is the raw test client (noise-c and Beast, either suite, scriptable misbehavior), `tests/silent_listener.h` a listener that holds the server's dials in their upgrade or handshake, `tests/test_recorder.h` a recording listener, an in-memory provider and `LocalServer`, a server started with them, `tests/wire_messages.h` the spec's binary message types and fields as the tests decode them, `tests/flac_reader.h` decodes a FLAC stream as a player gets it, `tests/opus_reader.h` an Opus stream, with the test tones and an SNR to compare lossy audio by, `tests/player_sink.h` holds the test signal and a real-time audio output for sendspin-cpp players, `tests/peer.h` what each in-process sendspin-cpp client shares (its network provider and blob store, its config and the URL the server dials, its main loop run on the test thread, the client_id at a URL), `tests/trust_fixtures.h` distinct PSKs and the client record a server saved, and `tests/freed_blocks.h` counts the heap blocks freed still holding a secret, through the sanitizer's free hook (a test that needs it skips itself without ASan or TSan).
- Tests: `cmake -B build-asan -DSENDSPIN_SERVER_ENABLE_WERROR=ON -DSENDSPIN_SERVER_BUILD_TESTS=ON -DSENDSPIN_SERVER_ENABLE_SANITIZERS=ON`, then `cmake --build build-asan` and `ctest --test-dir build-asan --output-on-failure`. The suites whose assertions depend on players keeping up with real time carry the CTest label `realtime` (`tests/CMakeLists.txt`); CI runs them on their own and the rest in parallel (`-LE realtime -j`). The tests also build `server_cli`'s WAV and FLAC readers, feeders, `--one-group`, timing figures and log and `--web` dashboard; `tests/temp_dir.h` gives a test a temporary directory, and `tests/job_view.h` a copy of what a group's pipelines read of a host view. `tests/test_ports.h` hands out loopback ports for test peers, and holds one that nothing listens on for a dial that must be refused; `tests/raw_client.h` is the raw test client (noise-c and Beast, either suite, scriptable misbehavior), `tests/silent_listener.h` a listener that holds the server's dials in their upgrade or handshake, `tests/test_recorder.h` a recording listener, an in-memory provider and `LocalServer`, a server started with them, `tests/wire_messages.h` the spec's binary message types and fields as the tests decode them, `tests/flac_reader.h` decodes a FLAC stream as a player gets it, `tests/opus_reader.h` an Opus stream, with the test tones and an SNR to compare lossy audio by, `tests/player_sink.h` holds the test signal and a real-time audio output for sendspin-cpp players, `tests/peer.h` what each in-process sendspin-cpp client shares (its network provider and blob store, its config and the URL the server dials, its main loop run on the test thread, the client_id at a URL), `tests/trust_fixtures.h` distinct PSKs and the client record a server saved, `tests/hex.h` hex text for known answers, `tests/gate.h` a gate a job waits at, the fixtures and harnesses a class's two test files share (`tests/host_view_fixture.h`, `tests/group_pipelines.h`, `tests/session_harness.h`, `tests/trust_harness.h`), `tests/group_job.h` a group's pipeline job wrapped between runs, for the files compiled with `-fno-access-control` alone, and `tests/freed_blocks.h` counts the heap blocks freed still holding a secret, through the sanitizer's free hook (a test that needs it skips itself without ASan or TSan).
- Soak: `tests/soak.cpp` (its trace checker in `tests/soak_wire_checker.h`, tested in the default suite), target `sendspin_server_cpp_soak` with its own entry point, registered as the CTest test `Soak` (label `soak`), which skips itself unless `SENDSPIN_SERVER_SOAK_SECONDS` is set (`SENDSPIN_SERVER_SOAK_SEED` picks the script); README.md has the command.
- CI also builds the library with Opus and discovery off and memory statistics on; as a subproject (`tests/consumer/`); and as the ESP-IDF component (`tests/esp_idf/`: `idf.py -C tests/esp_idf set-target esp32s3`, then `idf.py -C tests/esp_idf build`).
- Memory statistics: `SENDSPIN_SERVER_MEMORY_STATS` (default off; `CONFIG_SENDSPIN_SERVER_MEMORY_STATS` in the component's `Kconfig`) logs the heaps and the tasks' stacks at the server's start (on the thread calling `start()`) and, on the core thread, at a connection's start and end and a group's playback (`profile::MEMORY_STATS`); the tests count log lines and expect it off.
Expand Down Expand Up @@ -124,4 +124,4 @@ All pinned; see `cmake/`.
- `docs/comment-style.md` for every comment; ASCII punctuation only (no em or en dashes).
- Apache 2.0 license header on every source file.
- clang-format (v18, `.clang-format`), clang-tidy (`.clang-tidy`), markdownlint (`.markdownlint.yaml`); hooks in `.pre-commit-config.yaml`.
- Tests are host-only and white-box. A test file that reaches private members compiles with `-fno-access-control`, set per file in `tests/CMakeLists.txt` with the reason; never add a seam to production code.
- Tests are host-only and white-box. A test file that reaches private members compiles with `-fno-access-control`, set per file in `tests/CMakeLists.txt` with the reason; such tests go in a `test_*_internals.cpp` file of their own, so a class's other tests compile without it. Never add a seam to production code.
2 changes: 1 addition & 1 deletion docs/conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ The design standards that code in this repository is held to. This document is n
## Testing

- **Host-only and white-box.** Tests live in `tests/`, include private headers from `src/`, and run under ASan/UBSan and, in their own build, ThreadSanitizer; never run the two suites at once.
- **No seams in production.** `src/` and `include/` acquire nothing for tests: no friends, test-only hooks, widened visibility, extra template parameters, injectable clocks or transports, or fixture-aware naming. Hard-to-reach logic is extracted into a pure function and tested directly. A test that must reach a private member compiles with `-fno-access-control`, set on that file in `tests/CMakeLists.txt` beside a comment naming every member it reaches and why.
- **No seams in production.** `src/` and `include/` acquire nothing for tests: no friends, test-only hooks, widened visibility, extra template parameters, injectable clocks or transports, or fixture-aware naming. Hard-to-reach logic is extracted into a pure function and tested directly. A test that must reach a private member compiles with `-fno-access-control`, set on that file in `tests/CMakeLists.txt` beside a comment naming every member it reaches and why; such tests go in a `test_*_internals.cpp` file of their own, so a class's other tests compile without it.
- **Each layer has its harness:** pure tables for `src/sched/` and the policy files (seeded where randomized, as `test_sched_property.cpp`); `Session` driven with messages and deadlines and no I/O; the transport and `SessionDriver` over real loopback sockets; the raw test client (`tests/raw_client.h`) for malformed, out-of-order and adversarial peers and for AES-GCM; sendspin-cpp clients in process as real players, whose audio output shows what a player received. Servers listen on port 0 with discovery off (`local_config()`); a peer that cannot report an ephemeral port takes one from `tests/test_ports.h`.
- **A test defends a specific line** and fails when that line is deleted or its condition inverted; a test that cannot fail that way is filler and is deleted.
- **The unit of a test is a behavior.** A family of related guards is one table whose rows are the branches, with at least one accepting row marked `Control:` beside them; a guard gets its own test only for its own spec citation or failure mode.
Expand Down
2 changes: 1 addition & 1 deletion src/ORIGIN.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ Sources copied or adapted from [sendspin-cpp](https://github.com/Sendspin/sendsp
| `crypto/psk_wrap.h`, `crypto/psk_wrap.cpp` | `src/crypto/psk_wrap.h`, `src/crypto/psk_wrap.cpp` |
| `fragmentation.h`, `fragmentation.cpp` | The reassembly and fragmentation of `src/noise_transport.cpp` (`NoiseTransport::accept_plaintext()`, `NoiseTransport::fragment_and_send()`) |

The known answers in `tests/test_pairing_crypto.cpp` come from sendspin-cpp's `tests/test_cpace.cpp`, `tests/test_pairing_code.cpp` and `tests/test_psk_wrap.cpp` at the same commit.
The known answers in `tests/test_pairing_crypto.cpp` and `tests/test_pairing_crypto_internals.cpp` come from sendspin-cpp's `tests/test_cpace.cpp`, `tests/test_pairing_code.cpp` and `tests/test_psk_wrap.cpp` at the same commit.
Loading
Loading