forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Combine file stream reader unittests
These are all separate but different unittests that have grown organically apart from each other, combine into a single typed test so that future file stream reader unittests can reuse this structure. The one unfortunate part about doing it this way is that it requires a lot more `this->` everywhere because of C++ templates. However, the alternative of having a test fixture with a bunch of switch statements for the different types seems strictly worse. Change-Id: Id56bc28391e1a640748691cb383a19291c47fc51 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2406718 Commit-Queue: Nick Harper <nharper@chromium.org> Auto-Submit: enne <enne@chromium.org> Reviewed-by: Nick Harper <nharper@chromium.org> Reviewed-by: Marijn Kruisselbrink <mek@chromium.org> Cr-Commit-Position: refs/heads/master@{#807092}
- Loading branch information
Showing
9 changed files
with
425 additions
and
561 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Copyright 2020 The Chromium 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 "storage/browser/file_system/file_stream_reader_test.h" | ||
|
||
namespace storage { | ||
|
||
const base::StringPiece FileStreamReaderTest::kTestFileName; | ||
const base::StringPiece FileStreamReaderTest::kTestData; | ||
|
||
} // namespace storage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
// Copyright 2020 The Chromium Authors. All rights reserved. | ||
// 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_READER_TEST_H_ | ||
#define STORAGE_BROWSER_FILE_SYSTEM_FILE_STREAM_READER_TEST_H_ | ||
|
||
#include "base/bind_helpers.h" | ||
#include "base/files/file_path.h" | ||
#include "base/files/scoped_temp_dir.h" | ||
#include "base/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_reader.h" | ||
#include "storage/browser/file_system/file_stream_test_utils.h" | ||
#include "testing/gtest/include/gtest/gtest.h" | ||
|
||
namespace storage { | ||
|
||
// An interface for derived FileStreamReader to implement. | ||
// This allows multiple FileStreamReader implementations can share the | ||
// same test framework. Tests should implement CreateFileReader, WriteFile, and | ||
// TouchFile to manipulate files for their particular implementation. | ||
class FileStreamReaderTest : public testing::Test { | ||
public: | ||
static constexpr base::StringPiece kTestFileName = "test.dat"; | ||
static constexpr base::StringPiece kTestData = "0123456789"; | ||
|
||
virtual std::unique_ptr<FileStreamReader> CreateFileReader( | ||
const std::string& file_name, | ||
int64_t initial_offset, | ||
const base::Time& expected_modification_time) = 0; | ||
virtual void WriteFile(const std::string& file_name, | ||
const char* buf, | ||
size_t buf_size, | ||
base::Time* modification_time) = 0; | ||
// Adjust a file's last modified time by |delta|. | ||
virtual void TouchFile(const std::string& file_name, | ||
base::TimeDelta delta) = 0; | ||
virtual void EnsureFileTaskFinished() {} | ||
|
||
base::Time test_file_modification_time() const { | ||
return test_file_modification_time_; | ||
} | ||
|
||
void WriteTestFile() { | ||
WriteFile(kTestFileName.data(), kTestData.data(), kTestData.size(), | ||
&test_file_modification_time_); | ||
} | ||
|
||
static void NeverCalled(int unused) { ADD_FAILURE(); } | ||
|
||
private: | ||
base::test::SingleThreadTaskEnvironment task_environment_{ | ||
base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; | ||
base::Time test_file_modification_time_; | ||
}; | ||
|
||
template <class SubClass> | ||
class FileStreamReaderTypedTest : public SubClass { | ||
public: | ||
void SetUp() override { | ||
SubClass::SetUp(); | ||
this->WriteTestFile(); | ||
} | ||
}; | ||
|
||
TYPED_TEST_SUITE_P(FileStreamReaderTypedTest); | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, NonExistent) { | ||
const char kFileName[] = "nonexistent"; | ||
std::unique_ptr<FileStreamReader> reader( | ||
this->CreateFileReader(kFileName, 0, base::Time())); | ||
int result = 0; | ||
std::string data; | ||
ReadFromReader(reader.get(), &data, 10, &result); | ||
ASSERT_EQ(net::ERR_FILE_NOT_FOUND, result); | ||
ASSERT_EQ(0U, data.size()); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, Empty) { | ||
const char kFileName[] = "empty"; | ||
this->WriteFile(kFileName, nullptr, 0, nullptr); | ||
|
||
std::unique_ptr<FileStreamReader> reader( | ||
this->CreateFileReader(kFileName, 0, base::Time())); | ||
int result = 0; | ||
std::string data; | ||
ReadFromReader(reader.get(), &data, 10, &result); | ||
ASSERT_EQ(net::OK, result); | ||
ASSERT_EQ(0U, data.size()); | ||
|
||
net::TestInt64CompletionCallback callback; | ||
int64_t length_result = reader->GetLength(callback.callback()); | ||
if (length_result == net::ERR_IO_PENDING) | ||
length_result = callback.WaitForResult(); | ||
ASSERT_EQ(0, result); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, GetLengthNormal) { | ||
std::unique_ptr<FileStreamReader> reader(this->CreateFileReader( | ||
this->kTestFileName.data(), 0, this->test_file_modification_time())); | ||
net::TestInt64CompletionCallback callback; | ||
int64_t result = reader->GetLength(callback.callback()); | ||
if (result == net::ERR_IO_PENDING) | ||
result = callback.WaitForResult(); | ||
ASSERT_EQ(static_cast<int64_t>(this->kTestData.size()), result); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, GetLengthAfterModified) { | ||
this->TouchFile(this->kTestFileName.data(), base::TimeDelta::FromSeconds(10)); | ||
|
||
std::unique_ptr<FileStreamReader> reader(this->CreateFileReader( | ||
this->kTestFileName.data(), 0, this->test_file_modification_time())); | ||
net::TestInt64CompletionCallback callback1; | ||
int64_t result = reader->GetLength(callback1.callback()); | ||
if (result == net::ERR_IO_PENDING) | ||
result = callback1.WaitForResult(); | ||
ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result); | ||
|
||
// With nullptr expected modification time this should work. | ||
reader = this->CreateFileReader(this->kTestFileName.data(), 0, base::Time()); | ||
net::TestInt64CompletionCallback callback2; | ||
result = reader->GetLength(callback2.callback()); | ||
if (result == net::ERR_IO_PENDING) | ||
result = callback2.WaitForResult(); | ||
ASSERT_EQ(static_cast<int64_t>(this->kTestData.size()), result); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, GetLengthWithOffset) { | ||
std::unique_ptr<FileStreamReader> reader( | ||
this->CreateFileReader(this->kTestFileName.data(), 3, base::Time())); | ||
net::TestInt64CompletionCallback callback; | ||
int64_t result = reader->GetLength(callback.callback()); | ||
if (result == net::ERR_IO_PENDING) | ||
result = callback.WaitForResult(); | ||
// Initial offset does not affect the result of GetLength. | ||
ASSERT_EQ(static_cast<int64_t>(this->kTestData.size()), result); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, ReadNormal) { | ||
std::unique_ptr<FileStreamReader> reader(this->CreateFileReader( | ||
this->kTestFileName.data(), 0, this->test_file_modification_time())); | ||
int result = 0; | ||
std::string data; | ||
ReadFromReader(reader.get(), &data, this->kTestData.size(), &result); | ||
ASSERT_EQ(net::OK, result); | ||
ASSERT_EQ(this->kTestData, data); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, ReadAfterModified) { | ||
// Touch file so that the file's modification time becomes different | ||
// from what we expect. Note that the resolution on some filesystems | ||
// is 1s so we can't test with deltas less than that. | ||
this->TouchFile(this->kTestFileName.data(), base::TimeDelta::FromSeconds(-1)); | ||
|
||
std::unique_ptr<FileStreamReader> reader(this->CreateFileReader( | ||
this->kTestFileName.data(), 0, this->test_file_modification_time())); | ||
int result = 0; | ||
std::string data; | ||
ReadFromReader(reader.get(), &data, this->kTestData.size(), &result); | ||
ASSERT_EQ(net::ERR_UPLOAD_FILE_CHANGED, result); | ||
ASSERT_EQ(0U, data.size()); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, ReadAfterModifiedLessThanThreshold) { | ||
// Due to precision loss converting int64_t->double->int64_t (e.g. through | ||
// Blink) the expected/actual time may vary by microseconds. With | ||
// modification time delta < 10us this should work. | ||
this->TouchFile(this->kTestFileName.data(), | ||
base::TimeDelta::FromMicroseconds(1)); | ||
std::unique_ptr<FileStreamReader> reader(this->CreateFileReader( | ||
this->kTestFileName.data(), 0, this->test_file_modification_time())); | ||
int result = 0; | ||
std::string data; | ||
|
||
ReadFromReader(reader.get(), &data, this->kTestData.size(), &result); | ||
ASSERT_EQ(net::OK, result); | ||
ASSERT_EQ(this->kTestData, data); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, ReadAfterModifiedWithMatchingTimes) { | ||
this->TouchFile(this->kTestFileName.data(), base::TimeDelta()); | ||
std::unique_ptr<FileStreamReader> reader(this->CreateFileReader( | ||
this->kTestFileName.data(), 0, this->test_file_modification_time())); | ||
int result = 0; | ||
std::string data; | ||
|
||
ReadFromReader(reader.get(), &data, this->kTestData.size(), &result); | ||
ASSERT_EQ(net::OK, result); | ||
ASSERT_EQ(this->kTestData, data); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, ReadAfterModifiedWithoutExpectedTime) { | ||
this->TouchFile(this->kTestFileName.data(), base::TimeDelta::FromSeconds(-1)); | ||
std::unique_ptr<FileStreamReader> reader( | ||
this->CreateFileReader(this->kTestFileName.data(), 0, base::Time())); | ||
int result = 0; | ||
std::string data; | ||
|
||
ReadFromReader(reader.get(), &data, this->kTestData.size(), &result); | ||
ASSERT_EQ(net::OK, result); | ||
ASSERT_EQ(this->kTestData, data); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, ReadWithOffset) { | ||
std::unique_ptr<FileStreamReader> reader( | ||
this->CreateFileReader(this->kTestFileName.data(), 3, base::Time())); | ||
int result = 0; | ||
std::string data; | ||
ReadFromReader(reader.get(), &data, this->kTestData.size(), &result); | ||
ASSERT_EQ(net::OK, result); | ||
|
||
ASSERT_EQ(this->kTestData.substr(3), data); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, ReadWithNegativeOffset) { | ||
std::unique_ptr<FileStreamReader> reader( | ||
this->CreateFileReader(this->kTestFileName.data(), -1, base::Time())); | ||
int result = 0; | ||
std::string data; | ||
ReadFromReader(reader.get(), &data, 1, &result); | ||
ASSERT_EQ(net::ERR_INVALID_ARGUMENT, result); | ||
ASSERT_EQ(data.size(), 0u); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, ReadWithOffsetLargerThanFile) { | ||
std::unique_ptr<FileStreamReader> reader(this->CreateFileReader( | ||
this->kTestFileName.data(), this->kTestData.size() + 1, base::Time())); | ||
int result = 0; | ||
std::string data; | ||
ReadFromReader(reader.get(), &data, 1, &result); | ||
ASSERT_EQ(data.size(), 0u); | ||
ASSERT_EQ(net::OK, result); | ||
} | ||
|
||
TYPED_TEST_P(FileStreamReaderTypedTest, DeleteWithUnfinishedRead) { | ||
std::unique_ptr<FileStreamReader> reader( | ||
this->CreateFileReader(this->kTestFileName.data(), 0, base::Time())); | ||
|
||
net::TestCompletionCallback callback; | ||
scoped_refptr<net::IOBufferWithSize> buf = | ||
base::MakeRefCounted<net::IOBufferWithSize>(this->kTestData.size()); | ||
int rv = reader->Read(buf.get(), buf->size(), | ||
base::BindOnce(&FileStreamReaderTest::NeverCalled)); | ||
if (rv < 0) | ||
ASSERT_EQ(rv, net::ERR_IO_PENDING); | ||
|
||
// Delete immediately. | ||
// Should not crash; nor should NeverCalled be callback. | ||
reader = nullptr; | ||
this->EnsureFileTaskFinished(); | ||
} | ||
|
||
REGISTER_TYPED_TEST_SUITE_P(FileStreamReaderTypedTest, | ||
NonExistent, | ||
Empty, | ||
GetLengthNormal, | ||
GetLengthAfterModified, | ||
GetLengthWithOffset, | ||
ReadNormal, | ||
ReadAfterModified, | ||
ReadAfterModifiedLessThanThreshold, | ||
ReadAfterModifiedWithMatchingTimes, | ||
ReadAfterModifiedWithoutExpectedTime, | ||
ReadWithOffset, | ||
ReadWithNegativeOffset, | ||
ReadWithOffsetLargerThanFile, | ||
DeleteWithUnfinishedRead); | ||
|
||
} // namespace storage | ||
|
||
#endif // STORAGE_BROWSER_FILE_SYSTEM_FILE_STREAM_READER_TEST_H_ |
Oops, something went wrong.