Repository: google/gvisor
File: test/syscalls/linux/write.cc (C++)

Code before:
  FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_WRONLY));

  const std::string data1 = "first";
  const std::string data2 = "second";
  const std::string data3 = "third";

  EXPECT_THAT(write(fd.get(), data1.data(), data1.size()),
              SyscallSucceedsWithValue(data1.size()));
  EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR),
              SyscallSucceedsWithValue(data1.size()));

  EXPECT_THAT(write(fd.get(), data2.data(), data2.size()),
              SyscallSucceedsWithValue(data2.size()));
  EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR),
              SyscallSucceedsWithValue(data1.size() + data2.size()));

  EXPECT_THAT(write(fd.get(), data3.data(), data3.size()),
              SyscallSucceedsWithValue(data3.size()));
  EXPECT_THAT(
      lseek(fd.get(), 0, SEEK_CUR),
      SyscallSucceedsWithValue(data1.size() + data2.size() + data3.size()));
}

// Test that pwrite does not change offset.
TEST_F(WriteTest, PwriteDoesNotChangeOffset) {
  TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
  FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_WRONLY));

  const std::string data = "pwrite data";

  // Write some initial data.
  ASSERT_THAT(write(fd.get(), data.data(), data.size()),
              SyscallSucceedsWithValue(data.size()));
  const off_t offset_after_write = data.size();
  EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR),
              SyscallSucceedsWithValue(offset_after_write));

  // pwrite should not change the offset.
  EXPECT_THAT(pwrite(fd.get(), data.data(), data.size(), 100),
              SyscallSucceedsWithValue(data.size()));
  EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR),
              SyscallSucceedsWithValue(offset_after_write));
}

// Test write extends file past EOF.
TEST_F(WriteTest, WriteExtendsPastEOF) {
  const std::string initial_data = "initial";
  TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
      GetAbsoluteTestTmpdir(), initial_data, TempPath::kDefaultFileMode));

Review comment (suggestion):
I think this test is redundant; `PwriteNoChangeOffset` already tests the same behavior. Delete this test.

Code after:
// Test write extends file past EOF.
TEST_F(WriteTest, WriteExtendsPastEOF) {
  const std::string initial_data = "initial";
  TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
      GetAbsoluteTestTmpdir(), initial_data, TempPath::kDefaultFileMode));
  FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_RDWR));

  // Seek to end.
  ASSERT_THAT(lseek(fd.get(), 0, SEEK_END),
              SyscallSucceedsWithValue(initial_data.size()));

  // Write extends past EOF.
  const std::string additional = " plus more";
  EXPECT_THAT(write(fd.get(), additional.data(), additional.size()),
              SyscallSucceedsWithValue(additional.size()));

  // Verify the file size increased.
  struct stat st;
  ASSERT_THAT(fstat(fd.get(), &st), SyscallSucceeds());
  EXPECT_EQ(st.st_size, static_cast<off_t>(initial_data.size() + additional.size()));
}

// Test pwrite at offset beyond EOF creates a hole.
TEST_F(WriteTest, PwriteBeyondEOFCreatesHole) {
  TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
  FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_RDWR));

  const std::string data = "at offset 100";
  const off_t write_offset = 100;

  EXPECT_THAT(pwrite(fd.get(), data.data(), data.size(), write_offset),
              SyscallSucceedsWithValue(data.size()));

  // Verify file size.
  struct stat st;
  ASSERT_THAT(fstat(fd.get(), &st), SyscallSucceeds());
  EXPECT_EQ(st.st_size, static_cast<off_t>(write_offset + data.size()));

  // Read back and verify hole contains zeros.
  std::vector<char> buf(write_offset);
  EXPECT_THAT(pread(fd.get(), buf.data(), buf.size(), 0),
              SyscallSucceedsWithValue(buf.size()));
  EXPECT_EQ(buf, std::vector<char>(write_offset, '\0'));
}

}  // namespace

}  // namespace testing
}  // namespace gvisor