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