blob: 9f22a4cbf6cf999d82681cc2a1a9c3e589031393 [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.h"
#include <algorithm>
#include <string>
#include <utility>
#include "base/files/file.h"
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "base/time/time.h"
#include "chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_stream.h"
#include "chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char kTestFileName[] = "test.txt";
const char kLargeTestFileName[] = "large.file";
const char kTestDirName[] = "foo";
const char kTestFileContent[] = "Hello, World!";
const std::string kLargeTestFileContent(1234567, 'a');
class TestCompressorStream : public CompressorStream {
public:
TestCompressorStream() = default;
int64_t Flush() override { return 0; }
int64_t Write(int64_t zip_offset,
int64_t zip_length,
const char* zip_buffer) override {
CHECK_EQ(zip_offset, write_offset_);
CHECK_GT(zip_length, 0);
CHECK_NE(zip_buffer, nullptr);
if (write_error_)
return -1;
write_buffer_.append(zip_buffer, zip_length);
write_offset_ += zip_length;
return zip_length;
}
int64_t WriteChunkDone(int64_t write_bytes) override {
NOTREACHED();
return -1;
}
int64_t Read(int64_t bytes_to_read, char* destination_buffer) override {
if (read_buffer_.empty())
return 0;
int64_t read_length =
std::min(bytes_to_read, static_cast<int64_t>(read_buffer_.size()));
memcpy(destination_buffer, read_buffer_.data(), read_length);
read_buffer_ = read_buffer_.substr(read_length);
return read_length;
}
int64_t ReadFileChunkDone(int64_t read_bytes,
pp::VarArrayBuffer* buffer) override {
NOTREACHED();
return -1;
}
void SetReadBuffer(const std::string& buffer) { read_buffer_ = buffer; }
void SetWriteError() { write_error_ = true; }
const std::string& write_buffer() { return write_buffer_; }
private:
std::string write_buffer_;
int64_t write_offset_ = 0;
bool write_error_ = false;
std::string read_buffer_;
};
class InMemoryVolumeReader : public VolumeReader {
public:
explicit InMemoryVolumeReader(const std::string& file) : file_(file) {}
int64_t Read(int64_t bytes_to_read,
const void** destination_buffer) override {
if (file_offset_ >= static_cast<int64_t>(file_.size()))
return 0;
int64_t read_length = std::min(
bytes_to_read, static_cast<int64_t>(file_.size()) - file_offset_);
*destination_buffer = static_cast<const void*>(file_.data() + file_offset_);
file_offset_ += read_length;
return read_length;
}
int64_t Seek(int64_t offset, base::File::Whence whence) override {
switch (whence) {
case base::File::FROM_BEGIN:
file_offset_ = offset;
break;
case base::File::FROM_CURRENT:
file_offset_ += offset;
break;
case base::File::FROM_END:
file_offset_ = file_.size() + offset;
break;
}
return file_offset_;
}
base::Optional<std::string> Passphrase() override { return {}; }
int64_t offset() override { return file_offset_; }
int64_t archive_size() override { return file_.size(); }
private:
const std::string file_;
int64_t file_offset_ = 0;
};
class CompressorArchiveMinizipTest : public testing::Test {
public:
CompressorArchiveMinizipTest() = default;
void CheckZipContents(const std::string& volume,
const std::string& path,
const std::string& contents) {
std::unique_ptr<InMemoryVolumeReader> reader =
std::make_unique<InMemoryVolumeReader>(volume);
VolumeArchiveMinizip archive(std::move(reader));
ASSERT_TRUE(archive.Init(""));
EXPECT_TRUE(archive.SeekHeader(path));
const char* buffer = nullptr;
int64_t offset = 0;
while (offset < static_cast<int64_t>(contents.size())) {
int64_t read =
archive.ReadData(offset, contents.size() - offset, &buffer);
ASSERT_GT(read, 0);
EXPECT_EQ(contents.substr(offset, read), base::StringPiece(buffer, read));
offset += read;
}
EXPECT_EQ(offset, static_cast<int64_t>(contents.size()));
}
void CheckZipMetadata(const std::string& volume,
const std::string& path,
int64_t size,
base::Time mod_time,
bool is_directory) {
std::unique_ptr<InMemoryVolumeReader> reader =
std::make_unique<InMemoryVolumeReader>(volume);
VolumeArchiveMinizip archive(std::move(reader));
ASSERT_TRUE(archive.Init(""));
EXPECT_TRUE(archive.SeekHeader(path));
std::string volume_file_path;
bool volume_is_utf8 = false;
int64_t volume_size = -1;
bool volume_is_directory = false;
time_t volume_mod_time = 0;
auto result = archive.GetCurrentFileInfo(&volume_file_path, &volume_is_utf8,
&volume_size, &volume_is_directory,
&volume_mod_time);
EXPECT_EQ(result, VolumeArchive::RESULT_SUCCESS);
EXPECT_EQ(size, volume_size);
EXPECT_EQ(mod_time.ToTimeT(), volume_mod_time);
EXPECT_EQ(is_directory, volume_is_directory);
}
private:
std::unique_ptr<CompressorArchiveMinizip> archive_;
};
TEST_F(CompressorArchiveMinizipTest, Create) {
TestCompressorStream stream;
CompressorArchiveMinizip archive(&stream);
const base::Time add_time = base::Time::Now();
EXPECT_TRUE(archive.CreateArchive());
stream.SetReadBuffer(kTestFileContent);
EXPECT_TRUE(archive.AddToArchive(kTestFileName, sizeof(kTestFileContent) - 1,
add_time, false));
stream.SetReadBuffer(kLargeTestFileContent);
EXPECT_TRUE(archive.AddToArchive(
kLargeTestFileName, kLargeTestFileContent.size(), add_time, false));
EXPECT_TRUE(archive.AddToArchive(kTestDirName, 0, add_time, true));
EXPECT_TRUE(archive.CloseArchive(false));
EXPECT_FALSE(stream.write_buffer().empty());
CheckZipMetadata(stream.write_buffer(), kTestFileName,
sizeof(kTestFileContent) - 1, add_time, false);
CheckZipMetadata(stream.write_buffer(), kLargeTestFileName,
kLargeTestFileContent.size(), add_time, false);
CheckZipMetadata(stream.write_buffer(), std::string(kTestDirName) + "/", 0,
add_time, true);
CheckZipContents(stream.write_buffer(), kTestFileName, kTestFileContent);
CheckZipContents(stream.write_buffer(), kLargeTestFileName,
kLargeTestFileContent);
}
TEST_F(CompressorArchiveMinizipTest, Create_WriteError) {
TestCompressorStream stream;
CompressorArchiveMinizip archive(&stream);
const base::Time add_time = base::Time::Now();
EXPECT_TRUE(archive.CreateArchive());
stream.SetReadBuffer(kTestFileContent);
EXPECT_TRUE(archive.AddToArchive(kTestFileName, sizeof(kTestFileContent) - 1,
add_time, false));
stream.SetReadBuffer(kLargeTestFileContent);
stream.SetWriteError();
EXPECT_FALSE(archive.AddToArchive(
kLargeTestFileName, kLargeTestFileContent.size(), add_time, false));
EXPECT_FALSE(archive.error_message().empty());
}
TEST_F(CompressorArchiveMinizipTest, CreateAndCancel) {
TestCompressorStream stream;
CompressorArchiveMinizip archive(&stream);
const base::Time add_time = base::Time::Now();
EXPECT_TRUE(archive.CreateArchive());
stream.SetReadBuffer(kTestFileContent);
EXPECT_TRUE(archive.AddToArchive(kTestFileName, sizeof(kTestFileContent) - 1,
add_time, false));
stream.SetReadBuffer(kLargeTestFileContent);
archive.CancelArchive();
EXPECT_FALSE(archive.AddToArchive(
kLargeTestFileName, kLargeTestFileContent.size(), add_time, false));
EXPECT_TRUE(archive.error_message().empty());
}
} // namespace