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_path.h" |
13 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
14 #include "base/files/scoped_temp_dir.h" | 11 #include "base/files/scoped_temp_dir.h" |
15 #include "base/logging.h" | 12 #include "base/macros.h" |
16 #include "base/strings/string_util.h" | |
17 #include "chrome/installer/util/work_item.h" | 13 #include "chrome/installer/util/work_item.h" |
18 #include "testing/gtest/include/gtest/gtest.h" | 14 #include "testing/gtest/include/gtest/gtest.h" |
19 | 15 |
20 namespace { | 16 namespace { |
21 | 17 |
| 18 constexpr char kTextContent[] = "delete me"; |
| 19 |
22 class DeleteTreeWorkItemTest : public testing::Test { | 20 class DeleteTreeWorkItemTest : public testing::Test { |
23 protected: | 21 protected: |
24 void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } | 22 DeleteTreeWorkItemTest() = default; |
| 23 |
| 24 void SetUp() override { |
| 25 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| 26 |
| 27 dir_name_ = temp_dir_.path().Append(FILE_PATH_LITERAL("to_be_deleted")); |
| 28 ASSERT_TRUE(base::CreateDirectory(dir_name_)); |
| 29 ASSERT_TRUE(base::PathExists(dir_name_)); |
| 30 |
| 31 dir_name_1_ = dir_name_.Append(FILE_PATH_LITERAL("1")); |
| 32 ASSERT_TRUE(base::CreateDirectory(dir_name_1_)); |
| 33 ASSERT_TRUE(base::PathExists(dir_name_1_)); |
| 34 |
| 35 dir_name_2_ = dir_name_.Append(FILE_PATH_LITERAL("2")); |
| 36 ASSERT_TRUE(base::CreateDirectory(dir_name_2_)); |
| 37 ASSERT_TRUE(base::PathExists(dir_name_2_)); |
| 38 |
| 39 file_name_1_ = dir_name_1_.Append(FILE_PATH_LITERAL("File_1.txt")); |
| 40 ASSERT_TRUE( |
| 41 base::WriteFile(file_name_1_, kTextContent, sizeof(kTextContent))); |
| 42 ASSERT_TRUE(base::PathExists(file_name_1_)); |
| 43 |
| 44 file_name_2_ = dir_name_2_.Append(FILE_PATH_LITERAL("File_2.txt")); |
| 45 ASSERT_TRUE( |
| 46 base::WriteFile(file_name_2_, kTextContent, sizeof(kTextContent))); |
| 47 ASSERT_TRUE(base::PathExists(file_name_2_)); |
| 48 } |
| 49 |
| 50 void ExpectAllFilesExist() { |
| 51 EXPECT_TRUE(base::PathExists(dir_name_)); |
| 52 EXPECT_TRUE(base::PathExists(dir_name_1_)); |
| 53 EXPECT_TRUE(base::PathExists(dir_name_2_)); |
| 54 EXPECT_TRUE(base::PathExists(file_name_1_)); |
| 55 EXPECT_TRUE(base::PathExists(file_name_2_)); |
| 56 } |
| 57 |
| 58 void ExpectAllFilesDeleted() { |
| 59 EXPECT_FALSE(base::PathExists(dir_name_)); |
| 60 EXPECT_FALSE(base::PathExists(dir_name_1_)); |
| 61 EXPECT_FALSE(base::PathExists(dir_name_2_)); |
| 62 EXPECT_FALSE(base::PathExists(file_name_1_)); |
| 63 EXPECT_FALSE(base::PathExists(file_name_2_)); |
| 64 } |
25 | 65 |
26 // The temporary directory used to contain the test operations. | 66 // The temporary directory used to contain the test operations. |
27 base::ScopedTempDir temp_dir_; | 67 base::ScopedTempDir temp_dir_; |
| 68 |
| 69 base::FilePath dir_name_; |
| 70 base::FilePath dir_name_1_; |
| 71 base::FilePath dir_name_2_; |
| 72 base::FilePath file_name_1_; |
| 73 base::FilePath file_name_2_; |
| 74 |
| 75 DISALLOW_COPY_AND_ASSIGN(DeleteTreeWorkItemTest); |
28 }; | 76 }; |
29 | 77 |
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 | 78 } // namespace |
43 | 79 |
44 // Delete a tree without key path. Everything should be deleted. | 80 // Delete a tree with rollback enabled and no file in use. Do() should delete |
45 TEST_F(DeleteTreeWorkItemTest, DeleteTreeNoKeyPath) { | 81 // everything and Rollback() should bring back everything. |
46 // Create tree to be deleted. | 82 TEST_F(DeleteTreeWorkItemTest, Delete) { |
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; | 83 base::ScopedTempDir temp_dir; |
74 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 84 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
75 | 85 |
76 std::vector<base::FilePath> key_files; | |
77 std::unique_ptr<DeleteTreeWorkItem> work_item( | 86 std::unique_ptr<DeleteTreeWorkItem> work_item( |
78 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), | 87 WorkItem::CreateDeleteTreeWorkItem(dir_name_, temp_dir.path())); |
79 key_files)); | 88 |
80 EXPECT_TRUE(work_item->Do()); | 89 EXPECT_TRUE(work_item->Do()); |
81 | 90 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 | 91 |
87 work_item->Rollback(); | 92 work_item->Rollback(); |
88 // everything should come back | 93 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 } | 94 } |
93 | 95 |
94 | 96 // Delete a tree with rollback disabled and no file in use. Do() should delete |
95 // Delete a tree with keypath but not in use. Everything should be gone. | 97 // everything and Rollback() shouldn't bring back anything. |
96 // Rollback should bring back everything | 98 TEST_F(DeleteTreeWorkItemTest, DeleteRollbackDisabled) { |
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; | 99 base::ScopedTempDir temp_dir; |
126 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | 100 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
127 | 101 |
128 std::vector<base::FilePath> key_files(1, file_name_delete_1); | |
129 std::unique_ptr<DeleteTreeWorkItem> work_item( | 102 std::unique_ptr<DeleteTreeWorkItem> work_item( |
130 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), | 103 WorkItem::CreateDeleteTreeWorkItem(dir_name_, temp_dir.path())); |
131 key_files)); | 104 work_item->set_rollback_enabled(false); |
| 105 |
132 EXPECT_TRUE(work_item->Do()); | 106 EXPECT_TRUE(work_item->Do()); |
133 | 107 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 | 108 |
139 work_item->Rollback(); | 109 work_item->Rollback(); |
140 // everything should come back | 110 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 } | 111 } |
145 | |
146 // Delete a tree with key_path in use. Everything should still be there. | |
147 TEST_F(DeleteTreeWorkItemTest, DeleteTreeInUse) { | |
148 // Create tree to be deleted | |
149 base::FilePath dir_name_delete(temp_dir_.path()); | |
150 dir_name_delete = dir_name_delete.AppendASCII("to_be_delete"); | |
151 base::CreateDirectory(dir_name_delete); | |
152 ASSERT_TRUE(base::PathExists(dir_name_delete)); | |
153 | |
154 base::FilePath dir_name_delete_1(dir_name_delete); | |
155 dir_name_delete_1 = dir_name_delete_1.AppendASCII("1"); | |
156 base::CreateDirectory(dir_name_delete_1); | |
157 ASSERT_TRUE(base::PathExists(dir_name_delete_1)); | |
158 | |
159 base::FilePath dir_name_delete_2(dir_name_delete); | |
160 dir_name_delete_2 = dir_name_delete_2.AppendASCII("2"); | |
161 base::CreateDirectory(dir_name_delete_2); | |
162 ASSERT_TRUE(base::PathExists(dir_name_delete_2)); | |
163 | |
164 base::FilePath file_name_delete_1(dir_name_delete_1); | |
165 file_name_delete_1 = file_name_delete_1.AppendASCII("File_1.txt"); | |
166 CreateTextFile(file_name_delete_1.value(), text_content_1); | |
167 ASSERT_TRUE(base::PathExists(file_name_delete_1)); | |
168 | |
169 base::FilePath file_name_delete_2(dir_name_delete_2); | |
170 file_name_delete_2 = file_name_delete_2.AppendASCII("File_2.txt"); | |
171 CreateTextFile(file_name_delete_2.value(), text_content_1); | |
172 ASSERT_TRUE(base::PathExists(file_name_delete_2)); | |
173 | |
174 // Create a key path file. | |
175 base::FilePath key_path(dir_name_delete); | |
176 key_path = key_path.AppendASCII("key_file.exe"); | |
177 | |
178 wchar_t exe_full_path_str[MAX_PATH]; | |
179 ::GetModuleFileNameW(NULL, exe_full_path_str, MAX_PATH); | |
180 base::FilePath exe_full_path(exe_full_path_str); | |
181 | |
182 base::CopyFile(exe_full_path, key_path); | |
183 ASSERT_TRUE(base::PathExists(key_path)); | |
184 | |
185 VLOG(1) << "copy ourself from " << exe_full_path.value() | |
186 << " to " << key_path.value(); | |
187 | |
188 // Run the key path file to keep it in use. | |
189 STARTUPINFOW si = {sizeof(si)}; | |
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 | |
197 // test Do(). | |
198 { | |
199 base::ScopedTempDir temp_dir; | |
200 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
201 | |
202 std::vector<base::FilePath> key_paths(1, key_path); | |
203 std::unique_ptr<DeleteTreeWorkItem> work_item( | |
204 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), | |
205 key_paths)); | |
206 | |
207 // delete should fail as file in use. | |
208 EXPECT_FALSE(work_item->Do()); | |
209 } | |
210 | |
211 { | |
212 base::ScopedTempDir temp_dir; | |
213 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); | |
214 | |
215 // No key paths, the deletion should succeed. | |
216 std::vector<base::FilePath> key_paths; | |
217 std::unique_ptr<DeleteTreeWorkItem> work_item( | |
218 WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, temp_dir.path(), | |
219 key_paths)); | |
220 | |
221 EXPECT_TRUE(work_item->Do()); | |
222 work_item->Rollback(); | |
223 } | |
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 } | |
OLD | NEW |