blob: addbd83c8ce376f9edf58cad07d026e27850307a [file] [log] [blame]
// Copyright 2020 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 "src/storage/minfs/test/unit/journal_integration_fixture.h"
#include <lib/sync/completion.h>
namespace minfs {
namespace {
using ::block_client::FakeFVMBlockDevice;
// Helper for conversion from "Bcache" to "FakeFVMBlockDevice".
void TakeDeviceFromBcache(std::unique_ptr<Bcache> bcache,
std::unique_ptr<block_client::FakeFVMBlockDevice>* out) {
std::unique_ptr<block_client::BlockDevice> block_device = Bcache::Destroy(std::move(bcache));
out->reset(reinterpret_cast<block_client::FakeFVMBlockDevice*>(block_device.release()));
}
// Helper for conversion from "Minfs" to "FakeFVMBlockDevice".
void TakeDeviceFromMinfs(std::unique_ptr<Runner> minfs,
std::unique_ptr<block_client::FakeFVMBlockDevice>* out) {
std::unique_ptr<Bcache> bcache = Runner::Destroy(std::move(minfs));
TakeDeviceFromBcache(std::move(bcache), out);
}
} // namespace
JournalIntegrationFixture::JournalIntegrationFixture()
: vfs_loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {}
void JournalIntegrationFixture::SetUp() {
auto device =
std::make_unique<FakeFVMBlockDevice>(kBlockCount, kBlockSize, kSliceSize, kSliceCount);
ASSERT_NO_FATAL_FAILURE(CountWritesToPerformOperation(&device));
}
std::unique_ptr<Bcache> JournalIntegrationFixture::CutOffDevice(uint64_t allowed_blocks) {
auto device =
std::make_unique<FakeFVMBlockDevice>(kBlockCount, kBlockSize, kSliceSize, kSliceCount);
// Attempt to "cut-off" the operation partway by reducing the number of writes.
PerformOperationWithTransactionLimit(allowed_blocks, &device);
auto bcache = Bcache::Create(std::move(device), kBlockCount);
EXPECT_TRUE(bcache.is_ok());
return *std::move(bcache);
}
void JournalIntegrationFixture::RecordWriteCount(Minfs& fs) {
sync_completion_t completion;
fs.Sync([&completion](zx_status_t status) { sync_completion_signal(&completion); });
ASSERT_EQ(sync_completion_wait(&completion, zx::duration::infinite().get()), ZX_OK);
write_count_ =
static_cast<FakeFVMBlockDevice*>(fs.GetMutableBcache()->device())->GetWriteBlockCount();
}
void JournalIntegrationFixture::CountWritesToPerformOperation(
std::unique_ptr<FakeFVMBlockDevice>* in_out_device) {
auto device = std::move(*in_out_device);
auto bcache = Bcache::Create(std::move(device), kBlockCount);
ASSERT_TRUE(bcache.is_ok());
ASSERT_TRUE(Mkfs(bcache.value().get()).is_ok());
// After formatting the device, count the number of blocks issued to the underlying device.
TakeDeviceFromBcache(*std::move(bcache), &device);
device->ResetBlockCounts();
bcache = Bcache::Create(std::move(device), kBlockCount);
ASSERT_TRUE(bcache.is_ok());
MountOptions options = {};
auto fs = Runner::Create(dispatcher(), *std::move(bcache), options);
ASSERT_TRUE(fs.is_ok());
// Perform the caller-requested operation.
PerformOperation(fs->minfs());
if (write_count_ == 0) {
RecordWriteCount(fs->minfs());
}
TakeDeviceFromMinfs(*std::move(fs), &device);
*in_out_device = std::move(device);
}
void JournalIntegrationFixture::PerformOperationWithTransactionLimit(
uint64_t write_count, std::unique_ptr<FakeFVMBlockDevice>* in_out_device) {
auto device = std::move(*in_out_device);
auto bcache = Bcache::Create(std::move(device), kBlockCount);
ASSERT_TRUE(bcache.is_ok());
ASSERT_TRUE(Mkfs(bcache.value().get()).is_ok());
// After formatting the device, create a transaction limit on the underlying device.
TakeDeviceFromBcache(std::move(*bcache), &device);
device->ResetBlockCounts();
device->SetWriteBlockLimit(write_count);
bcache = Bcache::Create(std::move(device), kBlockCount);
ASSERT_TRUE(bcache.is_ok());
MountOptions options = {};
auto fs = Runner::Create(dispatcher(), *std::move(bcache), options);
ASSERT_TRUE(fs.is_ok());
// Perform the caller-requested operation.
PerformOperation(fs->minfs());
// Always do a sync (to match what happens in CountWritesToPerformOperation).
sync_completion_t completion;
fs->minfs().Sync([&completion](zx_status_t status) { sync_completion_signal(&completion); });
ASSERT_EQ(sync_completion_wait(&completion, zx::duration::infinite().get()), ZX_OK);
TakeDeviceFromMinfs(*std::move(fs), &device);
device->ResetWriteBlockLimit();
*in_out_device = std::move(device);
}
} // namespace minfs