summaryrefslogtreecommitdiffstats
path: root/chrome/installer
diff options
context:
space:
mode:
authorkuchhal@chromium.org <kuchhal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-04 21:36:56 +0000
committerkuchhal@chromium.org <kuchhal@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-04 21:36:56 +0000
commitf65f7dadd39f0f06391fd289faccb07b7b4468fd (patch)
treef0d6357441fc22bf4c364edc81140e3844dceac1 /chrome/installer
parent32aa8cc65e23a45c5e20f7ff22aa98ca4935eb0a (diff)
downloadchromium_src-f65f7dadd39f0f06391fd289faccb07b7b4468fd.zip
chromium_src-f65f7dadd39f0f06391fd289faccb07b7b4468fd.tar.gz
chromium_src-f65f7dadd39f0f06391fd289faccb07b7b4468fd.tar.bz2
Move files instead of copying them.
BUG=1184319,8218 Review URL: http://codereview.chromium.org/39048 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10921 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer')
-rw-r--r--chrome/installer/setup/install.cc13
-rw-r--r--chrome/installer/util/installer_unittests.vcproj4
-rw-r--r--chrome/installer/util/move_tree_work_item.cc70
-rw-r--r--chrome/installer/util/move_tree_work_item.h62
-rw-r--r--chrome/installer/util/move_tree_work_item_unittest.cc387
-rw-r--r--chrome/installer/util/util.vcproj8
-rw-r--r--chrome/installer/util/work_item.cc7
-rw-r--r--chrome/installer/util/work_item.h6
-rw-r--r--chrome/installer/util/work_item_list.cc8
-rw-r--r--chrome/installer/util/work_item_list.h4
10 files changed, 562 insertions, 7 deletions
diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc
index 1c17b81..e58c56d 100644
--- a/chrome/installer/setup/install.cc
+++ b/chrome/installer/setup/install.cc
@@ -108,8 +108,7 @@ void AddInstallerCopyTasks(const std::wstring& exe_path,
install_list->AddCopyTreeWorkItem(exe_path, exe_dst, temp_path,
WorkItem::ALWAYS);
- install_list->AddCopyTreeWorkItem(archive_path, archive_dst, temp_path,
- WorkItem::ALWAYS);
+ install_list->AddMoveTreeWorkItem(archive_path, archive_dst, temp_path);
}
@@ -149,11 +148,11 @@ bool installer::InstallNewVersion(const std::wstring& exe_path,
install_list->AddCreateDirWorkItem(temp_dir);
install_list->AddCreateDirWorkItem(install_path);
- // Copy the version folder
- install_list->AddCopyTreeWorkItem(
+ // Move the version folder
+ install_list->AddMoveTreeWorkItem(
AppendPath(src_path, new_version.GetString()),
AppendPath(install_path, new_version.GetString()),
- temp_dir, WorkItem::ALWAYS); // Always overwrite.
+ temp_dir);
// Delete any new_chrome.exe if present (we will end up create a new one
// if required) and then copy chrome.exe
@@ -176,10 +175,10 @@ bool installer::InstallNewVersion(const std::wstring& exe_path,
// Extra executable for 64 bit systems.
if (Is64bit()) {
- install_list->AddCopyTreeWorkItem(
+ install_list->AddMoveTreeWorkItem(
AppendPath(src_path, installer::kWowHelperExe),
AppendPath(install_path, installer::kWowHelperExe),
- temp_dir, WorkItem::ALWAYS);
+ temp_dir);
}
// Copy the default Dictionaries only if the folder doesnt exist already
diff --git a/chrome/installer/util/installer_unittests.vcproj b/chrome/installer/util/installer_unittests.vcproj
index 62ac85a..8078508 100644
--- a/chrome/installer/util/installer_unittests.vcproj
+++ b/chrome/installer/util/installer_unittests.vcproj
@@ -184,6 +184,10 @@
>
</File>
<File
+ RelativePath="move_tree_work_item_unittest.cc"
+ >
+ </File>
+ <File
RelativePath="set_reg_value_work_item_unittest.cc"
>
</File>
diff --git a/chrome/installer/util/move_tree_work_item.cc b/chrome/installer/util/move_tree_work_item.cc
new file mode 100644
index 0000000..da35ec1
--- /dev/null
+++ b/chrome/installer/util/move_tree_work_item.cc
@@ -0,0 +1,70 @@
+// 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 "chrome/installer/util/move_tree_work_item.h"
+
+#include <shlwapi.h>
+#include "base/file_util.h"
+#include "chrome/installer/util/logging_installer.h"
+
+MoveTreeWorkItem::~MoveTreeWorkItem() {
+ if (file_util::PathExists(backup_path_)) {
+ file_util::Delete(backup_path_, true);
+ }
+}
+
+MoveTreeWorkItem::MoveTreeWorkItem(const std::wstring& source_path,
+ const std::wstring& dest_path,
+ const std::wstring& temp_dir)
+ : source_path_(source_path),
+ dest_path_(dest_path),
+ temp_dir_(temp_dir),
+ moved_to_dest_path_(false),
+ moved_to_backup_(false) {
+}
+
+bool MoveTreeWorkItem::Do() {
+ if (!file_util::PathExists(source_path_)) {
+ LOG(ERROR) << source_path_ << " does not exist";
+ return false;
+ }
+
+ // If dest_path_ exists, move destination to a backup path.
+ if (file_util::PathExists(dest_path_)) {
+ // Generate a backup path that can keep the original files under dest_path_.
+ if (!file_util::CreateTemporaryFileNameInDir(temp_dir_, &backup_path_)) {
+ LOG(ERROR) << "Failed to get backup path in folder " << temp_dir_;
+ return false;
+ }
+
+ if (file_util::Move(dest_path_, backup_path_)) {
+ moved_to_backup_ = true;
+ LOG(INFO) << "Moved destination " << dest_path_
+ << " to backup path " << backup_path_;
+ } else {
+ LOG(ERROR) << "failed moving " << dest_path_ << " to " << backup_path_;
+ return false;
+ }
+ }
+
+ // Now move source to destination.
+ if (file_util::Move(source_path_, dest_path_)) {
+ moved_to_dest_path_ = true;
+ LOG(INFO) << "Moved source " << source_path_
+ << " to destination " << dest_path_;
+ } else {
+ LOG(ERROR) << "failed move " << source_path_ << " to " << dest_path_;
+ return false;
+ }
+
+ return true;
+}
+
+void MoveTreeWorkItem::Rollback() {
+ if (moved_to_dest_path_ && !file_util::Move(dest_path_, source_path_))
+ LOG(ERROR) << "Can not move " << dest_path_ << " to " << source_path_;
+
+ if (moved_to_backup_ && !file_util::Move(backup_path_, dest_path_))
+ LOG(ERROR) << "failed move " << backup_path_ << " to " << dest_path_;
+}
diff --git a/chrome/installer/util/move_tree_work_item.h b/chrome/installer/util/move_tree_work_item.h
new file mode 100644
index 0000000..fd938d2
--- /dev/null
+++ b/chrome/installer/util/move_tree_work_item.h
@@ -0,0 +1,62 @@
+// 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_MOVE_TREE_WORK_ITEM_H_
+#define CHROME_INSTALLER_UTIL_MOVE_TREE_WORK_ITEM_H_
+
+#include <string>
+#include <windows.h>
+
+#include "chrome/installer/util/work_item.h"
+
+// A WorkItem subclass that recursively move a file system hierarchy from
+// source path to destination path. The file system hierarchy could be a
+// single file, or a directory.
+//
+// Under the cover MoveTreeWorkItem moves the destination path, if existing,
+// to the temporary directory passed in, and then moves the source hierarchy
+// to the destination location. During rollback the original destination
+// hierarchy is moved back.
+class MoveTreeWorkItem : public WorkItem {
+ public:
+ virtual ~MoveTreeWorkItem();
+
+ virtual bool Do();
+
+ virtual void Rollback();
+
+ private:
+ friend class WorkItem;
+
+ // source_path specifies file or directory that will be moved to location
+ // specified by dest_path. To facilitate rollback, the caller needs to supply
+ // a temporary directory (temp_dir) to save the original files if they exist
+ // under dest_path.
+ MoveTreeWorkItem(const std::wstring& source_path,
+ const std::wstring& dest_path,
+ const std::wstring& temp_dir);
+
+ // Source path to move files from.
+ std::wstring source_path_;
+
+ // Destination path to move files to.
+ std::wstring dest_path_;
+
+ // Temporary directory to backup dest_path_ (if it already exists).
+ std::wstring temp_dir_;
+
+ // The full path in temp_dir_ where the original dest_path_ has
+ // been moved to.
+ std::wstring backup_path_;
+
+ // Whether the source was moved to dest_path_
+ bool moved_to_dest_path_;
+
+ // Whether the original files have been moved to backup path under
+ // temporary directory. If true, moving back is needed during rollback.
+ bool moved_to_backup_;
+};
+
+#endif // CHROME_INSTALLER_UTIL_MOVE_TREE_WORK_ITEM_H_
+
diff --git a/chrome/installer/util/move_tree_work_item_unittest.cc b/chrome/installer/util/move_tree_work_item_unittest.cc
new file mode 100644
index 0000000..642b2a2
--- /dev/null
+++ b/chrome/installer/util/move_tree_work_item_unittest.cc
@@ -0,0 +1,387 @@
+// 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 <fstream>
+#include <iostream>
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/installer/util/work_item.h"
+#include "chrome/installer/util/move_tree_work_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class MoveTreeWorkItemTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Name a subdirectory of the user temp directory.
+ ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
+ file_util::AppendToPath(&test_dir_, L"MoveTreeWorkItemTest");
+
+ // Create a fresh, empty copy of this test directory.
+ file_util::Delete(test_dir_, true);
+ CreateDirectory(test_dir_.c_str(), NULL);
+
+ // Create a tempory directory under the test directory.
+ temp_dir_.assign(test_dir_);
+ file_util::AppendToPath(&temp_dir_, L"temp");
+ CreateDirectory(temp_dir_.c_str(), NULL);
+
+ ASSERT_TRUE(file_util::PathExists(test_dir_));
+ ASSERT_TRUE(file_util::PathExists(temp_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
+ std::wstring test_dir_;
+ std::wstring temp_dir_;
+ };
+
+ // Simple function to dump some text into a new file.
+ void CreateTextFile(const std::wstring& filename,
+ const std::wstring& contents) {
+ std::ofstream file;
+ file.open(filename.c_str());
+ ASSERT_TRUE(file.is_open());
+ file << contents;
+ file.close();
+ }
+
+ // Simple function to read text from a file.
+ std::wstring ReadTextFile(const std::wstring& filename) {
+ WCHAR contents[64];
+ std::wifstream file;
+ file.open(filename.c_str());
+ EXPECT_TRUE(file.is_open());
+ file.getline(contents, 64);
+ file.close();
+ return std::wstring(contents);
+ }
+
+ wchar_t text_content_1[] = L"Gooooooooooooooooooooogle";
+ wchar_t text_content_2[] = L"Overwrite Me";
+};
+
+// Move one directory from source to destination when destination does not
+// exist.
+TEST_F(MoveTreeWorkItemTest, MoveDirectory) {
+ // Create two level deep source dir
+ std::wstring from_dir1(test_dir_);
+ file_util::AppendToPath(&from_dir1, L"From_Dir1");
+ CreateDirectory(from_dir1.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(from_dir1));
+
+ std::wstring from_dir2(from_dir1);
+ file_util::AppendToPath(&from_dir2, L"From_Dir2");
+ CreateDirectory(from_dir2.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(from_dir2));
+
+ std::wstring from_file(from_dir2);
+ file_util::AppendToPath(&from_file, L"From_File");
+ CreateTextFile(from_file, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(from_file));
+
+ // Generate destination path
+ std::wstring to_dir(test_dir_);
+ file_util::AppendToPath(&to_dir, L"To_Dir");
+ ASSERT_FALSE(file_util::PathExists(to_dir));
+
+ std::wstring to_file(to_dir);
+ file_util::AppendToPath(&to_file, L"From_Dir2");
+ file_util::AppendToPath(&to_file, L"From_File");
+ ASSERT_FALSE(file_util::PathExists(to_file));
+
+ // test Do()
+ scoped_ptr<MoveTreeWorkItem> work_item(WorkItem::CreateMoveTreeWorkItem(
+ from_dir1, to_dir, temp_dir_));
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_FALSE(file_util::PathExists(from_dir1));
+ EXPECT_TRUE(file_util::PathExists(to_dir));
+ EXPECT_TRUE(file_util::PathExists(to_file));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(from_dir1));
+ EXPECT_TRUE(file_util::PathExists(from_file));
+ EXPECT_FALSE(file_util::PathExists(to_dir));
+}
+
+// Move one directory from source to destination when destination already
+// exists.
+TEST_F(MoveTreeWorkItemTest, MoveDirectoryDestExists) {
+ // Create two level deep source dir
+ std::wstring from_dir1(test_dir_);
+ file_util::AppendToPath(&from_dir1, L"From_Dir1");
+ CreateDirectory(from_dir1.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(from_dir1));
+
+ std::wstring from_dir2(from_dir1);
+ file_util::AppendToPath(&from_dir2, L"From_Dir2");
+ CreateDirectory(from_dir2.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(from_dir2));
+
+ std::wstring from_file(from_dir2);
+ file_util::AppendToPath(&from_file, L"From_File");
+ CreateTextFile(from_file, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(from_file));
+
+ // Create destination path
+ std::wstring to_dir(test_dir_);
+ file_util::AppendToPath(&to_dir, L"To_Dir");
+ CreateDirectory(to_dir.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(to_dir));
+
+ std::wstring orig_to_file(to_dir);
+ file_util::AppendToPath(&orig_to_file, L"To_File");
+ CreateTextFile(orig_to_file, text_content_2);
+ ASSERT_TRUE(file_util::PathExists(orig_to_file));
+
+ std::wstring new_to_file(to_dir);
+ file_util::AppendToPath(&new_to_file, L"From_Dir2");
+ file_util::AppendToPath(&new_to_file, L"From_File");
+ ASSERT_FALSE(file_util::PathExists(new_to_file));
+
+ // test Do()
+ scoped_ptr<MoveTreeWorkItem> work_item(WorkItem::CreateMoveTreeWorkItem(
+ from_dir1, to_dir, temp_dir_));
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_FALSE(file_util::PathExists(from_dir1));
+ EXPECT_TRUE(file_util::PathExists(to_dir));
+ EXPECT_TRUE(file_util::PathExists(new_to_file));
+ EXPECT_FALSE(file_util::PathExists(orig_to_file));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(from_dir1));
+ EXPECT_TRUE(file_util::PathExists(to_dir));
+ EXPECT_FALSE(file_util::PathExists(new_to_file));
+ EXPECT_TRUE(file_util::PathExists(orig_to_file));
+ EXPECT_EQ(0, ReadTextFile(orig_to_file).compare(text_content_2));
+ EXPECT_EQ(0, ReadTextFile(from_file).compare(text_content_1));
+}
+
+// Move one file from source to destination when destination does not
+// exist.
+TEST_F(MoveTreeWorkItemTest, MoveAFile) {
+ // Create a file inside source dir
+ std::wstring from_dir(test_dir_);
+ file_util::AppendToPath(&from_dir, L"From_Dir");
+ CreateDirectory(from_dir.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(from_dir));
+
+ std::wstring from_file(from_dir);
+ file_util::AppendToPath(&from_file, L"From_File");
+ CreateTextFile(from_file, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(from_file));
+
+ // Generate destination file name
+ std::wstring to_file(test_dir_);
+ file_util::AppendToPath(&to_file, L"To_File");
+ ASSERT_FALSE(file_util::PathExists(to_file));
+
+ // test Do()
+ scoped_ptr<MoveTreeWorkItem> work_item(WorkItem::CreateMoveTreeWorkItem(
+ from_file, to_file, temp_dir_));
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(from_dir));
+ EXPECT_FALSE(file_util::PathExists(from_file));
+ EXPECT_TRUE(file_util::PathExists(to_file));
+ EXPECT_EQ(0, ReadTextFile(to_file).compare(text_content_1));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(from_dir));
+ EXPECT_TRUE(file_util::PathExists(from_file));
+ EXPECT_FALSE(file_util::PathExists(to_file));
+ EXPECT_EQ(0, ReadTextFile(from_file).compare(text_content_1));
+}
+
+// Move one file from source to destination when destination already
+// exists.
+TEST_F(MoveTreeWorkItemTest, MoveFileDestExists) {
+ // Create a file inside source dir
+ std::wstring from_dir(test_dir_);
+ file_util::AppendToPath(&from_dir, L"From_Dir");
+ CreateDirectory(from_dir.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(from_dir));
+
+ std::wstring from_file(from_dir);
+ file_util::AppendToPath(&from_file, L"From_File");
+ CreateTextFile(from_file, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(from_file));
+
+ // Create destination path
+ std::wstring to_dir(test_dir_);
+ file_util::AppendToPath(&to_dir, L"To_Dir");
+ CreateDirectory(to_dir.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(to_dir));
+
+ std::wstring to_file(to_dir);
+ file_util::AppendToPath(&to_file, L"To_File");
+ CreateTextFile(to_file, text_content_2);
+ ASSERT_TRUE(file_util::PathExists(to_file));
+
+ // test Do()
+ scoped_ptr<MoveTreeWorkItem> work_item(WorkItem::CreateMoveTreeWorkItem(
+ from_file, to_dir, temp_dir_));
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(from_dir));
+ EXPECT_FALSE(file_util::PathExists(from_file));
+ EXPECT_TRUE(file_util::PathExists(to_dir));
+ EXPECT_FALSE(file_util::PathExists(to_file));
+ EXPECT_EQ(0, ReadTextFile(to_dir).compare(text_content_1));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(from_dir));
+ EXPECT_EQ(0, ReadTextFile(from_file).compare(text_content_1));
+ EXPECT_TRUE(file_util::PathExists(to_dir));
+ EXPECT_EQ(0, ReadTextFile(to_file).compare(text_content_2));
+}
+
+// Move one file from source to destination when destination already
+// exists and is in use.
+TEST_F(MoveTreeWorkItemTest, MoveFileDestInUse) {
+ // Create a file inside source dir
+ std::wstring from_dir(test_dir_);
+ file_util::AppendToPath(&from_dir, L"From_Dir");
+ CreateDirectory(from_dir.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(from_dir));
+
+ std::wstring from_file(from_dir);
+ file_util::AppendToPath(&from_file, L"From_File");
+ CreateTextFile(from_file, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(from_file));
+
+ // Create an executable in destination path by copying ourself to it.
+ std::wstring to_dir(test_dir_);
+ file_util::AppendToPath(&to_dir, L"To_Dir");
+ CreateDirectory(to_dir.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(to_dir));
+
+ wchar_t exe_full_path_str[MAX_PATH];
+ ::GetModuleFileName(NULL, exe_full_path_str, MAX_PATH);
+ std::wstring exe_full_path(exe_full_path_str);
+ std::wstring to_file(to_dir);
+ file_util::AppendToPath(&to_file, L"To_File");
+ file_util::CopyFile(exe_full_path, to_file);
+ ASSERT_TRUE(file_util::PathExists(to_file));
+
+ // Run the executable in destination path
+ STARTUPINFOW si = {sizeof(si)};
+ PROCESS_INFORMATION pi = {0};
+ ASSERT_TRUE(::CreateProcess(NULL, const_cast<wchar_t*>(to_file.c_str()),
+ NULL, NULL, FALSE,
+ CREATE_NO_WINDOW | CREATE_SUSPENDED,
+ NULL, NULL, &si, &pi));
+
+ // test Do()
+ scoped_ptr<MoveTreeWorkItem> work_item(WorkItem::CreateMoveTreeWorkItem(
+ from_file, to_file, temp_dir_));
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(from_dir));
+ EXPECT_FALSE(file_util::PathExists(from_file));
+ EXPECT_TRUE(file_util::PathExists(to_dir));
+ EXPECT_EQ(0, ReadTextFile(to_file).compare(text_content_1));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(from_dir));
+ EXPECT_EQ(0, ReadTextFile(from_file).compare(text_content_1));
+ EXPECT_TRUE(file_util::PathExists(to_dir));
+ EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, to_file));
+
+ TerminateProcess(pi.hProcess, 0);
+ EXPECT_TRUE(WaitForSingleObject(pi.hProcess, 10000) == WAIT_OBJECT_0);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+}
+
+// Move one file that is in use to destination.
+TEST_F(MoveTreeWorkItemTest, MoveFileInUse) {
+ // Create an executable for source by copying ourself to a new source dir.
+ std::wstring from_dir(test_dir_);
+ file_util::AppendToPath(&from_dir, L"From_Dir");
+ CreateDirectory(from_dir.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(from_dir));
+
+ wchar_t exe_full_path_str[MAX_PATH];
+ ::GetModuleFileName(NULL, exe_full_path_str, MAX_PATH);
+ std::wstring exe_full_path(exe_full_path_str);
+ std::wstring from_file(from_dir);
+ file_util::AppendToPath(&from_file, L"From_File");
+ file_util::CopyFile(exe_full_path, from_file);
+ ASSERT_TRUE(file_util::PathExists(from_file));
+
+ // Create a destination source dir and generate destination file name.
+ std::wstring to_dir(test_dir_);
+ file_util::AppendToPath(&to_dir, L"To_Dir");
+ CreateDirectory(to_dir.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(to_dir));
+
+ std::wstring to_file(to_dir);
+ file_util::AppendToPath(&to_file, L"To_File");
+ CreateTextFile(to_file, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(to_file));
+
+ // Run the executable in source path
+ STARTUPINFOW si = {sizeof(si)};
+ PROCESS_INFORMATION pi = {0};
+ ASSERT_TRUE(::CreateProcess(NULL, const_cast<wchar_t*>(from_file.c_str()),
+ NULL, NULL, FALSE,
+ CREATE_NO_WINDOW | CREATE_SUSPENDED,
+ NULL, NULL, &si, &pi));
+
+ // test Do()
+ scoped_ptr<MoveTreeWorkItem> work_item(WorkItem::CreateMoveTreeWorkItem(
+ from_file, to_file, temp_dir_));
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(from_dir));
+ EXPECT_FALSE(file_util::PathExists(from_file));
+ EXPECT_TRUE(file_util::PathExists(to_dir));
+ EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, to_file));
+
+ // Close the process and make sure all the conditions after Do() are
+ // still true.
+ TerminateProcess(pi.hProcess, 0);
+ EXPECT_TRUE(WaitForSingleObject(pi.hProcess, 10000) == WAIT_OBJECT_0);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ EXPECT_TRUE(file_util::PathExists(from_dir));
+ EXPECT_FALSE(file_util::PathExists(from_file));
+ EXPECT_TRUE(file_util::PathExists(to_dir));
+ EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, to_file));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(from_dir));
+ EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, from_file));
+ EXPECT_TRUE(file_util::PathExists(to_dir));
+ EXPECT_EQ(0, ReadTextFile(to_file).compare(text_content_1));
+}
diff --git a/chrome/installer/util/util.vcproj b/chrome/installer/util/util.vcproj
index 99c7fe3..59f72da 100644
--- a/chrome/installer/util/util.vcproj
+++ b/chrome/installer/util/util.vcproj
@@ -265,6 +265,14 @@
>
</File>
<File
+ RelativePath=".\move_tree_work_item.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\move_tree_work_item.h"
+ >
+ </File>
+ <File
RelativePath=".\set_reg_value_work_item.cc"
>
</File>
diff --git a/chrome/installer/util/work_item.cc b/chrome/installer/util/work_item.cc
index 7905405..568a79d 100644
--- a/chrome/installer/util/work_item.cc
+++ b/chrome/installer/util/work_item.cc
@@ -9,6 +9,7 @@
#include "chrome/installer/util/create_reg_key_work_item.h"
#include "chrome/installer/util/delete_tree_work_item.h"
#include "chrome/installer/util/delete_reg_value_work_item.h"
+#include "chrome/installer/util/move_tree_work_item.h"
#include "chrome/installer/util/set_reg_value_work_item.h"
#include "chrome/installer/util/work_item_list.h"
@@ -46,6 +47,12 @@ DeleteTreeWorkItem* WorkItem::CreateDeleteTreeWorkItem(std::wstring root_path,
return new DeleteTreeWorkItem(root_path, key_path);
}
+MoveTreeWorkItem* WorkItem::CreateMoveTreeWorkItem(std::wstring source_path,
+ std::wstring dest_path,
+ std::wstring temp_dir) {
+ return new MoveTreeWorkItem(source_path, dest_path, temp_dir);
+}
+
SetRegValueWorkItem* WorkItem::CreateSetRegValueWorkItem(
HKEY predefined_root, std::wstring key_path,
std::wstring value_name, std::wstring value_data, bool overwrite) {
diff --git a/chrome/installer/util/work_item.h b/chrome/installer/util/work_item.h
index 08942a1..f23e181 100644
--- a/chrome/installer/util/work_item.h
+++ b/chrome/installer/util/work_item.h
@@ -17,6 +17,7 @@ class CreateDirWorkItem;
class CreateRegKeyWorkItem;
class DeleteTreeWorkItem;
class DeleteRegValueWorkItem;
+class MoveTreeWorkItem;
class SetRegValueWorkItem;
class WorkItemList;
@@ -65,6 +66,11 @@ class WorkItem {
static DeleteTreeWorkItem* CreateDeleteTreeWorkItem(std::wstring root_path,
std::wstring key_path);
+ // Create a MoveTreeWorkItem that recursively moves a file system hierarchy
+ // from source path to destination path.
+ static MoveTreeWorkItem* CreateMoveTreeWorkItem(std::wstring source_path,
+ std::wstring dest_path, std::wstring temp_dir);
+
// Create a SetRegValueWorkItem that sets a registry value with REG_SZ type
// at the key with specified path.
static SetRegValueWorkItem* CreateSetRegValueWorkItem(
diff --git a/chrome/installer/util/work_item_list.cc b/chrome/installer/util/work_item_list.cc
index ef0a9c4..0115e81 100644
--- a/chrome/installer/util/work_item_list.cc
+++ b/chrome/installer/util/work_item_list.cc
@@ -104,6 +104,14 @@ bool WorkItemList::AddDeleteTreeWorkItem(std::wstring root_path,
return AddWorkItem(item);
}
+bool WorkItemList::AddMoveTreeWorkItem(std::wstring source_path,
+ std::wstring dest_path,
+ std::wstring temp_dir) {
+ WorkItem* item = reinterpret_cast<WorkItem*>(
+ WorkItem::CreateMoveTreeWorkItem(source_path, dest_path, temp_dir));
+ return AddWorkItem(item);
+}
+
bool WorkItemList::AddSetRegValueWorkItem(HKEY predefined_root,
std::wstring key_path,
std::wstring value_name,
diff --git a/chrome/installer/util/work_item_list.h b/chrome/installer/util/work_item_list.h
index 18cb186..c59272f 100644
--- a/chrome/installer/util/work_item_list.h
+++ b/chrome/installer/util/work_item_list.h
@@ -54,6 +54,10 @@ class WorkItemList : public WorkItem {
// by key_path.
bool AddDeleteTreeWorkItem(std::wstring root_path, std::wstring key_path);
+ // Add a MoveTreeWorkItem to the list of work items.
+ bool AddMoveTreeWorkItem(std::wstring source_path, std::wstring dest_path,
+ std::wstring temp_dir);
+
// Add a SetRegValueWorkItem that sets a registry value with REG_SZ type
// at the key with specified path.
bool AddSetRegValueWorkItem(HKEY predefined_root, std::wstring key_path,