You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
8.9 KiB
272 lines
8.9 KiB
// 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
|
|
|