summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkuchhal@chromium.org <kuchhal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-05 20:59:12 +0000
committerkuchhal@chromium.org <kuchhal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-05 20:59:12 +0000
commitc256c0b0e7a2ca6401528b33b325cb19a981762b (patch)
tree4f21b432be4673c51f0dc6b6e1b3264c075504b4
parentba15f7a45453eaf4f4c928d0c8a572b48d6f7541 (diff)
downloadchromium_src-c256c0b0e7a2ca6401528b33b325cb19a981762b.zip
chromium_src-c256c0b0e7a2ca6401528b33b325cb19a981762b.tar.gz
chromium_src-c256c0b0e7a2ca6401528b33b325cb19a981762b.tar.bz2
Add unit tests for unpacking and patching methods.
BUG=12849 TEST=none Review URL: http://codereview.chromium.org/160623 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22535 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/installer/installer.gyp5
-rwxr-xr-xchrome/installer/mini_installer.gyp4
-rw-r--r--chrome/installer/setup/run_all_unittests.cc8
-rw-r--r--chrome/installer/setup/setup_constants.cc2
-rw-r--r--chrome/installer/setup/setup_constants.h2
-rw-r--r--chrome/installer/setup/setup_main.cc176
-rw-r--r--chrome/installer/setup/setup_util.cc21
-rw-r--r--chrome/installer/setup/setup_util.h9
-rw-r--r--chrome/installer/setup/setup_util_unittest.cc76
-rw-r--r--chrome/installer/util/lzma_util.cc56
-rw-r--r--chrome/installer/util/lzma_util.h27
-rw-r--r--chrome/installer/util/lzma_util_unittest.cc132
-rw-r--r--chrome/installer/util/run_all_unittests.cc8
-rw-r--r--chrome/test/data/installer/archive.diffbin0 -> 27136 bytes
-rw-r--r--chrome/test/data/installer/archive1.7zbin0 -> 27092 bytes
-rw-r--r--chrome/test/data/installer/archive2.7zbin0 -> 27092 bytes
-rw-r--r--chrome/test/data/installer/archive3.7zbin0 -> 27221 bytes
-rw-r--r--chrome/test/data/installer/invalid_archive.7z1
18 files changed, 338 insertions, 189 deletions
diff --git a/chrome/installer/installer.gyp b/chrome/installer/installer.gyp
index c926266..6e15398 100644
--- a/chrome/installer/installer.gyp
+++ b/chrome/installer/installer.gyp
@@ -20,6 +20,8 @@
'../chrome.gyp:common',
'../chrome.gyp:chrome_resources',
'../chrome.gyp:chrome_strings',
+ '../../courgette/courgette.gyp:courgette_lib',
+ '../../third_party/bspatch/bspatch.gyp:bspatch',
'../../third_party/icu38/icu38.gyp:icui18n',
'../../third_party/icu38/icu38.gyp:icuuc',
'../../third_party/libxml/libxml.gyp:libxml',
@@ -156,6 +158,7 @@
'util/helper_unittest.cc',
'util/installer_util_unittests.rc',
'util/installer_util_unittests_resource.h',
+ 'util/lzma_util_unittest.cc',
'util/master_preferences_unittest.cc',
'util/move_tree_work_item_unittest.cc',
'util/run_all_unittests.cc',
@@ -242,8 +245,6 @@
'installer_util_strings',
'../../build/util/build_util.gyp:lastchange',
'../../build/win/system.gyp:cygwin',
- '../../courgette/courgette.gyp:courgette_lib',
- '../../third_party/bspatch/bspatch.gyp:bspatch',
],
'include_dirs': [
'../..',
diff --git a/chrome/installer/mini_installer.gyp b/chrome/installer/mini_installer.gyp
index cf9be80..2ed541a 100755
--- a/chrome/installer/mini_installer.gyp
+++ b/chrome/installer/mini_installer.gyp
@@ -169,6 +169,10 @@
# TODO(sgk): may just use environment variables
#'--distribution=$(CHROMIUM_BUILD)',
'--distribution=_google_chrome',
+ # Optional arguments to generate diff installer
+ #'--last_chrome_installer=C:/Temp/base',
+ #'--setup_exe_format=DIFF',
+ #'--diff_algorithm=COURGETTE',
],
'message': 'Create installer archive'
},
diff --git a/chrome/installer/setup/run_all_unittests.cc b/chrome/installer/setup/run_all_unittests.cc
index b5fc4a6..8896217 100644
--- a/chrome/installer/setup/run_all_unittests.cc
+++ b/chrome/installer/setup/run_all_unittests.cc
@@ -3,7 +3,13 @@
// found in the LICENSE file.
#include "base/test_suite.h"
+#include "chrome/common/chrome_paths.h"
int main(int argc, char** argv) {
- return TestSuite(argc, argv).Run();
+ TestSuite test_suite(argc, argv);
+
+ // Register Chrome Path provider so that we can get test data dir.
+ chrome::RegisterPathProvider();
+
+ return test_suite.Run();
}
diff --git a/chrome/installer/setup/setup_constants.cc b/chrome/installer/setup/setup_constants.cc
index 1d33d33..5a5d869 100644
--- a/chrome/installer/setup/setup_constants.cc
+++ b/chrome/installer/setup/setup_constants.cc
@@ -9,9 +9,7 @@ namespace installer {
const wchar_t kWowHelperExe[] = L"wow_helper.exe";
const wchar_t kDictionaries[] = L"Dictionaries";
const wchar_t kChromeArchive[] = L"chrome.7z";
-const wchar_t kChromeArchivePatch[] = L"chrome_patch.diff";
const wchar_t kChromeCompressedArchive[] = L"chrome.packed.7z";
-const wchar_t kChromeCompressedPatchArchivePrefix[] = L"chrome_patch";
// Sub directory of install source package under install temporary directory.
const wchar_t kInstallSourceDir[] = L"source";
diff --git a/chrome/installer/setup/setup_constants.h b/chrome/installer/setup/setup_constants.h
index 4420977..1691f25 100644
--- a/chrome/installer/setup/setup_constants.h
+++ b/chrome/installer/setup/setup_constants.h
@@ -12,9 +12,7 @@ namespace installer {
extern const wchar_t kWowHelperExe[];
extern const wchar_t kDictionaries[];
extern const wchar_t kChromeArchive[];
-extern const wchar_t kChromeArchivePatch[];
extern const wchar_t kChromeCompressedArchive[];
-extern const wchar_t kChromeCompressedPatchArchivePrefix[];
extern const wchar_t kInstallSourceDir[];
extern const wchar_t kInstallSourceChromeDir[];
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index f976653..676a219 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -32,51 +32,12 @@
#include "chrome/installer/util/master_preferences.h"
#include "chrome/installer/util/shell_util.h"
#include "chrome/installer/util/util_constants.h"
-#include "chrome/installer/util/work_item.h"
-#include "courgette/courgette.h"
-#include "third_party/bspatch/mbspatch.h"
#include "installer_util_strings.h"
namespace {
-// Applies a binary patch to existing Chrome installer archive on the system.
-// Uses bspatch library.
-int PatchArchiveFile(bool system_install, const std::wstring& archive_path,
- const std::wstring& uncompressed_archive,
- const installer::Version* installed_version) {
- std::wstring existing_archive =
- installer::GetChromeInstallPath(system_install);
- file_util::AppendToPath(&existing_archive,
- installed_version->GetString());
- file_util::AppendToPath(&existing_archive, installer_util::kInstallerDir);
- file_util::AppendToPath(&existing_archive, installer::kChromeArchive);
-
- std::wstring patch_archive(archive_path);
- file_util::AppendToPath(&patch_archive, installer::kChromeArchivePatch);
-
- LOG(INFO) << "Applying patch " << patch_archive
- << " to file " << existing_archive
- << " and generating file " << uncompressed_archive;
-
- // Try Courgette first. Courgette checks the patch file first and fails
- // quickly if the patch file does not have a valid Courgette header.
- courgette::Status patch_status =
- courgette::ApplyEnsemblePatch(existing_archive.c_str(),
- patch_archive.c_str(),
- uncompressed_archive.c_str());
-
- if (patch_status == courgette::C_OK) {
- return 0;
- }
-
- return ApplyBinaryPatch(existing_archive.c_str(),
- patch_archive.c_str(),
- uncompressed_archive.c_str());
-}
-
-
-// This method unpacks and uncompresses the given archive file. For Chrome
+ // This method unpacks and uncompresses the given archive file. For Chrome
// install we are creating a uncompressed archive that contains all the files
// needed for the installer. This uncompressed archive is later compressed.
//
@@ -90,68 +51,43 @@ DWORD UnPackArchive(const std::wstring& archive, bool system_install,
const installer::Version* installed_version,
const std::wstring& temp_path, const std::wstring& path,
bool& incremental_install) {
- DWORD ret = NO_ERROR;
- installer::LzmaUtil util;
- // First uncompress the payload. This could be a differential
- // update (patch.7z) or full archive (chrome.7z). If this uncompress fails
- // return with error.
- LOG(INFO) << "Opening archive " << archive;
- if ((ret = util.OpenArchive(archive)) != NO_ERROR) {
- LOG(ERROR) << "Unable to open install archive: " << archive;
- } else {
- LOG(INFO) << "Uncompressing archive to path " << temp_path;
- if ((ret = util.UnPack(temp_path)) != NO_ERROR) {
- LOG(ERROR) << "Error during uncompression: " << ret;
- }
- util.CloseArchive();
- }
- if (ret != NO_ERROR)
- return ret;
-
- std::wstring uncompressed_archive(temp_path);
- file_util::AppendToPath(&uncompressed_archive, installer::kChromeArchive);
-
- // Check if this is differential update and if it is, patch it to the
- // installer archive that should already be on the machine.
- std::wstring archive_name = file_util::GetFilenameFromPath(archive);
- std::wstring prefix = installer::kChromeCompressedPatchArchivePrefix;
- if ((archive_name.size() >= prefix.size()) &&
- (std::equal(prefix.begin(), prefix.end(), archive_name.begin(),
- CaseInsensitiveCompare<wchar_t>()))) {
- incremental_install = true;
- LOG(INFO) << "Differential patch found. Applying to existing archive.";
- // First pre-emptively set flag in registry to get full installer next
- // time. If the current installer works, this flag will get reset at the
- // the end of installation.
- BrowserDistribution* dist = BrowserDistribution::GetDistribution();
- dist->UpdateDiffInstallStatus(system_install, incremental_install,
- installer_util::INSTALL_FAILED);
- if (!installed_version) {
- LOG(ERROR) << "Can not use differential update when Chrome is not "
- << "installed on the system.";
- return 1;
- }
- if (int i = PatchArchiveFile(system_install, temp_path,
- uncompressed_archive, installed_version)) {
- LOG(ERROR) << "Binary patching failed with error " << i;
- return 1;
- }
- }
+ // First uncompress the payload. This could be a differential
+ // update (patch.7z) or full archive (chrome.7z). If this uncompress fails
+ // return with error.
+ std::wstring unpacked_file;
+ int32 ret = LzmaUtil::UnPackArchive(archive, temp_path, &unpacked_file);
+ if (ret != NO_ERROR)
+ return ret;
- // If we got the uncompressed archive, lets unpack it
- LOG(INFO) << "Opening archive " << uncompressed_archive;
- if ((ret = util.OpenArchive(uncompressed_archive)) != NO_ERROR) {
- LOG(ERROR) << "Unable to open install archive: " <<
- uncompressed_archive;
- } else {
- LOG(INFO) << "Unpacking archive to path " << path;
- if ((ret = util.UnPack(path)) != NO_ERROR) {
- LOG(ERROR) << "Error during uncompression: " << ret;
- }
- util.CloseArchive();
+ std::wstring uncompressed_archive(temp_path);
+ file_util::AppendToPath(&uncompressed_archive, installer::kChromeArchive);
+
+ // Check if this is differential update and if it is, patch it to the
+ // installer archive that should already be on the machine. We assume
+ // it is a differential installer if chrome.7z is not found.
+ if (!file_util::PathExists(uncompressed_archive)) {
+ incremental_install = true;
+ LOG(INFO) << "Differential patch found. Applying to existing archive.";
+ if (!installed_version) {
+ LOG(ERROR) << "Can not use differential update when Chrome is not "
+ << "installed on the system.";
+ return installer_util::InstallStatus::CHROME_NOT_INSTALLED;
+ }
+ std::wstring existing_archive =
+ installer::GetChromeInstallPath(system_install);
+ file_util::AppendToPath(&existing_archive,
+ installed_version->GetString());
+ file_util::AppendToPath(&existing_archive, installer_util::kInstallerDir);
+ file_util::AppendToPath(&existing_archive, installer::kChromeArchive);
+ if (int i = setup_util::ApplyDiffPatch(existing_archive, unpacked_file,
+ uncompressed_archive)) {
+ LOG(ERROR) << "Binary patching failed with error " << i;
+ return i;
}
+ }
- return ret;
+ // Unpack the uncompressed archive.
+ return LzmaUtil::UnPackArchive(uncompressed_archive, path, &unpacked_file);
}
@@ -436,51 +372,19 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line,
if (!file_util::CreateNewTempDirectory(std::wstring(L"chrome_"),
&temp_path)) {
LOG(ERROR) << "Could not create temporary path.";
- status = installer_util::SETUP_PATCH_FAILED;
} else {
std::wstring setup_patch = cmd_line.GetSwitchValue(
installer_util::switches::kUpdateSetupExe);
LOG(INFO) << "Opening archive " << setup_patch;
- DWORD ret = NO_ERROR;
- installer::LzmaUtil util;
- if ((ret = util.OpenArchive(setup_patch)) != NO_ERROR) {
- LOG(ERROR) << "Unable to open install archive: " << setup_patch;
- } else {
- LOG(INFO) << "Uncompressing archive to path " << temp_path;
- if ((ret = util.UnPack(temp_path)) != NO_ERROR) {
- LOG(ERROR) << "Error during uncompression: " << ret;
- }
- util.CloseArchive();
- }
-
- if (ret != NO_ERROR) {
- status = installer_util::SETUP_PATCH_FAILED;
- } else {
+ std::wstring uncompressed_patch;
+ if (LzmaUtil::UnPackArchive(setup_patch, temp_path,
+ &uncompressed_patch) == NO_ERROR) {
std::wstring old_setup_exe = cmd_line.program();
- std::wstring uncompressed_setup_patch(temp_path);
- file_util::AppendToPath(&uncompressed_setup_patch,
- installer::kSetupExePatch);
std::wstring new_setup_exe = cmd_line.GetSwitchValue(
installer_util::switches::kNewSetupExe);
- LOG(INFO) << "Patching " << old_setup_exe
- << " with patch " << uncompressed_setup_patch
- << " and creating new exe " << new_setup_exe;
-
- // Try Courgette first.
- courgette::Status patch_status = courgette::ApplyEnsemblePatch(
- old_setup_exe.c_str(), uncompressed_setup_patch.c_str(),
- new_setup_exe.c_str());
-
- // If courgette didn't work, try regular bspatch.
- if (patch_status != courgette::C_OK) {
- LOG(WARNING) << "setup patch failed using courgette " << patch_status;
- if (!ApplyBinaryPatch(old_setup_exe.c_str(),
- uncompressed_setup_patch.c_str(),
- new_setup_exe.c_str()))
- status = installer_util::NEW_VERSION_UPDATED;
- } else {
+ if (!setup_util::ApplyDiffPatch(old_setup_exe, uncompressed_patch,
+ new_setup_exe))
status = installer_util::NEW_VERSION_UPDATED;
- }
}
}
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc
index 985d704..b90ec9c 100644
--- a/chrome/installer/setup/setup_util.cc
+++ b/chrome/installer/setup/setup_util.cc
@@ -10,7 +10,28 @@
#include "base/logging.h"
#include "chrome/installer/util/master_preferences.h"
#include "chrome/installer/util/util_constants.h"
+#include "courgette/courgette.h"
+#include "third_party/bspatch/mbspatch.h"
+
+int setup_util::ApplyDiffPatch(const std::wstring& src,
+ const std::wstring& patch,
+ const std::wstring& dest) {
+ LOG(INFO) << "Applying patch " << patch
+ << " to file " << src
+ << " and generating file " << dest;
+
+ // Try Courgette first. Courgette checks the patch file first and fails
+ // quickly if the patch file does not have a valid Courgette header.
+ courgette::Status patch_status =
+ courgette::ApplyEnsemblePatch(src.c_str(), patch.c_str(), dest.c_str());
+ if (patch_status == courgette::C_OK) {
+ return 0;
+ } else {
+ LOG(INFO) << "Failed to apply patch " << patch << " using courgette.";
+ }
+ return ApplyBinaryPatch(src.c_str(), patch.c_str(), dest.c_str());
+}
DictionaryValue* setup_util::GetInstallPreferences(
const CommandLine& cmd_line) {
diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h
index fc06482..3f646db 100644
--- a/chrome/installer/setup/setup_util.h
+++ b/chrome/installer/setup/setup_util.h
@@ -12,11 +12,18 @@
#include "chrome/installer/util/version.h"
namespace setup_util {
+ // Apply a diff patch to source file. First tries to apply it using courgette
+ // since it checks for courgette header and fails quickly. If that fails
+ // tries to apply the patch using regular bsdiff. Returns status code.
+ int ApplyDiffPatch(const std::wstring& src,
+ const std::wstring& patch,
+ const std::wstring& dest);
+
// Parse command line and read master preferences, if present, to get
// distribution related install options. Merge them if any command line
// options present (command line value takes precedence).
DictionaryValue* GetInstallPreferences(const CommandLine& cmd_line);
-
+
// Find the version of Chrome from an install source directory.
// Chrome_path should contain a version folder.
// Returns the first version found or NULL if no version is found.
diff --git a/chrome/installer/setup/setup_util_unittest.cc b/chrome/installer/setup/setup_util_unittest.cc
index d163816..6223f37 100644
--- a/chrome/installer/setup/setup_util_unittest.cc
+++ b/chrome/installer/setup/setup_util_unittest.cc
@@ -4,43 +4,65 @@
#include <windows.h>
-#include <fstream>
-#include <iostream>
-
#include "base/base_paths.h"
#include "base/file_util.h"
#include "base/path_service.h"
-#include "base/process_util.h"
-#include "base/string_util.h"
+#include "chrome/common/chrome_paths.h"
#include "chrome/installer/setup/setup_util.h"
#include "chrome/installer/util/master_preferences.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
- class SetupUtilTest : public testing::Test {
- protected:
- virtual void SetUp() {
- // Name a subdirectory of the user temp directory.
- ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
- test_dir_ = test_dir_.AppendASCII("SetupUtilTest");
-
- // Create a fresh, empty copy of this test directory.
- file_util::Delete(test_dir_, true);
- file_util::CreateDirectory(test_dir_);
- ASSERT_TRUE(file_util::PathExists(test_dir_));
- }
-
- virtual void TearDown() {
- // Clean up test directory
- ASSERT_TRUE(file_util::Delete(test_dir_, false));
- ASSERT_FALSE(file_util::PathExists(test_dir_));
- }
-
- // the path to temporary directory used to contain the test operations
- FilePath test_dir_;
- };
+class SetupUtilTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_dir_));
+ data_dir_ = data_dir_.AppendASCII("installer");
+ ASSERT_TRUE(file_util::PathExists(data_dir_));
+
+ // Name a subdirectory of the user temp directory.
+ ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
+ test_dir_ = test_dir_.AppendASCII("SetupUtilTest");
+
+ // Create a fresh, empty copy of this test directory.
+ file_util::Delete(test_dir_, true);
+ file_util::CreateDirectory(test_dir_);
+ ASSERT_TRUE(file_util::PathExists(test_dir_));
+ }
+
+ virtual void TearDown() {
+ // Clean up test directory
+ ASSERT_TRUE(file_util::Delete(test_dir_, false));
+ ASSERT_FALSE(file_util::PathExists(test_dir_));
+ }
+
+ // the path to temporary directory used to contain the test operations
+ FilePath test_dir_;
+
+ // The path to input data used in tests.
+ FilePath data_dir_;
+};
};
+// Test that we are parsing Chrome version correctly.
+TEST_F(SetupUtilTest, ApplyDiffPatchTest) {
+ FilePath work_dir(test_dir_);
+ work_dir = work_dir.AppendASCII("ApplyDiffPatchTest");
+ ASSERT_FALSE(file_util::PathExists(work_dir));
+ EXPECT_TRUE(file_util::CreateDirectory(work_dir));
+ ASSERT_TRUE(file_util::PathExists(work_dir));
+
+ FilePath src = data_dir_.AppendASCII("archive1.7z");
+ FilePath patch = data_dir_.AppendASCII("archive.diff");
+ FilePath dest = work_dir.AppendASCII("archive2.7z");
+ EXPECT_EQ(setup_util::ApplyDiffPatch(src.value(), patch.value(),
+ dest.value()), 0);
+ FilePath base = data_dir_.AppendASCII("archive2.7z");
+ EXPECT_TRUE(file_util::ContentsEqual(dest, base));
+
+ EXPECT_EQ(setup_util::ApplyDiffPatch(L"", L"", L""), 6);
+}
+
// Test that we are parsing master preferences correctly.
TEST_F(SetupUtilTest, GetInstallPreferencesTest) {
// Create a temporary prefs file.
diff --git a/chrome/installer/util/lzma_util.cc b/chrome/installer/util/lzma_util.cc
index cc8569d..5208e12 100644
--- a/chrome/installer/util/lzma_util.cc
+++ b/chrome/installer/util/lzma_util.cc
@@ -14,7 +14,7 @@ extern "C" {
}
-namespace installer {
+namespace {
typedef struct _CFileInStream {
ISzInStream InStream;
@@ -73,14 +73,40 @@ SZ_RESULT SzFileReadImp(void *object, void **buffer,
return SZ_OK;
}
+} // namespace
+
+// static
+int32 LzmaUtil::UnPackArchive(const std::wstring& archive,
+ const std::wstring& output_dir,
+ std::wstring* output_file) {
+ LOG(INFO) << "Opening archive " << archive;
+ LzmaUtil lzma_util;
+ DWORD ret;
+ if ((ret = lzma_util.OpenArchive(archive)) != NO_ERROR) {
+ LOG(ERROR) << "Unable to open install archive: " << archive
+ << ", error: " << ret;
+ } else {
+ LOG(INFO) << "Uncompressing archive to path " << output_dir;
+ if ((ret = lzma_util.UnPack(output_dir, output_file)) != NO_ERROR) {
+ LOG(ERROR) << "Unable to uncompress archive: " << archive
+ << ", error: " << ret;
+ }
+ lzma_util.CloseArchive();
+ }
+
+ return ret;
+}
LzmaUtil::LzmaUtil() : archive_handle_(NULL) {}
LzmaUtil::~LzmaUtil() {
- if (archive_handle_) CloseArchive();
+ CloseArchive();
}
DWORD LzmaUtil::OpenArchive(const std::wstring& archivePath) {
+ // Make sure file is not already open.
+ CloseArchive();
+
DWORD ret = NO_ERROR;
archive_handle_ = CreateFile(archivePath.c_str(), GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
@@ -90,8 +116,15 @@ DWORD LzmaUtil::OpenArchive(const std::wstring& archivePath) {
return ret;
}
-// Unpacks the archive to the given location.
DWORD LzmaUtil::UnPack(const std::wstring& location) {
+ return UnPack(location, NULL);
+}
+
+DWORD LzmaUtil::UnPack(const std::wstring& location,
+ std::wstring* output_file) {
+ if (!archive_handle_)
+ return ERROR_INVALID_HANDLE;
+
CFileInStream archiveStream;
ISzAlloc allocImp;
ISzAlloc allocTempImp;
@@ -110,12 +143,14 @@ DWORD LzmaUtil::UnPack(const std::wstring& location) {
SzArDbExInit(&db);
if ((ret = SzArchiveOpen(&archiveStream.InStream, &db,
&allocImp, &allocTempImp)) != SZ_OK) {
+ LOG(ERROR) << L"Error returned by SzArchiveOpen: " << ret;
return ret;
}
Byte *outBuffer = 0; // it must be 0 before first call for each new archive
UInt32 blockIndex = 0xFFFFFFFF; // can have any value if outBuffer = 0
size_t outBufferSize = 0; // can have any value if outBuffer = 0
+
for (unsigned int i = 0; i < db.Database.NumFiles; i++) {
DWORD written;
size_t offset;
@@ -125,12 +160,15 @@ DWORD LzmaUtil::UnPack(const std::wstring& location) {
if ((ret = SzExtract(&archiveStream.InStream, &db, i, &blockIndex,
&outBuffer, &outBufferSize, &offset, &outSizeProcessed,
&allocImp, &allocTempImp)) != SZ_OK) {
+ LOG(ERROR) << L"Error returned by SzExtract: " << ret;
break;
}
// Append location to the file path in archive, to get full path.
std::wstring wfileName(location);
file_util::AppendToPath(&wfileName, UTF8ToWide(f->Name));
+ if (output_file)
+ *output_file = wfileName;
// If archive entry is directory create it and move on to the next entry.
if (f->IsDirectory) {
@@ -146,6 +184,7 @@ DWORD LzmaUtil::UnPack(const std::wstring& location) {
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
ret = GetLastError();
+ LOG(ERROR) << L"Error returned by CreateFile: " << ret;
break;
}
@@ -154,6 +193,7 @@ DWORD LzmaUtil::UnPack(const std::wstring& location) {
(written != outSizeProcessed)) {
ret = GetLastError();
CloseHandle(hFile);
+ LOG(ERROR) << L"Error returned by WriteFile: " << ret;
break;
}
@@ -162,11 +202,13 @@ DWORD LzmaUtil::UnPack(const std::wstring& location) {
(const FILETIME *)&(f->LastWriteTime))) {
ret = GetLastError();
CloseHandle(hFile);
+ LOG(ERROR) << L"Error returned by SetFileTime: " << ret;
break;
}
}
if (!CloseHandle(hFile)) {
ret = GetLastError();
+ LOG(ERROR) << L"Error returned by CloseHandle: " << ret;
break;
}
} // for loop
@@ -177,8 +219,8 @@ DWORD LzmaUtil::UnPack(const std::wstring& location) {
}
void LzmaUtil::CloseArchive() {
- CloseHandle(archive_handle_);
- archive_handle_ = NULL;
+ if (archive_handle_) {
+ CloseHandle(archive_handle_);
+ archive_handle_ = NULL;
+ }
}
-
-} // namespace installer
diff --git a/chrome/installer/util/lzma_util.h b/chrome/installer/util/lzma_util.h
index f2a942e..e680444 100644
--- a/chrome/installer/util/lzma_util.h
+++ b/chrome/installer/util/lzma_util.h
@@ -1,20 +1,24 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009 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.
-#ifndef CHROME_INSTALLER_UTIL_LZMA_UTIL_H__
-#define CHROME_INSTALLER_UTIL_LZMA_UTIL_H__
+#ifndef CHROME_INSTALLER_UTIL_LZMA_UTIL_H_
+#define CHROME_INSTALLER_UTIL_LZMA_UTIL_H_
#include <string>
#include <windows.h>
#include "base/basictypes.h"
-namespace installer {
-
// This is a utility class that acts as a wrapper around LZMA SDK library
class LzmaUtil {
public:
+ // Utility method that does the job of calling OpenArchive(), UnPack()
+ // and CloseArchive() in order. Returns error code (NO_ERROR if successful).
+ static int32 UnPackArchive(const std::wstring& archive,
+ const std::wstring& output_dir,
+ std::wstring* output_file);
+
LzmaUtil();
~LzmaUtil();
@@ -23,15 +27,18 @@ class LzmaUtil {
// Unpacks the archive to the given location
DWORD UnPack(const std::wstring& location);
+ // Unpacks the archive to the given location and returns the last file
+ // extracted from archive. |single_file| is set to true iff only a single
+ // file is extracted from archive.
+ DWORD UnPack(const std::wstring& location,
+ std::wstring* output_file);
+
void CloseArchive();
private:
HANDLE archive_handle_;
- DISALLOW_EVIL_CONSTRUCTORS(LzmaUtil);
+ DISALLOW_COPY_AND_ASSIGN(LzmaUtil);
};
-} // namespace installer
-
-
-#endif // CHROME_INSTALLER_UTIL_LZMA_UTIL_H__
+#endif // CHROME_INSTALLER_UTIL_LZMA_UTIL_H_
diff --git a/chrome/installer/util/lzma_util_unittest.cc b/chrome/installer/util/lzma_util_unittest.cc
new file mode 100644
index 0000000..214b5c8
--- /dev/null
+++ b/chrome/installer/util/lzma_util_unittest.cc
@@ -0,0 +1,132 @@
+// Copyright (c) 2009 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 <windows.h>
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/installer/util/lzma_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+class LzmaUtilTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_dir_));
+ data_dir_ = data_dir_.AppendASCII("installer");
+ ASSERT_TRUE(file_util::PathExists(data_dir_));
+
+ // Name a subdirectory of the user temp directory.
+ ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
+ test_dir_ = test_dir_.AppendASCII("LzmaUtilTest");
+
+ // Create a fresh, empty copy of this test directory.
+ file_util::Delete(test_dir_, true);
+ file_util::CreateDirectoryW(test_dir_);
+ ASSERT_TRUE(file_util::PathExists(test_dir_));
+ }
+
+ virtual void TearDown() {
+ // Clean up test directory
+ ASSERT_TRUE(file_util::Delete(test_dir_, false));
+ ASSERT_FALSE(file_util::PathExists(test_dir_));
+ }
+
+ // The path to temporary directory used to contain the test operations.
+ FilePath test_dir_;
+
+ // The path to input data used in tests.
+ FilePath data_dir_;
+};
+};
+
+// Test that we can open archives successfully.
+TEST_F(LzmaUtilTest, OpenArchiveTest) {
+ FilePath archive = data_dir_.AppendASCII("archive1.7z");
+ LzmaUtil lzma_util;
+ EXPECT_EQ(lzma_util.OpenArchive(archive.value()), NO_ERROR);
+
+ // We allow opening another archive (which will automatically close the first
+ // archive).
+ archive = data_dir_.AppendASCII("archive2.7z");
+ EXPECT_EQ(lzma_util.OpenArchive(archive.value()), NO_ERROR);
+
+ // Explicitly close and open the first archive again.
+ lzma_util.CloseArchive();
+ archive = data_dir_.AppendASCII("archive1.7z");
+ EXPECT_EQ(lzma_util.OpenArchive(archive.value()), NO_ERROR);
+
+ // Make sure non-existent archive returns error.
+ archive = data_dir_.AppendASCII("archive.non_existent.7z");
+ EXPECT_EQ(lzma_util.OpenArchive(archive.value()), ERROR_FILE_NOT_FOUND);
+}
+
+// Test that we can extract archives successfully.
+TEST_F(LzmaUtilTest, UnPackTest) {
+ FilePath extract_dir(test_dir_);
+ extract_dir = extract_dir.AppendASCII("UnPackTest");
+ ASSERT_FALSE(file_util::PathExists(extract_dir));
+ EXPECT_TRUE(file_util::CreateDirectory(extract_dir));
+ ASSERT_TRUE(file_util::PathExists(extract_dir));
+
+ FilePath archive = data_dir_.AppendASCII("archive1.7z");
+ LzmaUtil lzma_util;
+ EXPECT_EQ(lzma_util.OpenArchive(archive.value()), NO_ERROR);
+ std::wstring unpacked_file;
+ EXPECT_EQ(lzma_util.UnPack(extract_dir.value(), &unpacked_file),
+ NO_ERROR);
+ EXPECT_TRUE(file_util::PathExists(extract_dir.AppendASCII("a.exe")));
+ EXPECT_TRUE(unpacked_file == extract_dir.AppendASCII("a.exe").value());
+
+ archive = data_dir_.AppendASCII("archive2.7z");
+ EXPECT_EQ(lzma_util.OpenArchive(archive.value()), NO_ERROR);
+ EXPECT_EQ(lzma_util.UnPack(extract_dir.value(), &unpacked_file),
+ NO_ERROR);
+ EXPECT_TRUE(file_util::PathExists(extract_dir.AppendASCII("b.exe")));
+ EXPECT_TRUE(unpacked_file == extract_dir.AppendASCII("b.exe").value());
+
+ lzma_util.CloseArchive();
+ archive = data_dir_.AppendASCII("invalid_archive.7z");
+ EXPECT_EQ(lzma_util.UnPack(extract_dir.value(), &unpacked_file),
+ ERROR_INVALID_HANDLE);
+ EXPECT_EQ(lzma_util.OpenArchive(archive.value()), NO_ERROR);
+ EXPECT_EQ(lzma_util.UnPack(extract_dir.value(), &unpacked_file),
+ ERROR_INVALID_HANDLE);
+
+ archive = data_dir_.AppendASCII("archive3.7z");
+ EXPECT_EQ(lzma_util.OpenArchive(archive.value()), NO_ERROR);
+ EXPECT_EQ(lzma_util.UnPack(extract_dir.value(), &unpacked_file),
+ NO_ERROR);
+ EXPECT_TRUE(file_util::PathExists(extract_dir.AppendASCII("archive\\a.exe")));
+ EXPECT_TRUE(file_util::PathExists(
+ extract_dir.AppendASCII("archive\\sub_dir\\text.txt")));
+}
+
+// Test the static method that can be used to unpack archives.
+TEST_F(LzmaUtilTest, UnPackArchiveTest) {
+ FilePath extract_dir(test_dir_);
+ extract_dir = extract_dir.AppendASCII("UnPackArchiveTest");
+ ASSERT_FALSE(file_util::PathExists(extract_dir));
+ EXPECT_TRUE(file_util::CreateDirectory(extract_dir));
+ ASSERT_TRUE(file_util::PathExists(extract_dir));
+
+ FilePath archive = data_dir_.AppendASCII("archive1.7z");
+ std::wstring unpacked_file;
+ EXPECT_EQ(LzmaUtil::UnPackArchive(archive.value(), extract_dir.value(),
+ &unpacked_file), NO_ERROR);
+ EXPECT_TRUE(file_util::PathExists(extract_dir.AppendASCII("a.exe")));
+ EXPECT_TRUE(unpacked_file == extract_dir.AppendASCII("a.exe").value());
+
+ archive = data_dir_.AppendASCII("archive2.7z");
+ EXPECT_EQ(LzmaUtil::UnPackArchive(archive.value(), extract_dir.value(),
+ &unpacked_file), NO_ERROR);
+ EXPECT_TRUE(file_util::PathExists(extract_dir.AppendASCII("b.exe")));
+ EXPECT_TRUE(unpacked_file == extract_dir.AppendASCII("b.exe").value());
+
+ archive = data_dir_.AppendASCII("invalid_archive.7z");
+ EXPECT_NE(LzmaUtil::UnPackArchive(archive.value(), extract_dir.value(),
+ &unpacked_file), NO_ERROR);
+} \ No newline at end of file
diff --git a/chrome/installer/util/run_all_unittests.cc b/chrome/installer/util/run_all_unittests.cc
index d9f6b58..d71e8ea 100644
--- a/chrome/installer/util/run_all_unittests.cc
+++ b/chrome/installer/util/run_all_unittests.cc
@@ -3,7 +3,13 @@
// found in the LICENSE file.
#include "base/test_suite.h"
+#include "chrome/common/chrome_paths.h"
int main(int argc, char** argv) {
- return TestSuite(argc, argv).Run();
+ TestSuite test_suite(argc, argv);
+
+ // Register Chrome Path provider so that we can get test data dir.
+ chrome::RegisterPathProvider();
+
+ return test_suite.Run();
}
diff --git a/chrome/test/data/installer/archive.diff b/chrome/test/data/installer/archive.diff
new file mode 100644
index 0000000..361781c
--- /dev/null
+++ b/chrome/test/data/installer/archive.diff
Binary files differ
diff --git a/chrome/test/data/installer/archive1.7z b/chrome/test/data/installer/archive1.7z
new file mode 100644
index 0000000..3130fd9
--- /dev/null
+++ b/chrome/test/data/installer/archive1.7z
Binary files differ
diff --git a/chrome/test/data/installer/archive2.7z b/chrome/test/data/installer/archive2.7z
new file mode 100644
index 0000000..bb3cee7
--- /dev/null
+++ b/chrome/test/data/installer/archive2.7z
Binary files differ
diff --git a/chrome/test/data/installer/archive3.7z b/chrome/test/data/installer/archive3.7z
new file mode 100644
index 0000000..ab1ea6e3
--- /dev/null
+++ b/chrome/test/data/installer/archive3.7z
Binary files differ
diff --git a/chrome/test/data/installer/invalid_archive.7z b/chrome/test/data/installer/invalid_archive.7z
new file mode 100644
index 0000000..3ab4480
--- /dev/null
+++ b/chrome/test/data/installer/invalid_archive.7z
@@ -0,0 +1 @@
+This is invalid archive for testing purposes.