diff options
author | kuchhal@chromium.org <kuchhal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-05 20:59:12 +0000 |
---|---|---|
committer | kuchhal@chromium.org <kuchhal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-05 20:59:12 +0000 |
commit | c256c0b0e7a2ca6401528b33b325cb19a981762b (patch) | |
tree | 4f21b432be4673c51f0dc6b6e1b3264c075504b4 | |
parent | ba15f7a45453eaf4f4c928d0c8a572b48d6f7541 (diff) | |
download | chromium_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.gyp | 5 | ||||
-rwxr-xr-x | chrome/installer/mini_installer.gyp | 4 | ||||
-rw-r--r-- | chrome/installer/setup/run_all_unittests.cc | 8 | ||||
-rw-r--r-- | chrome/installer/setup/setup_constants.cc | 2 | ||||
-rw-r--r-- | chrome/installer/setup/setup_constants.h | 2 | ||||
-rw-r--r-- | chrome/installer/setup/setup_main.cc | 176 | ||||
-rw-r--r-- | chrome/installer/setup/setup_util.cc | 21 | ||||
-rw-r--r-- | chrome/installer/setup/setup_util.h | 9 | ||||
-rw-r--r-- | chrome/installer/setup/setup_util_unittest.cc | 76 | ||||
-rw-r--r-- | chrome/installer/util/lzma_util.cc | 56 | ||||
-rw-r--r-- | chrome/installer/util/lzma_util.h | 27 | ||||
-rw-r--r-- | chrome/installer/util/lzma_util_unittest.cc | 132 | ||||
-rw-r--r-- | chrome/installer/util/run_all_unittests.cc | 8 | ||||
-rw-r--r-- | chrome/test/data/installer/archive.diff | bin | 0 -> 27136 bytes | |||
-rw-r--r-- | chrome/test/data/installer/archive1.7z | bin | 0 -> 27092 bytes | |||
-rw-r--r-- | chrome/test/data/installer/archive2.7z | bin | 0 -> 27092 bytes | |||
-rw-r--r-- | chrome/test/data/installer/archive3.7z | bin | 0 -> 27221 bytes | |||
-rw-r--r-- | chrome/test/data/installer/invalid_archive.7z | 1 |
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 Binary files differnew file mode 100644 index 0000000..361781c --- /dev/null +++ b/chrome/test/data/installer/archive.diff diff --git a/chrome/test/data/installer/archive1.7z b/chrome/test/data/installer/archive1.7z Binary files differnew file mode 100644 index 0000000..3130fd9 --- /dev/null +++ b/chrome/test/data/installer/archive1.7z diff --git a/chrome/test/data/installer/archive2.7z b/chrome/test/data/installer/archive2.7z Binary files differnew file mode 100644 index 0000000..bb3cee7 --- /dev/null +++ b/chrome/test/data/installer/archive2.7z diff --git a/chrome/test/data/installer/archive3.7z b/chrome/test/data/installer/archive3.7z Binary files differnew file mode 100644 index 0000000..ab1ea6e3 --- /dev/null +++ b/chrome/test/data/installer/archive3.7z 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. |