// Copyright 2015 The Chromium OS 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 <brillo/streams/file_stream.h>

#include <limits>
#include <numeric>
#include <string>
#include <sys/stat.h>
#include <vector>

#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/message_loop/message_loop.h>
#include <base/rand_util.h>
#include <base/run_loop.h>
#include <brillo/bind_lambda.h>
#include <brillo/errors/error_codes.h>
#include <brillo/message_loops/base_message_loop.h>
#include <brillo/message_loops/message_loop_utils.h>
#include <brillo/streams/stream_errors.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>

using testing::InSequence;
using testing::Return;
using testing::ReturnArg;
using testing::SaveArg;
using testing::SetErrnoAndReturn;
using testing::_;

namespace brillo {

namespace {

// gmock action that would return a blocking situation from a read() or write().
ACTION(ReturnWouldBlock) {
  errno = EWOULDBLOCK;
  return -1;
}

// Helper function to read one byte from the stream.
inline int ReadByte(Stream* stream) {
  uint8_t byte = 0;
  return stream->ReadAllBlocking(&byte, sizeof(byte), nullptr) ? byte : -1;
}

// Helper function to write one byte from the stream.
inline bool WriteByte(Stream* stream, uint8_t byte) {
  return stream->WriteAllBlocking(&byte, sizeof(byte), nullptr);
}

// Helper function to test file stream workflow on newly created file.
void TestCreateFile(Stream* stream) {
  ASSERT_TRUE(stream->IsOpen());

  // Set up a sample data buffer.
  std::vector<uint8_t> in_buffer(256);
  std::iota(in_buffer.begin(), in_buffer.end(), 0);

  // Initial assumptions about empty file stream.
  EXPECT_TRUE(stream->CanRead());
  EXPECT_TRUE(stream->CanWrite());
  EXPECT_TRUE(stream->CanSeek());
  EXPECT_TRUE(stream->CanGetSize());
  EXPECT_EQ(0, stream->GetPosition());
  EXPECT_EQ(0, stream->GetSize());

  // Write sample data.
  EXPECT_TRUE(stream->WriteAllBlocking(in_buffer.data(), in_buffer.size(),
                                       nullptr));
  EXPECT_EQ(in_buffer.size(), stream->GetPosition());
  EXPECT_EQ(in_buffer.size(), stream->GetSize());

  // Rewind the stream to the beginning.
  uint64_t pos = 0;
  EXPECT_TRUE(stream->Seek(0, Stream::Whence::FROM_BEGIN, &pos, nullptr));
  EXPECT_EQ(0, pos);
  EXPECT_EQ(0, stream->GetPosition());
  EXPECT_EQ(in_buffer.size(), stream->GetSize());

  // Read the file contents back.
  std::vector<uint8_t> out_buffer(256);
  EXPECT_TRUE(stream->ReadAllBlocking(out_buffer.data(), out_buffer.size(),
                                      nullptr));
  EXPECT_EQ(out_buffer.size(), stream->GetPosition());
  EXPECT_EQ(out_buffer.size(), stream->GetSize());

  // Make sure the data read matches those written.
  EXPECT_EQ(in_buffer, out_buffer);

  // Random read/write
  EXPECT_TRUE(stream->Seek(10, Stream::Whence::FROM_BEGIN, &pos, nullptr));
  EXPECT_EQ(10, pos);

  // Since our data buffer contained values from 0 to 255, the byte at position
  // 10 will contain the value of 10.
  EXPECT_EQ(10, ReadByte(stream));
  EXPECT_EQ(11, ReadByte(stream));
  EXPECT_EQ(12, ReadByte(stream));
  EXPECT_TRUE(stream->Seek(7, Stream::Whence::FROM_CURRENT, nullptr, nullptr));
  EXPECT_EQ(20, ReadByte(stream));

  EXPECT_EQ(21, stream->GetPosition());
  EXPECT_TRUE(stream->Seek(-2, Stream::Whence::FROM_CURRENT, &pos, nullptr));
  EXPECT_EQ(19, pos);
  EXPECT_TRUE(WriteByte(stream, 100));
  EXPECT_EQ(20, ReadByte(stream));
  EXPECT_TRUE(stream->Seek(-2, Stream::Whence::FROM_CURRENT, nullptr, nullptr));
  EXPECT_EQ(100, ReadByte(stream));
  EXPECT_EQ(20, ReadByte(stream));
  EXPECT_TRUE(stream->Seek(-1, Stream::Whence::FROM_END, &pos, nullptr));
  EXPECT_EQ(255, pos);
  EXPECT_EQ(255, ReadByte(stream));
  EXPECT_EQ(-1, ReadByte(stream));
}

}  // anonymous namespace

// A mock file descriptor wrapper to test low-level file API used by FileStream.
class MockFileDescriptor : public FileStream::FileDescriptorInterface {
 public:
  MOCK_CONST_METHOD0(IsOpen, bool());
  MOCK_METHOD2(Read, ssize_t(void*, size_t));
  MOCK_METHOD2(Write, ssize_t(const void*, size_t));
  MOCK_METHOD2(Seek, off64_t(off64_t, int));
  MOCK_CONST_METHOD0(GetFileMode, mode_t());
  MOCK_CONST_METHOD0(GetSize, uint64_t());
  MOCK_CONST_METHOD1(Truncate, int(off64_t));
  MOCK_METHOD0(Flush, int());
  MOCK_METHOD0(Close, int());
  MOCK_METHOD3(WaitForData,
               bool(Stream::AccessMode, const DataCallback&, ErrorPtr*));
  MOCK_METHOD3(WaitForDataBlocking,
               int(Stream::AccessMode, base::TimeDelta, Stream::AccessMode*));
  MOCK_METHOD0(CancelPendingAsyncOperations, void());
};

class FileStreamTest : public testing::Test {
 public:
  void SetUp() override {
    CreateStream(S_IFREG, Stream::AccessMode::READ_WRITE);
  }

  MockFileDescriptor& fd_mock() {
    return *static_cast<MockFileDescriptor*>(stream_->fd_interface_.get());
  }

  void CreateStream(mode_t file_mode, Stream::AccessMode access_mode) {
    std::unique_ptr<MockFileDescriptor> fd{new MockFileDescriptor{}};
    EXPECT_CALL(*fd, GetFileMode()).WillOnce(Return(file_mode));
    stream_.reset(new FileStream(std::move(fd), access_mode));
    EXPECT_CALL(fd_mock(), IsOpen()).WillRepeatedly(Return(true));
  }

  void ExpectStreamClosed(const ErrorPtr& error) const {
    EXPECT_EQ(errors::stream::kDomain, error->GetDomain());
    EXPECT_EQ(errors::stream::kStreamClosed, error->GetCode());
    EXPECT_EQ("Stream is closed", error->GetMessage());
  }

  void ExpectStreamOffsetTooLarge(const ErrorPtr& error) const {
    EXPECT_EQ(errors::stream::kDomain, error->GetDomain());
    EXPECT_EQ(errors::stream::kInvalidParameter, error->GetCode());
    EXPECT_EQ("The stream offset value is out of range", error->GetMessage());
  }

  inline static char* IntToPtr(int addr) {
    return reinterpret_cast<char*>(addr);
  }

  inline static const char* IntToConstPtr(int addr) {
    return reinterpret_cast<const char*>(addr);
  }

  bool CallWaitForData(Stream::AccessMode mode, ErrorPtr* error) {
    return stream_->WaitForData(mode, {}, error);
  }

  std::unique_ptr<FileStream> stream_;

  const uint64_t kMaxSize = std::numeric_limits<int64_t>::max();
  const uint64_t kTooLargeSize = std::numeric_limits<uint64_t>::max();

  // Dummy buffer pointer values to make sure that input pointer values
  // are delegated to the file interface without a change.
  char* const test_read_buffer_ = IntToPtr(12345);
  const char* const test_write_buffer_ = IntToConstPtr(67890);
};

TEST_F(FileStreamTest, IsOpen) {
  EXPECT_TRUE(stream_->IsOpen());
  EXPECT_CALL(fd_mock(), IsOpen()).WillOnce(Return(false));
  EXPECT_FALSE(stream_->IsOpen());
}

TEST_F(FileStreamTest, CanRead) {
  CreateStream(S_IFREG, Stream::AccessMode::READ_WRITE);
  EXPECT_TRUE(stream_->CanRead());
  EXPECT_CALL(fd_mock(), IsOpen()).WillRepeatedly(Return(false));
  EXPECT_FALSE(stream_->CanRead());
  CreateStream(S_IFREG, Stream::AccessMode::READ);
  EXPECT_TRUE(stream_->CanRead());
  CreateStream(S_IFREG, Stream::AccessMode::WRITE);
  EXPECT_FALSE(stream_->CanRead());
}

TEST_F(FileStreamTest, CanWrite) {
  CreateStream(S_IFREG, Stream::AccessMode::READ_WRITE);
  EXPECT_TRUE(stream_->CanWrite());
  EXPECT_CALL(fd_mock(), IsOpen()).WillRepeatedly(Return(false));
  EXPECT_FALSE(stream_->CanWrite());
  CreateStream(S_IFREG, Stream::AccessMode::READ);
  EXPECT_FALSE(stream_->CanWrite());
  CreateStream(S_IFREG, Stream::AccessMode::WRITE);
  EXPECT_TRUE(stream_->CanWrite());
}

TEST_F(FileStreamTest, CanSeek) {
  CreateStream(S_IFBLK, Stream::AccessMode::READ_WRITE);
  EXPECT_TRUE(stream_->CanSeek());
  CreateStream(S_IFDIR, Stream::AccessMode::READ_WRITE);
  EXPECT_TRUE(stream_->CanSeek());
  CreateStream(S_IFREG, Stream::AccessMode::READ_WRITE);
  EXPECT_TRUE(stream_->CanSeek());
  CreateStream(S_IFLNK, Stream::AccessMode::READ_WRITE);
  EXPECT_TRUE(stream_->CanSeek());
  CreateStream(S_IFCHR, Stream::AccessMode::READ_WRITE);
  EXPECT_FALSE(stream_->CanSeek());
  CreateStream(S_IFSOCK, Stream::AccessMode::READ_WRITE);
  EXPECT_FALSE(stream_->CanSeek());
  CreateStream(S_IFIFO, Stream::AccessMode::READ_WRITE);
  EXPECT_FALSE(stream_->CanSeek());

  CreateStream(S_IFREG, Stream::AccessMode::READ);
  EXPECT_TRUE(stream_->CanSeek());
  CreateStream(S_IFREG, Stream::AccessMode::WRITE);
  EXPECT_TRUE(stream_->CanSeek());
}

TEST_F(FileStreamTest, CanGetSize) {
  CreateStream(S_IFBLK, Stream::AccessMode::READ_WRITE);
  EXPECT_TRUE(stream_->CanGetSize());
  CreateStream(S_IFDIR, Stream::AccessMode::READ_WRITE);
  EXPECT_TRUE(stream_->CanGetSize());
  CreateStream(S_IFREG, Stream::AccessMode::READ_WRITE);
  EXPECT_TRUE(stream_->CanGetSize());
  CreateStream(S_IFLNK, Stream::AccessMode::READ_WRITE);
  EXPECT_TRUE(stream_->CanGetSize());
  CreateStream(S_IFCHR, Stream::AccessMode::READ_WRITE);
  EXPECT_FALSE(stream_->CanGetSize());
  CreateStream(S_IFSOCK, Stream::AccessMode::READ_WRITE);
  EXPECT_FALSE(stream_->CanGetSize());
  CreateStream(S_IFIFO, Stream::AccessMode::READ_WRITE);
  EXPECT_FALSE(stream_->CanGetSize());

  CreateStream(S_IFREG, Stream::AccessMode::READ);
  EXPECT_TRUE(stream_->CanGetSize());
  CreateStream(S_IFREG, Stream::AccessMode::WRITE);
  EXPECT_TRUE(stream_->CanGetSize());
}

TEST_F(FileStreamTest, GetSize) {
  EXPECT_CALL(fd_mock(), GetSize()).WillRepeatedly(Return(12345));
  EXPECT_EQ(12345u, stream_->GetSize());
  EXPECT_CALL(fd_mock(), IsOpen()).WillOnce(Return(false));
  EXPECT_EQ(0u, stream_->GetSize());
}

TEST_F(FileStreamTest, SetSizeBlocking) {
  EXPECT_CALL(fd_mock(), Truncate(0)).WillOnce(Return(0));
  EXPECT_TRUE(stream_->SetSizeBlocking(0, nullptr));

  EXPECT_CALL(fd_mock(), Truncate(123)).WillOnce(Return(0));
  EXPECT_TRUE(stream_->SetSizeBlocking(123, nullptr));

  EXPECT_CALL(fd_mock(), Truncate(kMaxSize)).WillOnce(Return(0));
  EXPECT_TRUE(stream_->SetSizeBlocking(kMaxSize, nullptr));
}

TEST_F(FileStreamTest, SetSizeBlocking_Fail) {
  brillo::ErrorPtr error;

  EXPECT_CALL(fd_mock(), Truncate(1235)).WillOnce(SetErrnoAndReturn(EIO, -1));
  EXPECT_FALSE(stream_->SetSizeBlocking(1235, &error));
  EXPECT_EQ(errors::system::kDomain, error->GetDomain());
  EXPECT_EQ("EIO", error->GetCode());

  error.reset();
  EXPECT_FALSE(stream_->SetSizeBlocking(kTooLargeSize, &error));
  ExpectStreamOffsetTooLarge(error);

  error.reset();
  EXPECT_CALL(fd_mock(), IsOpen()).WillOnce(Return(false));
  EXPECT_FALSE(stream_->SetSizeBlocking(1235, &error));
  ExpectStreamClosed(error);
}

TEST_F(FileStreamTest, GetRemainingSize) {
  EXPECT_CALL(fd_mock(), Seek(0, SEEK_CUR)).WillOnce(Return(234));
  EXPECT_CALL(fd_mock(), GetSize()).WillOnce(Return(1234));
  EXPECT_EQ(1000u, stream_->GetRemainingSize());

  EXPECT_CALL(fd_mock(), Seek(0, SEEK_CUR)).WillOnce(Return(1234));
  EXPECT_CALL(fd_mock(), GetSize()).WillOnce(Return(1000));
  EXPECT_EQ(0u, stream_->GetRemainingSize());
}

TEST_F(FileStreamTest, Seek_Set) {
  uint64_t pos = 0;

  EXPECT_CALL(fd_mock(), Seek(0, SEEK_SET)).WillOnce(Return(0));
  EXPECT_TRUE(stream_->Seek(0, Stream::Whence::FROM_BEGIN, &pos, nullptr));
  EXPECT_EQ(0u, pos);

  EXPECT_CALL(fd_mock(), Seek(123456, SEEK_SET)).WillOnce(Return(123456));
  EXPECT_TRUE(stream_->Seek(123456, Stream::Whence::FROM_BEGIN, &pos, nullptr));
  EXPECT_EQ(123456u, pos);

  EXPECT_CALL(fd_mock(), Seek(kMaxSize, SEEK_SET))
      .WillRepeatedly(Return(kMaxSize));
  EXPECT_TRUE(stream_->Seek(kMaxSize, Stream::Whence::FROM_BEGIN, &pos,
              nullptr));
  EXPECT_EQ(kMaxSize, pos);
  EXPECT_TRUE(stream_->Seek(kMaxSize, Stream::Whence::FROM_BEGIN, nullptr,
              nullptr));
}

TEST_F(FileStreamTest, Seek_Cur) {
  uint64_t pos = 0;

  EXPECT_CALL(fd_mock(), Seek(0, SEEK_CUR)).WillOnce(Return(100));
  EXPECT_TRUE(stream_->Seek(0, Stream::Whence::FROM_CURRENT, &pos, nullptr));
  EXPECT_EQ(100u, pos);

  EXPECT_CALL(fd_mock(), Seek(234, SEEK_CUR)).WillOnce(Return(1234));
  EXPECT_TRUE(stream_->Seek(234, Stream::Whence::FROM_CURRENT, &pos, nullptr));
  EXPECT_EQ(1234u, pos);

  EXPECT_CALL(fd_mock(), Seek(-100, SEEK_CUR)).WillOnce(Return(900));
  EXPECT_TRUE(stream_->Seek(-100, Stream::Whence::FROM_CURRENT, &pos, nullptr));
  EXPECT_EQ(900u, pos);
}

TEST_F(FileStreamTest, Seek_End) {
  uint64_t pos = 0;

  EXPECT_CALL(fd_mock(), Seek(0, SEEK_END)).WillOnce(Return(1000));
  EXPECT_TRUE(stream_->Seek(0, Stream::Whence::FROM_END, &pos, nullptr));
  EXPECT_EQ(1000u, pos);

  EXPECT_CALL(fd_mock(), Seek(234, SEEK_END)).WillOnce(Return(10234));
  EXPECT_TRUE(stream_->Seek(234, Stream::Whence::FROM_END, &pos, nullptr));
  EXPECT_EQ(10234u, pos);

  EXPECT_CALL(fd_mock(), Seek(-100, SEEK_END)).WillOnce(Return(9900));
  EXPECT_TRUE(stream_->Seek(-100, Stream::Whence::FROM_END, &pos, nullptr));
  EXPECT_EQ(9900u, pos);
}

TEST_F(FileStreamTest, Seek_Fail) {
  brillo::ErrorPtr error;
  EXPECT_CALL(fd_mock(), Seek(0, SEEK_SET))
      .WillOnce(SetErrnoAndReturn(EPIPE, -1));
  EXPECT_FALSE(stream_->Seek(0, Stream::Whence::FROM_BEGIN, nullptr, &error));
  EXPECT_EQ(errors::system::kDomain, error->GetDomain());
  EXPECT_EQ("EPIPE", error->GetCode());
}

TEST_F(FileStreamTest, ReadAsync) {
  size_t read_size = 0;
  bool failed = false;
  auto success_callback = [](size_t* read_size, size_t size) {
    *read_size = size;
  };
  auto error_callback = [](bool* failed, const Error* /* error */) {
    *failed = true;
  };
  FileStream::FileDescriptorInterface::DataCallback data_callback;

  EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 100))
      .WillOnce(ReturnWouldBlock());
  EXPECT_CALL(fd_mock(), WaitForData(Stream::AccessMode::READ, _, _))
      .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true)));
  EXPECT_TRUE(stream_->ReadAsync(
      test_read_buffer_,
      100,
      base::Bind(success_callback, base::Unretained(&read_size)),
      base::Bind(error_callback, base::Unretained(&failed)),
      nullptr));
  EXPECT_EQ(0u, read_size);
  EXPECT_FALSE(failed);

  EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 100)).WillOnce(Return(83));
  data_callback.Run(Stream::AccessMode::READ);
  EXPECT_EQ(83u, read_size);
  EXPECT_FALSE(failed);
}

TEST_F(FileStreamTest, ReadNonBlocking) {
  size_t size = 0;
  bool eos = false;
  EXPECT_CALL(fd_mock(), Read(test_read_buffer_, _))
      .WillRepeatedly(ReturnArg<1>());
  EXPECT_TRUE(stream_->ReadNonBlocking(test_read_buffer_, 100, &size, &eos,
                                       nullptr));
  EXPECT_EQ(100u, size);
  EXPECT_FALSE(eos);

  EXPECT_TRUE(stream_->ReadNonBlocking(test_read_buffer_, 0, &size, &eos,
                                       nullptr));
  EXPECT_EQ(0u, size);
  EXPECT_FALSE(eos);

  EXPECT_CALL(fd_mock(), Read(test_read_buffer_, _)).WillOnce(Return(0));
  EXPECT_TRUE(stream_->ReadNonBlocking(test_read_buffer_, 100, &size, &eos,
                                       nullptr));
  EXPECT_EQ(0u, size);
  EXPECT_TRUE(eos);

  EXPECT_CALL(fd_mock(), Read(test_read_buffer_, _))
      .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
  EXPECT_TRUE(stream_->ReadNonBlocking(test_read_buffer_, 100, &size, &eos,
                                       nullptr));
  EXPECT_EQ(0u, size);
  EXPECT_FALSE(eos);
}

TEST_F(FileStreamTest, ReadNonBlocking_Fail) {
  size_t size = 0;
  brillo::ErrorPtr error;
  EXPECT_CALL(fd_mock(), Read(test_read_buffer_, _))
      .WillOnce(SetErrnoAndReturn(EACCES, -1));
  EXPECT_FALSE(stream_->ReadNonBlocking(test_read_buffer_, 100, &size, nullptr,
                                        &error));
  EXPECT_EQ(errors::system::kDomain, error->GetDomain());
  EXPECT_EQ("EACCES", error->GetCode());
}

TEST_F(FileStreamTest, ReadBlocking) {
  size_t size = 0;
  EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 100)).WillOnce(Return(20));
  EXPECT_TRUE(stream_->ReadBlocking(test_read_buffer_, 100, &size, nullptr));
  EXPECT_EQ(20u, size);

  {
    InSequence seq;
    EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 80))
        .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _, _))
        .WillOnce(Return(1));
    EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 80)).WillOnce(Return(45));
  }
  EXPECT_TRUE(stream_->ReadBlocking(test_read_buffer_, 80, &size, nullptr));
  EXPECT_EQ(45u, size);

  EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 50)).WillOnce(Return(0));
  EXPECT_TRUE(stream_->ReadBlocking(test_read_buffer_, 50, &size, nullptr));
  EXPECT_EQ(0u, size);
}

TEST_F(FileStreamTest, ReadBlocking_Fail) {
  {
    InSequence seq;
    EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 80))
        .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _, _))
        .WillOnce(SetErrnoAndReturn(EBADF, -1));
  }
  brillo::ErrorPtr error;
  size_t size = 0;
  EXPECT_FALSE(stream_->ReadBlocking(test_read_buffer_, 80, &size, &error));
  EXPECT_EQ(errors::system::kDomain, error->GetDomain());
  EXPECT_EQ("EBADF", error->GetCode());
}

TEST_F(FileStreamTest, ReadAllBlocking) {
  {
    InSequence seq;
    EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 100)).WillOnce(Return(20));
    EXPECT_CALL(fd_mock(), Read(test_read_buffer_ + 20, 80))
        .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _, _))
        .WillOnce(Return(1));
    EXPECT_CALL(fd_mock(), Read(test_read_buffer_ + 20, 80))
        .WillOnce(Return(45));
    EXPECT_CALL(fd_mock(), Read(test_read_buffer_ + 65, 35))
        .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _, _))
        .WillOnce(Return(1));
    EXPECT_CALL(fd_mock(), Read(test_read_buffer_ + 65, 35))
        .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::READ, _, _))
        .WillOnce(Return(1));
    EXPECT_CALL(fd_mock(), Read(test_read_buffer_ + 65, 35))
        .WillOnce(Return(35));
  }
  EXPECT_TRUE(stream_->ReadAllBlocking(test_read_buffer_, 100, nullptr));
}

TEST_F(FileStreamTest, ReadAllBlocking_Fail) {
  {
    InSequence seq;
    EXPECT_CALL(fd_mock(), Read(test_read_buffer_, 100)).WillOnce(Return(20));
    EXPECT_CALL(fd_mock(), Read(test_read_buffer_ + 20, 80))
        .WillOnce(Return(0));
  }
  brillo::ErrorPtr error;
  EXPECT_FALSE(stream_->ReadAllBlocking(test_read_buffer_, 100, &error));
  EXPECT_EQ(errors::stream::kDomain, error->GetDomain());
  EXPECT_EQ(errors::stream::kPartialData, error->GetCode());
  EXPECT_EQ("Reading past the end of stream", error->GetMessage());
}

TEST_F(FileStreamTest, WriteAsync) {
  size_t write_size = 0;
  bool failed = false;
  auto success_callback = [](size_t* write_size, size_t size) {
    *write_size = size;
  };
  auto error_callback = [](bool* failed, const Error* /* error */) {
    *failed = true;
  };
  FileStream::FileDescriptorInterface::DataCallback data_callback;

  EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 100))
      .WillOnce(ReturnWouldBlock());
  EXPECT_CALL(fd_mock(), WaitForData(Stream::AccessMode::WRITE, _, _))
      .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true)));
  EXPECT_TRUE(stream_->WriteAsync(
      test_write_buffer_,
      100,
      base::Bind(success_callback, base::Unretained(&write_size)),
      base::Bind(error_callback, base::Unretained(&failed)),
      nullptr));
  EXPECT_EQ(0u, write_size);
  EXPECT_FALSE(failed);

  EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 100)).WillOnce(Return(87));
  data_callback.Run(Stream::AccessMode::WRITE);
  EXPECT_EQ(87u, write_size);
  EXPECT_FALSE(failed);
}

TEST_F(FileStreamTest, WriteNonBlocking) {
  size_t size = 0;
  EXPECT_CALL(fd_mock(), Write(test_write_buffer_, _))
      .WillRepeatedly(ReturnArg<1>());
  EXPECT_TRUE(stream_->WriteNonBlocking(test_write_buffer_, 100, &size,
                                        nullptr));
  EXPECT_EQ(100u, size);

  EXPECT_TRUE(stream_->WriteNonBlocking(test_write_buffer_, 0, &size, nullptr));
  EXPECT_EQ(0u, size);

  EXPECT_CALL(fd_mock(), Write(test_write_buffer_, _)).WillOnce(Return(0));
  EXPECT_TRUE(stream_->WriteNonBlocking(test_write_buffer_, 100, &size,
                                        nullptr));
  EXPECT_EQ(0u, size);

  EXPECT_CALL(fd_mock(), Write(test_write_buffer_, _))
      .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
  EXPECT_TRUE(stream_->WriteNonBlocking(test_write_buffer_, 100, &size,
                                        nullptr));
  EXPECT_EQ(0u, size);
}

TEST_F(FileStreamTest, WriteNonBlocking_Fail) {
  size_t size = 0;
  brillo::ErrorPtr error;
  EXPECT_CALL(fd_mock(), Write(test_write_buffer_, _))
      .WillOnce(SetErrnoAndReturn(EACCES, -1));
  EXPECT_FALSE(stream_->WriteNonBlocking(test_write_buffer_, 100, &size,
                                         &error));
  EXPECT_EQ(errors::system::kDomain, error->GetDomain());
  EXPECT_EQ("EACCES", error->GetCode());
}

TEST_F(FileStreamTest, WriteBlocking) {
  size_t size = 0;
  EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 100)).WillOnce(Return(20));
  EXPECT_TRUE(stream_->WriteBlocking(test_write_buffer_, 100, &size, nullptr));
  EXPECT_EQ(20u, size);

  {
    InSequence seq;
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 80))
        .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
        .WillOnce(Return(1));
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 80)).WillOnce(Return(45));
  }
  EXPECT_TRUE(stream_->WriteBlocking(test_write_buffer_, 80, &size, nullptr));
  EXPECT_EQ(45u, size);

  {
    InSequence seq;
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 50)).WillOnce(Return(0));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
        .WillOnce(Return(1));
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 50)).WillOnce(Return(1));
  }
  EXPECT_TRUE(stream_->WriteBlocking(test_write_buffer_, 50, &size, nullptr));
  EXPECT_EQ(1u, size);
}

TEST_F(FileStreamTest, WriteBlocking_Fail) {
  {
    InSequence seq;
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 80))
        .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
        .WillOnce(SetErrnoAndReturn(EBADF, -1));
  }
  brillo::ErrorPtr error;
  size_t size = 0;
  EXPECT_FALSE(stream_->WriteBlocking(test_write_buffer_, 80, &size, &error));
  EXPECT_EQ(errors::system::kDomain, error->GetDomain());
  EXPECT_EQ("EBADF", error->GetCode());
}

TEST_F(FileStreamTest, WriteAllBlocking) {
  {
    InSequence seq;
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 100)).WillOnce(Return(20));
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_ + 20, 80))
        .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
        .WillOnce(Return(1));
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_ + 20, 80))
        .WillOnce(Return(45));
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_ + 65, 35))
        .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
        .WillOnce(Return(1));
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_ + 65, 35))
        .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
        .WillOnce(Return(1));
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_ + 65, 35))
        .WillOnce(Return(35));
  }
  EXPECT_TRUE(stream_->WriteAllBlocking(test_write_buffer_, 100, nullptr));
}

TEST_F(FileStreamTest, WriteAllBlocking_Fail) {
  {
    InSequence seq;
    EXPECT_CALL(fd_mock(), Write(test_write_buffer_, 80))
        .WillOnce(SetErrnoAndReturn(EAGAIN, -1));
    EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
        .WillOnce(SetErrnoAndReturn(EBADF, -1));
  }
  brillo::ErrorPtr error;
  EXPECT_FALSE(stream_->WriteAllBlocking(test_write_buffer_, 80, &error));
  EXPECT_EQ(errors::system::kDomain, error->GetDomain());
  EXPECT_EQ("EBADF", error->GetCode());
}

TEST_F(FileStreamTest, WaitForDataBlocking_Timeout) {
  EXPECT_CALL(fd_mock(), WaitForDataBlocking(Stream::AccessMode::WRITE, _, _))
      .WillOnce(Return(0));
  brillo::ErrorPtr error;
  EXPECT_FALSE(stream_->WaitForDataBlocking(Stream::AccessMode::WRITE, {},
                                            nullptr, &error));
  EXPECT_EQ(errors::stream::kDomain, error->GetDomain());
  EXPECT_EQ(errors::stream::kTimeout, error->GetCode());
}

TEST_F(FileStreamTest, FlushBlocking) {
  EXPECT_TRUE(stream_->FlushBlocking(nullptr));
}

TEST_F(FileStreamTest, CloseBlocking) {
  EXPECT_CALL(fd_mock(), Close()).WillOnce(Return(0));
  EXPECT_TRUE(stream_->CloseBlocking(nullptr));

  EXPECT_CALL(fd_mock(), IsOpen()).WillOnce(Return(false));
  EXPECT_TRUE(stream_->CloseBlocking(nullptr));
}

TEST_F(FileStreamTest, CloseBlocking_Fail) {
  brillo::ErrorPtr error;
  EXPECT_CALL(fd_mock(), Close()).WillOnce(SetErrnoAndReturn(EFBIG, -1));
  EXPECT_FALSE(stream_->CloseBlocking(&error));
  EXPECT_EQ(errors::system::kDomain, error->GetDomain());
  EXPECT_EQ("EFBIG", error->GetCode());
}

TEST_F(FileStreamTest, WaitForData) {
  EXPECT_CALL(fd_mock(), WaitForData(Stream::AccessMode::READ, _, _))
      .WillOnce(Return(true));
  EXPECT_TRUE(CallWaitForData(Stream::AccessMode::READ, nullptr));

  EXPECT_CALL(fd_mock(), WaitForData(Stream::AccessMode::WRITE, _, _))
      .WillOnce(Return(true));
  EXPECT_TRUE(CallWaitForData(Stream::AccessMode::WRITE, nullptr));

  EXPECT_CALL(fd_mock(), WaitForData(Stream::AccessMode::READ_WRITE, _, _))
      .WillOnce(Return(true));
  EXPECT_TRUE(CallWaitForData(Stream::AccessMode::READ_WRITE, nullptr));

  EXPECT_CALL(fd_mock(), WaitForData(Stream::AccessMode::READ_WRITE, _, _))
      .WillOnce(Return(false));
  EXPECT_FALSE(CallWaitForData(Stream::AccessMode::READ_WRITE, nullptr));
}

TEST_F(FileStreamTest, CreateTemporary) {
  StreamPtr stream = FileStream::CreateTemporary(nullptr);
  ASSERT_NE(nullptr, stream.get());
  TestCreateFile(stream.get());
}

TEST_F(FileStreamTest, OpenRead) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::FilePath path = temp_dir.GetPath().Append(base::FilePath{"test.dat"});
  std::vector<char> buffer(1024 * 1024);
  base::RandBytes(buffer.data(), buffer.size());
  int file_size = buffer.size();  // Stupid base::WriteFile taking "int" size.
  ASSERT_EQ(file_size, base::WriteFile(path, buffer.data(), file_size));

  StreamPtr stream = FileStream::Open(path,
                                      Stream::AccessMode::READ,
                                      FileStream::Disposition::OPEN_EXISTING,
                                      nullptr);
  ASSERT_NE(nullptr, stream.get());
  ASSERT_TRUE(stream->IsOpen());
  EXPECT_TRUE(stream->CanRead());
  EXPECT_FALSE(stream->CanWrite());
  EXPECT_TRUE(stream->CanSeek());
  EXPECT_TRUE(stream->CanGetSize());
  EXPECT_EQ(0u, stream->GetPosition());
  EXPECT_EQ(buffer.size(), stream->GetSize());

  std::vector<char> buffer2(buffer.size());
  EXPECT_TRUE(stream->ReadAllBlocking(buffer2.data(), buffer2.size(), nullptr));
  EXPECT_EQ(buffer2, buffer);
  EXPECT_TRUE(stream->CloseBlocking(nullptr));
}

TEST_F(FileStreamTest, OpenWrite) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::FilePath path = temp_dir.GetPath().Append(base::FilePath{"test.dat"});
  std::vector<char> buffer(1024 * 1024);
  base::RandBytes(buffer.data(), buffer.size());

  StreamPtr stream = FileStream::Open(path,
                                      Stream::AccessMode::WRITE,
                                      FileStream::Disposition::CREATE_ALWAYS,
                                      nullptr);
  ASSERT_NE(nullptr, stream.get());
  ASSERT_TRUE(stream->IsOpen());
  EXPECT_FALSE(stream->CanRead());
  EXPECT_TRUE(stream->CanWrite());
  EXPECT_TRUE(stream->CanSeek());
  EXPECT_TRUE(stream->CanGetSize());
  EXPECT_EQ(0u, stream->GetPosition());
  EXPECT_EQ(0u, stream->GetSize());

  EXPECT_TRUE(stream->WriteAllBlocking(buffer.data(), buffer.size(), nullptr));
  EXPECT_TRUE(stream->CloseBlocking(nullptr));

  std::vector<char> buffer2(buffer.size());
  int file_size = buffer2.size();  // Stupid base::ReadFile taking "int" size.
  ASSERT_EQ(file_size, base::ReadFile(path, buffer2.data(), file_size));
  EXPECT_EQ(buffer2, buffer);
}

TEST_F(FileStreamTest, Open_OpenExisting) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::FilePath path = temp_dir.GetPath().Append(base::FilePath{"test.dat"});
  std::string data{"Lorem ipsum dolor sit amet ..."};
  int data_size = data.size();  // I hate ints for data size...
  ASSERT_EQ(data_size, base::WriteFile(path, data.data(), data_size));

  StreamPtr stream = FileStream::Open(path,
                                      Stream::AccessMode::READ_WRITE,
                                      FileStream::Disposition::OPEN_EXISTING,
                                      nullptr);
  ASSERT_NE(nullptr, stream.get());
  EXPECT_TRUE(stream->CanRead());
  EXPECT_TRUE(stream->CanWrite());
  EXPECT_TRUE(stream->CanSeek());
  EXPECT_TRUE(stream->CanGetSize());
  EXPECT_EQ(0u, stream->GetPosition());
  EXPECT_EQ(data.size(), stream->GetSize());
  EXPECT_TRUE(stream->CloseBlocking(nullptr));
}

TEST_F(FileStreamTest, Open_OpenExisting_Fail) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::FilePath path = temp_dir.GetPath().Append(base::FilePath{"test.dat"});

  ErrorPtr error;
  StreamPtr stream = FileStream::Open(path,
                                      Stream::AccessMode::READ_WRITE,
                                      FileStream::Disposition::OPEN_EXISTING,
                                      &error);
  ASSERT_EQ(nullptr, stream.get());
  EXPECT_EQ(errors::system::kDomain, error->GetDomain());
  EXPECT_EQ("ENOENT", error->GetCode());
}

TEST_F(FileStreamTest, Open_CreateAlways_New) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::FilePath path = temp_dir.GetPath().Append(base::FilePath{"test.dat"});

  StreamPtr stream = FileStream::Open(path,
                                      Stream::AccessMode::READ_WRITE,
                                      FileStream::Disposition::CREATE_ALWAYS,
                                      nullptr);
  ASSERT_NE(nullptr, stream.get());
  EXPECT_TRUE(stream->CanRead());
  EXPECT_TRUE(stream->CanWrite());
  EXPECT_TRUE(stream->CanSeek());
  EXPECT_TRUE(stream->CanGetSize());
  EXPECT_EQ(0u, stream->GetPosition());
  EXPECT_EQ(0u, stream->GetSize());
  EXPECT_TRUE(stream->CloseBlocking(nullptr));
}

TEST_F(FileStreamTest, Open_CreateAlways_Existing) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::FilePath path = temp_dir.GetPath().Append(base::FilePath{"test.dat"});
  std::string data{"Lorem ipsum dolor sit amet ..."};
  int data_size = data.size();  // I hate ints for data size...
  ASSERT_EQ(data_size, base::WriteFile(path, data.data(), data_size));

  StreamPtr stream = FileStream::Open(path,
                                      Stream::AccessMode::READ_WRITE,
                                      FileStream::Disposition::CREATE_ALWAYS,
                                      nullptr);
  ASSERT_NE(nullptr, stream.get());
  EXPECT_TRUE(stream->CanRead());
  EXPECT_TRUE(stream->CanWrite());
  EXPECT_TRUE(stream->CanSeek());
  EXPECT_TRUE(stream->CanGetSize());
  EXPECT_EQ(0u, stream->GetPosition());
  EXPECT_EQ(0u, stream->GetSize());
  EXPECT_TRUE(stream->CloseBlocking(nullptr));
}

TEST_F(FileStreamTest, Open_CreateNewOnly_New) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::FilePath path = temp_dir.GetPath().Append(base::FilePath{"test.dat"});

  StreamPtr stream = FileStream::Open(path,
                                      Stream::AccessMode::READ_WRITE,
                                      FileStream::Disposition::CREATE_NEW_ONLY,
                                      nullptr);
  ASSERT_NE(nullptr, stream.get());
  EXPECT_TRUE(stream->CanRead());
  EXPECT_TRUE(stream->CanWrite());
  EXPECT_TRUE(stream->CanSeek());
  EXPECT_TRUE(stream->CanGetSize());
  EXPECT_EQ(0u, stream->GetPosition());
  EXPECT_EQ(0u, stream->GetSize());
  EXPECT_TRUE(stream->CloseBlocking(nullptr));
}

TEST_F(FileStreamTest, Open_CreateNewOnly_Existing) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::FilePath path = temp_dir.GetPath().Append(base::FilePath{"test.dat"});
  std::string data{"Lorem ipsum dolor sit amet ..."};
  int data_size = data.size();  // I hate ints for data size...
  ASSERT_EQ(data_size, base::WriteFile(path, data.data(), data_size));

  ErrorPtr error;
  StreamPtr stream = FileStream::Open(path,
                                      Stream::AccessMode::READ_WRITE,
                                      FileStream::Disposition::CREATE_NEW_ONLY,
                                      &error);
  ASSERT_EQ(nullptr, stream.get());
  EXPECT_EQ(errors::system::kDomain, error->GetDomain());
  EXPECT_EQ("EEXIST", error->GetCode());
}

TEST_F(FileStreamTest, Open_TruncateExisting_New) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::FilePath path = temp_dir.GetPath().Append(base::FilePath{"test.dat"});

  ErrorPtr error;
  StreamPtr stream = FileStream::Open(
      path,
      Stream::AccessMode::READ_WRITE,
      FileStream::Disposition::TRUNCATE_EXISTING,
      &error);
  ASSERT_EQ(nullptr, stream.get());
  EXPECT_EQ(errors::system::kDomain, error->GetDomain());
  EXPECT_EQ("ENOENT", error->GetCode());
}

TEST_F(FileStreamTest, Open_TruncateExisting_Existing) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::FilePath path = temp_dir.GetPath().Append(base::FilePath{"test.dat"});
  std::string data{"Lorem ipsum dolor sit amet ..."};
  int data_size = data.size();  // I hate ints for data size...
  ASSERT_EQ(data_size, base::WriteFile(path, data.data(), data_size));

  StreamPtr stream = FileStream::Open(
      path,
      Stream::AccessMode::READ_WRITE,
      FileStream::Disposition::TRUNCATE_EXISTING,
      nullptr);
  ASSERT_NE(nullptr, stream.get());
  EXPECT_TRUE(stream->CanRead());
  EXPECT_TRUE(stream->CanWrite());
  EXPECT_TRUE(stream->CanSeek());
  EXPECT_TRUE(stream->CanGetSize());
  EXPECT_EQ(0u, stream->GetPosition());
  EXPECT_EQ(0u, stream->GetSize());
  EXPECT_TRUE(stream->CloseBlocking(nullptr));
}

TEST_F(FileStreamTest, FromFileDescriptor_StdIn) {
  StreamPtr stream =
      FileStream::FromFileDescriptor(STDIN_FILENO, false, nullptr);
  ASSERT_NE(nullptr, stream.get());
  EXPECT_TRUE(stream->IsOpen());
  EXPECT_TRUE(stream->CanRead());
  EXPECT_FALSE(stream->CanSeek());
  EXPECT_FALSE(stream->CanGetSize());
}

TEST_F(FileStreamTest, FromFileDescriptor_StdOut) {
  StreamPtr stream =
      FileStream::FromFileDescriptor(STDOUT_FILENO, false, nullptr);
  ASSERT_NE(nullptr, stream.get());
  EXPECT_TRUE(stream->IsOpen());
  EXPECT_TRUE(stream->CanWrite());
  EXPECT_FALSE(stream->CanSeek());
  EXPECT_FALSE(stream->CanGetSize());
}

TEST_F(FileStreamTest, FromFileDescriptor_StdErr) {
  StreamPtr stream =
      FileStream::FromFileDescriptor(STDERR_FILENO, false, nullptr);
  ASSERT_NE(nullptr, stream.get());
  EXPECT_TRUE(stream->IsOpen());
  EXPECT_TRUE(stream->CanWrite());
  EXPECT_FALSE(stream->CanSeek());
  EXPECT_FALSE(stream->CanGetSize());
}

TEST_F(FileStreamTest, FromFileDescriptor_ReadNonBlocking) {
  int fds[2] = {-1, -1};
  ASSERT_EQ(0, pipe(fds));

  StreamPtr stream = FileStream::FromFileDescriptor(fds[0], true, nullptr);
  ASSERT_NE(nullptr, stream.get());
  EXPECT_TRUE(stream->IsOpen());
  EXPECT_TRUE(stream->CanRead());
  EXPECT_FALSE(stream->CanWrite());
  EXPECT_FALSE(stream->CanSeek());
  EXPECT_FALSE(stream->CanGetSize());

  char buf[10];
  size_t read = 0;
  bool eos = true;
  EXPECT_TRUE(stream->ReadNonBlocking(buf, sizeof(buf), &read, &eos, nullptr));
  EXPECT_EQ(0, read);
  EXPECT_FALSE(eos);

  std::string data{"foo_bar"};
  EXPECT_TRUE(base::WriteFileDescriptor(fds[1], data.data(), data.size()));
  EXPECT_TRUE(stream->ReadNonBlocking(buf, sizeof(buf), &read, &eos, nullptr));
  EXPECT_EQ(data.size(), read);
  EXPECT_FALSE(eos);
  EXPECT_EQ(data, (std::string{buf, read}));

  EXPECT_TRUE(stream->ReadNonBlocking(buf, sizeof(buf), &read, &eos, nullptr));
  EXPECT_EQ(0, read);
  EXPECT_FALSE(eos);

  close(fds[1]);

  EXPECT_TRUE(stream->ReadNonBlocking(buf, sizeof(buf), &read, &eos, nullptr));
  EXPECT_EQ(0, read);
  EXPECT_TRUE(eos);

  EXPECT_TRUE(stream->CloseBlocking(nullptr));
}

TEST_F(FileStreamTest, FromFileDescriptor_WriteNonBlocking) {
  int fds[2] = {-1, -1};
  ASSERT_EQ(0, pipe(fds));

  StreamPtr stream = FileStream::FromFileDescriptor(fds[1], true, nullptr);
  ASSERT_NE(nullptr, stream.get());
  EXPECT_TRUE(stream->IsOpen());
  EXPECT_FALSE(stream->CanRead());
  EXPECT_TRUE(stream->CanWrite());
  EXPECT_FALSE(stream->CanSeek());
  EXPECT_FALSE(stream->CanGetSize());

  // Pipe buffer is generally 64K, so 128K should be more than enough.
  std::vector<char> buffer(128 * 1024);
  base::RandBytes(buffer.data(), buffer.size());
  size_t written = 0;
  size_t total_size = 0;

  // Fill the output buffer of the pipe until we can no longer write any data
  // to it.
  do {
    ASSERT_TRUE(stream->WriteNonBlocking(buffer.data(), buffer.size(), &written,
                                         nullptr));
    total_size += written;
  } while (written == buffer.size());

  EXPECT_TRUE(stream->WriteNonBlocking(buffer.data(), buffer.size(), &written,
                                       nullptr));
  EXPECT_EQ(0, written);

  std::vector<char> out_buffer(total_size);
  EXPECT_TRUE(base::ReadFromFD(fds[0], out_buffer.data(), out_buffer.size()));

  EXPECT_TRUE(stream->WriteNonBlocking(buffer.data(), buffer.size(), &written,
                                       nullptr));
  EXPECT_GT(written, 0);
  out_buffer.resize(written);
  EXPECT_TRUE(base::ReadFromFD(fds[0], out_buffer.data(), out_buffer.size()));
  EXPECT_TRUE(std::equal(out_buffer.begin(), out_buffer.end(), buffer.begin()));

  close(fds[0]);
  EXPECT_TRUE(stream->CloseBlocking(nullptr));
}

TEST_F(FileStreamTest, FromFileDescriptor_ReadAsync) {
  int fds[2] = {-1, -1};
  bool succeeded = false;
  bool failed = false;
  char buffer[100];
  base::MessageLoopForIO base_loop;
  BaseMessageLoop brillo_loop{&base_loop};
  brillo_loop.SetAsCurrent();

  auto success_callback = [](bool* succeeded, char* buffer, size_t size) {
    std::string data{buffer, buffer + size};
    ASSERT_EQ("abracadabra", data);
    *succeeded = true;
  };

  auto error_callback = [](bool* failed, const Error* /* error */) {
    *failed = true;
  };

  auto write_data_callback = [](int write_fd) {
    std::string data{"abracadabra"};
    EXPECT_TRUE(base::WriteFileDescriptor(write_fd, data.data(), data.size()));
  };

  ASSERT_EQ(0, pipe(fds));

  StreamPtr stream = FileStream::FromFileDescriptor(fds[0], true, nullptr);

  // Write to the pipe with a bit of delay.
  brillo_loop.PostDelayedTask(
      FROM_HERE,
      base::Bind(write_data_callback, fds[1]),
      base::TimeDelta::FromMilliseconds(10));

  EXPECT_TRUE(
      stream->ReadAsync(buffer,
                        100,
                        base::Bind(success_callback,
                                   base::Unretained(&succeeded),
                                   base::Unretained(buffer)),
                        base::Bind(error_callback, base::Unretained(&failed)),
                        nullptr));

  auto end_condition = [](bool* failed, bool* succeeded) {
    return *failed || *succeeded;
  };
  MessageLoopRunUntil(&brillo_loop,
                      base::TimeDelta::FromSeconds(1),
                      base::Bind(end_condition,
                                 base::Unretained(&failed),
                                 base::Unretained(&succeeded)));

  EXPECT_TRUE(succeeded);
  EXPECT_FALSE(failed);

  close(fds[1]);
  EXPECT_TRUE(stream->CloseBlocking(nullptr));
}

TEST_F(FileStreamTest, FromFileDescriptor_WriteAsync) {
  int fds[2] = {-1, -1};
  bool succeeded = false;
  bool failed = false;
  const std::string data{"abracadabra"};
  base::MessageLoopForIO base_loop;
  BaseMessageLoop brillo_loop{&base_loop};
  brillo_loop.SetAsCurrent();

  ASSERT_EQ(0, pipe(fds));

  auto success_callback = [](bool* succeeded,
                             const std::string& data,
                             int read_fd,
                             size_t /* size */) {
    char buffer[100];
    EXPECT_TRUE(base::ReadFromFD(read_fd, buffer, data.size()));
    EXPECT_EQ(data, (std::string{buffer, buffer + data.size()}));
    *succeeded = true;
  };

  auto error_callback = [](bool* failed, const Error* /* error */) {
    *failed = true;
  };

  StreamPtr stream = FileStream::FromFileDescriptor(fds[1], true, nullptr);

  EXPECT_TRUE(stream->WriteAsync(
      data.data(),
      data.size(),
      base::Bind(success_callback, base::Unretained(&succeeded), data, fds[0]),
      base::Bind(error_callback, base::Unretained(&failed)),
      nullptr));

  auto end_condition = [](bool* failed, bool* succeeded) {
    return *failed || *succeeded;
  };
  MessageLoopRunUntil(&brillo_loop,
                      base::TimeDelta::FromSeconds(1),
                      base::Bind(end_condition,
                                 base::Unretained(&failed),
                                 base::Unretained(&succeeded)));

  EXPECT_TRUE(succeeded);
  EXPECT_FALSE(failed);

  close(fds[0]);
  EXPECT_TRUE(stream->CloseBlocking(nullptr));
}

}  // namespace brillo