Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / installer / util / move_tree_work_item_unittest.cc
blob09da22eb67e5750345653c42f9a4d3ffa31f09d9
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
3 // found in the LICENSE file.
5 #include <windows.h>
7 #include <fstream>
9 #include "base/base_paths.h"
10 #include "base/files/file_util.h"
11 #include "base/files/memory_mapped_file.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/installer/util/installer_util_test_common.h"
16 #include "chrome/installer/util/move_tree_work_item.h"
17 #include "chrome/installer/util/work_item.h"
18 #include "testing/gtest/include/gtest/gtest.h"
20 namespace {
21 class MoveTreeWorkItemTest : public testing::Test {
22 protected:
23 void SetUp() override {
24 ASSERT_TRUE(temp_from_dir_.CreateUniqueTempDir());
25 ASSERT_TRUE(temp_to_dir_.CreateUniqueTempDir());
28 base::ScopedTempDir temp_from_dir_;
29 base::ScopedTempDir temp_to_dir_;
32 // Simple function to dump some text into a new file.
33 void CreateTextFile(const std::wstring& filename,
34 const std::wstring& contents) {
35 std::wofstream file;
36 file.open(base::UTF16ToASCII(filename).c_str());
37 ASSERT_TRUE(file.is_open());
38 file << contents;
39 file.close();
42 // Simple function to read text from a file.
43 std::wstring ReadTextFile(const base::FilePath& path) {
44 WCHAR contents[64];
45 std::wifstream file;
46 file.open(base::UTF16ToASCII(path.value()).c_str());
47 EXPECT_TRUE(file.is_open());
48 file.getline(contents, arraysize(contents));
49 file.close();
50 return std::wstring(contents);
53 const wchar_t kTextContent1[] = L"Gooooooooooooooooooooogle";
54 const wchar_t kTextContent2[] = L"Overwrite Me";
55 }; // namespace
57 // Move one directory from source to destination when destination does not
58 // exist.
59 TEST_F(MoveTreeWorkItemTest, MoveDirectory) {
60 // Create two level deep source dir
61 base::FilePath from_dir1(temp_from_dir_.path());
62 from_dir1 = from_dir1.AppendASCII("From_Dir1");
63 base::CreateDirectory(from_dir1);
64 ASSERT_TRUE(base::PathExists(from_dir1));
66 base::FilePath from_dir2(from_dir1);
67 from_dir2 = from_dir2.AppendASCII("From_Dir2");
68 base::CreateDirectory(from_dir2);
69 ASSERT_TRUE(base::PathExists(from_dir2));
71 base::FilePath from_file(from_dir2);
72 from_file = from_file.AppendASCII("From_File");
73 CreateTextFile(from_file.value(), kTextContent1);
74 ASSERT_TRUE(base::PathExists(from_file));
76 // Generate destination path
77 base::FilePath to_dir(temp_from_dir_.path());
78 to_dir = to_dir.AppendASCII("To_Dir");
79 ASSERT_FALSE(base::PathExists(to_dir));
81 base::FilePath to_file(to_dir);
82 to_file = to_file.AppendASCII("From_Dir2");
83 to_file = to_file.AppendASCII("From_File");
84 ASSERT_FALSE(base::PathExists(to_file));
86 // test Do()
87 scoped_ptr<MoveTreeWorkItem> work_item(
88 WorkItem::CreateMoveTreeWorkItem(from_dir1,
89 to_dir,
90 temp_to_dir_.path(),
91 WorkItem::ALWAYS_MOVE));
92 EXPECT_TRUE(work_item->Do());
94 EXPECT_FALSE(base::PathExists(from_dir1));
95 EXPECT_TRUE(base::PathExists(to_dir));
96 EXPECT_TRUE(base::PathExists(to_file));
98 // test rollback()
99 work_item->Rollback();
101 EXPECT_TRUE(base::PathExists(from_dir1));
102 EXPECT_TRUE(base::PathExists(from_file));
103 EXPECT_FALSE(base::PathExists(to_dir));
106 // Move one directory from source to destination when destination already
107 // exists.
108 TEST_F(MoveTreeWorkItemTest, MoveDirectoryDestExists) {
109 // Create two level deep source dir
110 base::FilePath from_dir1(temp_from_dir_.path());
111 from_dir1 = from_dir1.AppendASCII("From_Dir1");
112 base::CreateDirectory(from_dir1);
113 ASSERT_TRUE(base::PathExists(from_dir1));
115 base::FilePath from_dir2(from_dir1);
116 from_dir2 = from_dir2.AppendASCII("From_Dir2");
117 base::CreateDirectory(from_dir2);
118 ASSERT_TRUE(base::PathExists(from_dir2));
120 base::FilePath from_file(from_dir2);
121 from_file = from_file.AppendASCII("From_File");
122 CreateTextFile(from_file.value(), kTextContent1);
123 ASSERT_TRUE(base::PathExists(from_file));
125 // Create destination path
126 base::FilePath to_dir(temp_from_dir_.path());
127 to_dir = to_dir.AppendASCII("To_Dir");
128 base::CreateDirectory(to_dir);
129 ASSERT_TRUE(base::PathExists(to_dir));
131 base::FilePath orig_to_file(to_dir);
132 orig_to_file = orig_to_file.AppendASCII("To_File");
133 CreateTextFile(orig_to_file.value(), kTextContent2);
134 ASSERT_TRUE(base::PathExists(orig_to_file));
136 base::FilePath new_to_file(to_dir);
137 new_to_file = new_to_file.AppendASCII("From_Dir2");
138 new_to_file = new_to_file.AppendASCII("From_File");
139 ASSERT_FALSE(base::PathExists(new_to_file));
141 // test Do(), don't check for duplicates.
142 scoped_ptr<MoveTreeWorkItem> work_item(
143 WorkItem::CreateMoveTreeWorkItem(from_dir1,
144 to_dir,
145 temp_to_dir_.path(),
146 WorkItem::ALWAYS_MOVE));
147 EXPECT_TRUE(work_item->Do());
149 EXPECT_FALSE(base::PathExists(from_dir1));
150 EXPECT_TRUE(base::PathExists(to_dir));
151 EXPECT_TRUE(base::PathExists(new_to_file));
152 EXPECT_FALSE(base::PathExists(orig_to_file));
154 // test rollback()
155 work_item->Rollback();
157 EXPECT_TRUE(base::PathExists(from_dir1));
158 EXPECT_TRUE(base::PathExists(to_dir));
159 EXPECT_FALSE(base::PathExists(new_to_file));
160 EXPECT_TRUE(base::PathExists(orig_to_file));
161 EXPECT_EQ(0, ReadTextFile(orig_to_file).compare(kTextContent2));
162 EXPECT_EQ(0, ReadTextFile(from_file).compare(kTextContent1));
165 // Move one file from source to destination when destination does not
166 // exist.
167 TEST_F(MoveTreeWorkItemTest, MoveAFile) {
168 // Create a file inside source dir
169 base::FilePath from_dir(temp_from_dir_.path());
170 from_dir = from_dir.AppendASCII("From_Dir");
171 base::CreateDirectory(from_dir);
172 ASSERT_TRUE(base::PathExists(from_dir));
174 base::FilePath from_file(from_dir);
175 from_file = from_file.AppendASCII("From_File");
176 CreateTextFile(from_file.value(), kTextContent1);
177 ASSERT_TRUE(base::PathExists(from_file));
179 // Generate destination file name
180 base::FilePath to_file(temp_from_dir_.path());
181 to_file = to_file.AppendASCII("To_File");
182 ASSERT_FALSE(base::PathExists(to_file));
184 // test Do()
185 scoped_ptr<MoveTreeWorkItem> work_item(
186 WorkItem::CreateMoveTreeWorkItem(from_file,
187 to_file,
188 temp_to_dir_.path(),
189 WorkItem::ALWAYS_MOVE));
190 EXPECT_TRUE(work_item->Do());
192 EXPECT_TRUE(base::PathExists(from_dir));
193 EXPECT_FALSE(base::PathExists(from_file));
194 EXPECT_TRUE(base::PathExists(to_file));
195 EXPECT_EQ(0, ReadTextFile(to_file).compare(kTextContent1));
197 // test rollback()
198 work_item->Rollback();
200 EXPECT_TRUE(base::PathExists(from_dir));
201 EXPECT_TRUE(base::PathExists(from_file));
202 EXPECT_FALSE(base::PathExists(to_file));
203 EXPECT_EQ(0, ReadTextFile(from_file).compare(kTextContent1));
206 // Move one file from source to destination when destination already
207 // exists.
208 TEST_F(MoveTreeWorkItemTest, MoveFileDestExists) {
209 // Create a file inside source dir
210 base::FilePath from_dir(temp_from_dir_.path());
211 from_dir = from_dir.AppendASCII("From_Dir");
212 base::CreateDirectory(from_dir);
213 ASSERT_TRUE(base::PathExists(from_dir));
215 base::FilePath from_file(from_dir);
216 from_file = from_file.AppendASCII("From_File");
217 CreateTextFile(from_file.value(), kTextContent1);
218 ASSERT_TRUE(base::PathExists(from_file));
220 // Create destination path
221 base::FilePath to_dir(temp_from_dir_.path());
222 to_dir = to_dir.AppendASCII("To_Dir");
223 base::CreateDirectory(to_dir);
224 ASSERT_TRUE(base::PathExists(to_dir));
226 base::FilePath to_file(to_dir);
227 to_file = to_file.AppendASCII("To_File");
228 CreateTextFile(to_file.value(), kTextContent2);
229 ASSERT_TRUE(base::PathExists(to_file));
231 // test Do()
232 scoped_ptr<MoveTreeWorkItem> work_item(
233 WorkItem::CreateMoveTreeWorkItem(from_file,
234 to_dir,
235 temp_to_dir_.path(),
236 WorkItem::ALWAYS_MOVE));
237 EXPECT_TRUE(work_item->Do());
239 EXPECT_TRUE(base::PathExists(from_dir));
240 EXPECT_FALSE(base::PathExists(from_file));
241 EXPECT_TRUE(base::PathExists(to_dir));
242 EXPECT_FALSE(base::PathExists(to_file));
243 EXPECT_EQ(0, ReadTextFile(to_dir).compare(kTextContent1));
245 // test rollback()
246 work_item->Rollback();
248 EXPECT_TRUE(base::PathExists(from_dir));
249 EXPECT_EQ(0, ReadTextFile(from_file).compare(kTextContent1));
250 EXPECT_TRUE(base::PathExists(to_dir));
251 EXPECT_EQ(0, ReadTextFile(to_file).compare(kTextContent2));
254 // Move one file from source to destination when destination already
255 // exists and is in use.
256 TEST_F(MoveTreeWorkItemTest, MoveFileDestInUse) {
257 // Create a file inside source dir
258 base::FilePath from_dir(temp_from_dir_.path());
259 from_dir = from_dir.AppendASCII("From_Dir");
260 base::CreateDirectory(from_dir);
261 ASSERT_TRUE(base::PathExists(from_dir));
263 base::FilePath from_file(from_dir);
264 from_file = from_file.AppendASCII("From_File");
265 CreateTextFile(from_file.value(), kTextContent1);
266 ASSERT_TRUE(base::PathExists(from_file));
268 // Create an executable in destination path by copying ourself to it.
269 base::FilePath to_dir(temp_from_dir_.path());
270 to_dir = to_dir.AppendASCII("To_Dir");
271 base::CreateDirectory(to_dir);
272 ASSERT_TRUE(base::PathExists(to_dir));
274 wchar_t exe_full_path_str[MAX_PATH];
275 ::GetModuleFileName(NULL, exe_full_path_str, MAX_PATH);
276 base::FilePath exe_full_path(exe_full_path_str);
277 base::FilePath to_file(to_dir);
278 to_file = to_file.AppendASCII("To_File");
279 base::CopyFile(exe_full_path, to_file);
280 ASSERT_TRUE(base::PathExists(to_file));
282 // Run the executable in destination path
283 STARTUPINFOW si = {sizeof(si)};
284 PROCESS_INFORMATION pi = {0};
285 ASSERT_TRUE(::CreateProcess(NULL,
286 const_cast<wchar_t*>(to_file.value().c_str()),
287 NULL, NULL, FALSE,
288 CREATE_NO_WINDOW | CREATE_SUSPENDED,
289 NULL, NULL, &si, &pi));
291 // test Do()
292 scoped_ptr<MoveTreeWorkItem> work_item(
293 WorkItem::CreateMoveTreeWorkItem(from_file,
294 to_file,
295 temp_to_dir_.path(),
296 WorkItem::ALWAYS_MOVE));
297 EXPECT_TRUE(work_item->Do());
299 EXPECT_TRUE(base::PathExists(from_dir));
300 EXPECT_FALSE(base::PathExists(from_file));
301 EXPECT_TRUE(base::PathExists(to_dir));
302 EXPECT_EQ(0, ReadTextFile(to_file).compare(kTextContent1));
304 // test rollback()
305 work_item->Rollback();
307 EXPECT_TRUE(base::PathExists(from_dir));
308 EXPECT_EQ(0, ReadTextFile(from_file).compare(kTextContent1));
309 EXPECT_TRUE(base::PathExists(to_dir));
310 EXPECT_TRUE(base::ContentsEqual(exe_full_path, to_file));
312 TerminateProcess(pi.hProcess, 0);
313 EXPECT_TRUE(WaitForSingleObject(pi.hProcess, 10000) == WAIT_OBJECT_0);
314 CloseHandle(pi.hProcess);
315 CloseHandle(pi.hThread);
318 // Move one file that is in use to destination.
319 TEST_F(MoveTreeWorkItemTest, MoveFileInUse) {
320 // Create an executable for source by copying ourself to a new source dir.
321 base::FilePath from_dir(temp_from_dir_.path());
322 from_dir = from_dir.AppendASCII("From_Dir");
323 base::CreateDirectory(from_dir);
324 ASSERT_TRUE(base::PathExists(from_dir));
326 wchar_t exe_full_path_str[MAX_PATH];
327 ::GetModuleFileName(NULL, exe_full_path_str, MAX_PATH);
328 base::FilePath exe_full_path(exe_full_path_str);
329 base::FilePath from_file(from_dir);
330 from_file = from_file.AppendASCII("From_File");
331 base::CopyFile(exe_full_path, from_file);
332 ASSERT_TRUE(base::PathExists(from_file));
334 // Create a destination source dir and generate destination file name.
335 base::FilePath to_dir(temp_from_dir_.path());
336 to_dir = to_dir.AppendASCII("To_Dir");
337 base::CreateDirectory(to_dir);
338 ASSERT_TRUE(base::PathExists(to_dir));
340 base::FilePath to_file(to_dir);
341 to_file = to_file.AppendASCII("To_File");
342 CreateTextFile(to_file.value(), kTextContent1);
343 ASSERT_TRUE(base::PathExists(to_file));
345 // Run the executable in source path
346 STARTUPINFOW si = {sizeof(si)};
347 PROCESS_INFORMATION pi = {0};
348 ASSERT_TRUE(::CreateProcess(NULL,
349 const_cast<wchar_t*>(from_file.value().c_str()),
350 NULL, NULL, FALSE,
351 CREATE_NO_WINDOW | CREATE_SUSPENDED,
352 NULL, NULL, &si, &pi));
354 // test Do()
355 scoped_ptr<MoveTreeWorkItem> work_item(
356 WorkItem::CreateMoveTreeWorkItem(from_file,
357 to_file,
358 temp_to_dir_.path(),
359 WorkItem::ALWAYS_MOVE));
360 EXPECT_TRUE(work_item->Do());
362 EXPECT_TRUE(base::PathExists(from_dir));
363 EXPECT_FALSE(base::PathExists(from_file));
364 EXPECT_TRUE(base::PathExists(to_dir));
365 EXPECT_TRUE(base::ContentsEqual(exe_full_path, to_file));
367 // Close the process and make sure all the conditions after Do() are
368 // still true.
369 TerminateProcess(pi.hProcess, 0);
370 EXPECT_TRUE(WaitForSingleObject(pi.hProcess, 10000) == WAIT_OBJECT_0);
371 CloseHandle(pi.hProcess);
372 CloseHandle(pi.hThread);
374 EXPECT_TRUE(base::PathExists(from_dir));
375 EXPECT_FALSE(base::PathExists(from_file));
376 EXPECT_TRUE(base::PathExists(to_dir));
377 EXPECT_TRUE(base::ContentsEqual(exe_full_path, to_file));
379 // test rollback()
380 work_item->Rollback();
382 EXPECT_TRUE(base::PathExists(from_dir));
383 EXPECT_TRUE(base::ContentsEqual(exe_full_path, from_file));
384 EXPECT_TRUE(base::PathExists(to_dir));
385 EXPECT_EQ(0, ReadTextFile(to_file).compare(kTextContent1));
388 // Move one directory from source to destination when destination already
389 // exists.
390 TEST_F(MoveTreeWorkItemTest, MoveDirectoryDestExistsCheckForDuplicatesFull) {
391 // Create two level deep source dir
392 base::FilePath from_dir1(temp_from_dir_.path());
393 from_dir1 = from_dir1.AppendASCII("From_Dir1");
394 base::CreateDirectory(from_dir1);
395 ASSERT_TRUE(base::PathExists(from_dir1));
397 base::FilePath from_dir2(from_dir1);
398 from_dir2 = from_dir2.AppendASCII("From_Dir2");
399 base::CreateDirectory(from_dir2);
400 ASSERT_TRUE(base::PathExists(from_dir2));
402 base::FilePath from_file(from_dir2);
403 from_file = from_file.AppendASCII("From_File");
404 CreateTextFile(from_file.value(), kTextContent1);
405 ASSERT_TRUE(base::PathExists(from_file));
407 // // Create a file hierarchy identical to the one in the source directory.
408 base::FilePath to_dir(temp_from_dir_.path());
409 to_dir = to_dir.AppendASCII("To_Dir");
410 ASSERT_TRUE(installer::test::CopyFileHierarchy(from_dir1, to_dir));
412 // Lock one of the files in the to destination directory to prevent moves.
413 base::FilePath orig_to_file(
414 to_dir.AppendASCII("From_Dir2").AppendASCII("From_File"));
415 base::MemoryMappedFile mapped_file;
416 EXPECT_TRUE(mapped_file.Initialize(orig_to_file));
418 // First check that we can't do the regular Move().
419 scoped_ptr<MoveTreeWorkItem> work_item(
420 WorkItem::CreateMoveTreeWorkItem(from_dir1,
421 to_dir,
422 temp_to_dir_.path(),
423 WorkItem::ALWAYS_MOVE));
424 EXPECT_FALSE(work_item->Do());
425 work_item->Rollback();
427 // Now test Do() with the check for duplicates. This should pass.
428 work_item.reset(
429 WorkItem::CreateMoveTreeWorkItem(from_dir1,
430 to_dir,
431 temp_to_dir_.path(),
432 WorkItem::CHECK_DUPLICATES));
433 EXPECT_TRUE(work_item->Do());
435 // Make sure that we "moved" the files, i.e. that the source directory isn't
436 // there anymore,
437 EXPECT_FALSE(base::PathExists(from_dir1));
438 // Make sure that the original directory structure and file are still present.
439 EXPECT_TRUE(base::PathExists(to_dir));
440 EXPECT_TRUE(base::PathExists(orig_to_file));
441 // Make sure that the backup path is not empty.
442 EXPECT_FALSE(base::IsDirectoryEmpty(temp_to_dir_.path()));
444 // Check that the work item believes the source to have been moved.
445 EXPECT_TRUE(work_item->source_moved_to_backup_);
446 EXPECT_FALSE(work_item->moved_to_dest_path_);
447 EXPECT_FALSE(work_item->moved_to_backup_);
449 // test rollback()
450 work_item->Rollback();
452 // Once we rollback all the original files should still be there, as should
453 // the source files.
454 EXPECT_TRUE(base::PathExists(from_dir1));
455 EXPECT_TRUE(base::PathExists(to_dir));
456 EXPECT_TRUE(base::PathExists(orig_to_file));
457 EXPECT_EQ(0, ReadTextFile(orig_to_file).compare(kTextContent1));
458 EXPECT_EQ(0, ReadTextFile(from_file).compare(kTextContent1));
461 // Move one directory from source to destination when destination already
462 // exists but contains only a subset of the files in source.
463 TEST_F(MoveTreeWorkItemTest, MoveDirectoryDestExistsCheckForDuplicatesPartial) {
464 // Create two level deep source dir
465 base::FilePath from_dir1(temp_from_dir_.path());
466 from_dir1 = from_dir1.AppendASCII("From_Dir1");
467 base::CreateDirectory(from_dir1);
468 ASSERT_TRUE(base::PathExists(from_dir1));
470 base::FilePath from_dir2(from_dir1);
471 from_dir2 = from_dir2.AppendASCII("From_Dir2");
472 base::CreateDirectory(from_dir2);
473 ASSERT_TRUE(base::PathExists(from_dir2));
475 base::FilePath from_file(from_dir2);
476 from_file = from_file.AppendASCII("From_File");
477 CreateTextFile(from_file.value(), kTextContent1);
478 ASSERT_TRUE(base::PathExists(from_file));
480 base::FilePath from_file2(from_dir2);
481 from_file2 = from_file2.AppendASCII("From_File2");
482 CreateTextFile(from_file2.value(), kTextContent2);
483 ASSERT_TRUE(base::PathExists(from_file2));
485 // Create destination path
486 base::FilePath to_dir(temp_from_dir_.path());
487 to_dir = to_dir.AppendASCII("To_Dir");
488 base::CreateDirectory(to_dir);
489 ASSERT_TRUE(base::PathExists(to_dir));
491 // Create a sub-directory of the same name as in the source directory.
492 base::FilePath to_dir2(to_dir);
493 to_dir2 = to_dir2.AppendASCII("From_Dir2");
494 base::CreateDirectory(to_dir2);
495 ASSERT_TRUE(base::PathExists(to_dir2));
497 // Create one of the files in the to sub-directory, but not the other.
498 base::FilePath orig_to_file(to_dir2);
499 orig_to_file = orig_to_file.AppendASCII("From_File");
500 CreateTextFile(orig_to_file.value(), kTextContent1);
501 ASSERT_TRUE(base::PathExists(orig_to_file));
503 // test Do(), check for duplicates.
504 scoped_ptr<MoveTreeWorkItem> work_item(
505 WorkItem::CreateMoveTreeWorkItem(from_dir1,
506 to_dir,
507 temp_to_dir_.path(),
508 WorkItem::CHECK_DUPLICATES));
509 EXPECT_TRUE(work_item->Do());
511 // Make sure that we "moved" the files, i.e. that the source directory isn't
512 // there anymore,
513 EXPECT_FALSE(base::PathExists(from_dir1));
514 // Make sure that the original directory structure and file are still present.
515 EXPECT_TRUE(base::PathExists(to_dir));
516 EXPECT_TRUE(base::PathExists(orig_to_file));
517 // Make sure that the backup path is not empty.
518 EXPECT_FALSE(base::IsDirectoryEmpty(temp_to_dir_.path()));
519 // Make sure that the "new" file is also present.
520 base::FilePath new_to_file2(to_dir2);
521 new_to_file2 = new_to_file2.AppendASCII("From_File2");
522 EXPECT_TRUE(base::PathExists(new_to_file2));
524 // Check that the work item believes that this was a regular move.
525 EXPECT_FALSE(work_item->source_moved_to_backup_);
526 EXPECT_TRUE(work_item->moved_to_dest_path_);
527 EXPECT_TRUE(work_item->moved_to_backup_);
529 // test rollback()
530 work_item->Rollback();
532 // Once we rollback all the original files should still be there, as should
533 // the source files.
534 EXPECT_TRUE(base::PathExists(from_dir1));
535 EXPECT_TRUE(base::PathExists(to_dir));
536 EXPECT_TRUE(base::PathExists(orig_to_file));
537 EXPECT_EQ(0, ReadTextFile(orig_to_file).compare(kTextContent1));
538 EXPECT_EQ(0, ReadTextFile(from_file).compare(kTextContent1));
540 // Also, after rollback the new "to" file should be gone.
541 EXPECT_FALSE(base::PathExists(new_to_file2));