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