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.
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"
21 class MoveTreeWorkItemTest
: public testing::Test
{
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
) {
36 file
.open(base::UTF16ToASCII(filename
).c_str());
37 ASSERT_TRUE(file
.is_open());
42 // Simple function to read text from a file.
43 std::wstring
ReadTextFile(const base::FilePath
& path
) {
46 file
.open(base::UTF16ToASCII(path
.value()).c_str());
47 EXPECT_TRUE(file
.is_open());
48 file
.getline(contents
, arraysize(contents
));
50 return std::wstring(contents
);
53 const wchar_t kTextContent1
[] = L
"Gooooooooooooooooooooogle";
54 const wchar_t kTextContent2
[] = L
"Overwrite Me";
57 // Move one directory from source to destination when destination does not
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
));
87 scoped_ptr
<MoveTreeWorkItem
> work_item(
88 WorkItem::CreateMoveTreeWorkItem(from_dir1
,
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
));
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
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
,
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
));
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
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
));
185 scoped_ptr
<MoveTreeWorkItem
> work_item(
186 WorkItem::CreateMoveTreeWorkItem(from_file
,
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
));
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
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
));
232 scoped_ptr
<MoveTreeWorkItem
> work_item(
233 WorkItem::CreateMoveTreeWorkItem(from_file
,
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
));
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()),
288 CREATE_NO_WINDOW
| CREATE_SUSPENDED
,
289 NULL
, NULL
, &si
, &pi
));
292 scoped_ptr
<MoveTreeWorkItem
> work_item(
293 WorkItem::CreateMoveTreeWorkItem(from_file
,
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
));
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()),
351 CREATE_NO_WINDOW
| CREATE_SUSPENDED
,
352 NULL
, NULL
, &si
, &pi
));
355 scoped_ptr
<MoveTreeWorkItem
> work_item(
356 WorkItem::CreateMoveTreeWorkItem(from_file
,
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
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
));
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
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
,
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.
429 WorkItem::CreateMoveTreeWorkItem(from_dir1
,
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
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_
);
450 work_item
->Rollback();
452 // Once we rollback all the original files should still be there, as should
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
,
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
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_
);
530 work_item
->Rollback();
532 // Once we rollback all the original files should still be there, as should
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
));