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