blob: 2a01e000e0370952de73f66484af3b57dcc253a1 [file] [log] [blame] [edit]
// Copyright 2022 The Centipede 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 "./common/blob_file.h"
#include <memory>
#include <string>
#include <tuple>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "./common/defs.h"
#include "./common/test_util.h"
namespace fuzztest::internal {
namespace {
std::string TempFilePath() { return TempDir("test").GetFilePath("blob_file"); }
// We may have more than one BlobFile factory.
// Need to test every factory the same way.
class BlobFile : public testing::TestWithParam<bool> {};
// Tests correct way of using a BlobFile, and that files written in both formats
// can be appropriately detected and read.
void TestOneBlobFile(std::unique_ptr<BlobFileReader> (*ReaderFactory)(),
std::unique_ptr<BlobFileWriter> (*WriterFactory)(bool),
bool riegeli) {
ByteArray input1{1, 2, 3};
ByteArray input2{4, 5};
ByteArray input3{6, 7, 8, 9};
ByteArray input4{10, 11};
const auto path = TempFilePath();
ByteSpan blob;
// Append two blobs to a file.
{
auto appender = WriterFactory(riegeli);
EXPECT_OK(appender->Open(path, "a"));
EXPECT_OK(appender->Write(input1));
EXPECT_OK(appender->Write(input2));
EXPECT_OK(appender->Close());
}
// Read the blobs back.
{
auto reader = ReaderFactory();
EXPECT_OK(reader->Open(path));
EXPECT_OK(reader->Read(blob));
EXPECT_EQ(input1, blob);
EXPECT_OK(reader->Read(blob));
EXPECT_EQ(input2, blob);
EXPECT_EQ(reader->Read(blob), absl::OutOfRangeError("no more blobs"));
EXPECT_OK(reader->Close());
}
// Append one more blob to the same file.
{
auto appender = WriterFactory(riegeli);
EXPECT_OK(appender->Open(path, "a"));
EXPECT_OK(appender->Write(input3));
EXPECT_OK(appender->Close());
}
// Re-read the file, expect to see all 3 blobs.
{
auto reader = ReaderFactory();
EXPECT_OK(reader->Open(path));
EXPECT_OK(reader->Read(blob));
EXPECT_EQ(input1, blob);
EXPECT_OK(reader->Read(blob));
EXPECT_EQ(input2, blob);
EXPECT_OK(reader->Read(blob));
EXPECT_EQ(input3, blob);
EXPECT_EQ(reader->Read(blob), absl::OutOfRangeError("no more blobs"));
EXPECT_OK(reader->Close());
}
// Overwrite the contents of the file by a new blob.
{
auto appender = WriterFactory(riegeli);
EXPECT_OK(appender->Open(path, "w"));
EXPECT_OK(appender->Write(input4));
EXPECT_OK(appender->Close());
}
// Re-read the file, expect to see the single new blob.
{
auto reader = ReaderFactory();
EXPECT_OK(reader->Open(path));
EXPECT_OK(reader->Read(blob));
EXPECT_EQ(input4, blob);
EXPECT_EQ(reader->Read(blob), absl::OutOfRangeError("no more blobs"));
EXPECT_OK(reader->Close());
}
}
TEST_P(BlobFile, DefaultTest) {
TestOneBlobFile(&DefaultBlobFileReaderFactory, &DefaultBlobFileWriterFactory,
GetParam());
}
// An Open() failure should not interfere with future proper functioning.
TEST_P(BlobFile, AfterFailedOpenTest) {
auto reader = DefaultBlobFileReaderFactory();
auto appender = DefaultBlobFileWriterFactory(GetParam());
const std::string invalid_path = "/DOES/NOT/EXIST";
const std::string path = TempFilePath();
ByteArray input{1, 2, 3};
// Open failure of writer due to file that cannot be created.
ASSERT_FALSE(appender->Open(invalid_path, "a").ok());
// Follow that with opening and writing to a file that is expected to work.
ASSERT_OK(appender->Open(path, "a"));
ASSERT_OK(appender->Write(input));
ASSERT_OK(appender->Close());
// Open failure of reader due to non-existent file.
ASSERT_FALSE(reader->Open(invalid_path).ok());
ByteSpan blob;
// Follow that with reading the already written file and check that contents
// are as expected.
ASSERT_OK(reader->Open(path));
ASSERT_OK(reader->Read(blob));
EXPECT_EQ(input, blob);
EXPECT_EQ(reader->Read(blob), absl::OutOfRangeError("no more blobs"));
EXPECT_OK(reader->Close());
}
TEST_P(BlobFile, CloseReaderAfterFileRemovalTest) {
auto appender = DefaultBlobFileWriterFactory(GetParam());
const std::string path = TempFilePath();
ByteArray input{1};
ASSERT_OK(appender->Open(path, "a"));
ASSERT_OK(appender->Write(input));
ASSERT_OK(appender->Close());
auto reader = DefaultBlobFileReaderFactory();
ByteSpan blob;
ASSERT_OK(reader->Open(path));
ASSERT_OK(reader->Read(blob));
TempFilePath(); // Delete the file at `path`
EXPECT_OK(reader->Close());
}
// Tests incorrect ways of using a BlobFileReader/BlobFileWriter.
void TestIncorrectUsage(std::unique_ptr<BlobFileReader> (*ReaderFactory)(),
std::unique_ptr<BlobFileWriter> (*WriterFactory)(bool),
bool riegeli) {
const std::string invalid_path = "/DOES/NOT/EXIST";
const auto path = TempFilePath();
auto reader = ReaderFactory();
auto appender = WriterFactory(riegeli);
// Open invalid file path.
EXPECT_FALSE(reader->Open(invalid_path).ok());
EXPECT_FALSE(appender->Open(invalid_path, "a").ok());
ByteSpan blob;
// Write() on objects that are not in a successfuly open state.
EXPECT_FALSE(appender->Write(blob).ok());
EXPECT_OK(appender->Open(path, "a"));
EXPECT_OK(appender->Close());
EXPECT_FALSE(appender->Write(blob).ok());
// Read() on objects that are not in a successfully open state.
EXPECT_FALSE(reader->Read(blob).ok());
EXPECT_OK(reader->Open(path));
EXPECT_OK(reader->Close());
EXPECT_FALSE(reader->Read(blob).ok());
}
TEST_P(BlobFile, IncorrectUsage) {
TestIncorrectUsage(&DefaultBlobFileReaderFactory,
&DefaultBlobFileWriterFactory, GetParam());
}
#ifndef CENTIPEDE_DISABLE_RIEGELI
INSTANTIATE_TEST_SUITE_P(BlobFileTests, BlobFile, ::testing::Bool());
#else
INSTANTIATE_TEST_SUITE_P(BlobFileTests, BlobFile, ::testing::Values(false));
#endif // CENTIPEDE_DISABLE_RIEGELI
class ReadMultipleFiles
: public testing::TestWithParam<std::tuple<bool, bool>> {};
// Single reader object can be used to read multiple files, in the same or
// different formats.
TEST_P(ReadMultipleFiles, SingleObjectMultipleFormats) {
auto reader = DefaultBlobFileReaderFactory();
auto [first_riegeli, second_riegeli] = GetParam();
const auto path = TempFilePath();
ByteArray file1_blob1{1, 2, 3, 4, 5};
ByteArray file2_blob1{6, 7};
ByteArray file2_blob2{8, 9};
ByteSpan blob;
// Write in first format and read.
auto writer = DefaultBlobFileWriterFactory(first_riegeli);
EXPECT_OK(writer->Open(path, "w"));
EXPECT_OK(writer->Write(file1_blob1));
EXPECT_OK(writer->Close());
EXPECT_OK(reader->Open(path));
EXPECT_OK(reader->Read(blob));
EXPECT_EQ(file1_blob1, blob);
EXPECT_EQ(reader->Read(blob), absl::OutOfRangeError("no more blobs"));
EXPECT_OK(reader->Close());
// Write in second format and read.
writer = DefaultBlobFileWriterFactory(second_riegeli);
EXPECT_OK(writer->Open(path, "w"));
EXPECT_OK(writer->Write(file2_blob1));
EXPECT_OK(writer->Write(file2_blob2));
EXPECT_OK(writer->Close());
EXPECT_OK(reader->Open(path));
EXPECT_OK(reader->Read(blob));
EXPECT_EQ(file2_blob1, blob);
EXPECT_OK(reader->Read(blob));
EXPECT_EQ(file2_blob2, blob);
EXPECT_EQ(reader->Read(blob), absl::OutOfRangeError("no more blobs"));
EXPECT_OK(reader->Close());
}
#ifndef CENTIPEDE_DISABLE_RIEGELI
INSTANTIATE_TEST_SUITE_P(DefaultBlobFileReaderTests, ReadMultipleFiles,
::testing::Combine(::testing::Bool(),
::testing::Bool()));
#else
INSTANTIATE_TEST_SUITE_P(DefaultBlobFileReaderTests, ReadMultipleFiles,
::testing::Values(std::tuple{false, false}));
#endif // CENTIPEDE_DISABLE_RIEGELI
#ifdef CENTIPEDE_DISABLE_RIEGELI
TEST(WriterFactoryDeathTest, FailWhenBuiltWithoutRiegeli) {
ASSERT_DEATH(DefaultBlobFileWriterFactory(true), "");
}
#endif // CENTIPEDE_DISABLE_RIEGELI
void Append(ByteArray &to, const ByteArray &from) {
to.insert(to.end(), from.begin(), from.end());
}
TEST(UtilTest, AppendFile) {
ByteArray packed;
ByteArray a{1, 2, 3};
ByteArray b{3, 4, 5};
ByteArray c{111, 112, 113, 114, 115};
Append(packed, PackBytesForAppendFile(a));
Append(packed, PackBytesForAppendFile(b));
Append(packed, PackBytesForAppendFile(c));
std::vector<ByteArray> unpacked;
UnpackBytesFromAppendFile(packed, &unpacked);
EXPECT_EQ(a, unpacked[0]);
EXPECT_EQ(b, unpacked[1]);
EXPECT_EQ(c, unpacked[2]);
}
} // namespace
} // namespace fuzztest::internal