Testing

Real I/O is a poor foundation for unit tests. Network operations are slow, non-deterministic, and do not fail on demand — so error-handling paths go untested until production breaks them. Capy ships a self-contained toolkit. It replaces the transport with in-memory mocks, drives coroutines to completion on the calling thread, and injects failures at every maybe_fail() site. The toolkit therefore exercises every error branch automatically. Because each mock satisfies the same concept as its production counterpart, test code reads the same as production code. The only difference is the type of the stream you pass in.

What This Section Covers

  • Driving Tests — run_blocking drives a coroutine to completion on the calling thread without a real executor. fuse runs the test body repeatedly, injecting an error at each maybe_fail() site in turn until every failure path is covered. The thread_name header’s set_current_thread_name function labels worker threads so that failures in multi-threaded tests are easier to attribute.

  • Mock Streams — read_stream, write_stream, and stream (a connected pair) implement the partial-I/O concepts from Streams. Use them to test protocol logic that calls read_some and write_some without touching a socket.

  • Buffer Inspection — bufgrind iterates every split point of a buffer sequence, exercising every chunk-boundary condition; buffer_to_string concatenates buffer sequences into a std::string for easy assertion.

How the Pieces Fit

A typical test constructs one or more mocks, arms a fuse, and hands the mocks to the code under test inside a run_blocking call. The fuse repeats the test body automatically in two full sweeps — error-code mode, then exception mode. run_blocking keeps the whole thing on the calling thread. Buffer utilities such as bufgrind and buffer_to_string wrap the mock data for assertions. They let you verify that every split of an input buffer produces the same correct output.