delete_after_reboot_helper_unittest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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. 4 5#include <windows.h> 6#include <shlobj.h> 7 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" 14 15namespace { 16 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. 23class DeleteAfterRebootHelperTest : public testing::Test { 24 protected: 25 virtual void SetUp() { 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_); 29 30 temp_subdir_ = temp_dir_.Append(L"subdir"); 31 base::CreateDirectory(temp_subdir_); 32 base::CreateTemporaryFileInDir(temp_subdir_, &temp_subdir_file_); 33 34 // Copy the current pending moves and then clear it if we can: 35 if (IsUserAnAdmin()) { 36 GetPendingMovesValue(&original_pending_moves_); 37 } 38 } 39 virtual void TearDown() { 40 // Delete the temporary directory if it's still there. 41 base::DeleteFile(temp_dir_, true); 42 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."; 51 } 52 53 std::vector<char> buffer; 54 StringArrayToMultiSZBytes(original_pending_moves_, &buffer); 55 session_manager_key.WriteValue(kPendingFileRenameOps, &buffer[0], 56 buffer.size(), REG_MULTI_SZ); 57 } 58 } 59 60 // Compares two buffers of size len. Returns true if they are equal, 61 // false otherwise. Standard warnings about making sure the buffers 62 // are at least len chars long apply. 63 template<class Type> 64 bool CompareBuffers(Type* buf1, Type* buf2, int len) { 65 Type* comp1 = buf1; 66 Type* comp2 = buf2; 67 for (int i = 0; i < len; i++) { 68 if (*comp1 != *comp2) 69 return false; 70 comp1++; 71 comp2++; 72 } 73 return true; 74 } 75 76 // Returns the size of the given list of wstrings in bytes, including 77 // null chars, plus an additional terminating null char. 78 // e.g. the length of all the strings * sizeof(wchar_t). 79 virtual size_t WStringPairListSize( 80 const std::vector<PendingMove>& string_list) { 81 size_t length = 0; 82 std::vector<PendingMove>::const_iterator iter(string_list.begin()); 83 for (; iter != string_list.end(); ++iter) { 84 length += iter->first.size() + 1; // +1 for the null char. 85 length += iter->second.size() + 1; // +1 for the null char. 86 } 87 length++; // for the additional null char. 88 return length * sizeof(wchar_t); 89 } 90 91 std::vector<PendingMove> original_pending_moves_; 92 93 base::FilePath temp_dir_; 94 base::FilePath temp_file_; 95 base::FilePath temp_subdir_; 96 base::FilePath temp_subdir_file_; 97}; 98 99} // namespace 100 101TEST_F(DeleteAfterRebootHelperTest, TestStringListToMultiSZConversions) { 102 struct StringTest { 103 const wchar_t* test_name; 104 const wchar_t* str; 105 DWORD length; 106 size_t count; 107 } tests[] = { 108 { L"basic", L"foo\0bar\0fee\0bee\0boo\0bong\0\0", 26 * sizeof(wchar_t), 3 }, 109 { L"empty", L"\0\0", 2 * sizeof(wchar_t), 1 }, 110 { L"deletes", L"foo\0\0bar\0\0bizz\0\0", 16 * sizeof(wchar_t), 3 }, 111 }; 112 113 for (int i = 0; i < arraysize(tests); i++) { 114 std::vector<PendingMove> string_list; 115 EXPECT_TRUE(SUCCEEDED( 116 MultiSZBytesToStringArray(reinterpret_cast<const char*>(tests[i].str), 117 tests[i].length, 118 &string_list))) 119 << tests[i].test_name; 120 EXPECT_EQ(tests[i].count, string_list.size()) << tests[i].test_name; 121 std::vector<char> buffer; 122 buffer.resize(WStringPairListSize(string_list)); 123 StringArrayToMultiSZBytes(string_list, &buffer); 124 EXPECT_TRUE(CompareBuffers(const_cast<const char*>(&buffer[0]), 125 reinterpret_cast<const char*>(tests[i].str), 126 tests[i].length)) 127 << tests[i].test_name; 128 } 129 130 StringTest failures[] = { 131 L"malformed", reinterpret_cast<const wchar_t*>("oddnumb\0\0"), 9, 1}; 132 133 for (int i = 0; i < arraysize(failures); i++) { 134 std::vector<PendingMove> string_list; 135 EXPECT_FALSE(SUCCEEDED(MultiSZBytesToStringArray( 136 reinterpret_cast<const char*>(failures[i].str), 137 failures[i].length, 138 &string_list))) 139 << failures[i].test_name; 140 } 141} 142 143 144TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteScheduleAndUnschedule) { 145 if (!IsUserAnAdmin()) { 146 return; 147 } 148 149 EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_)); 150 151 std::vector<PendingMove> pending_moves; 152 EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves))); 153 154 // We should see, somewhere in this key, deletion writs for 155 // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order. 156 EXPECT_GT(pending_moves.size(), 3U); 157 158 // Get the short form of temp_file_ and use that to match. 159 base::FilePath short_temp_file(GetShortPathName(temp_file_)); 160 161 // Scan for the first expected delete. 162 std::vector<PendingMove>::const_iterator iter(pending_moves.begin()); 163 for (; iter != pending_moves.end(); iter++) { 164 base::FilePath move_path(iter->first); 165 if (MatchPendingDeletePath(short_temp_file, move_path)) 166 break; 167 } 168 169 // Check that each of the deletes we expect are there in order. 170 base::FilePath expected_paths[] = 171 { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ }; 172 for (int i = 0; i < arraysize(expected_paths); ++i) { 173 EXPECT_FALSE(iter == pending_moves.end()); 174 if (iter != pending_moves.end()) { 175 base::FilePath short_path_name(GetShortPathName(expected_paths[i])); 176 base::FilePath move_path(iter->first); 177 EXPECT_TRUE(MatchPendingDeletePath(short_path_name, move_path)); 178 ++iter; 179 } 180 } 181 182 // Test that we can remove the pending deletes. 183 EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_)); 184 HRESULT hr = GetPendingMovesValue(&pending_moves); 185 EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); 186 187 std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin()); 188 for (; check_iter != pending_moves.end(); ++check_iter) { 189 base::FilePath move_path(check_iter->first); 190 EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, move_path)); 191 } 192} 193 194TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteSchedulingWithActualDeletes) { 195 if (!IsUserAnAdmin()) { 196 return; 197 } 198 199 std::vector<PendingMove> initial_pending_moves; 200 GetPendingMovesValue(&initial_pending_moves); 201 size_t initial_pending_moves_size = initial_pending_moves.size(); 202 203 EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_)); 204 205 std::vector<PendingMove> pending_moves; 206 EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves))); 207 208 // We should see, somewhere in this key, deletion writs for 209 // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order. 210 EXPECT_TRUE(pending_moves.size() > 3); 211 212 // Get the short form of temp_file_ and use that to match. 213 base::FilePath short_temp_file(GetShortPathName(temp_file_)); 214 215 // Scan for the first expected delete. 216 std::vector<PendingMove>::const_iterator iter(pending_moves.begin()); 217 for (; iter != pending_moves.end(); iter++) { 218 base::FilePath move_path(iter->first); 219 if (MatchPendingDeletePath(short_temp_file, move_path)) 220 break; 221 } 222 223 // Check that each of the deletes we expect are there in order. 224 base::FilePath expected_paths[] = 225 { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ }; 226 for (int i = 0; i < arraysize(expected_paths); ++i) { 227 EXPECT_FALSE(iter == pending_moves.end()); 228 if (iter != pending_moves.end()) { 229 base::FilePath short_path_name(GetShortPathName(expected_paths[i])); 230 base::FilePath move_path(iter->first); 231 EXPECT_TRUE(MatchPendingDeletePath(short_path_name, move_path)); 232 ++iter; 233 } 234 } 235 236 // Delete the temporary directory. 237 base::DeleteFile(temp_dir_, true); 238 239 // Test that we can remove the pending deletes. 240 EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_)); 241 HRESULT hr = GetPendingMovesValue(&pending_moves); 242 EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); 243 244 EXPECT_EQ(initial_pending_moves_size, pending_moves.size()); 245 246 std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin()); 247 for (; check_iter != pending_moves.end(); ++check_iter) { 248 base::FilePath move_path(check_iter->first); 249 EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, move_path)); 250 } 251} 252 253