blob: c9391126e04d5e9dc609db4e10c969fc37e9c6c9 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "iterator/vector-extent-iterator.h"
#include <memory>
#include <zxtest/zxtest.h>
#include "iterator/block-iterator.h"
#include "utils.h"
namespace blobfs {
namespace {
// Allocates a blob with the provided number of extents / nodes.
//
// Returns the allocator, the extents, and nodes used.
void TestSetup(size_t blocks, size_t nodes, bool fragmented, MockSpaceManager* space_manager,
std::unique_ptr<Allocator>* out_allocator) {
// Block count is large enough to allow for both fragmentation and the
// allocation of |blocks| extents.
const size_t block_count = 3 * blocks;
ASSERT_NO_FAILURES(InitializeAllocator(block_count, nodes, space_manager, out_allocator));
if (fragmented) {
ASSERT_NO_FAILURES(ForceFragmentation(out_allocator->get(), block_count));
}
}
// Iterate over the null blob.
TEST(VectorExtentIteratorTest, Null) {
MockSpaceManager space_manager;
std::unique_ptr<Allocator> allocator;
fbl::Vector<Extent> allocated_extents;
fbl::Vector<uint32_t> allocated_nodes;
constexpr size_t kAllocatedExtents = 0;
constexpr size_t kAllocatedNodes = 1;
ASSERT_NO_FAILURES(TestSetup(kAllocatedExtents, kAllocatedNodes, /* fragmented=*/true,
&space_manager, &allocator));
fbl::Vector<ReservedExtent> extents;
ASSERT_OK(allocator->ReserveBlocks(kAllocatedExtents, &extents));
ASSERT_EQ(0, extents.size());
VectorExtentIterator iter(extents);
ASSERT_TRUE(iter.Done());
ASSERT_EQ(0, iter.BlockIndex());
}
// Iterate over a blob with some extents.
TEST(VectorExtentIteratorTest, MultiExtent) {
MockSpaceManager space_manager;
std::unique_ptr<Allocator> allocator;
fbl::Vector<Extent> allocated_extents;
fbl::Vector<uint32_t> allocated_nodes;
constexpr size_t kAllocatedExtents = 10;
constexpr size_t kAllocatedNodes = 1;
ASSERT_NO_FAILURES(TestSetup(kAllocatedExtents, kAllocatedNodes, /* fragmented=*/true,
&space_manager, &allocator));
fbl::Vector<ReservedExtent> extents;
ASSERT_OK(allocator->ReserveBlocks(kAllocatedExtents, &extents));
ASSERT_EQ(kAllocatedExtents, extents.size());
VectorExtentIterator iter(extents);
size_t blocks_seen = 0;
for (size_t i = 0; i < kAllocatedExtents; i++) {
ASSERT_FALSE(iter.Done());
const Extent* extent;
ASSERT_OK(iter.Next(&extent));
ASSERT_TRUE(extents[i].extent() == *extent);
blocks_seen += extent->Length();
ASSERT_EQ(blocks_seen, iter.BlockIndex());
}
ASSERT_TRUE(iter.Done());
}
// Test the usage of the BlockIterator over the vector extent iterator.
TEST(VectorExtentIteratorTest, BlockIterator) {
MockSpaceManager space_manager;
std::unique_ptr<Allocator> allocator;
constexpr size_t kAllocatedExtents = 10;
constexpr size_t kAllocatedNodes = 1;
ASSERT_NO_FAILURES(TestSetup(kAllocatedExtents, kAllocatedNodes, /* fragmented=*/true,
&space_manager, &allocator));
fbl::Vector<ReservedExtent> extents;
ASSERT_OK(allocator->ReserveBlocks(kAllocatedExtents, &extents));
ASSERT_EQ(kAllocatedExtents, extents.size());
BlockIterator iter(std::make_unique<VectorExtentIterator>(extents));
ASSERT_EQ(0, iter.BlockIndex());
ASSERT_FALSE(iter.Done());
uint32_t blocks_seen = 0;
for (size_t i = 0; i < extents.size(); i++) {
ASSERT_FALSE(iter.Done());
uint32_t actual_length;
uint64_t actual_start;
ASSERT_OK(iter.Next(1, &actual_length, &actual_start));
ASSERT_EQ(1, actual_length);
ASSERT_EQ(extents[i].extent().Start(), actual_start);
blocks_seen += actual_length;
ASSERT_EQ(blocks_seen, iter.BlockIndex());
}
ASSERT_TRUE(iter.Done());
}
// Test that |IterateToBlock| correctly iterates to the desired block.
TEST(VectorExtentIteratorTest, BlockIteratorRandomStart) {
MockSpaceManager space_manager;
std::unique_ptr<Allocator> allocator;
constexpr size_t kAllocatedExtents = 10;
constexpr size_t kAllocatedNodes = 1;
ASSERT_NO_FAILURES(TestSetup(kAllocatedExtents, kAllocatedNodes, /* fragmented=*/true,
&space_manager, &allocator));
fbl::Vector<ReservedExtent> extents;
ASSERT_OK(allocator->ReserveBlocks(kAllocatedExtents, &extents));
ASSERT_EQ(kAllocatedExtents, extents.size());
for (int i = 0; i < 20; i++) {
BlockIterator iter(std::make_unique<VectorExtentIterator>(extents));
uint32_t block_index = static_cast<uint32_t>(rand() % kAllocatedExtents);
ASSERT_OK(IterateToBlock(&iter, block_index));
ASSERT_EQ(block_index, iter.BlockIndex());
}
BlockIterator iter(std::make_unique<VectorExtentIterator>(extents));
ASSERT_EQ(IterateToBlock(&iter, kAllocatedExtents + 10), ZX_ERR_INVALID_ARGS);
}
void ValidateStreamBlocks(fbl::Vector<ReservedExtent> extents, uint32_t block_count) {
BlockIterator iter(std::make_unique<VectorExtentIterator>(extents));
size_t stream_blocks = 0;
size_t stream_index = 0;
auto stream_callback = [&](uint64_t local_offset, uint64_t dev_offset, uint32_t length) {
ZX_DEBUG_ASSERT(stream_blocks == local_offset);
ZX_DEBUG_ASSERT(extents[stream_index].extent().Start() == dev_offset);
ZX_DEBUG_ASSERT(extents[stream_index].extent().Length() == length);
stream_blocks += length;
stream_index++;
return ZX_OK;
};
ASSERT_OK(StreamBlocks(&iter, block_count, stream_callback));
ASSERT_TRUE(iter.Done());
}
// Test streaming blocks from a fragmented iterator.
TEST(VectorExtentIteratorTest, StreamBlocksFragmented) {
MockSpaceManager space_manager;
std::unique_ptr<Allocator> allocator;
constexpr size_t kAllocatedExtents = 10;
constexpr size_t kAllocatedBlocks = kAllocatedExtents;
constexpr size_t kAllocatedNodes = 1;
ASSERT_NO_FAILURES(TestSetup(kAllocatedBlocks, kAllocatedNodes, /* fragmented=*/true,
&space_manager, &allocator));
fbl::Vector<ReservedExtent> extents;
ASSERT_OK(allocator->ReserveBlocks(kAllocatedBlocks, &extents));
ASSERT_EQ(kAllocatedExtents, extents.size());
ValidateStreamBlocks(std::move(extents), kAllocatedBlocks);
}
// Test streaming blocks from a contiguous iterator.
TEST(VectorExtentIteratorTest, StreamBlocksContiguous) {
MockSpaceManager space_manager;
std::unique_ptr<Allocator> allocator;
constexpr size_t kAllocatedExtents = 1;
constexpr size_t kAllocatedBlocks = 10;
constexpr size_t kAllocatedNodes = 1;
ASSERT_NO_FAILURES(TestSetup(kAllocatedBlocks, kAllocatedNodes, /* fragmented=*/false,
&space_manager, &allocator));
fbl::Vector<ReservedExtent> extents;
ASSERT_OK(allocator->ReserveBlocks(kAllocatedBlocks, &extents));
ASSERT_EQ(kAllocatedExtents, extents.size());
ValidateStreamBlocks(std::move(extents), kAllocatedBlocks);
}
// Test streaming too many blocks using the vector iterator.
TEST(VectorExtentIteratorTest, StreamBlocksInvalidLength) {
MockSpaceManager space_manager;
std::unique_ptr<Allocator> allocator;
constexpr size_t kAllocatedExtents = 10;
constexpr size_t kAllocatedBlocks = 10;
constexpr size_t kAllocatedNodes = 1;
ASSERT_NO_FAILURES(TestSetup(kAllocatedBlocks, kAllocatedNodes, /* fragmented=*/true,
&space_manager, &allocator));
fbl::Vector<ReservedExtent> extents;
ASSERT_OK(allocator->ReserveBlocks(kAllocatedBlocks, &extents));
ASSERT_EQ(kAllocatedExtents, extents.size());
BlockIterator iter(std::make_unique<VectorExtentIterator>(extents));
size_t stream_blocks = 0;
size_t stream_index = 0;
auto stream_callback = [&](uint64_t local_offset, uint64_t dev_offset, uint32_t length) {
ZX_DEBUG_ASSERT(stream_blocks == local_offset);
ZX_DEBUG_ASSERT(extents[stream_index].extent().Start() == dev_offset);
ZX_DEBUG_ASSERT(extents[stream_index].extent().Length() == length);
stream_blocks += length;
stream_index++;
return ZX_OK;
};
// If we request more blocks than we allocated, streaming will fail.
//
// Up to the point of failure, however, we should still see only valid extents.
ASSERT_EQ(ZX_ERR_IO_DATA_INTEGRITY, StreamBlocks(&iter, kAllocatedBlocks + 10, stream_callback));
ASSERT_TRUE(iter.Done());
}
} // namespace
} // namespace blobfs