123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884 |
- // Copyright (c) 2012 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 "net/base/file_stream.h"
- #include <string>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/files/file.h"
- #include "base/files/file_util.h"
- #include "base/memory/raw_ptr.h"
- #include "base/path_service.h"
- #include "base/run_loop.h"
- #include "base/strings/string_util.h"
- #include "base/synchronization/waitable_event.h"
- #include "base/task/current_thread.h"
- #include "base/test/test_timeouts.h"
- #include "base/threading/thread.h"
- #include "base/threading/thread_restrictions.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "build/build_config.h"
- #include "net/base/io_buffer.h"
- #include "net/base/net_errors.h"
- #include "net/base/test_completion_callback.h"
- #include "net/log/test_net_log.h"
- #include "net/test/gtest_util.h"
- #include "net/test/test_with_task_environment.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "testing/platform_test.h"
- using net::test::IsError;
- using net::test::IsOk;
- #if BUILDFLAG(IS_ANDROID)
- #include "base/test/test_file_util.h"
- #endif
- namespace net {
- namespace {
- constexpr char kTestData[] = "0123456789";
- constexpr int kTestDataSize = std::size(kTestData) - 1;
- // Creates an IOBufferWithSize that contains the kTestDataSize.
- scoped_refptr<IOBufferWithSize> CreateTestDataBuffer() {
- scoped_refptr<IOBufferWithSize> buf =
- base::MakeRefCounted<IOBufferWithSize>(kTestDataSize);
- memcpy(buf->data(), kTestData, kTestDataSize);
- return buf;
- }
- } // namespace
- class FileStreamTest : public PlatformTest, public WithTaskEnvironment {
- public:
- void SetUp() override {
- PlatformTest::SetUp();
- base::CreateTemporaryFile(&temp_file_path_);
- base::WriteFile(temp_file_path_, kTestData, kTestDataSize);
- }
- void TearDown() override {
- // FileStreamContexts must be asynchronously closed on the file task runner
- // before they can be deleted. Pump the RunLoop to avoid leaks.
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(base::DeleteFile(temp_file_path_));
- PlatformTest::TearDown();
- }
- const base::FilePath temp_file_path() const { return temp_file_path_; }
- private:
- base::FilePath temp_file_path_;
- };
- namespace {
- TEST_F(FileStreamTest, OpenExplicitClose) {
- TestCompletionCallback callback;
- FileStream stream(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_OPEN |
- base::File::FLAG_READ |
- base::File::FLAG_ASYNC;
- int rv = stream.Open(temp_file_path(), flags, callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- EXPECT_TRUE(stream.IsOpen());
- EXPECT_THAT(stream.Close(callback.callback()), IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- EXPECT_FALSE(stream.IsOpen());
- }
- TEST_F(FileStreamTest, OpenExplicitCloseOrphaned) {
- TestCompletionCallback callback;
- auto stream =
- std::make_unique<FileStream>(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
- base::File::FLAG_ASYNC;
- int rv = stream->Open(temp_file_path(), flags, callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- EXPECT_TRUE(stream->IsOpen());
- EXPECT_THAT(stream->Close(callback.callback()), IsError(ERR_IO_PENDING));
- stream.reset();
- // File isn't actually closed yet.
- base::RunLoop runloop;
- runloop.RunUntilIdle();
- // The file should now be closed, though the callback has not been called.
- }
- // Test the use of FileStream with a file handle provided at construction.
- TEST_F(FileStreamTest, UseFileHandle) {
- int rv = 0;
- TestCompletionCallback callback;
- TestInt64CompletionCallback callback64;
- // 1. Test reading with a file handle.
- ASSERT_EQ(kTestDataSize,
- base::WriteFile(temp_file_path(), kTestData, kTestDataSize));
- int flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
- base::File::FLAG_ASYNC;
- base::File file1(temp_file_path(), flags);
- // Seek to the beginning of the file and read.
- auto read_stream = std::make_unique<FileStream>(
- std::move(file1), base::ThreadTaskRunnerHandle::Get());
- ASSERT_THAT(read_stream->Seek(0, callback64.callback()),
- IsError(ERR_IO_PENDING));
- ASSERT_EQ(0, callback64.WaitForResult());
- // Read into buffer and compare.
- scoped_refptr<IOBufferWithSize> read_buffer =
- base::MakeRefCounted<IOBufferWithSize>(kTestDataSize);
- rv = read_stream->Read(read_buffer.get(), kTestDataSize, callback.callback());
- ASSERT_EQ(kTestDataSize, callback.GetResult(rv));
- ASSERT_EQ(0, memcmp(kTestData, read_buffer->data(), kTestDataSize));
- read_stream.reset();
- // 2. Test writing with a file handle.
- base::DeleteFile(temp_file_path());
- flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE |
- base::File::FLAG_ASYNC;
- base::File file2(temp_file_path(), flags);
- auto write_stream = std::make_unique<FileStream>(
- std::move(file2), base::ThreadTaskRunnerHandle::Get());
- ASSERT_THAT(write_stream->Seek(0, callback64.callback()),
- IsError(ERR_IO_PENDING));
- ASSERT_EQ(0, callback64.WaitForResult());
- scoped_refptr<IOBufferWithSize> write_buffer = CreateTestDataBuffer();
- rv = write_stream->Write(write_buffer.get(), kTestDataSize,
- callback.callback());
- ASSERT_EQ(kTestDataSize, callback.GetResult(rv));
- write_stream.reset();
- // Read into buffer and compare to make sure the handle worked fine.
- ASSERT_EQ(kTestDataSize,
- base::ReadFile(temp_file_path(), read_buffer->data(),
- kTestDataSize));
- ASSERT_EQ(0, memcmp(kTestData, read_buffer->data(), kTestDataSize));
- }
- TEST_F(FileStreamTest, UseClosedStream) {
- int rv = 0;
- TestCompletionCallback callback;
- TestInt64CompletionCallback callback64;
- FileStream stream(base::ThreadTaskRunnerHandle::Get());
- EXPECT_FALSE(stream.IsOpen());
- // Try seeking...
- rv = stream.Seek(5, callback64.callback());
- EXPECT_THAT(callback64.GetResult(rv), IsError(ERR_UNEXPECTED));
- // Try reading...
- scoped_refptr<IOBufferWithSize> buf =
- base::MakeRefCounted<IOBufferWithSize>(10);
- rv = stream.Read(buf.get(), buf->size(), callback.callback());
- EXPECT_THAT(callback.GetResult(rv), IsError(ERR_UNEXPECTED));
- }
- TEST_F(FileStreamTest, Read) {
- int64_t file_size;
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- FileStream stream(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
- base::File::FLAG_ASYNC;
- TestCompletionCallback callback;
- int rv = stream.Open(temp_file_path(), flags, callback.callback());
- EXPECT_THAT(callback.GetResult(rv), IsOk());
- int total_bytes_read = 0;
- std::string data_read;
- for (;;) {
- scoped_refptr<IOBufferWithSize> buf =
- base::MakeRefCounted<IOBufferWithSize>(4);
- rv = stream.Read(buf.get(), buf->size(), callback.callback());
- rv = callback.GetResult(rv);
- EXPECT_LE(0, rv);
- if (rv <= 0)
- break;
- total_bytes_read += rv;
- data_read.append(buf->data(), rv);
- }
- EXPECT_EQ(file_size, total_bytes_read);
- EXPECT_EQ(kTestData, data_read);
- }
- TEST_F(FileStreamTest, Read_EarlyDelete) {
- int64_t file_size;
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- auto stream =
- std::make_unique<FileStream>(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
- base::File::FLAG_ASYNC;
- TestCompletionCallback callback;
- int rv = stream->Open(temp_file_path(), flags, callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- scoped_refptr<IOBufferWithSize> buf =
- base::MakeRefCounted<IOBufferWithSize>(4);
- rv = stream->Read(buf.get(), buf->size(), callback.callback());
- stream.reset(); // Delete instead of closing it.
- if (rv < 0) {
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // The callback should not be called if the request is cancelled.
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(callback.have_result());
- } else {
- EXPECT_EQ(std::string(kTestData, rv), std::string(buf->data(), rv));
- }
- }
- TEST_F(FileStreamTest, Read_FromOffset) {
- int64_t file_size;
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- FileStream stream(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
- base::File::FLAG_ASYNC;
- TestCompletionCallback callback;
- int rv = stream.Open(temp_file_path(), flags, callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- TestInt64CompletionCallback callback64;
- const int64_t kOffset = 3;
- rv = stream.Seek(kOffset, callback64.callback());
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- int64_t new_offset = callback64.WaitForResult();
- EXPECT_EQ(kOffset, new_offset);
- int total_bytes_read = 0;
- std::string data_read;
- for (;;) {
- scoped_refptr<IOBufferWithSize> buf =
- base::MakeRefCounted<IOBufferWithSize>(4);
- rv = stream.Read(buf.get(), buf->size(), callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- EXPECT_LE(0, rv);
- if (rv <= 0)
- break;
- total_bytes_read += rv;
- data_read.append(buf->data(), rv);
- }
- EXPECT_EQ(file_size - kOffset, total_bytes_read);
- EXPECT_EQ(kTestData + kOffset, data_read);
- }
- TEST_F(FileStreamTest, Write) {
- FileStream stream(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
- base::File::FLAG_ASYNC;
- TestCompletionCallback callback;
- int rv = stream.Open(temp_file_path(), flags, callback.callback());
- EXPECT_THAT(callback.GetResult(rv), IsOk());
- int64_t file_size;
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- EXPECT_EQ(0, file_size);
- scoped_refptr<IOBuffer> buf = CreateTestDataBuffer();
- rv = stream.Write(buf.get(), kTestDataSize, callback.callback());
- rv = callback.GetResult(rv);
- EXPECT_EQ(kTestDataSize, rv);
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- EXPECT_EQ(kTestDataSize, file_size);
- std::string data_read;
- EXPECT_TRUE(base::ReadFileToString(temp_file_path(), &data_read));
- EXPECT_EQ(kTestData, data_read);
- }
- TEST_F(FileStreamTest, Write_EarlyDelete) {
- auto stream =
- std::make_unique<FileStream>(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
- base::File::FLAG_ASYNC;
- TestCompletionCallback callback;
- int rv = stream->Open(temp_file_path(), flags, callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- int64_t file_size;
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- EXPECT_EQ(0, file_size);
- scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
- rv = stream->Write(buf.get(), buf->size(), callback.callback());
- stream.reset();
- if (rv < 0) {
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // The callback should not be called if the request is cancelled.
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(callback.have_result());
- } else {
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- EXPECT_EQ(file_size, rv);
- }
- }
- TEST_F(FileStreamTest, Write_FromOffset) {
- int64_t file_size;
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- FileStream stream(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_OPEN | base::File::FLAG_WRITE |
- base::File::FLAG_ASYNC;
- TestCompletionCallback callback;
- int rv = stream.Open(temp_file_path(), flags, callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- TestInt64CompletionCallback callback64;
- const int64_t kOffset = kTestDataSize;
- rv = stream.Seek(kOffset, callback64.callback());
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- int64_t new_offset = callback64.WaitForResult();
- EXPECT_EQ(kTestDataSize, new_offset);
- int total_bytes_written = 0;
- scoped_refptr<IOBufferWithSize> buffer = CreateTestDataBuffer();
- int buffer_size = buffer->size();
- scoped_refptr<DrainableIOBuffer> drainable =
- base::MakeRefCounted<DrainableIOBuffer>(std::move(buffer), buffer_size);
- while (total_bytes_written != kTestDataSize) {
- rv = stream.Write(drainable.get(), drainable->BytesRemaining(),
- callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- EXPECT_LT(0, rv);
- if (rv <= 0)
- break;
- drainable->DidConsume(rv);
- total_bytes_written += rv;
- }
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- EXPECT_EQ(file_size, kTestDataSize * 2);
- }
- TEST_F(FileStreamTest, BasicReadWrite) {
- int64_t file_size;
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- auto stream =
- std::make_unique<FileStream>(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
- base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
- TestCompletionCallback callback;
- int rv = stream->Open(temp_file_path(), flags, callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- int64_t total_bytes_read = 0;
- std::string data_read;
- for (;;) {
- scoped_refptr<IOBufferWithSize> buf =
- base::MakeRefCounted<IOBufferWithSize>(4);
- rv = stream->Read(buf.get(), buf->size(), callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- EXPECT_LE(0, rv);
- if (rv <= 0)
- break;
- total_bytes_read += rv;
- data_read.append(buf->data(), rv);
- }
- EXPECT_EQ(file_size, total_bytes_read);
- EXPECT_TRUE(data_read == kTestData);
- int total_bytes_written = 0;
- scoped_refptr<IOBufferWithSize> buffer = CreateTestDataBuffer();
- int buffer_size = buffer->size();
- scoped_refptr<DrainableIOBuffer> drainable =
- base::MakeRefCounted<DrainableIOBuffer>(std::move(buffer), buffer_size);
- while (total_bytes_written != kTestDataSize) {
- rv = stream->Write(drainable.get(), drainable->BytesRemaining(),
- callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- EXPECT_LT(0, rv);
- if (rv <= 0)
- break;
- drainable->DidConsume(rv);
- total_bytes_written += rv;
- }
- stream.reset();
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- EXPECT_EQ(kTestDataSize * 2, file_size);
- }
- TEST_F(FileStreamTest, BasicWriteRead) {
- int64_t file_size;
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- auto stream =
- std::make_unique<FileStream>(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
- base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
- TestCompletionCallback callback;
- int rv = stream->Open(temp_file_path(), flags, callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- TestInt64CompletionCallback callback64;
- rv = stream->Seek(file_size, callback64.callback());
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- int64_t offset = callback64.WaitForResult();
- EXPECT_EQ(offset, file_size);
- int total_bytes_written = 0;
- scoped_refptr<IOBufferWithSize> buffer = CreateTestDataBuffer();
- int buffer_size = buffer->size();
- scoped_refptr<DrainableIOBuffer> drainable =
- base::MakeRefCounted<DrainableIOBuffer>(std::move(buffer), buffer_size);
- while (total_bytes_written != kTestDataSize) {
- rv = stream->Write(drainable.get(), drainable->BytesRemaining(),
- callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- EXPECT_LT(0, rv);
- if (rv <= 0)
- break;
- drainable->DidConsume(rv);
- total_bytes_written += rv;
- }
- EXPECT_EQ(kTestDataSize, total_bytes_written);
- rv = stream->Seek(0, callback64.callback());
- ASSERT_THAT(rv, IsError(ERR_IO_PENDING));
- offset = callback64.WaitForResult();
- EXPECT_EQ(0, offset);
- int total_bytes_read = 0;
- std::string data_read;
- for (;;) {
- scoped_refptr<IOBufferWithSize> buf =
- base::MakeRefCounted<IOBufferWithSize>(4);
- rv = stream->Read(buf.get(), buf->size(), callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- EXPECT_LE(0, rv);
- if (rv <= 0)
- break;
- total_bytes_read += rv;
- data_read.append(buf->data(), rv);
- }
- stream.reset();
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- EXPECT_EQ(kTestDataSize * 2, file_size);
- EXPECT_EQ(kTestDataSize * 2, total_bytes_read);
- const std::string kExpectedFileData =
- std::string(kTestData) + std::string(kTestData);
- EXPECT_EQ(kExpectedFileData, data_read);
- }
- class TestWriteReadCompletionCallback {
- public:
- TestWriteReadCompletionCallback(FileStream* stream,
- int* total_bytes_written,
- int* total_bytes_read,
- std::string* data_read)
- : stream_(stream),
- total_bytes_written_(total_bytes_written),
- total_bytes_read_(total_bytes_read),
- data_read_(data_read),
- drainable_(
- base::MakeRefCounted<DrainableIOBuffer>(CreateTestDataBuffer(),
- kTestDataSize)) {}
- TestWriteReadCompletionCallback(const TestWriteReadCompletionCallback&) =
- delete;
- TestWriteReadCompletionCallback& operator=(
- const TestWriteReadCompletionCallback&) = delete;
- int WaitForResult() {
- DCHECK(!waiting_for_result_);
- while (!have_result_) {
- waiting_for_result_ = true;
- base::RunLoop().Run();
- waiting_for_result_ = false;
- }
- have_result_ = false; // auto-reset for next callback
- return result_;
- }
- CompletionOnceCallback callback() {
- return base::BindOnce(&TestWriteReadCompletionCallback::OnComplete,
- base::Unretained(this));
- }
- void ValidateWrittenData() {
- TestCompletionCallback callback;
- int rv = 0;
- for (;;) {
- scoped_refptr<IOBufferWithSize> buf =
- base::MakeRefCounted<IOBufferWithSize>(4);
- rv = stream_->Read(buf.get(), buf->size(), callback.callback());
- if (rv == ERR_IO_PENDING) {
- rv = callback.WaitForResult();
- }
- EXPECT_LE(0, rv);
- if (rv <= 0)
- break;
- *total_bytes_read_ += rv;
- data_read_->append(buf->data(), rv);
- }
- }
- private:
- void OnComplete(int result) {
- DCHECK_LT(0, result);
- *total_bytes_written_ += result;
- int rv;
- if (*total_bytes_written_ != kTestDataSize) {
- // Recurse to finish writing all data.
- int total_bytes_written = 0, total_bytes_read = 0;
- std::string data_read;
- TestWriteReadCompletionCallback callback(
- stream_, &total_bytes_written, &total_bytes_read, &data_read);
- rv = stream_->Write(
- drainable_.get(), drainable_->BytesRemaining(), callback.callback());
- DCHECK_EQ(ERR_IO_PENDING, rv);
- rv = callback.WaitForResult();
- drainable_->DidConsume(total_bytes_written);
- *total_bytes_written_ += total_bytes_written;
- *total_bytes_read_ += total_bytes_read;
- *data_read_ += data_read;
- } else { // We're done writing all data. Start reading the data.
- TestInt64CompletionCallback callback64;
- EXPECT_THAT(stream_->Seek(0, callback64.callback()),
- IsError(ERR_IO_PENDING));
- {
- EXPECT_LE(0, callback64.WaitForResult());
- }
- }
- result_ = *total_bytes_written_;
- have_result_ = true;
- if (waiting_for_result_)
- base::RunLoop::QuitCurrentWhenIdleDeprecated();
- }
- int result_ = 0;
- bool have_result_ = false;
- bool waiting_for_result_ = false;
- raw_ptr<FileStream> stream_;
- raw_ptr<int> total_bytes_written_;
- raw_ptr<int> total_bytes_read_;
- raw_ptr<std::string> data_read_;
- scoped_refptr<DrainableIOBuffer> drainable_;
- };
- TEST_F(FileStreamTest, WriteRead) {
- int64_t file_size;
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- auto stream =
- std::make_unique<FileStream>(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
- base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
- TestCompletionCallback open_callback;
- int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(open_callback.WaitForResult(), IsOk());
- TestInt64CompletionCallback callback64;
- EXPECT_THAT(stream->Seek(file_size, callback64.callback()),
- IsError(ERR_IO_PENDING));
- EXPECT_EQ(file_size, callback64.WaitForResult());
- int total_bytes_written = 0;
- int total_bytes_read = 0;
- std::string data_read;
- TestWriteReadCompletionCallback callback(stream.get(), &total_bytes_written,
- &total_bytes_read, &data_read);
- scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
- rv = stream->Write(buf.get(), buf->size(), callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- EXPECT_LT(0, rv);
- EXPECT_EQ(kTestDataSize, total_bytes_written);
- callback.ValidateWrittenData();
- stream.reset();
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- EXPECT_EQ(kTestDataSize * 2, file_size);
- EXPECT_EQ(kTestDataSize * 2, total_bytes_read);
- const std::string kExpectedFileData =
- std::string(kTestData) + std::string(kTestData);
- EXPECT_EQ(kExpectedFileData, data_read);
- }
- class TestWriteCloseCompletionCallback {
- public:
- TestWriteCloseCompletionCallback(FileStream* stream, int* total_bytes_written)
- : stream_(stream),
- total_bytes_written_(total_bytes_written),
- drainable_(
- base::MakeRefCounted<DrainableIOBuffer>(CreateTestDataBuffer(),
- kTestDataSize)) {}
- TestWriteCloseCompletionCallback(const TestWriteCloseCompletionCallback&) =
- delete;
- TestWriteCloseCompletionCallback& operator=(
- const TestWriteCloseCompletionCallback&) = delete;
- int WaitForResult() {
- DCHECK(!waiting_for_result_);
- while (!have_result_) {
- waiting_for_result_ = true;
- base::RunLoop().Run();
- waiting_for_result_ = false;
- }
- have_result_ = false; // auto-reset for next callback
- return result_;
- }
- CompletionOnceCallback callback() {
- return base::BindOnce(&TestWriteCloseCompletionCallback::OnComplete,
- base::Unretained(this));
- }
- private:
- void OnComplete(int result) {
- DCHECK_LT(0, result);
- *total_bytes_written_ += result;
- int rv;
- if (*total_bytes_written_ != kTestDataSize) {
- // Recurse to finish writing all data.
- int total_bytes_written = 0;
- TestWriteCloseCompletionCallback callback(stream_, &total_bytes_written);
- rv = stream_->Write(
- drainable_.get(), drainable_->BytesRemaining(), callback.callback());
- DCHECK_EQ(ERR_IO_PENDING, rv);
- rv = callback.WaitForResult();
- drainable_->DidConsume(total_bytes_written);
- *total_bytes_written_ += total_bytes_written;
- }
- result_ = *total_bytes_written_;
- have_result_ = true;
- if (waiting_for_result_)
- base::RunLoop::QuitCurrentWhenIdleDeprecated();
- }
- int result_ = 0;
- bool have_result_ = false;
- bool waiting_for_result_ = false;
- raw_ptr<FileStream> stream_;
- raw_ptr<int> total_bytes_written_;
- scoped_refptr<DrainableIOBuffer> drainable_;
- };
- TEST_F(FileStreamTest, WriteClose) {
- int64_t file_size;
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- auto stream =
- std::make_unique<FileStream>(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
- base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
- TestCompletionCallback open_callback;
- int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(open_callback.WaitForResult(), IsOk());
- TestInt64CompletionCallback callback64;
- EXPECT_THAT(stream->Seek(file_size, callback64.callback()),
- IsError(ERR_IO_PENDING));
- EXPECT_EQ(file_size, callback64.WaitForResult());
- int total_bytes_written = 0;
- TestWriteCloseCompletionCallback callback(stream.get(), &total_bytes_written);
- scoped_refptr<IOBufferWithSize> buf = CreateTestDataBuffer();
- rv = stream->Write(buf.get(), buf->size(), callback.callback());
- if (rv == ERR_IO_PENDING)
- total_bytes_written = callback.WaitForResult();
- EXPECT_LT(0, total_bytes_written);
- EXPECT_EQ(kTestDataSize, total_bytes_written);
- stream.reset();
- EXPECT_TRUE(base::GetFileSize(temp_file_path(), &file_size));
- EXPECT_EQ(kTestDataSize * 2, file_size);
- }
- TEST_F(FileStreamTest, OpenAndDelete) {
- base::Thread worker_thread("StreamTest");
- ASSERT_TRUE(worker_thread.Start());
- base::ScopedDisallowBlocking disallow_blocking;
- auto stream = std::make_unique<FileStream>(worker_thread.task_runner());
- int flags = base::File::FLAG_OPEN | base::File::FLAG_WRITE |
- base::File::FLAG_ASYNC;
- TestCompletionCallback open_callback;
- int rv = stream->Open(temp_file_path(), flags, open_callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- // Delete the stream without waiting for the open operation to be
- // complete. Should be safe.
- stream.reset();
- // Force an operation through the worker.
- auto stream2 = std::make_unique<FileStream>(worker_thread.task_runner());
- TestCompletionCallback open_callback2;
- rv = stream2->Open(temp_file_path(), flags, open_callback2.callback());
- EXPECT_THAT(open_callback2.GetResult(rv), IsOk());
- stream2.reset();
- // open_callback won't be called.
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(open_callback.have_result());
- }
- // Verify that Write() errors are mapped correctly.
- TEST_F(FileStreamTest, WriteError) {
- // Try opening file as read-only and then writing to it using FileStream.
- uint32_t flags =
- base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_ASYNC;
- base::File file(temp_file_path(), flags);
- ASSERT_TRUE(file.IsValid());
- auto stream = std::make_unique<FileStream>(
- std::move(file), base::ThreadTaskRunnerHandle::Get());
- scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBuffer>(1);
- buf->data()[0] = 0;
- TestCompletionCallback callback;
- int rv = stream->Write(buf.get(), 1, callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- EXPECT_LT(rv, 0);
- stream.reset();
- base::RunLoop().RunUntilIdle();
- }
- // Verify that Read() errors are mapped correctly.
- TEST_F(FileStreamTest, ReadError) {
- // Try opening file for write and then reading from it using FileStream.
- uint32_t flags =
- base::File::FLAG_OPEN | base::File::FLAG_WRITE | base::File::FLAG_ASYNC;
- base::File file(temp_file_path(), flags);
- ASSERT_TRUE(file.IsValid());
- auto stream = std::make_unique<FileStream>(
- std::move(file), base::ThreadTaskRunnerHandle::Get());
- scoped_refptr<IOBuffer> buf = base::MakeRefCounted<IOBuffer>(1);
- TestCompletionCallback callback;
- int rv = stream->Read(buf.get(), 1, callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- EXPECT_LT(rv, 0);
- stream.reset();
- base::RunLoop().RunUntilIdle();
- }
- #if BUILDFLAG(IS_WIN)
- // Verifies that a FileStream will close itself if it receives a File whose
- // async flag doesn't match the async state of the underlying handle.
- TEST_F(FileStreamTest, AsyncFlagMismatch) {
- // Open the test file without async, then make a File with the same sync
- // handle but with the async flag set to true.
- uint32_t flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
- base::File file(temp_file_path(), flags);
- base::File lying_file(file.TakePlatformFile(), true);
- ASSERT_TRUE(lying_file.IsValid());
- FileStream stream(std::move(lying_file), base::ThreadTaskRunnerHandle::Get());
- ASSERT_FALSE(stream.IsOpen());
- TestCompletionCallback callback;
- scoped_refptr<IOBufferWithSize> buf =
- base::MakeRefCounted<IOBufferWithSize>(4);
- int rv = stream.Read(buf.get(), buf->size(), callback.callback());
- EXPECT_THAT(callback.GetResult(rv), IsError(ERR_UNEXPECTED));
- }
- #endif
- #if BUILDFLAG(IS_ANDROID)
- // TODO(https://crbug.com/894599): flaky on both android and cronet bots.
- TEST_F(FileStreamTest, DISABLED_ContentUriRead) {
- base::FilePath test_dir;
- base::PathService::Get(base::DIR_SOURCE_ROOT, &test_dir);
- test_dir = test_dir.AppendASCII("net");
- test_dir = test_dir.AppendASCII("data");
- test_dir = test_dir.AppendASCII("file_stream_unittest");
- ASSERT_TRUE(base::PathExists(test_dir));
- base::FilePath image_file = test_dir.Append(FILE_PATH_LITERAL("red.png"));
- // Insert the image into MediaStore. MediaStore will do some conversions, and
- // return the content URI.
- base::FilePath path = base::InsertImageIntoMediaStore(image_file);
- EXPECT_TRUE(path.IsContentUri());
- EXPECT_TRUE(base::PathExists(path));
- int64_t file_size;
- EXPECT_TRUE(base::GetFileSize(path, &file_size));
- EXPECT_LT(0, file_size);
- FileStream stream(base::ThreadTaskRunnerHandle::Get());
- int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
- base::File::FLAG_ASYNC;
- TestCompletionCallback callback;
- int rv = stream.Open(path, flags, callback.callback());
- EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
- EXPECT_THAT(callback.WaitForResult(), IsOk());
- int total_bytes_read = 0;
- std::string data_read;
- for (;;) {
- scoped_refptr<IOBufferWithSize> buf =
- base::MakeRefCounted<IOBufferWithSize>(4);
- rv = stream.Read(buf.get(), buf->size(), callback.callback());
- if (rv == ERR_IO_PENDING)
- rv = callback.WaitForResult();
- EXPECT_LE(0, rv);
- if (rv <= 0)
- break;
- total_bytes_read += rv;
- data_read.append(buf->data(), rv);
- }
- EXPECT_EQ(file_size, total_bytes_read);
- }
- #endif
- } // namespace
- } // namespace net
|