Commit 2919be01 authored by grt's avatar grt Committed by Commit bot

Add ZipReader::ExtractCurrentEntry with a delegate interface.

This change gives consumers of ZipReader a way to have the current entry
streamed to them via a Delegate interface. It also:

- Reduces duplication in the ExtractCurrentEntry* functions.
- Uses the heap rather than the stack for intermediate buffers.
- Changes ExtractCurrentEntryToFd to ExtractCurrentEntryToFile, making
  it cross-platform in the process.

BUG=462584

Review URL: https://codereview.chromium.org/1014653002

Cr-Commit-Position: refs/heads/master@{#320948}
parent 4a4b5584
...@@ -23,6 +23,103 @@ ...@@ -23,6 +23,103 @@
namespace zip { namespace zip {
namespace {
// FilePathWriterDelegate ------------------------------------------------------
// A writer delegate that writes a file at a given path.
class FilePathWriterDelegate : public WriterDelegate {
public:
explicit FilePathWriterDelegate(const base::FilePath& output_file_path);
~FilePathWriterDelegate() override;
// WriterDelegate methods:
// Creates the output file and any necessary intermediate directories.
bool PrepareOutput() override;
// Writes |num_bytes| bytes of |data| to the file, returning false if not all
// bytes could be written.
bool WriteBytes(const char* data, int num_bytes) override;
private:
base::FilePath output_file_path_;
base::File file_;
DISALLOW_COPY_AND_ASSIGN(FilePathWriterDelegate);
};
FilePathWriterDelegate::FilePathWriterDelegate(
const base::FilePath& output_file_path)
: output_file_path_(output_file_path) {
}
FilePathWriterDelegate::~FilePathWriterDelegate() {
}
bool FilePathWriterDelegate::PrepareOutput() {
// We can't rely on parent directory entries being specified in the
// zip, so we make sure they are created.
if (!base::CreateDirectory(output_file_path_.DirName()))
return false;
file_.Initialize(output_file_path_,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
return file_.IsValid();
}
bool FilePathWriterDelegate::WriteBytes(const char* data, int num_bytes) {
return num_bytes == file_.WriteAtCurrentPos(data, num_bytes);
}
// StringWriterDelegate --------------------------------------------------------
// A writer delegate that writes no more than |max_read_bytes| to a given
// std::string.
class StringWriterDelegate : public WriterDelegate {
public:
StringWriterDelegate(size_t max_read_bytes, std::string* output);
~StringWriterDelegate() override;
// WriterDelegate methods:
// Returns true.
bool PrepareOutput() override;
// Appends |num_bytes| bytes from |data| to the output string. Returns false
// if |num_bytes| will cause the string to exceed |max_read_bytes|.
bool WriteBytes(const char* data, int num_bytes) override;
private:
size_t max_read_bytes_;
std::string* output_;
DISALLOW_COPY_AND_ASSIGN(StringWriterDelegate);
};
StringWriterDelegate::StringWriterDelegate(size_t max_read_bytes,
std::string* output)
: max_read_bytes_(max_read_bytes),
output_(output) {
}
StringWriterDelegate::~StringWriterDelegate() {
}
bool StringWriterDelegate::PrepareOutput() {
return true;
}
bool StringWriterDelegate::WriteBytes(const char* data, int num_bytes) {
if (output_->size() + num_bytes > max_read_bytes_)
return false;
output_->append(data, num_bytes);
return true;
}
} // namespace
// TODO(satorux): The implementation assumes that file names in zip files // TODO(satorux): The implementation assumes that file names in zip files
// are encoded in UTF-8. This is true for zip files created by Zip() // are encoded in UTF-8. This is true for zip files created by Zip()
// function in zip.h, but not true for user-supplied random zip files. // function in zip.h, but not true for user-supplied random zip files.
...@@ -187,33 +284,20 @@ bool ZipReader::LocateAndOpenEntry(const base::FilePath& path_in_zip) { ...@@ -187,33 +284,20 @@ bool ZipReader::LocateAndOpenEntry(const base::FilePath& path_in_zip) {
return OpenCurrentEntryInZip(); return OpenCurrentEntryInZip();
} }
bool ZipReader::ExtractCurrentEntryToFilePath( bool ZipReader::ExtractCurrentEntry(WriterDelegate* delegate) const {
const base::FilePath& output_file_path) {
DCHECK(zip_file_); DCHECK(zip_file_);
// If this is a directory, just create it and return.
if (current_entry_info()->is_directory())
return base::CreateDirectory(output_file_path);
const int open_result = unzOpenCurrentFile(zip_file_); const int open_result = unzOpenCurrentFile(zip_file_);
if (open_result != UNZ_OK) if (open_result != UNZ_OK)
return false; return false;
// We can't rely on parent directory entries being specified in the if (!delegate->PrepareOutput())
// zip, so we make sure they are created.
base::FilePath output_dir_path = output_file_path.DirName();
if (!base::CreateDirectory(output_dir_path))
return false;
base::File file(output_file_path,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
if (!file.IsValid())
return false; return false;
bool success = true; // This becomes false when something bad happens. bool success = true; // This becomes false when something bad happens.
scoped_ptr<char[]> buf(new char[internal::kZipBufSize]);
while (true) { while (true) {
char buf[internal::kZipBufSize]; const int num_bytes_read = unzReadCurrentFile(zip_file_, buf.get(),
const int num_bytes_read = unzReadCurrentFile(zip_file_, buf,
internal::kZipBufSize); internal::kZipBufSize);
if (num_bytes_read == 0) { if (num_bytes_read == 0) {
// Reached the end of the file. // Reached the end of the file.
...@@ -223,21 +307,39 @@ bool ZipReader::ExtractCurrentEntryToFilePath( ...@@ -223,21 +307,39 @@ bool ZipReader::ExtractCurrentEntryToFilePath(
success = false; success = false;
break; break;
} else if (num_bytes_read > 0) { } else if (num_bytes_read > 0) {
// Some data is read. Write it to the output file. // Some data is read.
if (num_bytes_read != file.WriteAtCurrentPos(buf, num_bytes_read)) { if (!delegate->WriteBytes(buf.get(), num_bytes_read)) {
success = false; success = false;
break; break;
} }
} }
} }
file.Close();
unzCloseCurrentFile(zip_file_); unzCloseCurrentFile(zip_file_);
if (current_entry_info()->last_modified() != base::Time::UnixEpoch()) return success;
}
bool ZipReader::ExtractCurrentEntryToFilePath(
const base::FilePath& output_file_path) const {
DCHECK(zip_file_);
// If this is a directory, just create it and return.
if (current_entry_info()->is_directory())
return base::CreateDirectory(output_file_path);
bool success = false;
{
FilePathWriterDelegate writer(output_file_path);
success = ExtractCurrentEntry(&writer);
}
if (success &&
current_entry_info()->last_modified() != base::Time::UnixEpoch()) {
base::TouchFile(output_file_path, base::TouchFile(output_file_path,
base::Time::Now(), base::Time::Now(),
current_entry_info()->last_modified()); current_entry_info()->last_modified());
}
return success; return success;
} }
...@@ -296,7 +398,7 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync( ...@@ -296,7 +398,7 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync(
} }
bool ZipReader::ExtractCurrentEntryIntoDirectory( bool ZipReader::ExtractCurrentEntryIntoDirectory(
const base::FilePath& output_directory_path) { const base::FilePath& output_directory_path) const {
DCHECK(current_entry_info_.get()); DCHECK(current_entry_info_.get());
base::FilePath output_file_path = output_directory_path.Append( base::FilePath output_file_path = output_directory_path.Append(
...@@ -304,61 +406,29 @@ bool ZipReader::ExtractCurrentEntryIntoDirectory( ...@@ -304,61 +406,29 @@ bool ZipReader::ExtractCurrentEntryIntoDirectory(
return ExtractCurrentEntryToFilePath(output_file_path); return ExtractCurrentEntryToFilePath(output_file_path);
} }
#if defined(OS_POSIX) bool ZipReader::ExtractCurrentEntryToFile(base::File* file) const {
bool ZipReader::ExtractCurrentEntryToFd(const int fd) {
DCHECK(zip_file_); DCHECK(zip_file_);
// If this is a directory, there's nothing to extract to the file descriptor, // If this is a directory, there's nothing to extract to the file, so return
// so return false. // false.
if (current_entry_info()->is_directory()) if (current_entry_info()->is_directory())
return false; return false;
const int open_result = unzOpenCurrentFile(zip_file_); FileWriterDelegate writer(file);
if (open_result != UNZ_OK) return ExtractCurrentEntry(&writer);
return false;
bool success = true; // This becomes false when something bad happens.
while (true) {
char buf[internal::kZipBufSize];
const int num_bytes_read = unzReadCurrentFile(zip_file_, buf,
internal::kZipBufSize);
if (num_bytes_read == 0) {
// Reached the end of the file.
break;
} else if (num_bytes_read < 0) {
// If num_bytes_read < 0, then it's a specific UNZ_* error code.
success = false;
break;
} else if (num_bytes_read > 0) {
// Some data is read. Write it to the output file descriptor.
if (!base::WriteFileDescriptor(fd, buf, num_bytes_read)) {
success = false;
break;
}
}
}
unzCloseCurrentFile(zip_file_);
return success;
} }
#endif // defined(OS_POSIX)
bool ZipReader::ExtractCurrentEntryToString( bool ZipReader::ExtractCurrentEntryToString(size_t max_read_bytes,
size_t max_read_bytes, std::string* output) const {
std::string* output) const {
DCHECK(output); DCHECK(output);
DCHECK(zip_file_); DCHECK(zip_file_);
DCHECK(max_read_bytes != 0); DCHECK_NE(0U, max_read_bytes);
if (current_entry_info()->is_directory()) { if (current_entry_info()->is_directory()) {
output->clear(); output->clear();
return true; return true;
} }
const int open_result = unzOpenCurrentFile(zip_file_);
if (open_result != UNZ_OK)
return false;
// The original_size() is the best hint for the real size, so it saves // The original_size() is the best hint for the real size, so it saves
// doing reallocations for the common case when the uncompressed size is // doing reallocations for the common case when the uncompressed size is
// correct. However, we need to assume that the uncompressed size could be // correct. However, we need to assume that the uncompressed size could be
...@@ -368,32 +438,11 @@ bool ZipReader::ExtractCurrentEntryToString( ...@@ -368,32 +438,11 @@ bool ZipReader::ExtractCurrentEntryToString(
static_cast<int64>(max_read_bytes), static_cast<int64>(max_read_bytes),
current_entry_info()->original_size()))); current_entry_info()->original_size())));
bool success = true; // This becomes false when something bad happens. StringWriterDelegate writer(max_read_bytes, &contents);
char buf[internal::kZipBufSize]; if (!ExtractCurrentEntry(&writer))
while (true) { return false;
const int num_bytes_read = unzReadCurrentFile(zip_file_, buf, output->swap(contents);
internal::kZipBufSize); return true;
if (num_bytes_read == 0) {
// Reached the end of the file.
break;
} else if (num_bytes_read < 0) {
// If num_bytes_read < 0, then it's a specific UNZ_* error code.
success = false;
break;
} else if (num_bytes_read > 0) {
if (contents.size() + num_bytes_read > max_read_bytes) {
success = false;
break;
}
contents.append(buf, num_bytes_read);
}
}
unzCloseCurrentFile(zip_file_);
if (success)
output->swap(contents);
return success;
} }
bool ZipReader::OpenInternal() { bool ZipReader::OpenInternal() {
...@@ -461,5 +510,30 @@ void ZipReader::ExtractChunk(base::File output_file, ...@@ -461,5 +510,30 @@ void ZipReader::ExtractChunk(base::File output_file,
} }
} }
// FileWriterDelegate ----------------------------------------------------------
FileWriterDelegate::FileWriterDelegate(base::File* file)
: file_(file),
file_length_(0) {
}
FileWriterDelegate::~FileWriterDelegate() {
#if !defined(NDEBUG)
const bool success =
#endif
file_->SetLength(file_length_);
DPLOG_IF(ERROR, !success) << "Failed updating length of written file";
}
bool FileWriterDelegate::PrepareOutput() {
return file_->Seek(base::File::FROM_BEGIN, 0) >= 0;
}
bool FileWriterDelegate::WriteBytes(const char* data, int num_bytes) {
int bytes_written = file_->WriteAtCurrentPos(data, num_bytes);
if (bytes_written > 0)
file_length_ += bytes_written;
return bytes_written == num_bytes;
}
} // namespace zip } // namespace zip
...@@ -23,6 +23,21 @@ ...@@ -23,6 +23,21 @@
namespace zip { namespace zip {
// A delegate interface used to stream out an entry; see
// ZipReader::ExtractCurrentEntry.
class WriterDelegate {
public:
virtual ~WriterDelegate() {}
// Invoked once before any data is streamed out to pave the way (e.g., to open
// the output file). Return false on failure to cancel extraction.
virtual bool PrepareOutput() = 0;
// Invoked to write the next chunk of data. Return false on failure to cancel
// extraction.
virtual bool WriteBytes(const char* data, int num_bytes) = 0;
};
// This class is used for reading zip files. A typical use case of this // This class is used for reading zip files. A typical use case of this
// class is to scan entries in a zip file and extract them. The code will // class is to scan entries in a zip file and extract them. The code will
// look like: // look like:
...@@ -141,6 +156,9 @@ class ZipReader { ...@@ -141,6 +156,9 @@ class ZipReader {
// success. On failure, current_entry_info() becomes NULL. // success. On failure, current_entry_info() becomes NULL.
bool LocateAndOpenEntry(const base::FilePath& path_in_zip); bool LocateAndOpenEntry(const base::FilePath& path_in_zip);
// Extracts the current entry in chunks to |delegate|.
bool ExtractCurrentEntry(WriterDelegate* delegate) const;
// Extracts the current entry to the given output file path. If the // Extracts the current entry to the given output file path. If the
// current file is a directory, just creates a directory // current file is a directory, just creates a directory
// instead. Returns true on success. OpenCurrentEntryInZip() must be // instead. Returns true on success. OpenCurrentEntryInZip() must be
...@@ -148,7 +166,8 @@ class ZipReader { ...@@ -148,7 +166,8 @@ class ZipReader {
// //
// This function preserves the timestamp of the original entry. If that // This function preserves the timestamp of the original entry. If that
// timestamp is not valid, the timestamp will be set to the current time. // timestamp is not valid, the timestamp will be set to the current time.
bool ExtractCurrentEntryToFilePath(const base::FilePath& output_file_path); bool ExtractCurrentEntryToFilePath(
const base::FilePath& output_file_path) const;
// Asynchronously extracts the current entry to the given output file path. // Asynchronously extracts the current entry to the given output file path.
// If the current entry is a directory it just creates the directory // If the current entry is a directory it just creates the directory
...@@ -174,13 +193,11 @@ class ZipReader { ...@@ -174,13 +193,11 @@ class ZipReader {
// This function preserves the timestamp of the original entry. If that // This function preserves the timestamp of the original entry. If that
// timestamp is not valid, the timestamp will be set to the current time. // timestamp is not valid, the timestamp will be set to the current time.
bool ExtractCurrentEntryIntoDirectory( bool ExtractCurrentEntryIntoDirectory(
const base::FilePath& output_directory_path); const base::FilePath& output_directory_path) const;
#if defined(OS_POSIX) // Extracts the current entry by writing directly to a platform file.
// Extracts the current entry by writing directly to a file descriptor. // Does not close the file. Returns true on success.
// Does not close the file descriptor. Returns true on success. bool ExtractCurrentEntryToFile(base::File* file) const;
bool ExtractCurrentEntryToFd(int fd);
#endif
// Extracts the current entry into memory. If the current entry is a directory // Extracts the current entry into memory. If the current entry is a directory
// the |output| parameter is set to the empty string. If the current entry is // the |output| parameter is set to the empty string. If the current entry is
...@@ -232,6 +249,30 @@ class ZipReader { ...@@ -232,6 +249,30 @@ class ZipReader {
DISALLOW_COPY_AND_ASSIGN(ZipReader); DISALLOW_COPY_AND_ASSIGN(ZipReader);
}; };
// A writer delegate that writes to a given File.
class FileWriterDelegate : public WriterDelegate {
public:
explicit FileWriterDelegate(base::File* file);
// Truncates the file to the number of bytes written.
~FileWriterDelegate() override;
// WriterDelegate methods:
// Seeks to the beginning of the file, returning false if the seek fails.
bool PrepareOutput() override;
// Writes |num_bytes| bytes of |data| to the file, returning false on error or
// if not all bytes could be written.
bool WriteBytes(const char* data, int num_bytes) override;
private:
base::File* file_;
int64_t file_length_;
DISALLOW_COPY_AND_ASSIGN(FileWriterDelegate);
};
} // namespace zip } // namespace zip
#endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_ #endif // THIRD_PARTY_ZLIB_GOOGLE_ZIP_READER_H_
...@@ -18,10 +18,14 @@ ...@@ -18,10 +18,14 @@
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h" #include "testing/platform_test.h"
#include "third_party/zlib/google/zip_internal.h" #include "third_party/zlib/google/zip_internal.h"
using ::testing::Return;
using ::testing::_;
namespace { namespace {
const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6"; const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6";
...@@ -47,6 +51,8 @@ class FileWrapper { ...@@ -47,6 +51,8 @@ class FileWrapper {
base::PlatformFile platform_file() { return file_.GetPlatformFile(); } base::PlatformFile platform_file() { return file_.GetPlatformFile(); }
base::File* file() { return &file_; }
private: private:
base::File file_; base::File file_;
}; };
...@@ -93,6 +99,12 @@ class MockUnzipListener : public base::SupportsWeakPtr<MockUnzipListener> { ...@@ -93,6 +99,12 @@ class MockUnzipListener : public base::SupportsWeakPtr<MockUnzipListener> {
int64 current_progress_; int64 current_progress_;
}; };
class MockWriterDelegate : public zip::WriterDelegate {
public:
MOCK_METHOD0(PrepareOutput, bool());
MOCK_METHOD2(WriteBytes, bool(const char*, int));
};
} // namespace } // namespace
namespace zip { namespace zip {
...@@ -287,8 +299,7 @@ TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFilePath_RegularFile) { ...@@ -287,8 +299,7 @@ TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFilePath_RegularFile) {
EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size()); EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size());
} }
#if defined(OS_POSIX) TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFile_RegularFile) {
TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFd_RegularFile) {
ZipReader reader; ZipReader reader;
FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY); FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY);
ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file())); ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file()));
...@@ -296,18 +307,16 @@ TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFd_RegularFile) { ...@@ -296,18 +307,16 @@ TEST_F(ZipReaderTest, PlatformFileExtractCurrentEntryToFd_RegularFile) {
base::FilePath out_path = test_dir_.AppendASCII("quux.txt"); base::FilePath out_path = test_dir_.AppendASCII("quux.txt");
FileWrapper out_fd_w(out_path, FileWrapper::READ_WRITE); FileWrapper out_fd_w(out_path, FileWrapper::READ_WRITE);
ASSERT_TRUE(reader.LocateAndOpenEntry(target_path)); ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
ASSERT_TRUE(reader.ExtractCurrentEntryToFd(out_fd_w.platform_file())); ASSERT_TRUE(reader.ExtractCurrentEntryToFile(out_fd_w.file()));
// Read the output file and compute the MD5. // Read the output file and compute the MD5.
std::string output; std::string output;
ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"), ASSERT_TRUE(base::ReadFileToString(out_path, &output));
&output));
const std::string md5 = base::MD5String(output); const std::string md5 = base::MD5String(output);
EXPECT_EQ(kQuuxExpectedMD5, md5); EXPECT_EQ(kQuuxExpectedMD5, md5);
// quux.txt should be larger than kZipBufSize so that we can exercise // quux.txt should be larger than kZipBufSize so that we can exercise
// the loop in ExtractCurrentEntry(). // the loop in ExtractCurrentEntry().
EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size()); EXPECT_LT(static_cast<size_t>(internal::kZipBufSize), output.size());
} }
#endif
TEST_F(ZipReaderTest, ExtractCurrentEntryToFilePath_Directory) { TEST_F(ZipReaderTest, ExtractCurrentEntryToFilePath_Directory) {
ZipReader reader; ZipReader reader;
...@@ -582,4 +591,97 @@ TEST_F(ZipReaderTest, DISABLED_LeakDetectionTest) { ...@@ -582,4 +591,97 @@ TEST_F(ZipReaderTest, DISABLED_LeakDetectionTest) {
} }
} }
// Test that when WriterDelegate::PrepareMock returns false, no other methods on
// the delegate are called and the extraction fails.
TEST_F(ZipReaderTest, ExtractCurrentEntryPrepareFailure) {
testing::StrictMock<MockWriterDelegate> mock_writer;
EXPECT_CALL(mock_writer, PrepareOutput())
.WillOnce(Return(false));
base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
ZipReader reader;
ASSERT_TRUE(reader.Open(test_zip_file_));
ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer));
}
// Test that when WriterDelegate::WriteBytes returns false, no other methods on
// the delegate are called and the extraction fails.
TEST_F(ZipReaderTest, ExtractCurrentEntryWriteBytesFailure) {
testing::StrictMock<MockWriterDelegate> mock_writer;
EXPECT_CALL(mock_writer, PrepareOutput())
.WillOnce(Return(true));
EXPECT_CALL(mock_writer, WriteBytes(_, _))
.WillOnce(Return(false));
base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
ZipReader reader;
ASSERT_TRUE(reader.Open(test_zip_file_));
ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
ASSERT_FALSE(reader.ExtractCurrentEntry(&mock_writer));
}
// Test that extraction succeeds when the writer delegate reports all is well.
TEST_F(ZipReaderTest, ExtractCurrentEntrySuccess) {
testing::StrictMock<MockWriterDelegate> mock_writer;
EXPECT_CALL(mock_writer, PrepareOutput())
.WillOnce(Return(true));
EXPECT_CALL(mock_writer, WriteBytes(_, _))
.WillRepeatedly(Return(true));
base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
ZipReader reader;
ASSERT_TRUE(reader.Open(test_zip_file_));
ASSERT_TRUE(reader.LocateAndOpenEntry(target_path));
ASSERT_TRUE(reader.ExtractCurrentEntry(&mock_writer));
}
class FileWriterDelegateTest : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path_));
file_.Initialize(temp_file_path_, (base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_READ |
base::File::FLAG_WRITE |
base::File::FLAG_TEMPORARY |
base::File::FLAG_DELETE_ON_CLOSE));
ASSERT_TRUE(file_.IsValid());
}
// Writes data to the file, leaving the current position at the end of the
// write.
void PopulateFile() {
static const char kSomeData[] = "this sure is some data.";
static const size_t kSomeDataLen = sizeof(kSomeData) - 1;
ASSERT_NE(-1LL, file_.Write(0LL, kSomeData, kSomeDataLen));
}
base::FilePath temp_file_path_;
base::File file_;
};
TEST_F(FileWriterDelegateTest, WriteToStartAndTruncate) {
// Write stuff and advance.
PopulateFile();
// This should rewind, write, then truncate.
static const char kSomeData[] = "short";
static const int kSomeDataLen = sizeof(kSomeData) - 1;
{
FileWriterDelegate writer(&file_);
ASSERT_TRUE(writer.PrepareOutput());
ASSERT_TRUE(writer.WriteBytes(kSomeData, kSomeDataLen));
}
ASSERT_EQ(kSomeDataLen, file_.GetLength());
char buf[kSomeDataLen] = {};
ASSERT_EQ(kSomeDataLen, file_.Read(0LL, buf, kSomeDataLen));
ASSERT_EQ(std::string(kSomeData), std::string(buf, kSomeDataLen));
}
} // namespace zip } // namespace zip
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment