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 "gtest/gtest.h"
20 #include "pw_stream/null_stream.h"
21
22 namespace pw::stream {
23 namespace {
24
25 static_assert(sizeof(Stream) <= 2 * sizeof(void*),
26 "Stream should be no larger than two pointers (vtable pointer & "
27 "packed members)");
28
29 class TestNonSeekableReader : public NonSeekableReader {
30 private:
DoRead(ByteSpan)31 StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
32 };
33
34 class TestRelativeSeekableReader : public RelativeSeekableReader {
35 private:
DoRead(ByteSpan)36 StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
DoSeek(ptrdiff_t,Whence)37 Status DoSeek(ptrdiff_t, Whence) override { return Status(); }
38 };
39
40 class TestSeekableReader : public SeekableReader {
41 private:
DoRead(ByteSpan)42 StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
DoSeek(ptrdiff_t,Whence)43 Status DoSeek(ptrdiff_t, Whence) override { return Status(); }
44 };
45
46 class TestNonSeekableWriter : public NonSeekableWriter {
47 private:
DoWrite(ConstByteSpan)48 Status DoWrite(ConstByteSpan) override { return OkStatus(); }
49 };
50
51 class TestRelativeSeekableWriter : public RelativeSeekableWriter {
52 private:
DoWrite(ConstByteSpan)53 Status DoWrite(ConstByteSpan) override { return OkStatus(); }
DoSeek(ptrdiff_t,Whence)54 Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); }
55 };
56
57 class TestSeekableWriter : public SeekableWriter {
58 private:
DoWrite(ConstByteSpan)59 Status DoWrite(ConstByteSpan) override { return OkStatus(); }
DoSeek(ptrdiff_t,Whence)60 Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); }
61 };
62
63 class TestNonSeekableReaderWriter : public NonSeekableReaderWriter {
64 private:
DoRead(ByteSpan)65 StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
DoWrite(ConstByteSpan)66 Status DoWrite(ConstByteSpan) override { return OkStatus(); }
67 };
68
69 class TestRelativeSeekableReaderWriter : public RelativeSeekableReaderWriter {
70 private:
DoRead(ByteSpan)71 StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
DoWrite(ConstByteSpan)72 Status DoWrite(ConstByteSpan) override { return OkStatus(); }
DoSeek(ptrdiff_t,Whence)73 Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); }
74 };
75
76 class TestSeekableReaderWriter : public SeekableReaderWriter {
77 private:
DoRead(ByteSpan)78 StatusWithSize DoRead(ByteSpan) override { return StatusWithSize(0); }
DoWrite(ConstByteSpan)79 Status DoWrite(ConstByteSpan) override { return OkStatus(); }
DoSeek(ptrdiff_t,Whence)80 Status DoSeek(ptrdiff_t, Whence) override { return OkStatus(); }
81 };
82
83 // Test ReaderWriter conversions to Reader/Writer.
84 // clang-format off
85 static_assert(std::is_convertible<TestNonSeekableReaderWriter, Reader&>());
86 static_assert(std::is_convertible<TestNonSeekableReaderWriter, Writer&>());
87 static_assert(!std::is_convertible<TestNonSeekableReaderWriter, RelativeSeekableReader&>());
88 static_assert(!std::is_convertible<TestNonSeekableReaderWriter, RelativeSeekableWriter&>());
89 static_assert(!std::is_convertible<TestNonSeekableReaderWriter, SeekableWriter&>());
90 static_assert(!std::is_convertible<TestNonSeekableReaderWriter, SeekableReader&>());
91
92 static_assert(std::is_convertible<TestRelativeSeekableReaderWriter, Reader&>());
93 static_assert(std::is_convertible<TestRelativeSeekableReaderWriter, Writer&>());
94 static_assert(std::is_convertible<TestRelativeSeekableReaderWriter, RelativeSeekableReader&>());
95 static_assert(std::is_convertible<TestRelativeSeekableReaderWriter, RelativeSeekableWriter&>());
96 static_assert(!std::is_convertible<TestRelativeSeekableReaderWriter, SeekableWriter&>());
97 static_assert(!std::is_convertible<TestRelativeSeekableReaderWriter, SeekableReader&>());
98
99 static_assert(std::is_convertible<TestSeekableReaderWriter, Reader&>());
100 static_assert(std::is_convertible<TestSeekableReaderWriter, Writer&>());
101 static_assert(std::is_convertible<TestSeekableReaderWriter, RelativeSeekableReader&>());
102 static_assert(std::is_convertible<TestSeekableReaderWriter, RelativeSeekableWriter&>());
103 static_assert(std::is_convertible<TestSeekableReaderWriter, SeekableWriter&>());
104 static_assert(std::is_convertible<TestSeekableReaderWriter, SeekableReader&>());
105 // clang-format on
106
107 constexpr uint8_t kSeekable =
108 Stream::kBeginning | Stream::kCurrent | Stream::kEnd;
109 constexpr uint8_t kRelativeSeekable = Stream::kCurrent;
110 constexpr uint8_t kNonSeekable = 0;
111
112 enum Readable : bool { kNonReadable = false, kReadable = true };
113 enum Writable : bool { kNonWritable = false, kWritable = true };
114
115 template <typename T, Readable readable, Writable writable, uint8_t seekable>
TestStreamImpl()116 void TestStreamImpl() {
117 T derived_stream;
118 Stream& stream = derived_stream;
119
120 // Check stream properties
121 ASSERT_EQ(writable, stream.writable());
122 ASSERT_EQ(readable, stream.readable());
123
124 ASSERT_EQ((seekable & Stream::kBeginning) != 0,
125 stream.seekable(Stream::kBeginning));
126 ASSERT_EQ((seekable & Stream::kCurrent) != 0,
127 stream.seekable(Stream::kCurrent));
128 ASSERT_EQ((seekable & Stream::kEnd) != 0, stream.seekable(Stream::kEnd));
129
130 ASSERT_EQ(seekable != kNonSeekable, stream.seekable());
131
132 // Check Read()/Write()/Seek()
133 ASSERT_EQ(readable ? OkStatus() : Status::Unimplemented(),
134 stream.Read({}).status());
135 ASSERT_EQ(writable ? OkStatus() : Status::Unimplemented(), stream.Write({}));
136 ASSERT_EQ(seekable ? OkStatus() : Status::Unimplemented(), stream.Seek(0));
137
138 // Check ConservativeLimits()
139 ASSERT_EQ(readable ? Stream::kUnlimited : 0, stream.ConservativeReadLimit());
140 ASSERT_EQ(writable ? Stream::kUnlimited : 0, stream.ConservativeWriteLimit());
141 }
142
TEST(Stream,NonSeekableReader)143 TEST(Stream, NonSeekableReader) {
144 TestStreamImpl<TestNonSeekableReader,
145 kReadable,
146 kNonWritable,
147 kNonSeekable>();
148 }
149
TEST(Stream,RelativeSeekableReader)150 TEST(Stream, RelativeSeekableReader) {
151 TestStreamImpl<TestRelativeSeekableReader,
152 kReadable,
153 kNonWritable,
154 kRelativeSeekable>();
155 }
156
TEST(Stream,SeekableReader)157 TEST(Stream, SeekableReader) {
158 TestStreamImpl<TestSeekableReader, kReadable, kNonWritable, kSeekable>();
159 }
160
TEST(Stream,NonSeekableWriter)161 TEST(Stream, NonSeekableWriter) {
162 TestStreamImpl<TestNonSeekableWriter,
163 kNonReadable,
164 kWritable,
165 kNonSeekable>();
166 }
167
TEST(Stream,RelativeSeekableWriter)168 TEST(Stream, RelativeSeekableWriter) {
169 TestStreamImpl<TestRelativeSeekableWriter,
170 kNonReadable,
171 kWritable,
172 kRelativeSeekable>();
173 }
174
TEST(Stream,SeekableWriter)175 TEST(Stream, SeekableWriter) {
176 TestStreamImpl<TestSeekableWriter, kNonReadable, kWritable, kSeekable>();
177 }
178
TEST(Stream,NonSeekableReaderWriter)179 TEST(Stream, NonSeekableReaderWriter) {
180 TestStreamImpl<TestNonSeekableReaderWriter,
181 kReadable,
182 kWritable,
183 kNonSeekable>();
184 }
185
TEST(Stream,RelativeSeekableReaderWriter)186 TEST(Stream, RelativeSeekableReaderWriter) {
187 TestStreamImpl<TestRelativeSeekableReaderWriter,
188 kReadable,
189 kWritable,
190 kRelativeSeekable>();
191 }
192
TEST(Stream,SeekableReaderWriter)193 TEST(Stream, SeekableReaderWriter) {
194 TestStreamImpl<TestSeekableReaderWriter, kReadable, kWritable, kSeekable>();
195 }
196
TEST(NullStream,DefaultConservativeWriteLimit)197 TEST(NullStream, DefaultConservativeWriteLimit) {
198 NullStream stream;
199 EXPECT_EQ(stream.ConservativeWriteLimit(), Stream::kUnlimited);
200 }
201
TEST(NullStream,DefaultConservativeReadLimit)202 TEST(NullStream, DefaultConservativeReadLimit) {
203 NullStream stream;
204 EXPECT_EQ(stream.ConservativeReadLimit(), Stream::kUnlimited);
205 }
206
TEST(NullStream,DefaultConservativeReadWriteLimit)207 TEST(NullStream, DefaultConservativeReadWriteLimit) {
208 NullStream stream;
209 EXPECT_EQ(stream.ConservativeWriteLimit(), Stream::kUnlimited);
210 EXPECT_EQ(stream.ConservativeReadLimit(), Stream::kUnlimited);
211 }
212
TEST(NullStream,DefaultTell)213 TEST(NullStream, DefaultTell) {
214 NullStream stream;
215 EXPECT_EQ(stream.Tell(), Stream::kUnknownPosition);
216 }
217
218 } // namespace
219 } // namespace pw::stream
220