Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / installer / util / delete_after_reboot_helper_unittest.cc
blob867106e81808d3f643778e8e7f202a78d98c8932
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>
6 #include <shlobj.h>
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"
15 namespace {
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 {
24 protected:
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()),
57 REG_MULTI_SZ);
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.
64 template<class Type>
65 bool CompareBuffers(Type* buf1, Type* buf2, int len) {
66 Type* comp1 = buf1;
67 Type* comp2 = buf2;
68 for (int i = 0; i < len; i++) {
69 if (*comp1 != *comp2)
70 return false;
71 comp1++;
72 comp2++;
74 return true;
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) {
82 size_t length = 0;
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_;
100 } // namespace
102 TEST_F(DeleteAfterRebootHelperTest, TestStringListToMultiSZConversions) {
103 struct StringTest {
104 const wchar_t* test_name;
105 const wchar_t* str;
106 DWORD length;
107 size_t count;
108 } tests[] = {
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),
118 tests[i].length,
119 &string_list)))
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),
127 tests[i].length))
128 << tests[i].test_name;
131 StringTest failures[] = {
132 {L"malformed", reinterpret_cast<const wchar_t*>("oddnumb\0\0"), 9, 1},
135 for (int i = 0; i < arraysize(failures); i++) {
136 std::vector<PendingMove> string_list;
137 EXPECT_FALSE(SUCCEEDED(MultiSZBytesToStringArray(
138 reinterpret_cast<const char*>(failures[i].str),
139 failures[i].length,
140 &string_list)))
141 << failures[i].test_name;
146 TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteScheduleAndUnschedule) {
147 if (!IsUserAnAdmin()) {
148 return;
151 EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_));
153 std::vector<PendingMove> pending_moves;
154 EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves)));
156 // We should see, somewhere in this key, deletion writs for
157 // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order.
158 EXPECT_GT(pending_moves.size(), 3U);
160 // Get the short form of temp_file_ and use that to match.
161 base::FilePath short_temp_file(GetShortPathName(temp_file_));
163 // Scan for the first expected delete.
164 std::vector<PendingMove>::const_iterator iter(pending_moves.begin());
165 for (; iter != pending_moves.end(); iter++) {
166 base::FilePath move_path(iter->first);
167 if (MatchPendingDeletePath(short_temp_file, move_path))
168 break;
171 // Check that each of the deletes we expect are there in order.
172 base::FilePath expected_paths[] =
173 { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ };
174 for (int i = 0; i < arraysize(expected_paths); ++i) {
175 EXPECT_FALSE(iter == pending_moves.end());
176 if (iter != pending_moves.end()) {
177 base::FilePath short_path_name(GetShortPathName(expected_paths[i]));
178 base::FilePath move_path(iter->first);
179 EXPECT_TRUE(MatchPendingDeletePath(short_path_name, move_path));
180 ++iter;
184 // Test that we can remove the pending deletes.
185 EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_));
186 HRESULT hr = GetPendingMovesValue(&pending_moves);
187 EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
189 std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin());
190 for (; check_iter != pending_moves.end(); ++check_iter) {
191 base::FilePath move_path(check_iter->first);
192 EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, move_path));
196 TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteSchedulingWithActualDeletes) {
197 if (!IsUserAnAdmin()) {
198 return;
201 std::vector<PendingMove> initial_pending_moves;
202 GetPendingMovesValue(&initial_pending_moves);
203 size_t initial_pending_moves_size = initial_pending_moves.size();
205 EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_));
207 std::vector<PendingMove> pending_moves;
208 EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves)));
210 // We should see, somewhere in this key, deletion writs for
211 // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order.
212 EXPECT_TRUE(pending_moves.size() > 3);
214 // Get the short form of temp_file_ and use that to match.
215 base::FilePath short_temp_file(GetShortPathName(temp_file_));
217 // Scan for the first expected delete.
218 std::vector<PendingMove>::const_iterator iter(pending_moves.begin());
219 for (; iter != pending_moves.end(); iter++) {
220 base::FilePath move_path(iter->first);
221 if (MatchPendingDeletePath(short_temp_file, move_path))
222 break;
225 // Check that each of the deletes we expect are there in order.
226 base::FilePath expected_paths[] =
227 { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ };
228 for (int i = 0; i < arraysize(expected_paths); ++i) {
229 EXPECT_FALSE(iter == pending_moves.end());
230 if (iter != pending_moves.end()) {
231 base::FilePath short_path_name(GetShortPathName(expected_paths[i]));
232 base::FilePath move_path(iter->first);
233 EXPECT_TRUE(MatchPendingDeletePath(short_path_name, move_path));
234 ++iter;
238 // Delete the temporary directory.
239 base::DeleteFile(temp_dir_, true);
241 // Test that we can remove the pending deletes.
242 EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_));
243 HRESULT hr = GetPendingMovesValue(&pending_moves);
244 EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
246 EXPECT_EQ(initial_pending_moves_size, pending_moves.size());
248 std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin());
249 for (; check_iter != pending_moves.end(); ++check_iter) {
250 base::FilePath move_path(check_iter->first);
251 EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, move_path));