| // Copyright 2024 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include "pw_multibuf/stream.h" |
| |
| #include "pw_bytes/array.h" |
| #include "pw_multibuf/multibuf.h" |
| #include "pw_multibuf_private/test_utils.h" |
| #include "pw_status/status.h" |
| #include "pw_unit_test/framework.h" |
| |
| namespace pw::multibuf { |
| namespace { |
| |
| using namespace pw::multibuf::test_utils; |
| |
| constexpr auto kData64 = bytes::Initialized<64>([](size_t i) { return i; }); |
| |
| TEST(MultibufStream, Write_SingleChunkMultibuf_Succeeds) { |
| AllocatorForTest<kArbitraryAllocatorSize> allocator; |
| MultiBuf buf; |
| buf.PushFrontChunk(MakeChunk(allocator, 128, kPoisonByte)); |
| Stream writer(buf); |
| EXPECT_EQ(writer.Write(kData64), OkStatus()); |
| |
| ExpectElementsEqual(buf, kData64); |
| buf.DiscardPrefix(kData64.size()); |
| ExpectElementsAre(buf, std::byte{kPoisonByte}); |
| } |
| |
| TEST(MultibufStream, Write_SingleChunkMultibuf_ExactSize_Succeeds) { |
| AllocatorForTest<kArbitraryAllocatorSize> allocator; |
| MultiBuf buf; |
| buf.PushFrontChunk(MakeChunk(allocator, kData64.size(), kPoisonByte)); |
| Stream writer(buf); |
| EXPECT_EQ(writer.Write(kData64), OkStatus()); |
| |
| EXPECT_EQ(buf.size(), kData64.size()); |
| ExpectElementsEqual(buf, kData64); |
| } |
| |
| TEST(MultibufStream, Write_MultiChunkMultibuf_Succeeds) { |
| AllocatorForTest<kArbitraryAllocatorSize> allocator; |
| MultiBuf buf; |
| buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 24, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte)); |
| Stream writer(buf); |
| ASSERT_EQ(writer.Write(kData64), OkStatus()); |
| |
| ExpectElementsEqual(buf, kData64); |
| } |
| |
| TEST(MultibufStream, Write_MultiChunkMultibuf_OutOfRange) { |
| AllocatorForTest<kArbitraryAllocatorSize> allocator; |
| MultiBuf buf; |
| buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte)); |
| Stream writer(buf); |
| ASSERT_EQ(writer.Write(kData64), Status::OutOfRange()); |
| |
| ExpectElementsEqual(buf, span(kData64).first(24)); |
| } |
| |
| TEST(MultibufStream, Write_EmptyMultibuf_ReturnsOutOfRange) { |
| MultiBuf buf; |
| Stream writer(buf); |
| EXPECT_EQ(writer.Write(kData64), Status::OutOfRange()); |
| } |
| |
| TEST(MultibufStream, Seek_Empty) { |
| MultiBuf buf; |
| Stream writer(buf); |
| EXPECT_EQ(writer.Seek(0), Status::OutOfRange()); |
| EXPECT_EQ(writer.Seek(-100), Status::OutOfRange()); |
| EXPECT_EQ(writer.Seek(100), Status::OutOfRange()); |
| } |
| |
| TEST(MultibufStream, Seek_OutOfBounds) { |
| AllocatorForTest<kArbitraryAllocatorSize> allocator; |
| MultiBuf buf; |
| buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte)); |
| Stream writer(buf); |
| EXPECT_EQ(writer.Seek(-1), Status::OutOfRange()); |
| EXPECT_EQ(writer.Seek(buf.size()), Status::OutOfRange()); |
| } |
| |
| TEST(MultibufStream, Seek_SingleChunkMultibuf_Succeeds) { |
| AllocatorForTest<kArbitraryAllocatorSize> allocator; |
| MultiBuf buf; |
| buf.PushFrontChunk(MakeChunk(allocator, 64, kPoisonByte)); |
| Stream writer(buf); |
| EXPECT_EQ(writer.Seek(32), OkStatus()); |
| EXPECT_EQ(writer.Write(bytes::Initialized<8>(2)), OkStatus()); |
| EXPECT_EQ(writer.Seek(40), OkStatus()); |
| EXPECT_EQ(writer.Write(bytes::Initialized<24>(1)), OkStatus()); |
| |
| constexpr auto kExpected = |
| bytes::Concat(bytes::Initialized<32>(static_cast<uint8_t>(kPoisonByte)), |
| bytes::Initialized<8>(2), |
| bytes::Initialized<24>(1)); |
| |
| ExpectElementsEqual(buf, kExpected); |
| } |
| |
| TEST(MultibufStream, Seek_MultiChunkMultiBuf_Succeeds) { |
| AllocatorForTest<kArbitraryAllocatorSize> allocator; |
| MultiBuf buf; |
| buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte)); |
| Stream writer(buf); |
| EXPECT_EQ(writer.Seek(32), OkStatus()); |
| EXPECT_EQ(writer.Write(bytes::Initialized<8>(1)), OkStatus()); |
| EXPECT_EQ(writer.Seek(40), OkStatus()); |
| EXPECT_EQ(writer.Write(bytes::Initialized<24>(2)), OkStatus()); |
| |
| constexpr auto kExpected = |
| bytes::Concat(bytes::Initialized<32>(static_cast<uint8_t>(kPoisonByte)), |
| bytes::Initialized<8>(1), |
| bytes::Initialized<24>(2)); |
| |
| ExpectElementsEqual(buf, kExpected); |
| } |
| |
| TEST(MultibufStream, Seek_Backwards_ReturnsOutOfRange) { |
| AllocatorForTest<kArbitraryAllocatorSize> allocator; |
| MultiBuf buf; |
| buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 8, kPoisonByte)); |
| buf.PushFrontChunk(MakeChunk(allocator, 16, kPoisonByte)); |
| Stream writer(buf); |
| EXPECT_EQ(writer.Seek(32), OkStatus()); |
| EXPECT_EQ(writer.Seek(30), Status::OutOfRange()); |
| EXPECT_EQ(writer.Seek(48), OkStatus()); |
| EXPECT_EQ(writer.Seek(-4, Stream::Whence::kCurrent), Status::OutOfRange()); |
| EXPECT_EQ(writer.Seek(60), OkStatus()); |
| EXPECT_EQ(writer.Seek(64), Status::OutOfRange()); |
| } |
| |
| TEST(MultibufStream, Read_EmptyMultibuf_ReturnsOutOfRange) { |
| auto destination = bytes::Initialized<64>(static_cast<uint8_t>(kPoisonByte)); |
| MultiBuf buf; |
| Stream reader(buf); |
| EXPECT_EQ(reader.Read(destination).status(), Status::OutOfRange()); |
| ExpectElementsAre(destination, kPoisonByte); |
| } |
| |
| TEST(MultibufStream, Read_SingleChunkMultiBuf_Succeeds) { |
| AllocatorForTest<kArbitraryAllocatorSize> allocator; |
| auto destination = bytes::Initialized<64>(static_cast<uint8_t>(kPoisonByte)); |
| MultiBuf buf; |
| buf.PushFrontChunk(MakeChunk(allocator, 16, std::byte{1})); |
| Stream reader(buf); |
| |
| Result<ByteSpan> result = reader.Read(destination); |
| EXPECT_EQ(result.status(), OkStatus()); |
| EXPECT_EQ(result->size(), 16u); |
| ExpectElementsAre(*result, std::byte{1}); |
| |
| result = reader.Read(destination); |
| EXPECT_EQ(result.status(), Status::OutOfRange()); |
| ExpectElementsAre(span(destination).first(16), std::byte{1}); |
| ExpectElementsAre(span(destination).subspan(16), kPoisonByte); |
| } |
| |
| TEST(MultibufStream, Read_MultiChunkMultiBuf_Succeeds) { |
| AllocatorForTest<kArbitraryAllocatorSize> allocator; |
| auto destination = bytes::Initialized<64>(static_cast<uint8_t>(kPoisonByte)); |
| MultiBuf buf; |
| buf.PushFrontChunk(MakeChunk(allocator, 16, std::byte{2})); |
| buf.PushFrontChunk(MakeChunk(allocator, 8, std::byte{3})); |
| buf.PushFrontChunk(MakeChunk(allocator, 8, std::byte{4})); |
| Stream reader(buf); |
| |
| constexpr auto kExpected = bytes::Concat(bytes::Initialized<8>(4), |
| bytes::Initialized<8>(3), |
| bytes::Initialized<16>(2)); |
| |
| Result<ByteSpan> result = reader.Read(destination); |
| EXPECT_EQ(result.status(), OkStatus()); |
| EXPECT_EQ(result->size(), 32u); |
| ExpectElementsEqual(*result, kExpected); |
| |
| result = reader.Read(destination); |
| EXPECT_EQ(result.status(), Status::OutOfRange()); |
| ExpectElementsEqual(span(destination).first(32), kExpected); |
| ExpectElementsAre(span(destination).subspan(32), kPoisonByte); |
| } |
| |
| } // namespace |
| } // namespace pw::multibuf |