pw_stream: Add Reader tests
Add test for Reader and MemoryReader classes.
Update pw_stream rst docs.
Change-Id: I20e1a4cfeff1bcdd1968eb5a1397a81566b2bed4
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/14767
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Commit-Queue: David Rogers <davidrogers@google.com>
diff --git a/pw_stream/docs.rst b/pw_stream/docs.rst
index 753a0e3..9b3da1c 100644
--- a/pw_stream/docs.rst
+++ b/pw_stream/docs.rst
@@ -46,13 +46,15 @@
Buffering
^^^^^^^^^
If any buffering occurs in a ``Writer`` and data must be flushed before it is
-fully committed to the sink, a ``Writer`` may optionally override ``Flush()``.
-Writes to a buffer may optimistically return ``Status::OK``, so depend on the
-return value of ``Flush()`` to ensure any pending data is committed to a sink.
+fully committed to the sink, a ``Writer`` implementation is resposible for any
+``Flush()`` capability.
-Generally speaking, the scope that instantiates the concrete ``Writer`` class
-should be in charge of calling ``Flush()``, and functions that only have access
-to the Writer interface should avoid calling this function.
+pw::stream::Reader
+------------------
+This is the foundational stream ``Reader`` abstract class. Any class that wishes
+to implement the ``Reader`` interface **must** provide a ``DoRead()``
+implementation. Note that ``Read()`` itself is **not** virtual, and should not
+be overridden.
pw::stream::MemoryWriter
------------------------
@@ -61,6 +63,11 @@
``MemoryWriterBuffer`` extends ``MemoryWriter`` to internally provide a memory
buffer.
+pw::stream::MemoryReader
+------------------------
+The ``MemoryReader`` class implements the ``Reader`` interface by backing the
+data source with an **externally-provided** memory buffer.
+
Why use pw_stream?
==================
diff --git a/pw_stream/memory_stream_test.cc b/pw_stream/memory_stream_test.cc
index 1a40d65..e08a299 100644
--- a/pw_stream/memory_stream_test.cc
+++ b/pw_stream/memory_stream_test.cc
@@ -138,7 +138,133 @@
memory_writer.Write(nullptr, 21);
}
+// TODO(davidrogers): Ensure that this test triggers an assert.
+TEST(MemoryReader, NullSpan) {
+ ByteSpan dest(nullptr, 5);
+ MemoryReader memory_reader(memory_buffer);
+ memory_reader.Read(dest);
+}
+
+// TODO(davidrogers): Ensure that this test triggers an assert.
+TEST(MemoryReader, NullPointer) {
+ MemoryReader memory_reader(memory_buffer);
+ memory_reader.Read(nullptr, 21);
+}
+
#endif // CHECK_TEST_CRASHES
+TEST(MemoryReader, SingleFullRead) {
+ constexpr size_t kTempBufferSize = 32;
+
+ std::array<std::byte, kTempBufferSize> source;
+ std::array<std::byte, kTempBufferSize> dest;
+
+ uint8_t counter = 0;
+ for (std::byte& value : source) {
+ value = std::byte(counter++);
+ }
+
+ MemoryReader memory_reader(source);
+
+ // Read exactly the available bytes.
+ EXPECT_EQ(memory_reader.ConservativeReadLimit(), dest.size());
+ Result<ByteSpan> result = memory_reader.Read(dest);
+ EXPECT_EQ(result.status(), Status::OK);
+ EXPECT_EQ(result.value().size_bytes(), dest.size());
+
+ ASSERT_EQ(source.size(), result.value().size_bytes());
+ for (size_t i = 0; i < source.size(); i++) {
+ EXPECT_EQ(source[i], result.value()[i]);
+ }
+
+ // Shoud be no byte remaining.
+ EXPECT_EQ(memory_reader.ConservativeReadLimit(), 0u);
+ result = memory_reader.Read(dest);
+ EXPECT_EQ(result.status(), Status::OUT_OF_RANGE);
+}
+
+TEST(MemoryReader, EmptySpanRead) {
+ constexpr size_t kTempBufferSize = 32;
+ std::array<std::byte, kTempBufferSize> source;
+
+ // Use a span with nullptr and zero length;
+ ByteSpan dest(nullptr, 0);
+ EXPECT_EQ(dest.size_bytes(), 0u);
+
+ MemoryReader memory_reader(source);
+
+ // Read exactly the available bytes.
+ Result<ByteSpan> result = memory_reader.Read(dest);
+ EXPECT_EQ(result.status(), Status::OK);
+ EXPECT_EQ(result.value().size_bytes(), 0u);
+ EXPECT_EQ(result.value().data(), dest.data());
+
+ // Shoud be original bytes remaining.
+ EXPECT_EQ(memory_reader.ConservativeReadLimit(), source.size());
+}
+
+TEST(MemoryReader, SinglePartialRead) {
+ constexpr size_t kTempBufferSize = 32;
+ std::array<std::byte, kTempBufferSize> source;
+ std::array<std::byte, kTempBufferSize * 2> dest;
+
+ uint8_t counter = 0;
+ for (std::byte& value : source) {
+ value = std::byte(counter++);
+ }
+
+ MemoryReader memory_reader(source);
+
+ // Try and read double the bytes available. Use the pointer/size version of
+ // the API.
+ Result<ByteSpan> result = memory_reader.Read(dest.data(), dest.size());
+ EXPECT_EQ(result.status(), Status::OK);
+ EXPECT_EQ(result.value().size_bytes(), source.size());
+
+ ASSERT_EQ(source.size(), result.value().size_bytes());
+ for (size_t i = 0; i < source.size(); i++) {
+ EXPECT_EQ(source[i], result.value()[i]);
+ }
+
+ // Shoud be no byte remaining.
+ EXPECT_EQ(memory_reader.ConservativeReadLimit(), 0u);
+ result = memory_reader.Read(dest);
+ EXPECT_EQ(result.status(), Status::OUT_OF_RANGE);
+}
+
+TEST(MemoryReader, MultipleReads) {
+ constexpr size_t kTempBufferSize = 32;
+
+ std::array<std::byte, kTempBufferSize * 5> source;
+ std::array<std::byte, kTempBufferSize> dest;
+
+ uint8_t counter = 0;
+
+ for (std::byte& value : source) {
+ value = std::byte(counter++);
+ }
+
+ MemoryReader memory_reader(source);
+
+ size_t source_chunk_base = 0;
+
+ while (memory_reader.ConservativeReadLimit() > 0) {
+ size_t read_limit = memory_reader.ConservativeReadLimit();
+
+ // Try and read a chunk of bytes.
+ Result<ByteSpan> result = memory_reader.Read(dest);
+ EXPECT_EQ(result.status(), Status::OK);
+ EXPECT_EQ(result.value().size_bytes(), dest.size());
+ EXPECT_EQ(memory_reader.ConservativeReadLimit(),
+ read_limit - result.value().size_bytes());
+
+ // Verify the chunk of byte that was read.
+ for (size_t i = 0; i < result.value().size_bytes(); i++) {
+ EXPECT_EQ(source[source_chunk_base + i], result.value()[i]);
+ }
+ source_chunk_base += result.value().size_bytes();
+ }
+}
+
} // namespace
} // namespace pw::stream