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
68 changes: 37 additions & 31 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -34,35 +34,41 @@
** xref:6.streams/6d.buffer-concepts.adoc[Buffer Sources and Sinks]
** xref:6.streams/6e.algorithms.adoc[Transfer Algorithms]
** xref:6.streams/6f.isolation.adoc[Physical Isolation]
* xref:7.examples/7.intro.adoc[Example Programs]
** xref:7.examples/7a.hello-task.adoc[Hello Task]
** xref:7.examples/7b.producer-consumer.adoc[Producer-Consumer]
** xref:7.examples/7c.buffer-composition.adoc[Buffer Composition]
** xref:7.examples/7d.mock-stream-testing.adoc[Mock Stream Testing]
** xref:7.examples/7e.type-erased-echo.adoc[Type-Erased Echo]
** xref:7.examples/7f.timeout-cancellation.adoc[Timeout with Cancellation]
** xref:7.examples/7g.parallel-fetch.adoc[Parallel Fetch]
** xref:7.examples/7h.custom-dynamic-buffer.adoc[Custom Dynamic Buffer]
** xref:7.examples/7i.echo-server-corosio.adoc[Echo Server with Corosio]
** xref:7.examples/7j.stream-pipeline.adoc[Stream Pipeline]
** xref:7.examples/7k.strand-serialization.adoc[Strand Serialization]
** xref:7.examples/7l.async-mutex.adoc[Async Mutex]
** xref:7.examples/7m.parallel-tasks.adoc[Parallel Tasks]
** xref:7.examples/7n.custom-executor.adoc[Custom Executor]
* xref:8.design/8.intro.adoc[Design]
** xref:8.design/8a.CapyLayering.adoc[Layered Abstractions]
** xref:8.design/8b.Separation.adoc[Why Capy Is Separate]
** xref:8.design/8c.ReadStream.adoc[ReadStream]
** xref:8.design/8d.ReadSource.adoc[ReadSource]
** xref:8.design/8e.BufferSource.adoc[BufferSource]
** xref:8.design/8f.WriteStream.adoc[WriteStream]
** xref:8.design/8g.WriteSink.adoc[WriteSink]
** xref:8.design/8h.BufferSink.adoc[BufferSink]
** xref:8.design/8i.TypeEraseAwaitable.adoc[Type-Erasing Awaitables]
** xref:8.design/8j.any_buffer_sink.adoc[AnyBufferSink]
** xref:8.design/8k.Executor.adoc[Executor]
** xref:8.design/8l.RunApi.adoc[Run API]
** xref:8.design/8m.WhyNotCobalt.adoc[Why Not Cobalt?]
** xref:8.design/8n.WhyNotCobaltConcepts.adoc[Why Not Cobalt Concepts?]
** xref:8.design/8o.WhyNotTMC.adoc[Why Not TooManyCooks?]
* xref:7.testing/7.intro.adoc[Testing]
** xref:7.testing/7a.drivers.adoc[Driving Tests]
** xref:7.testing/7b.mock-streams.adoc[Mock Streams]
** xref:7.testing/7c.mock-sources-sinks.adoc[Mock Sources and Sinks]
** xref:7.testing/7d.mock-buffer-concepts.adoc[Mock Buffer Sources and Sinks]
** xref:7.testing/7e.buffer-inspection.adoc[Buffer Inspection]
* xref:8.examples/8.intro.adoc[Example Programs]
** xref:8.examples/8a.hello-task.adoc[Hello Task]
** xref:8.examples/8b.producer-consumer.adoc[Producer-Consumer]
** xref:8.examples/8c.buffer-composition.adoc[Buffer Composition]
** xref:8.examples/8d.mock-stream-testing.adoc[Mock Stream Testing]
** xref:8.examples/8e.type-erased-echo.adoc[Type-Erased Echo]
** xref:8.examples/8f.timeout-cancellation.adoc[Timeout with Cancellation]
** xref:8.examples/8g.parallel-fetch.adoc[Parallel Fetch]
** xref:8.examples/8h.custom-dynamic-buffer.adoc[Custom Dynamic Buffer]
** xref:8.examples/8i.echo-server-corosio.adoc[Echo Server with Corosio]
** xref:8.examples/8j.stream-pipeline.adoc[Stream Pipeline]
** xref:8.examples/8k.strand-serialization.adoc[Strand Serialization]
** xref:8.examples/8l.async-mutex.adoc[Async Mutex]
** xref:8.examples/8m.parallel-tasks.adoc[Parallel Tasks]
** xref:8.examples/8n.custom-executor.adoc[Custom Executor]
* xref:9.design/9.intro.adoc[Design]
** xref:9.design/9a.CapyLayering.adoc[Layered Abstractions]
** xref:9.design/9b.Separation.adoc[Why Capy Is Separate]
** xref:9.design/9c.ReadStream.adoc[ReadStream]
** xref:9.design/9d.ReadSource.adoc[ReadSource]
** xref:9.design/9e.BufferSource.adoc[BufferSource]
** xref:9.design/9f.WriteStream.adoc[WriteStream]
** xref:9.design/9g.WriteSink.adoc[WriteSink]
** xref:9.design/9h.BufferSink.adoc[BufferSink]
** xref:9.design/9i.TypeEraseAwaitable.adoc[Type-Erasing Awaitables]
** xref:9.design/9j.any_buffer_sink.adoc[AnyBufferSink]
** xref:9.design/9k.Executor.adoc[Executor]
** xref:9.design/9l.RunApi.adoc[Run API]
** xref:9.design/9m.WhyNotCobalt.adoc[Why Not Cobalt?]
** xref:9.design/9n.WhyNotCobaltConcepts.adoc[Why Not Cobalt Concepts?]
** xref:9.design/9o.WhyNotTMC.adoc[Why Not TooManyCooks?]
* xref:reference:boost/capy.adoc[Reference]
2 changes: 1 addition & 1 deletion doc/modules/ROOT/pages/4.coroutines/4g.allocators.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ To prevent this, any code that calls `.resume()` on a coroutine handle must use
capy::safe_resume(h); // saves and restores TLS around h.resume()
----

`safe_resume` saves the current thread-local allocator, calls `h.resume()`, then restores the saved value. This makes TLS behave like a stack: nested resumes cannot spoil the outer value. All of Capy's built-in executors (`thread_pool`, strands, `blocking_context`) use `safe_resume` internally. Custom executor event loops must do the same -- see xref:7.examples/7n.custom-executor.adoc[Custom Executor] for an example.
`safe_resume` saves the current thread-local allocator, calls `h.resume()`, then restores the saved value. This makes TLS behave like a stack: nested resumes cannot spoil the outer value. All of Capy's built-in executors (`thread_pool`, strands, `blocking_context`) use `safe_resume` internally. Custom executor event loops must do the same -- see xref:8.examples/8n.custom-executor.adoc[Custom Executor] for an example.

== The FrameAllocator Concept

Expand Down
17 changes: 0 additions & 17 deletions doc/modules/ROOT/pages/5.buffers/5a.overview.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,6 @@ This single signature accepts:
* A custom composite type
* *Any composition of the above—without allocation*

== Zero-Allocation Composition

With concepts, composition creates views, not copies:

[source,cpp]
----
HeaderBuffers headers = /* ... */;
BodyBuffers body = /* ... */;

// cat() creates a view that iterates both sequences
auto combined = cat(headers, body); // No allocation!

write_data(combined); // Works because combined satisfies ConstBufferSequence
----

The `cat` function returns a lightweight object that, when iterated, first yields buffers from `headers`, then from `body`. The buffers themselves are not copied—only iterators are composed.

== STL Parallel

This design follows Stepanov's insight from the STL: algorithms parameterized on concepts (iterators), not concrete types (containers), enable composition that concrete types forbid.
Expand Down
17 changes: 0 additions & 17 deletions doc/modules/ROOT/pages/5.buffers/5c.sequences.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -133,23 +133,6 @@ task<std::size_t> read_all(Stream& stream, Buffers buffers)
* Iteration over unconsumed buffers
* `buffer_size()` of remaining bytes

== Zero-Allocation Composition

The `cat()` function composes buffer sequences without allocation:

[source,cpp]
----
auto headers = std::array{header_buf1, header_buf2};
auto body = body_buffer;

auto combined = cat(headers, body); // No allocation

// combined satisfies ConstBufferSequence
// Iteration yields: header_buf1, header_buf2, body_buffer
----

The returned object stores references (or small copies for single buffers) and iterates through the composed sequence on demand.

== Why Bidirectional?

The concepts require bidirectional ranges (not just forward ranges) for two reasons:
Expand Down
3 changes: 2 additions & 1 deletion doc/modules/ROOT/pages/5.buffers/5e.algorithms.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ std::string header = build_header();
std::vector<char> body = load_body();

// No copying—header and body are written directly
co_await write(stream, cat(make_buffer(header), make_buffer(body)));
std::array buffers = {make_buffer(header), make_buffer(body)};
co_await write(stream, buffers);
----

=== Scatter/Gather Operations
Expand Down
2 changes: 1 addition & 1 deletion doc/modules/ROOT/pages/6.streams/6f.isolation.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -237,4 +237,4 @@ Type-erased wrappers are in `<boost/capy/io/>`:
* `any_read_source`, `any_write_sink`
* `any_buffer_source`, `any_buffer_sink`

You have now completed the Stream Concepts section. These abstractions—streams, sources, sinks, and their type-erased wrappers—form the foundation for Capy's I/O model. Continue to xref:../7.examples/7a.hello-task.adoc[Example Programs] to see complete working examples.
You have now completed the Stream Concepts section. These abstractions—streams, sources, sinks, and their type-erased wrappers—form the foundation for Capy's I/O model. Continue to xref:../8.examples/8a.hello-task.adoc[Example Programs] to see complete working examples.
66 changes: 66 additions & 0 deletions doc/modules/ROOT/pages/7.testing/7.intro.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// Copyright (c) 2026 Steve Gerbino
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/capy
//

= 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
that replaces the transport with in-memory mocks, drives coroutines to
completion on the calling thread, and injects failures at every
`maybe_fail()` site so that every error branch is exercised 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 or source you pass in.

== What This Section Covers

* xref:7.testing/7a.drivers.adoc[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 has been covered;
and the `thread_name` header's `set_current_thread_name` function labels
worker threads so that failures in multi-threaded tests are easier to
attribute.

* xref:7.testing/7b.mock-streams.adoc[Mock Streams] -- `read_stream`,
`write_stream`, and `stream` (a connected pair) implement the partial-I/O
concepts from xref:6.streams/6b.streams.adoc[Streams]. Use them to test
protocol logic that calls `read_some` and `write_some` without touching a
socket.

* xref:7.testing/7c.mock-sources-sinks.adoc[Mock Sources and Sinks] --
`read_source` and `write_sink` implement the complete-I/O concepts from
xref:6.streams/6c.sources-sinks.adoc[Sources and Sinks]. Unlike the
stream mocks, they loop internally until the buffer is fully filled or
drained, and `write_sink` accepts an explicit EOF signal.

* xref:7.testing/7d.mock-buffer-concepts.adoc[Mock Buffer Sources and Sinks]
-- `buffer_source` and `buffer_sink` implement the buffer concepts from
xref:6.streams/6d.buffer-concepts.adoc[Buffer Sources and Sinks].
`buffer_source` exposes staged bytes via a pull interface;
`buffer_sink` provides callee-owned storage that the algorithm writes
into directly.

* xref:7.testing/7e.buffer-inspection.adoc[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 -- once for each failure site and once
in exception mode -- while `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, letting you verify that every split of
an input buffer produces the same correct output.

Continue to xref:7.testing/7a.drivers.adoc[Driving Tests] to begin.
Loading
Loading