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.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, `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).
- 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/raw_roles.h` the messages a raw client sends to drive the roles and the readers of what it receives, `tests/players_rig.h` the rig, writes and checks of the in-process player tests, `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
38 changes: 23 additions & 15 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ add_executable(sendspin_server_cpp_tests
test_frame_encoder.cpp
test_group_audio.cpp
test_group_audio_internals.cpp
test_group_internals.cpp
test_host_view.cpp
test_host_view_internals.cpp
test_inbound_hold.cpp
Expand Down Expand Up @@ -64,16 +65,19 @@ add_executable(sendspin_server_cpp_tests
test_sched_lifecycle.cpp
test_sched_lowering.cpp
test_sched_property.cpp
test_sched_run_view.cpp
test_sched_selection.cpp
test_sched_stalls.cpp
test_sched_timeline.cpp
test_scheduled_updates.cpp
test_send_queues.cpp
test_server.cpp
test_server_accept.cpp
test_server_artwork.cpp
test_server_core.cpp
test_server_core_internals.cpp
test_server_discovery.cpp
test_server_groups.cpp
test_server_limits.cpp
test_server_log.cpp
test_server_pairing.cpp
Expand All @@ -92,6 +96,7 @@ add_executable(sendspin_server_cpp_tests
test_session_internals.cpp
test_source_clock.cpp
test_source_decoder.cpp
test_source_feed.cpp
test_source_sink.cpp
test_source_stream.cpp
test_soak_wire_checker.cpp
Expand All @@ -100,6 +105,7 @@ add_executable(sendspin_server_cpp_tests
test_timing_log.cpp
test_timing_stats.cpp
test_transport.cpp
test_transport_url.cpp
test_trust_policy.cpp
test_url_text.cpp
test_visualizer.cpp
Expand Down Expand Up @@ -143,21 +149,7 @@ target_compile_definitions(sendspin_server_cpp_tests PRIVATE
# White-box: each file below compiles with -fno-access-control, beside the private members it
# reaches and why; a class's other tests are in a file that compiles without it.
# test_audio_workers_internals.cpp reads AudioWorkers::pool_ (Pool::mutex and stopping) to let a
# job finish only once a stop has begun, which no observable state shows. It wraps a group's
# pipeline job (Group::audio_) with group_job.h, to hold it running while core erases the group or
# to learn when its next run is over, and reads Group::playing_ and input_ on core to show a
# release ended the run and detached the input. Its stall tests set the state an epoch's start
# sets on a Group with no input (playing_, epoch_, anchor_, rings_, pipelines_.analyses), call its
# on_frame(), on_anchor_shift() and report_stalls() as the group-to-core FIFO and its timer do,
# and replace Group::stalls_ with a count whose last report was made at a chosen time, so a
# held-back report is due within the test rather than after 10 s, and take from it to read the
# counts, which only the log line shows otherwise. It calls Group::on_wake() as the group's wakeup
# does, since a write's signal can fold into a pipeline job's that is not posted yet, and reads
# HostView::slew_ and next_slew_ under HostView::mutex_, as only their storage shows that core
# freed the slew a write left it.
# group_job.h, which only the files given the flag include, waits for a task between runs under
# its pool's mutex (AudioWorkers::Task::pool_, running_ and queued_) to replace its job
# (Task::job_), and reads a group's pipelines from the job (Group::PipelineJob::audio).
# job finish only once a stop has begun, which no observable state shows.
set_source_files_properties(test_audio_workers_internals.cpp PROPERTIES COMPILE_OPTIONS
-fno-access-control)
# test_dispatcher_internals.cpp reads Dispatcher::token_ once the thread has ended: a later thread
Expand All @@ -173,6 +165,22 @@ set_source_files_properties(test_dispatcher_internals.cpp PROPERTIES COMPILE_OPT
# the pipelines publish for admission, which only shows in admission's bounds.
set_source_files_properties(test_group_audio_internals.cpp PROPERTIES COMPILE_OPTIONS
-fno-access-control)
# test_group_internals.cpp wraps a group's pipeline job (Group::audio_) with group_job.h, to hold
# it running while core erases the group or to learn when its next run is over, and reads
# Group::playing_ and input_ on core to show a release ended the run and detached the input. Its
# stall tests set the state an epoch's start sets on a Group with no input (playing_, epoch_,
# anchor_, rings_, pipelines_.analyses), call its on_frame(), on_anchor_shift() and
# report_stalls() as the group-to-core FIFO and its timer do, and replace Group::stalls_ with a
# count whose last report was made at a chosen time, so a held-back report is due within the test
# rather than after 10 s, and take from it to read the counts, which only the log line shows
# otherwise. It calls Group::on_wake() as the group's wakeup does, since a write's signal can fold
# into a pipeline job's that is not posted yet, and reads HostView::slew_ and next_slew_ under
# HostView::mutex_, as only their storage shows that core freed the slew a write left it.
# group_job.h, which only the files given the flag include, waits for a task between runs under
# its pool's mutex (AudioWorkers::Task::pool_, running_ and queued_) to replace its job
# (Task::job_), and reads a group's pipelines from the job (Group::PipelineJob::audio).
set_source_files_properties(test_group_internals.cpp PROPERTIES COMPILE_OPTIONS
-fno-access-control)
# test_host_view_internals.cpp leaves the outbox (HostView::outbox_, outbox_posted_) as a host call
# does between deciding a callback and posting it, so that close() racing that host call can be
# checked without a race. It sets HostView::lowering_ to a window of live writes already behind
Expand Down
Loading
Loading