blob: b05ce2df6559fc8217a65009e4d2d3beac4b9912 [file] [log] [blame]
// Copyright (C) 2019 Google LLC
//
// 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
//
// http://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 "icing/file/file-backed-vector.h"
#include <errno.h>
#include <algorithm>
#include <cstdint>
#include <memory>
#include <string_view>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "icing/file/filesystem.h"
#include "icing/file/memory-mapped-file.h"
#include "icing/testing/common-matchers.h"
#include "icing/testing/tmp-directory.h"
#include "icing/util/crc32.h"
#include "icing/util/logging.h"
using ::testing::Eq;
using ::testing::IsTrue;
using ::testing::Pointee;
namespace icing {
namespace lib {
namespace {
class FileBackedVectorTest : public testing::Test {
protected:
void SetUp() override {
file_path_ = GetTestTempDir() + "/test.array";
fd_ = filesystem_.OpenForWrite(file_path_.c_str());
ASSERT_NE(-1, fd_);
ASSERT_TRUE(filesystem_.Truncate(fd_, 0));
}
void TearDown() override {
close(fd_);
filesystem_.DeleteFile(file_path_.c_str());
}
// Helper method to loop over some data and insert into the vector at some idx
template <typename T>
void Insert(FileBackedVector<T>* vector, int32_t idx, std::string data) {
for (int i = 0; i < data.length(); ++i) {
ICING_ASSERT_OK(vector->Set(idx + i, data.at(i)));
}
}
// Helper method to retrieve data from the beginning of the vector
template <typename T>
std::string_view Get(FileBackedVector<T>* vector, int32_t expected_len) {
return Get(vector, 0, expected_len);
}
template <typename T>
std::string_view Get(FileBackedVector<T>* vector, int32_t idx,
int32_t expected_len) {
return std::string_view(vector->array() + idx, expected_len);
}
Filesystem filesystem_;
std::string file_path_;
int fd_;
};
TEST_F(FileBackedVectorTest, Create) {
{
// Create a vector for a new file
ICING_ASSERT_OK_AND_ASSIGN(
auto vector, FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
}
{
// We can create it again based on the same file.
ICING_ASSERT_OK_AND_ASSIGN(
auto vector, FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
}
}
TEST_F(FileBackedVectorTest, SimpleShared) {
// Create a vector and add some data.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(0)));
std::string expected = "abcde";
Insert(vector.get(), 0, expected);
EXPECT_EQ(expected.length(), vector->num_elements());
EXPECT_EQ(expected, Get(vector.get(), expected.length()));
uint32_t good_crc_value = 1134899064U;
const Crc32 good_crc(good_crc_value);
// Explicit call to update the crc does update the value
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(good_crc));
// PersistToDisk does nothing bad.
ICING_EXPECT_OK(vector->PersistToDisk());
// Close out the old vector to ensure everything persists properly before we
// reassign it
vector.reset();
// Write a bad crc, this would be a mismatch compared to the computed crc of
// the contents on reinitialization.
uint32_t bad_crc_value = 123;
filesystem_.PWrite(file_path_.data(),
offsetof(FileBackedVector<char>::Header, vector_checksum),
&bad_crc_value, sizeof(bad_crc_value));
ASSERT_THAT(FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC),
StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
// Get it back into an ok state
filesystem_.PWrite(file_path_.data(),
offsetof(FileBackedVector<char>::Header, vector_checksum),
&good_crc_value, sizeof(good_crc_value));
ICING_ASSERT_OK_AND_ASSIGN(
vector, FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
EXPECT_EQ(expected, Get(vector.get(), expected.length()));
// Close out the old vector to ensure everything persists properly before we
// reassign it
vector.reset();
// Can reinitialize it safely
ICING_ASSERT_OK_AND_ASSIGN(
vector, FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
// Truncate the content
ICING_EXPECT_OK(vector->TruncateTo(0));
// Crc is cleared after truncation and reset to 0.
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(0)));
EXPECT_EQ(0u, vector->num_elements());
}
TEST_F(FileBackedVectorTest, Get) {
// Create a vector and add some data.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(0)));
std::string expected = "abc";
Insert(vector.get(), 0, expected);
EXPECT_EQ(expected.length(), vector->num_elements());
EXPECT_THAT(vector->Get(0), IsOkAndHolds(Pointee(Eq('a'))));
EXPECT_THAT(vector->Get(1), IsOkAndHolds(Pointee(Eq('b'))));
EXPECT_THAT(vector->Get(2), IsOkAndHolds(Pointee(Eq('c'))));
// Out of bounds error
EXPECT_THAT(vector->Get(3),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
EXPECT_THAT(vector->Get(-1),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
}
TEST_F(FileBackedVectorTest, IncrementalCrc_NonOverlappingChanges) {
int num_elements = 1000;
int incremental_size = 3;
// Create an array with some data.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
Insert(vector.get(), 0, std::string(num_elements, 'a'));
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(2620640643U)));
// Non-overlapping changes to the array, with increasing intervals
// between updating the checksum. Validate by mapping another array on top.
uint32_t next_update = 2;
for (uint32_t i = 0; i < num_elements; i += incremental_size) {
Insert(vector.get(), i, std::string(incremental_size, 'b'));
if (i >= next_update) {
ICING_ASSERT_OK_AND_ASSIGN(Crc32 incremental_crc,
vector->ComputeChecksum());
ICING_LOG(INFO) << "Now crc @" << incremental_crc.Get();
Crc32 full_crc;
std::string_view reconstructed_view =
std::string_view(vector->array(), vector->num_elements());
full_crc.Append(reconstructed_view);
ASSERT_EQ(incremental_crc, full_crc);
next_update *= 2;
}
}
for (uint32_t i = 0; i < num_elements; ++i) {
EXPECT_THAT(vector->Get(i), IsOkAndHolds(Pointee(Eq('b'))));
}
}
TEST_F(FileBackedVectorTest, IncrementalCrc_OverlappingChanges) {
int num_elements = 1000;
int incremental_size = 3;
// Create an array with some data.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
Insert(vector.get(), 0, std::string(num_elements, 'a'));
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(2620640643U)));
// Overlapping changes to the array, with increasing intervals
// between updating the checksum. Validate by mapping another array on top.
uint32_t next_update = 2;
for (uint32_t i = 0; i < num_elements; i++) {
Insert(vector.get(), i, std::string(incremental_size, 'b'));
if (i >= next_update) {
ICING_ASSERT_OK_AND_ASSIGN(Crc32 incremental_crc,
vector->ComputeChecksum());
ICING_LOG(INFO) << "Now crc @" << incremental_crc.Get();
Crc32 full_crc;
std::string_view reconstructed_view =
std::string_view(vector->array(), vector->num_elements());
full_crc.Append(reconstructed_view);
ASSERT_EQ(incremental_crc, full_crc);
next_update *= 2;
}
}
for (uint32_t i = 0; i < num_elements; ++i) {
EXPECT_THAT(vector->Get(i), IsOkAndHolds(Pointee(Eq('b'))));
}
}
TEST_F(FileBackedVectorTest, Grow) {
// This is the same value as FileBackedVector::kMaxNumElts
constexpr int32_t kMaxNumElts = 1U << 20;
ASSERT_TRUE(filesystem_.Truncate(fd_, 0));
// Create an array and add some data.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(0)));
EXPECT_THAT(vector->Set(kMaxNumElts + 11, 'a'),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
EXPECT_THAT(vector->Set(-1, 'a'),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
uint32_t start = kMaxNumElts - 13;
Insert(vector.get(), start, "abcde");
// Crc works?
const Crc32 good_crc(1134899064U);
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(good_crc));
// PersistToDisk does nothing bad, and ensures the content is still there
// after we recreate the vector
ICING_EXPECT_OK(vector->PersistToDisk());
// Close out the old vector to ensure everything persists properly before we
// reassign it
vector.reset();
ICING_ASSERT_OK_AND_ASSIGN(
vector, FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
std::string expected = "abcde";
EXPECT_EQ(expected, Get(vector.get(), start, expected.length()));
}
TEST_F(FileBackedVectorTest, GrowsInChunks) {
// This is the same value as FileBackedVector::kGrowElements
constexpr int32_t kGrowElements = 1U << 14; // 16K
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<int>> vector,
FileBackedVector<int>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
// Our initial file size should just be the size of the header. Disk usage
// will indicate that one block has been allocated, which contains the header.
int header_size = sizeof(FileBackedVector<char>::Header);
int page_size = getpagesize();
EXPECT_THAT(filesystem_.GetFileSize(fd_), Eq(header_size));
EXPECT_THAT(filesystem_.GetDiskUsage(fd_), Eq(page_size));
// Once we add something though, we'll grow to be kGrowElements big. From this
// point on, file size and disk usage should be the same because Growing will
// explicitly allocate the number of blocks needed to accomodate the file.
Insert(vector.get(), 0, "a");
int file_size = kGrowElements * sizeof(int);
EXPECT_THAT(filesystem_.GetFileSize(fd_), Eq(file_size));
EXPECT_THAT(filesystem_.GetDiskUsage(fd_), Eq(file_size));
// Should still be the same size, don't need to grow underlying file
Insert(vector.get(), 1, "b");
EXPECT_THAT(filesystem_.GetFileSize(fd_), Eq(file_size));
EXPECT_THAT(filesystem_.GetDiskUsage(fd_), Eq(file_size));
// Now we grow by a kGrowElements chunk, so the underlying file is 2
// kGrowElements big
file_size *= 2;
Insert(vector.get(), 2, std::string(kGrowElements, 'c'));
EXPECT_THAT(filesystem_.GetFileSize(fd_), Eq(file_size));
EXPECT_THAT(filesystem_.GetDiskUsage(fd_), Eq(file_size));
// Destroy/persist the contents.
vector.reset();
// Reinitialize
ICING_ASSERT_OK_AND_ASSIGN(
vector, FileBackedVector<int>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
// Should be the same file size as before
EXPECT_THAT(filesystem_.GetFileSize(file_path_.c_str()),
Eq(kGrowElements * 2 * sizeof(int)));
}
TEST_F(FileBackedVectorTest, Delete) {
// Can delete even if there's nothing there
ICING_EXPECT_OK(FileBackedVector<int64_t>::Delete(filesystem_, file_path_));
// Create a vector and add some data.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(0)));
std::string expected = "abcde";
Insert(vector.get(), 0, expected);
ASSERT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(1134899064U)));
ASSERT_EQ(expected.length(), vector->num_elements());
// Close out the old vector to ensure everything persists properly before we
// delete the underlying files
vector.reset();
ICING_EXPECT_OK(FileBackedVector<int64_t>::Delete(filesystem_, file_path_));
EXPECT_FALSE(filesystem_.FileExists(file_path_.data()));
// Can successfully create again.
ICING_ASSERT_OK_AND_ASSIGN(
vector, FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
}
TEST_F(FileBackedVectorTest, TruncateTo) {
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(0)));
Insert(vector.get(), 0, "A");
Insert(vector.get(), 1, "Z");
EXPECT_EQ(2, vector->num_elements());
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(1658635950)));
// Modify 1 element, out of 2 total elements. 1/2 changes exceeds the partial
// crc limit, so our next checksum call will recompute the entire vector's
// checksum.
Insert(vector.get(), 1, "J");
// We'll ignore everything after the 1st element, so the full vector's
// checksum will only include "J".
ICING_EXPECT_OK(vector->TruncateTo(1));
EXPECT_EQ(1, vector->num_elements());
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(31158534)));
// Truncating clears the checksum and resets it to 0
ICING_EXPECT_OK(vector->TruncateTo(0));
EXPECT_EQ(0, vector->num_elements());
EXPECT_THAT(vector->ComputeChecksum(), IsOkAndHolds(Crc32(0)));
// Can't truncate past end.
EXPECT_THAT(vector->TruncateTo(100),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
// Must be greater than or equal to 0
EXPECT_THAT(vector->TruncateTo(-1),
StatusIs(libtextclassifier3::StatusCode::OUT_OF_RANGE));
}
TEST_F(FileBackedVectorTest, TruncateAndReReadFile) {
{
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<float>> vector,
FileBackedVector<float>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
ICING_ASSERT_OK(vector->Set(0, 1.0));
ICING_ASSERT_OK(vector->Set(1, 2.0));
ICING_ASSERT_OK(vector->Set(2, 2.0));
ICING_ASSERT_OK(vector->Set(3, 2.0));
ICING_ASSERT_OK(vector->Set(4, 2.0));
} // Destroying the vector should trigger a checksum of the 5 elements
{
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<float>> vector,
FileBackedVector<float>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
EXPECT_EQ(5, vector->num_elements());
ICING_EXPECT_OK(vector->TruncateTo(4));
EXPECT_EQ(4, vector->num_elements());
} // Destroying the vector should update the checksum to 4 elements
// Creating again should double check that our checksum of 4 elements matches
// what was previously saved.
{
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<float>> vector,
FileBackedVector<float>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
EXPECT_EQ(vector->num_elements(), 4);
}
}
TEST_F(FileBackedVectorTest, InitFileTooSmallForHeaderFails) {
{
// 1. Create a vector with a few elements.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
Insert(vector.get(), 0, "A");
Insert(vector.get(), 1, "Z");
ASSERT_THAT(vector->PersistToDisk(), IsOk());
}
// 2. Shrink the file to be smaller than the header.
filesystem_.Truncate(fd_, sizeof(FileBackedVector<char>::Header) - 1);
{
// 3. Attempt to create the file and confirm that it fails.
EXPECT_THAT(FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC),
StatusIs(libtextclassifier3::StatusCode::INTERNAL));
}
}
TEST_F(FileBackedVectorTest, InitWrongDataSizeFails) {
{
// 1. Create a vector with a few elements.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
Insert(vector.get(), 0, "A");
Insert(vector.get(), 1, "Z");
ASSERT_THAT(vector->PersistToDisk(), IsOk());
}
{
// 2. Attempt to create the file with a different element size and confirm
// that it fails.
EXPECT_THAT(FileBackedVector<int>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC),
StatusIs(libtextclassifier3::StatusCode::INTERNAL));
}
}
TEST_F(FileBackedVectorTest, InitCorruptHeaderFails) {
{
// 1. Create a vector with a few elements.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
Insert(vector.get(), 0, "A");
Insert(vector.get(), 1, "Z");
ASSERT_THAT(vector->PersistToDisk(), IsOk());
}
// 2. Modify the header, but don't update the checksum. This would be similar
// to corruption of the header.
FileBackedVector<char>::Header header;
ASSERT_THAT(filesystem_.PRead(fd_, &header, sizeof(header), /*offset=*/0),
IsTrue());
header.num_elements = 1;
ASSERT_THAT(filesystem_.PWrite(fd_, /*offset=*/0, &header, sizeof(header)),
IsTrue());
{
// 3. Attempt to create the file with a header that doesn't match its
// checksum and confirm that it fails.
EXPECT_THAT(FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC),
StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
}
}
TEST_F(FileBackedVectorTest, InitHeaderElementSizeTooBigFails) {
{
// 1. Create a vector with a few elements.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
Insert(vector.get(), 0, "A");
Insert(vector.get(), 1, "Z");
ASSERT_THAT(vector->PersistToDisk(), IsOk());
}
// 2. Modify the header so that the number of elements exceeds the actual size
// of the underlying file.
FileBackedVector<char>::Header header;
ASSERT_THAT(filesystem_.PRead(fd_, &header, sizeof(header), /*offset=*/0),
IsTrue());
int64_t file_size = filesystem_.GetFileSize(fd_);
int64_t allocated_elements_size = file_size - sizeof(header);
header.num_elements = (allocated_elements_size / sizeof(char)) + 1;
header.header_checksum = header.CalculateHeaderChecksum();
ASSERT_THAT(filesystem_.PWrite(fd_, /*offset=*/0, &header, sizeof(header)),
IsTrue());
{
// 3. Attempt to create the file with num_elements that is larger than the
// underlying file and confirm that it fails.
EXPECT_THAT(FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC),
StatusIs(libtextclassifier3::StatusCode::INTERNAL));
}
}
TEST_F(FileBackedVectorTest, InitCorruptElementsFails) {
{
// 1. Create a vector with a few elements.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
Insert(vector.get(), 0, "A");
Insert(vector.get(), 1, "Z");
ASSERT_THAT(vector->PersistToDisk(), IsOk());
}
// 2. Overwrite the values of the first two elements.
std::string corrupted_content = "BY";
ASSERT_THAT(
filesystem_.PWrite(fd_, /*offset=*/sizeof(FileBackedVector<char>::Header),
corrupted_content.c_str(), corrupted_content.length()),
IsTrue());
{
// 3. Attempt to create the file with elements that don't match their
// checksum and confirm that it fails.
EXPECT_THAT(FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC),
StatusIs(libtextclassifier3::StatusCode::FAILED_PRECONDITION));
}
}
TEST_F(FileBackedVectorTest, InitNormalSucceeds) {
{
// 1. Create a vector with a few elements.
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<FileBackedVector<char>> vector,
FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC));
Insert(vector.get(), 0, "A");
Insert(vector.get(), 1, "Z");
ASSERT_THAT(vector->PersistToDisk(), IsOk());
}
{
// 2. Attempt to create the file with a completely valid header and elements
// region. This should succeed.
EXPECT_THAT(FileBackedVector<char>::Create(
filesystem_, file_path_,
MemoryMappedFile::Strategy::READ_WRITE_AUTO_SYNC),
IsOk());
}
}
} // namespace
} // namespace lib
} // namespace icing