blob: 39763a619854b24e42715d23ac129ab8b628f5fb [file] [log] [blame]
// 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