xref: /aosp_15_r20/external/pigweed/pw_stream/stream_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_stream/stream.h"
16 
17 #include <limits>
18 
19 #include "pw_assert/check.h"
20 #include "pw_bytes/array.h"
21 #include "pw_bytes/span.h"
22 #include "pw_containers/to_array.h"
23 #include "pw_span/span.h"
24 #include "pw_unit_test/framework.h"
25 
26 namespace pw::stream {
27 namespace {
28 
29 static_assert(sizeof(Stream) <= 2 * sizeof(void*),
30               "Stream should be no larger than two pointers (vtable pointer & "
31               "packed members)");
32 
33 class TestNonSeekableReader : public NonSeekableReader {
34  private:
DoRead(ByteSpan)35   StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
36 };
37 
38 class TestRelativeSeekableReader : public RelativeSeekableReader {
39  private:
DoRead(ByteSpan)40   StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
DoSeek(ptrdiff_t,Whence)41   Status DoSeek(ptrdiff_t, Whence) override { return Status(); }
42 };
43 
44 class TestSeekableReader : public SeekableReader {
45  private:
DoRead(ByteSpan)46   StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
DoSeek(ptrdiff_t,Whence)47   Status DoSeek(ptrdiff_t, Whence) override { return Status(); }
48 };
49 
50 class TestNonSeekableWriter : public NonSeekableWriter {
51  private:
DoWrite(ConstByteSpan)52   Status DoWrite(ConstByteSpan) override { return OkStatus(); }
53 };
54 
55 class TestRelativeSeekableWriter : public RelativeSeekableWriter {
56  private:
DoWrite(ConstByteSpan)57   Status DoWrite(ConstByteSpan) override { return OkStatus(); }
DoSeek(ptrdiff_t,Whence)58   Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); }
59 };
60 
61 class TestSeekableWriter : public SeekableWriter {
62  private:
DoWrite(ConstByteSpan)63   Status DoWrite(ConstByteSpan) override { return OkStatus(); }
DoSeek(ptrdiff_t,Whence)64   Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); }
65 };
66 
67 class TestNonSeekableReaderWriter : public NonSeekableReaderWriter {
68  private:
DoRead(ByteSpan)69   StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
DoWrite(ConstByteSpan)70   Status DoWrite(ConstByteSpan) override { return OkStatus(); }
71 };
72 
73 class TestRelativeSeekableReaderWriter : public RelativeSeekableReaderWriter {
74  private:
DoRead(ByteSpan)75   StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
DoWrite(ConstByteSpan)76   Status DoWrite(ConstByteSpan) override { return OkStatus(); }
DoSeek(ptrdiff_t,Whence)77   Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); }
78 };
79 
80 class TestSeekableReaderWriter : public SeekableReaderWriter {
81  private:
DoRead(ByteSpan)82   StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
DoWrite(ConstByteSpan)83   Status DoWrite(ConstByteSpan) override { return OkStatus(); }
DoSeek(ptrdiff_t,Whence)84   Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); }
85 };
86 
87 // Test ReaderWriter conversions to Reader/Writer.
88 // clang-format off
89 static_assert(std::is_convertible<TestNonSeekableReaderWriter, Reader&>());
90 static_assert(std::is_convertible<TestNonSeekableReaderWriter, Writer&>());
91 static_assert(!std::is_convertible<TestNonSeekableReaderWriter, RelativeSeekableReader&>());
92 static_assert(!std::is_convertible<TestNonSeekableReaderWriter, RelativeSeekableWriter&>());
93 static_assert(!std::is_convertible<TestNonSeekableReaderWriter, SeekableWriter&>());
94 static_assert(!std::is_convertible<TestNonSeekableReaderWriter, SeekableReader&>());
95 
96 static_assert(std::is_convertible<TestRelativeSeekableReaderWriter, Reader&>());
97 static_assert(std::is_convertible<TestRelativeSeekableReaderWriter, Writer&>());
98 static_assert(std::is_convertible<TestRelativeSeekableReaderWriter, RelativeSeekableReader&>());
99 static_assert(std::is_convertible<TestRelativeSeekableReaderWriter, RelativeSeekableWriter&>());
100 static_assert(!std::is_convertible<TestRelativeSeekableReaderWriter, SeekableWriter&>());
101 static_assert(!std::is_convertible<TestRelativeSeekableReaderWriter, SeekableReader&>());
102 
103 static_assert(std::is_convertible<TestSeekableReaderWriter, Reader&>());
104 static_assert(std::is_convertible<TestSeekableReaderWriter, Writer&>());
105 static_assert(std::is_convertible<TestSeekableReaderWriter, RelativeSeekableReader&>());
106 static_assert(std::is_convertible<TestSeekableReaderWriter, RelativeSeekableWriter&>());
107 static_assert(std::is_convertible<TestSeekableReaderWriter, SeekableWriter&>());
108 static_assert(std::is_convertible<TestSeekableReaderWriter, SeekableReader&>());
109 // clang-format on
110 
111 constexpr uint8_t kSeekable =
112     Stream::kBeginning | Stream::kCurrent | Stream::kEnd;
113 constexpr uint8_t kRelativeSeekable = Stream::kCurrent;
114 constexpr uint8_t kNonSeekable = 0;
115 
116 enum Readable : bool { kNonReadable = false, kReadable = true };
117 enum Writable : bool { kNonWritable = false, kWritable = true };
118 
119 template <typename T, Readable readable, Writable writable, uint8_t seekable>
TestStreamImpl()120 void TestStreamImpl() {
121   T derived_stream;
122   Stream& stream = derived_stream;
123 
124   // Check stream properties
125   ASSERT_EQ(writable, stream.writable());
126   ASSERT_EQ(readable, stream.readable());
127 
128   ASSERT_EQ((seekable & Stream::kBeginning) != 0,
129             stream.seekable(Stream::kBeginning));
130   ASSERT_EQ((seekable & Stream::kCurrent) != 0,
131             stream.seekable(Stream::kCurrent));
132   ASSERT_EQ((seekable & Stream::kEnd) != 0, stream.seekable(Stream::kEnd));
133 
134   ASSERT_EQ(seekable != kNonSeekable, stream.seekable());
135 
136   // Check Read()/Write()/Seek()
137   ASSERT_EQ(readable ? OkStatus() : Status::Unimplemented(),
138             stream.Read({}).status());
139   ASSERT_EQ(writable ? OkStatus() : Status::Unimplemented(), stream.Write({}));
140   ASSERT_EQ(seekable ? OkStatus() : Status::Unimplemented(), stream.Seek(0));
141 
142   // Check ConservativeLimits()
143   ASSERT_EQ(readable ? Stream::kUnlimited : 0, stream.ConservativeReadLimit());
144   ASSERT_EQ(writable ? Stream::kUnlimited : 0, stream.ConservativeWriteLimit());
145 }
146 
TEST(Stream,NonSeekableReader)147 TEST(Stream, NonSeekableReader) {
148   TestStreamImpl<TestNonSeekableReader,
149                  kReadable,
150                  kNonWritable,
151                  kNonSeekable>();
152 }
153 
TEST(Stream,RelativeSeekableReader)154 TEST(Stream, RelativeSeekableReader) {
155   TestStreamImpl<TestRelativeSeekableReader,
156                  kReadable,
157                  kNonWritable,
158                  kRelativeSeekable>();
159 }
160 
TEST(Stream,SeekableReader)161 TEST(Stream, SeekableReader) {
162   TestStreamImpl<TestSeekableReader, kReadable, kNonWritable, kSeekable>();
163 }
164 
TEST(Stream,NonSeekableWriter)165 TEST(Stream, NonSeekableWriter) {
166   TestStreamImpl<TestNonSeekableWriter,
167                  kNonReadable,
168                  kWritable,
169                  kNonSeekable>();
170 }
171 
TEST(Stream,RelativeSeekableWriter)172 TEST(Stream, RelativeSeekableWriter) {
173   TestStreamImpl<TestRelativeSeekableWriter,
174                  kNonReadable,
175                  kWritable,
176                  kRelativeSeekable>();
177 }
178 
TEST(Stream,SeekableWriter)179 TEST(Stream, SeekableWriter) {
180   TestStreamImpl<TestSeekableWriter, kNonReadable, kWritable, kSeekable>();
181 }
182 
TEST(Stream,NonSeekableReaderWriter)183 TEST(Stream, NonSeekableReaderWriter) {
184   TestStreamImpl<TestNonSeekableReaderWriter,
185                  kReadable,
186                  kWritable,
187                  kNonSeekable>();
188 }
189 
TEST(Stream,RelativeSeekableReaderWriter)190 TEST(Stream, RelativeSeekableReaderWriter) {
191   TestStreamImpl<TestRelativeSeekableReaderWriter,
192                  kReadable,
193                  kWritable,
194                  kRelativeSeekable>();
195 }
196 
TEST(Stream,SeekableReaderWriter)197 TEST(Stream, SeekableReaderWriter) {
198   TestStreamImpl<TestSeekableReaderWriter, kReadable, kWritable, kSeekable>();
199 }
200 
201 class TestFragmentedReader : public NonSeekableReader {
202  public:
TestFragmentedReader(ConstByteSpan data,span<StatusWithSize> frags)203   TestFragmentedReader(ConstByteSpan data, span<StatusWithSize> frags)
204       : data_(data), frags_(frags), current_frag_(frags.begin()) {
205     size_t frags_sum = 0;
206     for (const auto& frag : frags) {
207       frags_sum += frag.size();
208     }
209     PW_CHECK_UINT_LE(frags_sum, data.size());
210   }
211 
212  private:
DoRead(ByteSpan dest)213   StatusWithSize DoRead(ByteSpan dest) override {
214     // Each fragment is consumed entirely on each read.
215     PW_CHECK_UINT_GE(dest.size(), current_frag_->size());
216     PW_CHECK(current_frag_ != frags_.end());
217 
218     auto frag = current_frag_++;
219     auto source = data_.subspan(data_offset_, frag->size());
220     data_offset_ += frag->size();
221 
222     std::copy(source.begin(), source.end(), dest.begin());
223     return *frag;
224   }
225 
226   ConstByteSpan data_;
227   span<StatusWithSize> frags_;
228   decltype(frags_)::iterator current_frag_;
229   size_t data_offset_ = 0;
230 };
231 
TEST(Stream,ReadExact_Works)232 TEST(Stream, ReadExact_Works) {
233   constexpr auto kData = bytes::
234       Array<0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A>();
235 
236   auto frags = containers::to_array<StatusWithSize>({
237       StatusWithSize(3),  // 0x00, 0x01, 0x02
238       StatusWithSize(5),  // 0x03, 0x04, 0x05, 0x06, 0x07
239       StatusWithSize(1),  // 0x08
240       StatusWithSize(2),  // 0x09, 0x0A
241   });
242 
243   TestFragmentedReader reader(kData, frags);
244 
245   std::array<std::byte, kData.size()> dest;
246   auto result = reader.ReadExact(dest);
247 
248   PW_TEST_ASSERT_OK(result);
249   EXPECT_EQ(result->data(), dest.data());
250   EXPECT_EQ(result->size(), dest.size());
251   EXPECT_TRUE(std::equal(result->begin(), result->end(), kData.begin()));
252 }
253 
TEST(Stream,ReadExact_HandlesError)254 TEST(Stream, ReadExact_HandlesError) {
255   constexpr auto kData = bytes::
256       Array<0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A>();
257 
258   auto frags = containers::to_array<StatusWithSize>({
259       StatusWithSize(3),            // 0x00, 0x01, 0x02
260       StatusWithSize(5),            // 0x03, 0x04, 0x05, 0x06, 0x07
261       StatusWithSize::Internal(1),  // 0x08
262       StatusWithSize(2),            // 0x09, 0x0A
263   });
264 
265   TestFragmentedReader reader(kData, frags);
266 
267   std::array<std::byte, kData.size()> dest;
268   auto result = reader.ReadExact(dest);
269 
270   EXPECT_EQ(result.status(), Status::Internal());
271 }
272 
273 }  // namespace
274 }  // namespace pw::stream
275