Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/installer/util/delete_tree_work_item.h" | 5 #include "chrome/installer/util/delete_tree_work_item.h" |
| 6 | 6 |
| 7 #include <windows.h> | |
| 8 | |
| 9 #include <fstream> | |
| 10 #include <memory> | 7 #include <memory> |
| 11 | 8 |
| 12 #include "base/base_paths.h" | 9 #include "base/files/file.h" |
| 10 #include "base/files/file_path.h" | |
| 13 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
| 14 #include "base/files/scoped_temp_dir.h" | 12 #include "base/files/scoped_temp_dir.h" |
| 15 #include "base/logging.h" | 13 #include "base/macros.h" |
| 16 #include "base/strings/string_util.h" | |
| 17 #include "chrome/installer/util/work_item.h" | 14 #include "chrome/installer/util/work_item.h" |
| 18 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
| 19 | 16 |
| 20 namespace { | 17 namespace { |
| 21 | 18 |
| 19 const char kTextContent[] = "delete me"; | |
|
grt (UTC plus 2)
2016/05/06 18:39:13
nit: constexpr
fdoray
2016/05/10 18:35:28
Done.
| |
| 20 | |
| 21 class ScopedFileInUse { | |
| 22 public: | |
| 23 explicit ScopedFileInUse(const base::FilePath path) | |
|
grt (UTC plus 2)
2016/05/06 18:39:13
FilePath&
fdoray
2016/05/10 18:35:29
n/a with new code
| |
| 24 : file_in_use_(path, base::File::FLAG_OPEN | base::File::FLAG_READ) { | |
| 25 EXPECT_TRUE(file_in_use_.IsValid()); | |
| 26 } | |
| 27 | |
| 28 private: | |
| 29 const base::File file_in_use_; | |
| 30 | |
| 31 DISALLOW_COPY_AND_ASSIGN(ScopedFileInUse); | |
| 32 }; | |
| 33 | |
| 22 class DeleteTreeWorkItemTest : public testing::Test { | 34 class DeleteTreeWorkItemTest : public testing::Test { |
| 23 protected: | 35 protected: |
| 24 void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } | 36 DeleteTreeWorkItemTest() = default; |
| 37 | |
| 38 void SetUp() override { | |
| 39 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | |
| 40 | |
| 41 dir_name_ = temp_dir_.path().AppendASCII("to_be_deleted"); | |
|
grt (UTC plus 2)
2016/05/06 18:39:13
nit: slightly better to use Append(FILE_PATH_LITER
fdoray
2016/05/10 18:35:28
Done.
| |
| 42 ASSERT_TRUE(base::CreateDirectory(dir_name_)); | |
| 43 ASSERT_TRUE(base::PathExists(dir_name_)); | |
| 44 | |
| 45 dir_name_1_ = dir_name_.AppendASCII("1"); | |
| 46 ASSERT_TRUE(base::CreateDirectory(dir_name_1_)); | |
| 47 ASSERT_TRUE(base::PathExists(dir_name_1_)); | |
| 48 | |
| 49 dir_name_2_ = dir_name_.AppendASCII("2"); | |
| 50 ASSERT_TRUE(base::CreateDirectory(dir_name_2_)); | |
| 51 ASSERT_TRUE(base::PathExists(dir_name_2_)); | |
| 52 | |
| 53 file_name_1_ = dir_name_1_.AppendASCII("File_1.txt"); | |
| 54 ASSERT_TRUE( | |
| 55 base::WriteFile(file_name_1_, kTextContent, sizeof(kTextContent))); | |
| 56 ASSERT_TRUE(base::PathExists(file_name_1_)); | |
| 57 | |
| 58 file_name_2_ = dir_name_2_.AppendASCII("File_2.txt"); | |
| 59 ASSERT_TRUE( | |
| 60 base::WriteFile(file_name_2_, kTextContent, sizeof(kTextContent))); | |
| 61 ASSERT_TRUE(base::PathExists(file_name_2_)); | |
| 62 } | |
| 63 | |
| 64 void ExpectAllFilesExist() { | |
| 65 EXPECT_TRUE(base::PathExists(dir_name_)); | |
| 66 EXPECT_TRUE(base::PathExists(dir_name_1_)); | |
| 67 EXPECT_TRUE(base::PathExists(dir_name_2_)); | |
| 68 EXPECT_TRUE(base::PathExists(file_name_1_)); | |
| 69 EXPECT_TRUE(base::PathExists(file_name_2_)); | |
| 70 } | |
| 71 | |
| 72 void ExpectAllFilesDeleted() { | |
| 73 EXPECT_FALSE(base::PathExists(dir_name_)); | |
| 74 EXPECT_FALSE(base::PathExists(dir_name_1_)); | |
| 75 EXPECT_FALSE(base::PathExists(dir_name_2_)); | |
| 76 EXPECT_FALSE(base::PathExists(file_name_1_)); | |
| 77 EXPECT_FALSE(base::PathExists(file_name_2_)); | |
| 78 } | |
| 25 | 79 |
| 26 // The temporary directory used to contain the test operations. | 80 // The temporary directory used to contain the test operations. |
| 27 base::ScopedTempDir temp_dir_; | 81 base::ScopedTempDir temp_dir_; |
| 82 | |
| 83 base::FilePath dir_name_; | |
| 84 base::FilePath dir_name_1_; | |
| 85 base::FilePath dir_name_2_; | |
| 86 base::FilePath file_name_1_; | |
| 87 base::FilePath file_name_2_; | |
| 88 | |
| 89 DISALLOW_COPY_AND_ASSIGN(DeleteTreeWorkItemTest); | |
| 28 }; | 90 }; |
| 29 | 91 |
| 30 // Simple function to dump some text into a new file. | |
| 31 void CreateTextFile(const std::wstring& filename, | |
| 32 const std::wstring& contents) { | |
| 33 std::ofstream file; | |
| 34 file.open(filename.c_str()); | |
| 35 ASSERT_TRUE(file.is_open()); | |
| 36 file << contents; | |
| 37 file.close(); | |
| 38 } | |
| 39 | |
| 40 const wchar_t text_content_1[] = L"delete me"; | |
| 41 | |
| 42 } // namespace | 92 } // namespace |
| 43 | 93 |
| 44 // Delete a tree without key path. Everything should be deleted. | 94 // Delete a tree without key path. Do() should delete everything and Rollback() |
| 45 TEST_F(DeleteTreeWorkItemTest, DeleteTreeNoKeyPath) { | 95 // should bring back everything. |
| 46 // Create tree to be deleted. | 96 TEST_F(DeleteTreeWorkItemTest, NoKeyPath) { |
| 47 base::FilePath dir_name_delete(temp_dir_.path()); | |
| 48 dir_name_delete = dir_name_delete.AppendASCII("to_be_delete"); | |
| 49 base::CreateDirectory(dir_name_delete); | |
| 50 ASSERT_TRUE(base::PathExists(dir_name_delete)); | |
| 51 | |
| 52 base::FilePath dir_name_delete_1(dir_name_delete); | |
| 53 dir_name_delete_1 = dir_name_delete_1.AppendASCII("1"); | |
| 54 base::CreateDirectory(dir_name_delete_1); | |
| 55 ASSERT_TRUE(base::PathExists(dir_name_delete_1)); | |
| 56 | |
| 57 base::FilePath dir_name_delete_2(dir_name_delete); | |
| 58 dir_name_delete_2 = dir_name_delete_2.AppendASCII("2"); | |
| 59 base::CreateDirectory(dir_name_delete_2); | |
| 60 ASSERT_TRUE(base::PathExists(dir_name_delete_2)); | |
| 61 | |
| 62 base::FilePath file_name_delete_1(dir_name_delete_1); | |
| 63 file_name_delete_1 = file_name_delete_1.AppendASCII("File_1.txt"); | |
| 64 CreateTextFile(file_name_delete_1.value(), text_content_1); | |
| 65 ASSERT_TRUE(base::PathExists(file_name_delete_1)); | |
| 66 | |
| 67 base::FilePath file_name_delete_2(dir_name_delete_2); | |
| 68 file_name_delete_2 = file_name_delete_2.AppendASCII("File_2.txt"); | |
| 69 CreateTextFile(file_name_delete_2.value(), text_content_1); | |
| 70 ASSERT_TRUE(base::PathExists(file_name_delete_2)); | |
| 71 | |
| 72 // Test Do(). | |
| 73 base::ScopedTempDir temp_dir; | 97 base::ScopedTempDir temp_dir; |
| 74 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 98 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 75 | 99 |
| 76 std::vector<base::FilePath> key_files; | 100 std::vector<base::FilePath> key_paths; |
| 77 std::unique_ptr<DeleteTreeWorkItem> work_item( | 101 std::unique_ptr<DeleteTreeWorkItem> work_item( |
| 78 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), | 102 WorkItem::CreateDeleteTreeWorkItem(dir_name_, temp_dir.path(), |
| 79 key_files)); | 103 key_paths)); |
| 104 | |
| 80 EXPECT_TRUE(work_item->Do()); | 105 EXPECT_TRUE(work_item->Do()); |
| 81 | 106 ExpectAllFilesDeleted(); |
| 82 // everything should be gone | |
| 83 EXPECT_FALSE(base::PathExists(file_name_delete_1)); | |
| 84 EXPECT_FALSE(base::PathExists(file_name_delete_2)); | |
| 85 EXPECT_FALSE(base::PathExists(dir_name_delete)); | |
| 86 | 107 |
| 87 work_item->Rollback(); | 108 work_item->Rollback(); |
| 88 // everything should come back | 109 ExpectAllFilesExist(); |
| 89 EXPECT_TRUE(base::PathExists(file_name_delete_1)); | |
| 90 EXPECT_TRUE(base::PathExists(file_name_delete_2)); | |
| 91 EXPECT_TRUE(base::PathExists(dir_name_delete)); | |
| 92 } | 110 } |
| 93 | 111 |
| 94 | 112 // Delete a tree without key path and with rollback disabled. Do() should delete |
| 95 // Delete a tree with keypath but not in use. Everything should be gone. | 113 // everything and Rollback() shouldn't bring back anything. |
| 96 // Rollback should bring back everything | 114 TEST_F(DeleteTreeWorkItemTest, NoKeyPathRollbackDisabled) { |
| 97 TEST_F(DeleteTreeWorkItemTest, DeleteTree) { | |
| 98 // Create tree to be deleted | |
| 99 base::FilePath dir_name_delete(temp_dir_.path()); | |
| 100 dir_name_delete = dir_name_delete.AppendASCII("to_be_delete"); | |
| 101 base::CreateDirectory(dir_name_delete); | |
| 102 ASSERT_TRUE(base::PathExists(dir_name_delete)); | |
| 103 | |
| 104 base::FilePath dir_name_delete_1(dir_name_delete); | |
| 105 dir_name_delete_1 = dir_name_delete_1.AppendASCII("1"); | |
| 106 base::CreateDirectory(dir_name_delete_1); | |
| 107 ASSERT_TRUE(base::PathExists(dir_name_delete_1)); | |
| 108 | |
| 109 base::FilePath dir_name_delete_2(dir_name_delete); | |
| 110 dir_name_delete_2 = dir_name_delete_2.AppendASCII("2"); | |
| 111 base::CreateDirectory(dir_name_delete_2); | |
| 112 ASSERT_TRUE(base::PathExists(dir_name_delete_2)); | |
| 113 | |
| 114 base::FilePath file_name_delete_1(dir_name_delete_1); | |
| 115 file_name_delete_1 = file_name_delete_1.AppendASCII("File_1.txt"); | |
| 116 CreateTextFile(file_name_delete_1.value(), text_content_1); | |
| 117 ASSERT_TRUE(base::PathExists(file_name_delete_1)); | |
| 118 | |
| 119 base::FilePath file_name_delete_2(dir_name_delete_2); | |
| 120 file_name_delete_2 = file_name_delete_2.AppendASCII("File_2.txt"); | |
| 121 CreateTextFile(file_name_delete_2.value(), text_content_1); | |
| 122 ASSERT_TRUE(base::PathExists(file_name_delete_2)); | |
| 123 | |
| 124 // test Do() | |
| 125 base::ScopedTempDir temp_dir; | 115 base::ScopedTempDir temp_dir; |
| 126 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 116 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 127 | 117 |
| 128 std::vector<base::FilePath> key_files(1, file_name_delete_1); | 118 std::vector<base::FilePath> key_paths; |
| 129 std::unique_ptr<DeleteTreeWorkItem> work_item( | 119 std::unique_ptr<DeleteTreeWorkItem> work_item( |
| 130 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), | 120 WorkItem::CreateDeleteTreeWorkItem(dir_name_, temp_dir.path(), |
| 131 key_files)); | 121 key_paths)); |
| 122 work_item->set_rollback_enabled(false); | |
| 123 | |
| 132 EXPECT_TRUE(work_item->Do()); | 124 EXPECT_TRUE(work_item->Do()); |
| 133 | 125 ExpectAllFilesDeleted(); |
| 134 // everything should be gone | |
| 135 EXPECT_FALSE(base::PathExists(file_name_delete_1)); | |
| 136 EXPECT_FALSE(base::PathExists(file_name_delete_2)); | |
| 137 EXPECT_FALSE(base::PathExists(dir_name_delete)); | |
| 138 | 126 |
| 139 work_item->Rollback(); | 127 work_item->Rollback(); |
| 140 // everything should come back | 128 ExpectAllFilesDeleted(); |
| 141 EXPECT_TRUE(base::PathExists(file_name_delete_1)); | |
| 142 EXPECT_TRUE(base::PathExists(file_name_delete_2)); | |
| 143 EXPECT_TRUE(base::PathExists(dir_name_delete)); | |
| 144 } | 129 } |
| 145 | 130 |
| 146 // Delete a tree with key_path in use. Everything should still be there. | 131 // Delete a tree with key path not in use. Do() should delete everything and |
| 147 TEST_F(DeleteTreeWorkItemTest, DeleteTreeInUse) { | 132 // Rollback() should bring back everything. |
| 148 // Create tree to be deleted | 133 TEST_F(DeleteTreeWorkItemTest, KeyPathNotInUse) { |
| 149 base::FilePath dir_name_delete(temp_dir_.path()); | 134 base::ScopedTempDir temp_dir; |
| 150 dir_name_delete = dir_name_delete.AppendASCII("to_be_delete"); | 135 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 151 base::CreateDirectory(dir_name_delete); | |
| 152 ASSERT_TRUE(base::PathExists(dir_name_delete)); | |
| 153 | 136 |
| 154 base::FilePath dir_name_delete_1(dir_name_delete); | 137 std::vector<base::FilePath> key_paths(1, file_name_1_); |
| 155 dir_name_delete_1 = dir_name_delete_1.AppendASCII("1"); | 138 std::unique_ptr<DeleteTreeWorkItem> work_item( |
| 156 base::CreateDirectory(dir_name_delete_1); | 139 WorkItem::CreateDeleteTreeWorkItem(dir_name_, temp_dir.path(), |
| 157 ASSERT_TRUE(base::PathExists(dir_name_delete_1)); | 140 key_paths)); |
| 158 | 141 |
| 159 base::FilePath dir_name_delete_2(dir_name_delete); | 142 EXPECT_TRUE(work_item->Do()); |
| 160 dir_name_delete_2 = dir_name_delete_2.AppendASCII("2"); | 143 ExpectAllFilesDeleted(); |
| 161 base::CreateDirectory(dir_name_delete_2); | |
| 162 ASSERT_TRUE(base::PathExists(dir_name_delete_2)); | |
| 163 | 144 |
| 164 base::FilePath file_name_delete_1(dir_name_delete_1); | 145 work_item->Rollback(); |
| 165 file_name_delete_1 = file_name_delete_1.AppendASCII("File_1.txt"); | 146 ExpectAllFilesExist(); |
| 166 CreateTextFile(file_name_delete_1.value(), text_content_1); | 147 } |
| 167 ASSERT_TRUE(base::PathExists(file_name_delete_1)); | |
| 168 | 148 |
| 169 base::FilePath file_name_delete_2(dir_name_delete_2); | 149 // Delete a tree with key path not in use and rollback disabled. Do() should |
| 170 file_name_delete_2 = file_name_delete_2.AppendASCII("File_2.txt"); | 150 // delete everything and Rollback() shouldn't bring back anything. |
| 171 CreateTextFile(file_name_delete_2.value(), text_content_1); | 151 TEST_F(DeleteTreeWorkItemTest, KeyPathNotInUseRollbackDisabled) { |
| 172 ASSERT_TRUE(base::PathExists(file_name_delete_2)); | 152 base::ScopedTempDir temp_dir; |
| 153 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
| 173 | 154 |
| 174 // Create a key path file. | 155 std::vector<base::FilePath> key_paths(1, file_name_1_); |
| 175 base::FilePath key_path(dir_name_delete); | 156 std::unique_ptr<DeleteTreeWorkItem> work_item( |
| 176 key_path = key_path.AppendASCII("key_file.exe"); | 157 WorkItem::CreateDeleteTreeWorkItem(dir_name_, temp_dir.path(), |
| 158 key_paths)); | |
| 159 work_item->set_rollback_enabled(false); | |
| 177 | 160 |
| 178 wchar_t exe_full_path_str[MAX_PATH]; | 161 EXPECT_TRUE(work_item->Do()); |
| 179 ::GetModuleFileNameW(NULL, exe_full_path_str, MAX_PATH); | 162 ExpectAllFilesDeleted(); |
| 180 base::FilePath exe_full_path(exe_full_path_str); | |
| 181 | 163 |
| 182 base::CopyFile(exe_full_path, key_path); | 164 work_item->Rollback(); |
| 183 ASSERT_TRUE(base::PathExists(key_path)); | 165 ExpectAllFilesDeleted(); |
| 166 } | |
| 184 | 167 |
| 185 VLOG(1) << "copy ourself from " << exe_full_path.value() | 168 // Delete a tree with key path in use. Do() shouldn't delete anything. |
| 186 << " to " << key_path.value(); | 169 TEST_F(DeleteTreeWorkItemTest, KeyPathInUse) { |
| 170 ScopedFileInUse file_in_use(file_name_1_); | |
| 187 | 171 |
| 188 // Run the key path file to keep it in use. | 172 base::ScopedTempDir temp_dir; |
| 189 STARTUPINFOW si = {sizeof(si)}; | 173 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 190 PROCESS_INFORMATION pi = {0}; | |
| 191 base::FilePath::StringType writable_key_path = key_path.value(); | |
| 192 ASSERT_TRUE( | |
| 193 ::CreateProcessW(NULL, &writable_key_path[0], | |
| 194 NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED, | |
| 195 NULL, NULL, &si, &pi)); | |
| 196 | 174 |
| 197 // test Do(). | 175 std::vector<base::FilePath> key_paths(1, file_name_1_); |
| 198 { | 176 std::unique_ptr<DeleteTreeWorkItem> work_item( |
| 199 base::ScopedTempDir temp_dir; | 177 WorkItem::CreateDeleteTreeWorkItem(dir_name_, temp_dir.path(), |
| 200 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 178 key_paths)); |
| 201 | 179 |
| 202 std::vector<base::FilePath> key_paths(1, key_path); | 180 // Do() should fail as the key path is in use. |
| 203 std::unique_ptr<DeleteTreeWorkItem> work_item( | 181 EXPECT_FALSE(work_item->Do()); |
| 204 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), | 182 ExpectAllFilesExist(); |
| 205 key_paths)); | 183 } |
| 206 | 184 |
| 207 // delete should fail as file in use. | 185 // Delete a tree with key path in use and rollback disabled. Do() shouldn't |
| 208 EXPECT_FALSE(work_item->Do()); | 186 // delete anything. |
| 209 } | 187 TEST_F(DeleteTreeWorkItemTest, KeyPathInUseRollbackDisabled) { |
| 188 ScopedFileInUse file_in_use(file_name_1_); | |
| 210 | 189 |
| 211 { | 190 base::ScopedTempDir temp_dir; |
| 212 base::ScopedTempDir temp_dir; | 191 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 213 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
| 214 | 192 |
| 215 // No key paths, the deletion should succeed. | 193 std::vector<base::FilePath> key_paths(1, file_name_1_); |
| 216 std::vector<base::FilePath> key_paths; | 194 std::unique_ptr<DeleteTreeWorkItem> work_item( |
| 217 std::unique_ptr<DeleteTreeWorkItem> work_item( | 195 WorkItem::CreateDeleteTreeWorkItem(dir_name_, temp_dir.path(), |
| 218 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), | 196 key_paths)); |
| 219 key_paths)); | 197 work_item->set_rollback_enabled(false); |
| 220 | 198 |
| 221 EXPECT_TRUE(work_item->Do()); | 199 // Do() should fail as the key path is in use. |
| 222 work_item->Rollback(); | 200 EXPECT_FALSE(work_item->Do()); |
| 223 } | 201 ExpectAllFilesExist(); |
| 224 | |
| 225 // verify everything is still there. | |
| 226 EXPECT_TRUE(base::PathExists(key_path)); | |
| 227 EXPECT_TRUE(base::PathExists(file_name_delete_1)); | |
| 228 EXPECT_TRUE(base::PathExists(file_name_delete_2)); | |
| 229 | |
| 230 TerminateProcess(pi.hProcess, 0); | |
| 231 // make sure the handle is closed. | |
| 232 WaitForSingleObject(pi.hProcess, INFINITE); | |
| 233 } | 202 } |
| OLD | NEW |