blob: a1f80559b2666122b8d14248779e10ddd849cea4 [file] [log] [blame]
// Copyright 2021 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/volume_image/ftl/ftl_io.h"
#include <cstdlib>
#include <memory>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/storage/volume_image/ftl/ftl_test_helper.h"
namespace storage::volume_image {
namespace {
constexpr uint64_t kPageSize = 8192;
constexpr uint32_t kOobBytesSize = 16;
constexpr uint64_t kPagesPerBlock = 32;
constexpr uint64_t kBlockCount = 20;
RawNandOptions GetOptions() {
RawNandOptions options;
options.oob_bytes_size = kOobBytesSize;
options.page_size = kPageSize;
options.pages_per_block = kPagesPerBlock;
options.page_count = kPagesPerBlock * kBlockCount;
return options;
}
void FillRandomRange(cpp20::span<uint8_t> data) {
// fill in a range with random contents.
unsigned int seed = ::testing::UnitTest::GetInstance()->random_seed();
size_t start = rand_r(&seed) % (data.size() - 1);
size_t end = start + rand_r(&seed) % (data.size() - start);
for (size_t i = start; i <= end; ++i) {
data[i] = static_cast<uint8_t>(rand_r(&seed));
}
}
constexpr uint32_t kPageOffset = 48;
constexpr uint32_t kPageCount = 6;
TEST(FtlHandleReaderTest, ContentsAreReadOnePageAtATimeCorrectly) {
FtlHandle handle;
InMemoryRawNand raw_nand;
raw_nand.options = GetOptions();
std::unique_ptr<InMemoryNdm> ndm =
std::make_unique<InMemoryNdm>(&raw_nand, kPageSize, kOobBytesSize);
auto init_result = handle.Init(std::move(ndm));
ASSERT_TRUE(init_result.is_ok()) << init_result.error();
// Format the FTL so we can read and write to it.
ASSERT_EQ(handle.volume().Format(), ZX_OK);
std::vector<uint8_t> my_data;
my_data.resize(handle.instance().page_size() * kPageCount, 0);
FillRandomRange(my_data);
ASSERT_EQ(handle.volume().Write(kPageOffset, kPageCount, my_data.data()), ZX_OK);
// Use a reader to pull back that page.
auto reader = handle.MakeReader();
std::vector<uint8_t> actual_data;
actual_data.resize(handle.instance().page_size(), 0);
for (size_t i = 0; i < kPageCount; ++i) {
auto read_result = reader->Read((kPageOffset + i) * handle.instance().page_size(), actual_data);
ASSERT_TRUE(read_result.is_ok()) << read_result.error();
EXPECT_TRUE(memcmp(actual_data.data(), my_data.data() + (i * handle.instance().page_size()),
handle.instance().page_size()) == 0)
<< i;
}
}
TEST(FtlHandleReaderTest, ContentsAreReadMultiplePageAtATimeCorrectly) {
FtlHandle handle;
InMemoryRawNand raw_nand;
raw_nand.options = GetOptions();
std::unique_ptr<InMemoryNdm> ndm =
std::make_unique<InMemoryNdm>(&raw_nand, kPageSize, kOobBytesSize);
auto init_result = handle.Init(std::move(ndm));
ASSERT_TRUE(init_result.is_ok()) << init_result.error();
// Format the FTL so we can read and write to it.
ASSERT_EQ(handle.volume().Format(), ZX_OK);
std::vector<uint8_t> my_data;
my_data.resize(handle.instance().page_size() * kPageCount, 0);
FillRandomRange(my_data);
ASSERT_EQ(handle.volume().Write(kPageOffset, kPageCount, my_data.data()), ZX_OK);
// Use a reader to pull back that page.
auto reader = handle.MakeReader();
std::vector<uint8_t> actual_data;
actual_data.resize(handle.instance().page_size() * kPageCount, 0);
auto read_result = reader->Read(kPageOffset * handle.instance().page_size(), actual_data);
ASSERT_TRUE(read_result.is_ok()) << read_result.error();
EXPECT_TRUE(memcmp(actual_data.data(), my_data.data(), my_data.size()) == 0);
}
TEST(FtlHandleWriterTest, ContentsAreWrittenSinglePageAtATimeCorrectly) {
FtlHandle handle;
InMemoryRawNand raw_nand;
raw_nand.options = GetOptions();
std::unique_ptr<InMemoryNdm> ndm =
std::make_unique<InMemoryNdm>(&raw_nand, kPageSize, kOobBytesSize);
auto init_result = handle.Init(std::move(ndm));
ASSERT_TRUE(init_result.is_ok()) << init_result.error();
// Format the FTL so we can read and write to it.
ASSERT_EQ(handle.volume().Format(), ZX_OK);
auto writer = handle.MakeWriter();
std::vector<uint8_t> my_data;
my_data.resize(handle.instance().page_size() * kPageCount, 0);
FillRandomRange(my_data);
auto write_result = writer->Write(kPageOffset * handle.instance().page_size(), my_data);
ASSERT_TRUE(write_result.is_ok()) << write_result.error();
// Now read from the volume.
std::vector<uint8_t> actual_data;
actual_data.resize(handle.instance().page_size(), 0);
for (size_t i = 0; i < kPageCount; ++i) {
ASSERT_EQ(handle.volume().Read(static_cast<uint32_t>(kPageOffset + i), 1, actual_data.data()),
ZX_OK);
EXPECT_TRUE(memcmp(actual_data.data(), my_data.data() + (i * handle.instance().page_size()),
handle.instance().page_size()) == 0)
<< i;
}
}
TEST(FtlHandleWriterTest, ContentsAreWrittenMultiplePagesAtATimeCorrectly) {
FtlHandle handle;
InMemoryRawNand raw_nand;
raw_nand.options = GetOptions();
std::unique_ptr<InMemoryNdm> ndm =
std::make_unique<InMemoryNdm>(&raw_nand, kPageSize, kOobBytesSize);
auto init_result = handle.Init(std::move(ndm));
ASSERT_TRUE(init_result.is_ok()) << init_result.error();
// Format the FTL so we can read and write to it.
ASSERT_EQ(handle.volume().Format(), ZX_OK);
auto writer = handle.MakeWriter();
std::vector<uint8_t> my_data;
my_data.resize(handle.instance().page_size() * kPageCount, 0);
FillRandomRange(my_data);
auto write_result = writer->Write(kPageOffset * handle.instance().page_size(), my_data);
ASSERT_TRUE(write_result.is_ok()) << write_result.error();
// Now read from the volume.
std::vector<uint8_t> actual_data;
actual_data.resize(handle.instance().page_size() * kPageCount, 0);
ASSERT_EQ(handle.volume().Read(kPageOffset, kPageCount, actual_data.data()), ZX_OK);
EXPECT_TRUE(memcmp(actual_data.data(), my_data.data(), handle.instance().page_size()) == 0);
}
} // namespace
} // namespace storage::volume_image