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/path_service.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/installer/util/installer_util_test_common.h"
17 #include "chrome/installer/util/move_tree_work_item.h"
18 #include "chrome/installer/util/work_item.h"
19 #include "testing/gtest/include/gtest/gtest.h"
22 class MoveTreeWorkItemTest
: public testing::Test
{
24 virtual void SetUp() {
25 ASSERT_TRUE(temp_from_dir_
.CreateUniqueTempDir());
26 ASSERT_TRUE(temp_to_dir_
.CreateUniqueTempDir());
29 base::ScopedTempDir temp_from_dir_
;
30 base::ScopedTempDir temp_to_dir_
;
33 // Simple function to dump some text into a new file.
34 void CreateTextFile(const std::wstring
& filename
,
35 const std::wstring
& contents
) {
37 file
.open(base::UTF16ToASCII(filename
).c_str());
38 ASSERT_TRUE(file
.is_open());
43 // Simple function to read text from a file.
44 std::wstring
ReadTextFile(const base::FilePath
& path
) {
47 file
.open(base::UTF16ToASCII(path
.value()).c_str());
48 EXPECT_TRUE(file
.is_open());
49 file
.getline(contents
, arraysize(contents
));
51 return std::wstring(contents
);
54 const wchar_t kTextContent1
[] = L
"Gooooooooooooooooooooogle";
55 const wchar_t kTextContent2
[] = L
"Overwrite Me";
58 // Move one directory from source to destination when destination does not
60 TEST_F(MoveTreeWorkItemTest
, MoveDirectory
) {
61 // Create two level deep source dir
62 base::FilePath
from_dir1(temp_from_dir_
.path());
63 from_dir1
= from_dir1
.AppendASCII("From_Dir1");
64 base::CreateDirectory(from_dir1
);
65 ASSERT_TRUE(base::PathExists(from_dir1
));
67 base::FilePath
from_dir2(from_dir1
);
68 from_dir2
= from_dir2
.AppendASCII("From_Dir2");
69 base::CreateDirectory(from_dir2
);
70 ASSERT_TRUE(base::PathExists(from_dir2
));
72 base::FilePath
from_file(from_dir2
);
73 from_file
= from_file
.AppendASCII("From_File");
74 CreateTextFile(from_file
.value(), kTextContent1
);
75 ASSERT_TRUE(base::PathExists(from_file
));
77 // Generate destination path
78 base::FilePath
to_dir(temp_from_dir_
.path());
79 to_dir
= to_dir
.AppendASCII("To_Dir");
80 ASSERT_FALSE(base::PathExists(to_dir
));
82 base::FilePath
to_file(to_dir
);
83 to_file
= to_file
.AppendASCII("From_Dir2");
84 to_file
= to_file
.AppendASCII("From_File");
85 ASSERT_FALSE(base::PathExists(to_file
));
88 scoped_ptr
<MoveTreeWorkItem
> work_item(
89 WorkItem::CreateMoveTreeWorkItem(from_dir1
,
92 WorkItem::ALWAYS_MOVE
));
93 EXPECT_TRUE(work_item
->Do());
95 EXPECT_FALSE(base::PathExists(from_dir1
));
96 EXPECT_TRUE(base::PathExists(to_dir
));
97 EXPECT_TRUE(base::PathExists(to_file
));
100 work_item
->Rollback();
102 EXPECT_TRUE(base::PathExists(from_dir1
));
103 EXPECT_TRUE(base::PathExists(from_file
));
104 EXPECT_FALSE(base::PathExists(to_dir
));
107 // Move one directory from source to destination when destination already
109 TEST_F(MoveTreeWorkItemTest
, MoveDirectoryDestExists
) {
110 // Create two level deep source dir
111 base::FilePath
from_dir1(temp_from_dir_
.path());
112 from_dir1
= from_dir1
.AppendASCII("From_Dir1");
113 base::CreateDirectory(from_dir1
);
114 ASSERT_TRUE(base::PathExists(from_dir1
));
116 base::FilePath
from_dir2(from_dir1
);
117 from_dir2
= from_dir2
.AppendASCII("From_Dir2");
118 base::CreateDirectory(from_dir2
);
119 ASSERT_TRUE(base::PathExists(from_dir2
));
121 base::FilePath
from_file(from_dir2
);
122 from_file
= from_file
.AppendASCII("From_File");
123 CreateTextFile(from_file
.value(), kTextContent1
);
124 ASSERT_TRUE(base::PathExists(from_file
));
126 // Create destination path
127 base::FilePath
to_dir(temp_from_dir_
.path());
128 to_dir
= to_dir
.AppendASCII("To_Dir");
129 base::CreateDirectory(to_dir
);
130 ASSERT_TRUE(base::PathExists(to_dir
));
132 base::FilePath
orig_to_file(to_dir
);
133 orig_to_file
= orig_to_file
.AppendASCII("To_File");
134 CreateTextFile(orig_to_file
.value(), kTextContent2
);
135 ASSERT_TRUE(base::PathExists(orig_to_file
));
137 base::FilePath
new_to_file(to_dir
);
138 new_to_file
= new_to_file
.AppendASCII("From_Dir2");
139 new_to_file
= new_to_file
.AppendASCII("From_File");
140 ASSERT_FALSE(base::PathExists(new_to_file
));
142 // test Do(), don't check for duplicates.
143 scoped_ptr
<MoveTreeWorkItem
> work_item(
144 WorkItem::CreateMoveTreeWorkItem(from_dir1
,
147 WorkItem::ALWAYS_MOVE
));
148 EXPECT_TRUE(work_item
->Do());
150 EXPECT_FALSE(base::PathExists(from_dir1
));
151 EXPECT_TRUE(base::PathExists(to_dir
));
152 EXPECT_TRUE(base::PathExists(new_to_file
));
153 EXPECT_FALSE(base::PathExists(orig_to_file
));
156 work_item
->Rollback();
158 EXPECT_TRUE(base::PathExists(from_dir1
));
159 EXPECT_TRUE(base::PathExists(to_dir
));
160 EXPECT_FALSE(base::PathExists(new_to_file
));
161 EXPECT_TRUE(base::PathExists(orig_to_file
));
162 EXPECT_EQ(0, ReadTextFile(orig_to_file
).compare(kTextContent2
));
163 EXPECT_EQ(0, ReadTextFile(from_file
).compare(kTextContent1
));
166 // Move one file from source to destination when destination does not
168 TEST_F(MoveTreeWorkItemTest
, MoveAFile
) {
169 // Create a file inside source dir
170 base::FilePath
from_dir(temp_from_dir_
.path());
171 from_dir
= from_dir
.AppendASCII("From_Dir");
172 base::CreateDirectory(from_dir
);
173 ASSERT_TRUE(base::PathExists(from_dir
));
175 base::FilePath
from_file(from_dir
);
176 from_file
= from_file
.AppendASCII("From_File");
177 CreateTextFile(from_file
.value(), kTextContent1
);
178 ASSERT_TRUE(base::PathExists(from_file
));
180 // Generate destination file name
181 base::FilePath
to_file(temp_from_dir_
.path());
182 to_file
= to_file
.AppendASCII("To_File");
183 ASSERT_FALSE(base::PathExists(to_file
));
186 scoped_ptr
<MoveTreeWorkItem
> work_item(
187 WorkItem::CreateMoveTreeWorkItem(from_file
,
190 WorkItem::ALWAYS_MOVE
));
191 EXPECT_TRUE(work_item
->Do());
193 EXPECT_TRUE(base::PathExists(from_dir
));
194 EXPECT_FALSE(base::PathExists(from_file
));
195 EXPECT_TRUE(base::PathExists(to_file
));
196 EXPECT_EQ(0, ReadTextFile(to_file
).compare(kTextContent1
));
199 work_item
->Rollback();
201 EXPECT_TRUE(base::PathExists(from_dir
));
202 EXPECT_TRUE(base::PathExists(from_file
));
203 EXPECT_FALSE(base::PathExists(to_file
));
204 EXPECT_EQ(0, ReadTextFile(from_file
).compare(kTextContent1
));
207 // Move one file from source to destination when destination already
209 TEST_F(MoveTreeWorkItemTest
, MoveFileDestExists
) {
210 // Create a file inside source dir
211 base::FilePath
from_dir(temp_from_dir_
.path());
212 from_dir
= from_dir
.AppendASCII("From_Dir");
213 base::CreateDirectory(from_dir
);
214 ASSERT_TRUE(base::PathExists(from_dir
));
216 base::FilePath
from_file(from_dir
);
217 from_file
= from_file
.AppendASCII("From_File");
218 CreateTextFile(from_file
.value(), kTextContent1
);
219 ASSERT_TRUE(base::PathExists(from_file
));
221 // Create destination path
222 base::FilePath
to_dir(temp_from_dir_
.path());
223 to_dir
= to_dir
.AppendASCII("To_Dir");
224 base::CreateDirectory(to_dir
);
225 ASSERT_TRUE(base::PathExists(to_dir
));
227 base::FilePath
to_file(to_dir
);
228 to_file
= to_file
.AppendASCII("To_File");
229 CreateTextFile(to_file
.value(), kTextContent2
);
230 ASSERT_TRUE(base::PathExists(to_file
));
233 scoped_ptr
<MoveTreeWorkItem
> work_item(
234 WorkItem::CreateMoveTreeWorkItem(from_file
,
237 WorkItem::ALWAYS_MOVE
));
238 EXPECT_TRUE(work_item
->Do());
240 EXPECT_TRUE(base::PathExists(from_dir
));
241 EXPECT_FALSE(base::PathExists(from_file
));
242 EXPECT_TRUE(base::PathExists(to_dir
));
243 EXPECT_FALSE(base::PathExists(to_file
));
244 EXPECT_EQ(0, ReadTextFile(to_dir
).compare(kTextContent1
));
247 work_item
->Rollback();
249 EXPECT_TRUE(base::PathExists(from_dir
));
250 EXPECT_EQ(0, ReadTextFile(from_file
).compare(kTextContent1
));
251 EXPECT_TRUE(base::PathExists(to_dir
));
252 EXPECT_EQ(0, ReadTextFile(to_file
).compare(kTextContent2
));
255 // Move one file from source to destination when destination already
256 // exists and is in use.
257 TEST_F(MoveTreeWorkItemTest
, MoveFileDestInUse
) {
258 // Create a file inside source dir
259 base::FilePath
from_dir(temp_from_dir_
.path());
260 from_dir
= from_dir
.AppendASCII("From_Dir");
261 base::CreateDirectory(from_dir
);
262 ASSERT_TRUE(base::PathExists(from_dir
));
264 base::FilePath
from_file(from_dir
);
265 from_file
= from_file
.AppendASCII("From_File");
266 CreateTextFile(from_file
.value(), kTextContent1
);
267 ASSERT_TRUE(base::PathExists(from_file
));
269 // Create an executable in destination path by copying ourself to it.
270 base::FilePath
to_dir(temp_from_dir_
.path());
271 to_dir
= to_dir
.AppendASCII("To_Dir");
272 base::CreateDirectory(to_dir
);
273 ASSERT_TRUE(base::PathExists(to_dir
));
275 wchar_t exe_full_path_str
[MAX_PATH
];
276 ::GetModuleFileName(NULL
, exe_full_path_str
, MAX_PATH
);
277 base::FilePath
exe_full_path(exe_full_path_str
);
278 base::FilePath
to_file(to_dir
);
279 to_file
= to_file
.AppendASCII("To_File");
280 base::CopyFile(exe_full_path
, to_file
);
281 ASSERT_TRUE(base::PathExists(to_file
));
283 // Run the executable in destination path
284 STARTUPINFOW si
= {sizeof(si
)};
285 PROCESS_INFORMATION pi
= {0};
286 ASSERT_TRUE(::CreateProcess(NULL
,
287 const_cast<wchar_t*>(to_file
.value().c_str()),
289 CREATE_NO_WINDOW
| CREATE_SUSPENDED
,
290 NULL
, NULL
, &si
, &pi
));
293 scoped_ptr
<MoveTreeWorkItem
> work_item(
294 WorkItem::CreateMoveTreeWorkItem(from_file
,
297 WorkItem::ALWAYS_MOVE
));
298 EXPECT_TRUE(work_item
->Do());
300 EXPECT_TRUE(base::PathExists(from_dir
));
301 EXPECT_FALSE(base::PathExists(from_file
));
302 EXPECT_TRUE(base::PathExists(to_dir
));
303 EXPECT_EQ(0, ReadTextFile(to_file
).compare(kTextContent1
));
306 work_item
->Rollback();
308 EXPECT_TRUE(base::PathExists(from_dir
));
309 EXPECT_EQ(0, ReadTextFile(from_file
).compare(kTextContent1
));
310 EXPECT_TRUE(base::PathExists(to_dir
));
311 EXPECT_TRUE(base::ContentsEqual(exe_full_path
, to_file
));
313 TerminateProcess(pi
.hProcess
, 0);
314 EXPECT_TRUE(WaitForSingleObject(pi
.hProcess
, 10000) == WAIT_OBJECT_0
);
315 CloseHandle(pi
.hProcess
);
316 CloseHandle(pi
.hThread
);
319 // Move one file that is in use to destination.
320 TEST_F(MoveTreeWorkItemTest
, MoveFileInUse
) {
321 // Create an executable for source by copying ourself to a new source dir.
322 base::FilePath
from_dir(temp_from_dir_
.path());
323 from_dir
= from_dir
.AppendASCII("From_Dir");
324 base::CreateDirectory(from_dir
);
325 ASSERT_TRUE(base::PathExists(from_dir
));
327 wchar_t exe_full_path_str
[MAX_PATH
];
328 ::GetModuleFileName(NULL
, exe_full_path_str
, MAX_PATH
);
329 base::FilePath
exe_full_path(exe_full_path_str
);
330 base::FilePath
from_file(from_dir
);
331 from_file
= from_file
.AppendASCII("From_File");
332 base::CopyFile(exe_full_path
, from_file
);
333 ASSERT_TRUE(base::PathExists(from_file
));
335 // Create a destination source dir and generate destination file name.
336 base::FilePath
to_dir(temp_from_dir_
.path());
337 to_dir
= to_dir
.AppendASCII("To_Dir");
338 base::CreateDirectory(to_dir
);
339 ASSERT_TRUE(base::PathExists(to_dir
));
341 base::FilePath
to_file(to_dir
);
342 to_file
= to_file
.AppendASCII("To_File");
343 CreateTextFile(to_file
.value(), kTextContent1
);
344 ASSERT_TRUE(base::PathExists(to_file
));
346 // Run the executable in source path
347 STARTUPINFOW si
= {sizeof(si
)};
348 PROCESS_INFORMATION pi
= {0};
349 ASSERT_TRUE(::CreateProcess(NULL
,
350 const_cast<wchar_t*>(from_file
.value().c_str()),
352 CREATE_NO_WINDOW
| CREATE_SUSPENDED
,
353 NULL
, NULL
, &si
, &pi
));
356 scoped_ptr
<MoveTreeWorkItem
> work_item(
357 WorkItem::CreateMoveTreeWorkItem(from_file
,
360 WorkItem::ALWAYS_MOVE
));
361 EXPECT_TRUE(work_item
->Do());
363 EXPECT_TRUE(base::PathExists(from_dir
));
364 EXPECT_FALSE(base::PathExists(from_file
));
365 EXPECT_TRUE(base::PathExists(to_dir
));
366 EXPECT_TRUE(base::ContentsEqual(exe_full_path
, to_file
));
368 // Close the process and make sure all the conditions after Do() are
370 TerminateProcess(pi
.hProcess
, 0);
371 EXPECT_TRUE(WaitForSingleObject(pi
.hProcess
, 10000) == WAIT_OBJECT_0
);
372 CloseHandle(pi
.hProcess
);
373 CloseHandle(pi
.hThread
);
375 EXPECT_TRUE(base::PathExists(from_dir
));
376 EXPECT_FALSE(base::PathExists(from_file
));
377 EXPECT_TRUE(base::PathExists(to_dir
));
378 EXPECT_TRUE(base::ContentsEqual(exe_full_path
, to_file
));
381 work_item
->Rollback();
383 EXPECT_TRUE(base::PathExists(from_dir
));
384 EXPECT_TRUE(base::ContentsEqual(exe_full_path
, from_file
));
385 EXPECT_TRUE(base::PathExists(to_dir
));
386 EXPECT_EQ(0, ReadTextFile(to_file
).compare(kTextContent1
));
389 // Move one directory from source to destination when destination already
391 TEST_F(MoveTreeWorkItemTest
, MoveDirectoryDestExistsCheckForDuplicatesFull
) {
392 // Create two level deep source dir
393 base::FilePath
from_dir1(temp_from_dir_
.path());
394 from_dir1
= from_dir1
.AppendASCII("From_Dir1");
395 base::CreateDirectory(from_dir1
);
396 ASSERT_TRUE(base::PathExists(from_dir1
));
398 base::FilePath
from_dir2(from_dir1
);
399 from_dir2
= from_dir2
.AppendASCII("From_Dir2");
400 base::CreateDirectory(from_dir2
);
401 ASSERT_TRUE(base::PathExists(from_dir2
));
403 base::FilePath
from_file(from_dir2
);
404 from_file
= from_file
.AppendASCII("From_File");
405 CreateTextFile(from_file
.value(), kTextContent1
);
406 ASSERT_TRUE(base::PathExists(from_file
));
408 // // Create a file hierarchy identical to the one in the source directory.
409 base::FilePath
to_dir(temp_from_dir_
.path());
410 to_dir
= to_dir
.AppendASCII("To_Dir");
411 ASSERT_TRUE(installer::test::CopyFileHierarchy(from_dir1
, to_dir
));
413 // Lock one of the files in the to destination directory to prevent moves.
414 base::FilePath
orig_to_file(
415 to_dir
.AppendASCII("From_Dir2").AppendASCII("From_File"));
416 base::MemoryMappedFile mapped_file
;
417 EXPECT_TRUE(mapped_file
.Initialize(orig_to_file
));
419 // First check that we can't do the regular Move().
420 scoped_ptr
<MoveTreeWorkItem
> work_item(
421 WorkItem::CreateMoveTreeWorkItem(from_dir1
,
424 WorkItem::ALWAYS_MOVE
));
425 EXPECT_FALSE(work_item
->Do());
426 work_item
->Rollback();
428 // Now test Do() with the check for duplicates. This should pass.
430 WorkItem::CreateMoveTreeWorkItem(from_dir1
,
433 WorkItem::CHECK_DUPLICATES
));
434 EXPECT_TRUE(work_item
->Do());
436 // Make sure that we "moved" the files, i.e. that the source directory isn't
438 EXPECT_FALSE(base::PathExists(from_dir1
));
439 // Make sure that the original directory structure and file are still present.
440 EXPECT_TRUE(base::PathExists(to_dir
));
441 EXPECT_TRUE(base::PathExists(orig_to_file
));
442 // Make sure that the backup path is not empty.
443 EXPECT_FALSE(base::IsDirectoryEmpty(temp_to_dir_
.path()));
445 // Check that the work item believes the source to have been moved.
446 EXPECT_TRUE(work_item
->source_moved_to_backup_
);
447 EXPECT_FALSE(work_item
->moved_to_dest_path_
);
448 EXPECT_FALSE(work_item
->moved_to_backup_
);
451 work_item
->Rollback();
453 // Once we rollback all the original files should still be there, as should
455 EXPECT_TRUE(base::PathExists(from_dir1
));
456 EXPECT_TRUE(base::PathExists(to_dir
));
457 EXPECT_TRUE(base::PathExists(orig_to_file
));
458 EXPECT_EQ(0, ReadTextFile(orig_to_file
).compare(kTextContent1
));
459 EXPECT_EQ(0, ReadTextFile(from_file
).compare(kTextContent1
));
462 // Move one directory from source to destination when destination already
463 // exists but contains only a subset of the files in source.
464 TEST_F(MoveTreeWorkItemTest
, MoveDirectoryDestExistsCheckForDuplicatesPartial
) {
465 // Create two level deep source dir
466 base::FilePath
from_dir1(temp_from_dir_
.path());
467 from_dir1
= from_dir1
.AppendASCII("From_Dir1");
468 base::CreateDirectory(from_dir1
);
469 ASSERT_TRUE(base::PathExists(from_dir1
));
471 base::FilePath
from_dir2(from_dir1
);
472 from_dir2
= from_dir2
.AppendASCII("From_Dir2");
473 base::CreateDirectory(from_dir2
);
474 ASSERT_TRUE(base::PathExists(from_dir2
));
476 base::FilePath
from_file(from_dir2
);
477 from_file
= from_file
.AppendASCII("From_File");
478 CreateTextFile(from_file
.value(), kTextContent1
);
479 ASSERT_TRUE(base::PathExists(from_file
));
481 base::FilePath
from_file2(from_dir2
);
482 from_file2
= from_file2
.AppendASCII("From_File2");
483 CreateTextFile(from_file2
.value(), kTextContent2
);
484 ASSERT_TRUE(base::PathExists(from_file2
));
486 // Create destination path
487 base::FilePath
to_dir(temp_from_dir_
.path());
488 to_dir
= to_dir
.AppendASCII("To_Dir");
489 base::CreateDirectory(to_dir
);
490 ASSERT_TRUE(base::PathExists(to_dir
));
492 // Create a sub-directory of the same name as in the source directory.
493 base::FilePath
to_dir2(to_dir
);
494 to_dir2
= to_dir2
.AppendASCII("From_Dir2");
495 base::CreateDirectory(to_dir2
);
496 ASSERT_TRUE(base::PathExists(to_dir2
));
498 // Create one of the files in the to sub-directory, but not the other.
499 base::FilePath
orig_to_file(to_dir2
);
500 orig_to_file
= orig_to_file
.AppendASCII("From_File");
501 CreateTextFile(orig_to_file
.value(), kTextContent1
);
502 ASSERT_TRUE(base::PathExists(orig_to_file
));
504 // test Do(), check for duplicates.
505 scoped_ptr
<MoveTreeWorkItem
> work_item(
506 WorkItem::CreateMoveTreeWorkItem(from_dir1
,
509 WorkItem::CHECK_DUPLICATES
));
510 EXPECT_TRUE(work_item
->Do());
512 // Make sure that we "moved" the files, i.e. that the source directory isn't
514 EXPECT_FALSE(base::PathExists(from_dir1
));
515 // Make sure that the original directory structure and file are still present.
516 EXPECT_TRUE(base::PathExists(to_dir
));
517 EXPECT_TRUE(base::PathExists(orig_to_file
));
518 // Make sure that the backup path is not empty.
519 EXPECT_FALSE(base::IsDirectoryEmpty(temp_to_dir_
.path()));
520 // Make sure that the "new" file is also present.
521 base::FilePath
new_to_file2(to_dir2
);
522 new_to_file2
= new_to_file2
.AppendASCII("From_File2");
523 EXPECT_TRUE(base::PathExists(new_to_file2
));
525 // Check that the work item believes that this was a regular move.
526 EXPECT_FALSE(work_item
->source_moved_to_backup_
);
527 EXPECT_TRUE(work_item
->moved_to_dest_path_
);
528 EXPECT_TRUE(work_item
->moved_to_backup_
);
531 work_item
->Rollback();
533 // Once we rollback all the original files should still be there, as should
535 EXPECT_TRUE(base::PathExists(from_dir1
));
536 EXPECT_TRUE(base::PathExists(to_dir
));
537 EXPECT_TRUE(base::PathExists(orig_to_file
));
538 EXPECT_EQ(0, ReadTextFile(orig_to_file
).compare(kTextContent1
));
539 EXPECT_EQ(0, ReadTextFile(from_file
).compare(kTextContent1
));
541 // Also, after rollback the new "to" file should be gone.
542 EXPECT_FALSE(base::PathExists(new_to_file2
));