blob: 79ae4b756ea0dadf1ab30d5b36c1576a8e2b0316 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef STORAGE_BROWSER_FILE_SYSTEM_FILE_STREAM_WRITER_TEST_H_
#define STORAGE_BROWSER_FILE_SYSTEM_FILE_STREAM_WRITER_TEST_H_
#include <cstdio>
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/callback_helpers.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/task_environment.h"
#include "base/threading/thread.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "storage/browser/file_system/file_stream_test_utils.h"
#include "storage/browser/file_system/file_stream_writer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace storage {
// An interface for derived FileStreamWriter to implement. This allows multiple
// FileStreamWriter implementations can share the same test framework. Tests
// should implement CreateFileWithContent, CreateWriter, FilePathExists, and
// GetFileContent to manipulate files for their particular implementation.
class FileStreamWriterTest : public testing::Test {
public:
static constexpr std::string_view kTestFileName = "file_a";
virtual bool CreateFileWithContent(const std::string& name,
const std::string& data) = 0;
virtual std::unique_ptr<FileStreamWriter> CreateWriter(
const std::string& name,
int64_t offset) = 0;
virtual bool FilePathExists(const std::string& name) = 0;
virtual std::string GetFileContent(const std::string& name) = 0;
static void NeverCalled(int unused) { ADD_FAILURE(); }
protected:
// Must be listed before base::test::TaskEnvironment.
base::ScopedTempDir dir_;
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::IO};
};
template <class SubClass>
class FileStreamWriterTypedTest : public SubClass {
public:
void SetUp() override { SubClass::SetUp(); }
};
TYPED_TEST_SUITE_P(FileStreamWriterTypedTest);
TYPED_TEST_P(FileStreamWriterTypedTest, Write) {
EXPECT_TRUE(
this->CreateFileWithContent(std::string(this->kTestFileName), "foobar"));
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 0));
EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "foo"));
EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "bar"));
EXPECT_TRUE(this->FilePathExists(std::string(this->kTestFileName)));
EXPECT_EQ("foobar", this->GetFileContent(std::string(this->kTestFileName)));
}
TYPED_TEST_P(FileStreamWriterTypedTest, WriteMiddle) {
EXPECT_TRUE(
this->CreateFileWithContent(std::string(this->kTestFileName), "foobar"));
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 2));
EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx"));
EXPECT_TRUE(this->FilePathExists(std::string(this->kTestFileName)));
EXPECT_EQ("foxxxr", this->GetFileContent(std::string(this->kTestFileName)));
}
TYPED_TEST_P(FileStreamWriterTypedTest, WriteNearEnd) {
EXPECT_TRUE(
this->CreateFileWithContent(std::string(this->kTestFileName), "foobar"));
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 5));
EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx"));
EXPECT_TRUE(this->FilePathExists(std::string(this->kTestFileName)));
EXPECT_EQ("foobaxxx", this->GetFileContent(std::string(this->kTestFileName)));
}
TYPED_TEST_P(FileStreamWriterTypedTest, WriteEnd) {
EXPECT_TRUE(
this->CreateFileWithContent(std::string(this->kTestFileName), "foobar"));
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 6));
EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx"));
EXPECT_TRUE(this->FilePathExists(std::string(this->kTestFileName)));
EXPECT_EQ("foobarxxx",
this->GetFileContent(std::string(this->kTestFileName)));
}
TYPED_TEST_P(FileStreamWriterTypedTest, WriteAfterEnd) {
EXPECT_TRUE(
this->CreateFileWithContent(std::string(this->kTestFileName), "foobar"));
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 7));
EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx"));
EXPECT_TRUE(this->FilePathExists(std::string(this->kTestFileName)));
EXPECT_EQ(std::string("foobar\0xxx", 10),
this->GetFileContent(std::string(this->kTestFileName)));
}
TYPED_TEST_P(FileStreamWriterTypedTest, WriteFailForNonexistingFile) {
ASSERT_FALSE(this->FilePathExists(std::string(this->kTestFileName)));
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 0));
EXPECT_EQ(net::ERR_FILE_NOT_FOUND, WriteStringToWriter(writer.get(), "foo"));
EXPECT_FALSE(this->FilePathExists(std::string(this->kTestFileName)));
}
TYPED_TEST_P(FileStreamWriterTypedTest, CancelBeforeOperation) {
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 0));
// Cancel immediately fails when there's no in-flight operation.
EXPECT_EQ(net::ERR_UNEXPECTED, writer->Cancel(base::DoNothing()));
}
TYPED_TEST_P(FileStreamWriterTypedTest, CancelAfterFinishedOperation) {
EXPECT_TRUE(
this->CreateFileWithContent(std::string(this->kTestFileName), "foobar"));
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 0));
EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "foo"));
// Cancel immediately fails when there's no in-flight operation.
EXPECT_EQ(net::ERR_UNEXPECTED, writer->Cancel(base::DoNothing()));
// Write operation is already completed.
EXPECT_TRUE(this->FilePathExists(std::string(this->kTestFileName)));
EXPECT_EQ("foobar", this->GetFileContent(std::string(this->kTestFileName)));
}
TYPED_TEST_P(FileStreamWriterTypedTest, CancelWrite) {
EXPECT_TRUE(
this->CreateFileWithContent(std::string(this->kTestFileName), "foobar"));
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 0));
scoped_refptr<net::StringIOBuffer> buffer(
base::MakeRefCounted<net::StringIOBuffer>("xxx"));
int result =
writer->Write(buffer.get(), buffer->size(),
base::BindOnce(&FileStreamWriterTest::NeverCalled));
ASSERT_EQ(net::ERR_IO_PENDING, result);
net::TestCompletionCallback callback;
writer->Cancel(callback.callback());
int cancel_result = writer->Cancel(callback.callback());
EXPECT_EQ(net::OK, callback.GetResult(cancel_result));
}
TYPED_TEST_P(FileStreamWriterTypedTest, CancelFlush) {
EXPECT_TRUE(
this->CreateFileWithContent(std::string(this->kTestFileName), ""));
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 0));
EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "foo"));
int cancel_expectation = net::OK;
int result = writer->Flush(FlushMode::kEndOfFile, base::DoNothing());
// Flush can run synchronously or asynchronously.
if (result == net::OK) {
// Cancel() should error if called when there is no in-flight operation.
cancel_expectation = net::ERR_UNEXPECTED;
} else {
EXPECT_EQ(net::ERR_IO_PENDING, result);
}
net::TestCompletionCallback callback;
int cancel_result = writer->Cancel(callback.callback());
EXPECT_EQ(cancel_expectation, callback.GetResult(cancel_result));
EXPECT_EQ("foo", this->GetFileContent(std::string(this->kTestFileName)));
}
TYPED_TEST_P(FileStreamWriterTypedTest, FlushBeforeWriting) {
EXPECT_TRUE(
this->CreateFileWithContent(std::string(this->kTestFileName), ""));
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 0));
EXPECT_EQ(net::OK, writer->Flush(FlushMode::kDefault, base::DoNothing()));
EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "foo"));
EXPECT_EQ("foo", this->GetFileContent(std::string(this->kTestFileName)));
}
TYPED_TEST_P(FileStreamWriterTypedTest, FlushAfterWriting) {
EXPECT_TRUE(
this->CreateFileWithContent(std::string(this->kTestFileName), ""));
std::unique_ptr<FileStreamWriter> writer(
this->CreateWriter(std::string(this->kTestFileName), 0));
EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "foo"));
net::TestCompletionCallback callback;
int result =
writer->Flush(FlushMode::kEndOfFile,
base::OnceCallback<void(int)>(callback.callback()));
ASSERT_EQ(net::OK, callback.GetResult(result));
EXPECT_EQ("foo", this->GetFileContent(std::string(this->kTestFileName)));
}
REGISTER_TYPED_TEST_SUITE_P(FileStreamWriterTypedTest,
Write,
WriteMiddle,
WriteNearEnd,
WriteEnd,
WriteAfterEnd,
WriteFailForNonexistingFile,
CancelBeforeOperation,
CancelAfterFinishedOperation,
CancelWrite,
CancelFlush,
FlushBeforeWriting,
FlushAfterWriting);
} // namespace storage
#endif // STORAGE_BROWSER_FILE_SYSTEM_FILE_STREAM_WRITER_TEST_H_