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.
8 #include "base/files/file_util.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_util.h"
11 #include "base/win/registry.h"
12 #include "chrome/installer/util/delete_after_reboot_helper.h"
13 #include "testing/gtest/include/gtest/gtest.h"
17 // These tests exercise the Delete-After-Reboot code which requires
18 // modifications to HKLM. This will fail on Vista and above if the user
19 // is not an admin or if UAC is on.
20 // I tried using RegOverridePredefKey to test, but MoveFileEx ignore this
21 // even on 32 bit machines :-( As such, running this test may pollute
22 // your PendingFileRenameOperations value.
23 class DeleteAfterRebootHelperTest
: public testing::Test
{
25 void SetUp() override
{
26 // Create a temporary directory for testing and fill it with some files.
27 base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_dir_
);
28 base::CreateTemporaryFileInDir(temp_dir_
, &temp_file_
);
30 temp_subdir_
= temp_dir_
.Append(L
"subdir");
31 base::CreateDirectory(temp_subdir_
);
32 base::CreateTemporaryFileInDir(temp_subdir_
, &temp_subdir_file_
);
34 // Copy the current pending moves and then clear it if we can:
35 if (IsUserAnAdmin()) {
36 GetPendingMovesValue(&original_pending_moves_
);
39 void TearDown() override
{
40 // Delete the temporary directory if it's still there.
41 base::DeleteFile(temp_dir_
, true);
43 // Try and restore the pending moves value, if we have one.
44 if (IsUserAnAdmin() && original_pending_moves_
.size() > 1) {
45 base::win::RegKey
session_manager_key(
46 HKEY_LOCAL_MACHINE
, kSessionManagerKey
,
47 KEY_CREATE_SUB_KEY
| KEY_SET_VALUE
);
48 if (!session_manager_key
.Handle()) {
49 // Couldn't open / create the key.
50 DLOG(ERROR
) << "Failed to open session manager key for writing.";
53 std::vector
<char> buffer
;
54 StringArrayToMultiSZBytes(original_pending_moves_
, &buffer
);
55 session_manager_key
.WriteValue(kPendingFileRenameOps
, &buffer
[0],
56 static_cast<int>(buffer
.size()),
61 // Compares two buffers of size len. Returns true if they are equal,
62 // false otherwise. Standard warnings about making sure the buffers
63 // are at least len chars long apply.
65 bool CompareBuffers(Type
* buf1
, Type
* buf2
, int len
) {
68 for (int i
= 0; i
< len
; i
++) {
77 // Returns the size of the given list of wstrings in bytes, including
78 // null chars, plus an additional terminating null char.
79 // e.g. the length of all the strings * sizeof(wchar_t).
80 virtual size_t WStringPairListSize(
81 const std::vector
<PendingMove
>& string_list
) {
83 std::vector
<PendingMove
>::const_iterator
iter(string_list
.begin());
84 for (; iter
!= string_list
.end(); ++iter
) {
85 length
+= iter
->first
.size() + 1; // +1 for the null char.
86 length
+= iter
->second
.size() + 1; // +1 for the null char.
88 length
++; // for the additional null char.
89 return length
* sizeof(wchar_t);
92 std::vector
<PendingMove
> original_pending_moves_
;
94 base::FilePath temp_dir_
;
95 base::FilePath temp_file_
;
96 base::FilePath temp_subdir_
;
97 base::FilePath temp_subdir_file_
;
102 TEST_F(DeleteAfterRebootHelperTest
, TestStringListToMultiSZConversions
) {
104 const wchar_t* test_name
;
109 { L
"basic", L
"foo\0bar\0fee\0bee\0boo\0bong\0\0", 26 * sizeof(wchar_t), 3 },
110 { L
"empty", L
"\0\0", 2 * sizeof(wchar_t), 1 },
111 { L
"deletes", L
"foo\0\0bar\0\0bizz\0\0", 16 * sizeof(wchar_t), 3 },
114 for (int i
= 0; i
< arraysize(tests
); i
++) {
115 std::vector
<PendingMove
> string_list
;
116 EXPECT_TRUE(SUCCEEDED(
117 MultiSZBytesToStringArray(reinterpret_cast<const char*>(tests
[i
].str
),
120 << tests
[i
].test_name
;
121 EXPECT_EQ(tests
[i
].count
, string_list
.size()) << tests
[i
].test_name
;
122 std::vector
<char> buffer
;
123 buffer
.resize(WStringPairListSize(string_list
));
124 StringArrayToMultiSZBytes(string_list
, &buffer
);
125 EXPECT_TRUE(CompareBuffers(const_cast<const char*>(&buffer
[0]),
126 reinterpret_cast<const char*>(tests
[i
].str
),
128 << tests
[i
].test_name
;
131 StringTest failures
[] = {
132 L
"malformed", reinterpret_cast<const wchar_t*>("oddnumb\0\0"), 9, 1};
134 for (int i
= 0; i
< arraysize(failures
); i
++) {
135 std::vector
<PendingMove
> string_list
;
136 EXPECT_FALSE(SUCCEEDED(MultiSZBytesToStringArray(
137 reinterpret_cast<const char*>(failures
[i
].str
),
140 << failures
[i
].test_name
;
145 TEST_F(DeleteAfterRebootHelperTest
, TestFileDeleteScheduleAndUnschedule
) {
146 if (!IsUserAnAdmin()) {
150 EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_
));
152 std::vector
<PendingMove
> pending_moves
;
153 EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves
)));
155 // We should see, somewhere in this key, deletion writs for
156 // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order.
157 EXPECT_GT(pending_moves
.size(), 3U);
159 // Get the short form of temp_file_ and use that to match.
160 base::FilePath
short_temp_file(GetShortPathName(temp_file_
));
162 // Scan for the first expected delete.
163 std::vector
<PendingMove
>::const_iterator
iter(pending_moves
.begin());
164 for (; iter
!= pending_moves
.end(); iter
++) {
165 base::FilePath
move_path(iter
->first
);
166 if (MatchPendingDeletePath(short_temp_file
, move_path
))
170 // Check that each of the deletes we expect are there in order.
171 base::FilePath expected_paths
[] =
172 { temp_file_
, temp_subdir_file_
, temp_subdir_
, temp_dir_
};
173 for (int i
= 0; i
< arraysize(expected_paths
); ++i
) {
174 EXPECT_FALSE(iter
== pending_moves
.end());
175 if (iter
!= pending_moves
.end()) {
176 base::FilePath
short_path_name(GetShortPathName(expected_paths
[i
]));
177 base::FilePath
move_path(iter
->first
);
178 EXPECT_TRUE(MatchPendingDeletePath(short_path_name
, move_path
));
183 // Test that we can remove the pending deletes.
184 EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_
));
185 HRESULT hr
= GetPendingMovesValue(&pending_moves
);
186 EXPECT_TRUE(hr
== S_OK
|| hr
== HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
));
188 std::vector
<PendingMove
>::const_iterator
check_iter(pending_moves
.begin());
189 for (; check_iter
!= pending_moves
.end(); ++check_iter
) {
190 base::FilePath
move_path(check_iter
->first
);
191 EXPECT_FALSE(MatchPendingDeletePath(short_temp_file
, move_path
));
195 TEST_F(DeleteAfterRebootHelperTest
, TestFileDeleteSchedulingWithActualDeletes
) {
196 if (!IsUserAnAdmin()) {
200 std::vector
<PendingMove
> initial_pending_moves
;
201 GetPendingMovesValue(&initial_pending_moves
);
202 size_t initial_pending_moves_size
= initial_pending_moves
.size();
204 EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_
));
206 std::vector
<PendingMove
> pending_moves
;
207 EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves
)));
209 // We should see, somewhere in this key, deletion writs for
210 // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order.
211 EXPECT_TRUE(pending_moves
.size() > 3);
213 // Get the short form of temp_file_ and use that to match.
214 base::FilePath
short_temp_file(GetShortPathName(temp_file_
));
216 // Scan for the first expected delete.
217 std::vector
<PendingMove
>::const_iterator
iter(pending_moves
.begin());
218 for (; iter
!= pending_moves
.end(); iter
++) {
219 base::FilePath
move_path(iter
->first
);
220 if (MatchPendingDeletePath(short_temp_file
, move_path
))
224 // Check that each of the deletes we expect are there in order.
225 base::FilePath expected_paths
[] =
226 { temp_file_
, temp_subdir_file_
, temp_subdir_
, temp_dir_
};
227 for (int i
= 0; i
< arraysize(expected_paths
); ++i
) {
228 EXPECT_FALSE(iter
== pending_moves
.end());
229 if (iter
!= pending_moves
.end()) {
230 base::FilePath
short_path_name(GetShortPathName(expected_paths
[i
]));
231 base::FilePath
move_path(iter
->first
);
232 EXPECT_TRUE(MatchPendingDeletePath(short_path_name
, move_path
));
237 // Delete the temporary directory.
238 base::DeleteFile(temp_dir_
, true);
240 // Test that we can remove the pending deletes.
241 EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_
));
242 HRESULT hr
= GetPendingMovesValue(&pending_moves
);
243 EXPECT_TRUE(hr
== S_OK
|| hr
== HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
));
245 EXPECT_EQ(initial_pending_moves_size
, pending_moves
.size());
247 std::vector
<PendingMove
>::const_iterator
check_iter(pending_moves
.begin());
248 for (; check_iter
!= pending_moves
.end(); ++check_iter
) {
249 base::FilePath
move_path(check_iter
->first
);
250 EXPECT_FALSE(MatchPendingDeletePath(short_temp_file
, move_path
));