1 // Copyright 2024 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_multibuf/stream.h"
16
17 #include "pw_bytes/array.h"
18 #include "pw_multibuf/multibuf.h"
19 #include "pw_multibuf_private/test_utils.h"
20 #include "pw_status/status.h"
21 #include "pw_unit_test/framework.h"
22
23 namespace pw::multibuf {
24 namespace {
25
26 using namespace pw::multibuf::test_utils;
27
__anon9fdcf5f70202(size_t i) 28 constexpr auto kData64 = bytes::Initialized<64>([](size_t i) { return i; });
29
TEST(MultibufStream,Write_SingleChunkMultibuf_Succeeds)30 TEST(MultibufStream, Write_SingleChunkMultibuf_Succeeds) {
31 AllocatorForTest<kArbitraryAllocatorSize> allocator;
32 MultiBuf buf;
33 buf.PushFrontChunk(MakeChunk(allocator, 128, kPoisonByte));
34 Stream writer(buf);
35 EXPECT_EQ(writer.Write(kData64), OkStatus());
36
37 ExpectElementsEqual(buf, kData64);
38 buf.DiscardPrefix(kData64.size());
39 ExpectElementsAre(buf, std::byte{kPoisonByte});
40 }
41
TEST(MultibufStream,Write_SingleChunkMultibuf_ExactSize_Succeeds)42 TEST(MultibufStream, Write_SingleChunkMultibuf_ExactSize_Succeeds) {
43 AllocatorForTest<kArbitraryAllocatorSize> allocator;
44 MultiBuf buf;
45 buf.PushFrontChunk(MakeChunk(allocator, kData64.size(), kPoisonByte));
46 Stream writer(buf);
47 EXPECT_EQ(writer.Write(kData64), OkStatus());
48
49 EXPECT_EQ(buf.size(), kData64.size());
50 ExpectElementsEqual(buf, kData64);
51 }
52
TEST(MultibufStream,Write_MultiChunkMultibuf_Succeeds)53 TEST(MultibufStream, Write_MultiChunkMultibuf_Succeeds) {
54 AllocatorForTest<kArbitraryAllocatorSize> allocator;
55 MultiBuf buf;
56 buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte));
57 buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte));
58 buf.PushFrontChunk(MakeChunk(allocator, 24, kPoisonByte));
59 buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte));
60 Stream writer(buf);
61 ASSERT_EQ(writer.Write(kData64), OkStatus());
62
63 ExpectElementsEqual(buf, kData64);
64 }
65
TEST(MultibufStream,Write_MultiChunkMultibuf_OutOfRange)66 TEST(MultibufStream, Write_MultiChunkMultibuf_OutOfRange) {
67 AllocatorForTest<kArbitraryAllocatorSize> allocator;
68 MultiBuf buf;
69 buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte));
70 buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte));
71 Stream writer(buf);
72 ASSERT_EQ(writer.Write(kData64), Status::OutOfRange());
73
74 ExpectElementsEqual(buf, span(kData64).first(24));
75 }
76
TEST(MultibufStream,Write_EmptyMultibuf_ReturnsOutOfRange)77 TEST(MultibufStream, Write_EmptyMultibuf_ReturnsOutOfRange) {
78 MultiBuf buf;
79 Stream writer(buf);
80 EXPECT_EQ(writer.Write(kData64), Status::OutOfRange());
81 }
82
TEST(MultibufStream,Seek_Empty)83 TEST(MultibufStream, Seek_Empty) {
84 MultiBuf buf;
85 Stream writer(buf);
86 EXPECT_EQ(writer.Seek(0), Status::OutOfRange());
87 EXPECT_EQ(writer.Seek(-100), Status::OutOfRange());
88 EXPECT_EQ(writer.Seek(100), Status::OutOfRange());
89 }
90
TEST(MultibufStream,Seek_OutOfBounds)91 TEST(MultibufStream, Seek_OutOfBounds) {
92 AllocatorForTest<kArbitraryAllocatorSize> allocator;
93 MultiBuf buf;
94 buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte));
95 buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte));
96 Stream writer(buf);
97 EXPECT_EQ(writer.Seek(-1), Status::OutOfRange());
98 EXPECT_EQ(writer.Seek(buf.size()), Status::OutOfRange());
99 }
100
TEST(MultibufStream,Seek_SingleChunkMultibuf_Succeeds)101 TEST(MultibufStream, Seek_SingleChunkMultibuf_Succeeds) {
102 AllocatorForTest<kArbitraryAllocatorSize> allocator;
103 MultiBuf buf;
104 buf.PushFrontChunk(MakeChunk(allocator, 64, kPoisonByte));
105 Stream writer(buf);
106 EXPECT_EQ(writer.Seek(32), OkStatus());
107 EXPECT_EQ(writer.Write(bytes::Initialized<8>(2)), OkStatus());
108 EXPECT_EQ(writer.Seek(40), OkStatus());
109 EXPECT_EQ(writer.Write(bytes::Initialized<24>(1)), OkStatus());
110
111 constexpr auto kExpected =
112 bytes::Concat(bytes::Initialized<32>(static_cast<uint8_t>(kPoisonByte)),
113 bytes::Initialized<8>(2),
114 bytes::Initialized<24>(1));
115
116 ExpectElementsEqual(buf, kExpected);
117 }
118
TEST(MultibufStream,Seek_MultiChunkMultiBuf_Succeeds)119 TEST(MultibufStream, Seek_MultiChunkMultiBuf_Succeeds) {
120 AllocatorForTest<kArbitraryAllocatorSize> allocator;
121 MultiBuf buf;
122 buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte));
123 buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte));
124 buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte));
125 buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte));
126 buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte));
127 Stream writer(buf);
128 EXPECT_EQ(writer.Seek(32), OkStatus());
129 EXPECT_EQ(writer.Write(bytes::Initialized<8>(1)), OkStatus());
130 EXPECT_EQ(writer.Seek(40), OkStatus());
131 EXPECT_EQ(writer.Write(bytes::Initialized<24>(2)), OkStatus());
132
133 constexpr auto kExpected =
134 bytes::Concat(bytes::Initialized<32>(static_cast<uint8_t>(kPoisonByte)),
135 bytes::Initialized<8>(1),
136 bytes::Initialized<24>(2));
137
138 ExpectElementsEqual(buf, kExpected);
139 }
140
TEST(MultibufStream,Seek_Backwards_ReturnsOutOfRange)141 TEST(MultibufStream, Seek_Backwards_ReturnsOutOfRange) {
142 AllocatorForTest<kArbitraryAllocatorSize> allocator;
143 MultiBuf buf;
144 buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte));
145 buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte));
146 buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte));
147 buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte));
148 buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte));
149 Stream writer(buf);
150 EXPECT_EQ(writer.Seek(32), OkStatus());
151 EXPECT_EQ(writer.Seek(30), Status::OutOfRange());
152 EXPECT_EQ(writer.Seek(48), OkStatus());
153 EXPECT_EQ(writer.Seek(-4, Stream::Whence::kCurrent), Status::OutOfRange());
154 EXPECT_EQ(writer.Seek(60), OkStatus());
155 EXPECT_EQ(writer.Seek(64), Status::OutOfRange());
156 }
157
TEST(MultibufStream,Read_EmptyMultibuf_ReturnsOutOfRange)158 TEST(MultibufStream, Read_EmptyMultibuf_ReturnsOutOfRange) {
159 auto destination = bytes::Initialized<64>(static_cast<uint8_t>(kPoisonByte));
160 MultiBuf buf;
161 Stream reader(buf);
162 EXPECT_EQ(reader.Read(destination).status(), Status::OutOfRange());
163 ExpectElementsAre(destination, kPoisonByte);
164 }
165
TEST(MultibufStream,Read_SingleChunkMultiBuf_Succeeds)166 TEST(MultibufStream, Read_SingleChunkMultiBuf_Succeeds) {
167 AllocatorForTest<kArbitraryAllocatorSize> allocator;
168 auto destination = bytes::Initialized<64>(static_cast<uint8_t>(kPoisonByte));
169 MultiBuf buf;
170 buf.PushFrontChunk(MakeChunk(allocator, 16, std::byte{1}));
171 Stream reader(buf);
172
173 Result<ByteSpan> result = reader.Read(destination);
174 EXPECT_EQ(result.status(), OkStatus());
175 EXPECT_EQ(result->size(), 16u);
176 ExpectElementsAre(*result, std::byte{1});
177
178 result = reader.Read(destination);
179 EXPECT_EQ(result.status(), Status::OutOfRange());
180 ExpectElementsAre(span(destination).first(16), std::byte{1});
181 ExpectElementsAre(span(destination).subspan(16), kPoisonByte);
182 }
183
TEST(MultibufStream,Read_MultiChunkMultiBuf_Succeeds)184 TEST(MultibufStream, Read_MultiChunkMultiBuf_Succeeds) {
185 AllocatorForTest<kArbitraryAllocatorSize> allocator;
186 auto destination = bytes::Initialized<64>(static_cast<uint8_t>(kPoisonByte));
187 MultiBuf buf;
188 buf.PushFrontChunk(MakeChunk(allocator, 16, std::byte{2}));
189 buf.PushFrontChunk(MakeChunk(allocator, 8, std::byte{3}));
190 buf.PushFrontChunk(MakeChunk(allocator, 8, std::byte{4}));
191 Stream reader(buf);
192
193 constexpr auto kExpected = bytes::Concat(bytes::Initialized<8>(4),
194 bytes::Initialized<8>(3),
195 bytes::Initialized<16>(2));
196
197 Result<ByteSpan> result = reader.Read(destination);
198 EXPECT_EQ(result.status(), OkStatus());
199 EXPECT_EQ(result->size(), 32u);
200 ExpectElementsEqual(*result, kExpected);
201
202 result = reader.Read(destination);
203 EXPECT_EQ(result.status(), Status::OutOfRange());
204 ExpectElementsEqual(span(destination).first(32), kExpected);
205 ExpectElementsAre(span(destination).subspan(32), kPoisonByte);
206 }
207
208 } // namespace
209 } // namespace pw::multibuf
210