delete_after_reboot_helper_unittest.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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/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 std::wstring no_prefix; 28 file_util::CreateNewTempDirectory(no_prefix, &temp_dir_); 29 file_util::CreateTemporaryFileInDir(temp_dir_, &temp_file_); 30 31 temp_subdir_ = temp_dir_.Append(L"subdir"); 32 file_util::CreateDirectory(temp_subdir_); 33 file_util::CreateTemporaryFileInDir(temp_subdir_, &temp_subdir_file_); 34 35 // Copy the current pending moves and then clear it if we can: 36 if (IsUserAnAdmin()) { 37 GetPendingMovesValue(&original_pending_moves_); 38 } 39 } 40 virtual void TearDown() { 41 // Delete the temporary directory if it's still there. 42 base::Delete(temp_dir_, true); 43 44 // Try and restore the pending moves value, if we have one. 45 if (IsUserAnAdmin() && original_pending_moves_.size() > 1) { 46 base::win::RegKey session_manager_key( 47 HKEY_LOCAL_MACHINE, kSessionManagerKey, 48 KEY_CREATE_SUB_KEY | KEY_SET_VALUE); 49 if (!session_manager_key.Handle()) { 50 // Couldn't open / create the key. 51 DLOG(ERROR) << "Failed to open session manager key for writing."; 52 } 53 54 std::vector<char> buffer; 55 StringArrayToMultiSZBytes(original_pending_moves_, &buffer); 56 session_manager_key.WriteValue(kPendingFileRenameOps, &buffer[0], 57 buffer.size(), REG_MULTI_SZ); 58 } 59 } 60 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++; 73 } 74 return true; 75 } 76 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. 87 } 88 length++; // for the additional null char. 89 return length * sizeof(wchar_t); 90 } 91 92 std::vector<PendingMove> original_pending_moves_; 93 94 base::FilePath temp_dir_; 95 base::FilePath temp_file_; 96 base::FilePath temp_subdir_; 97 base::FilePath temp_subdir_file_; 98}; 99} 100 101TEST_F(DeleteAfterRebootHelperTest, TestStringListToMultiSZConversions) { 102 struct StringTest { 103 wchar_t* test_name; 104 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<char*>(tests[i].str), 117 tests[i].length, &string_list))) 118 << tests[i].test_name; 119 EXPECT_EQ(tests[i].count, string_list.size()) << tests[i].test_name; 120 std::vector<char> buffer; 121 buffer.resize(WStringPairListSize(string_list)); 122 StringArrayToMultiSZBytes(string_list, &buffer); 123 EXPECT_TRUE(CompareBuffers(&buffer[0], 124 reinterpret_cast<char*>(tests[i].str), 125 tests[i].length)) << tests[i].test_name; 126 } 127 128 StringTest failures[] = 129 { L"malformed", reinterpret_cast<wchar_t*>("oddnumb\0\0"), 9, 1 }; 130 131 for (int i = 0; i < arraysize(failures); i++) { 132 std::vector<PendingMove> string_list; 133 EXPECT_FALSE(SUCCEEDED( 134 MultiSZBytesToStringArray(reinterpret_cast<char*>(failures[i].str), 135 failures[i].length, &string_list))) 136 << failures[i].test_name; 137 } 138} 139 140 141TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteScheduleAndUnschedule) { 142 if (!IsUserAnAdmin()) { 143 return; 144 } 145 146 EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_.value().c_str())); 147 148 std::vector<PendingMove> pending_moves; 149 EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves))); 150 151 // We should see, somewhere in this key, deletion writs for 152 // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order. 153 EXPECT_TRUE(pending_moves.size() > 3); 154 155 // Get the short form of temp_file_ and use that to match. 156 std::wstring short_temp_file(GetShortPathName(temp_file_.value().c_str())); 157 158 // Scan for the first expected delete. 159 std::vector<PendingMove>::const_iterator iter(pending_moves.begin()); 160 for (; iter != pending_moves.end(); iter++) { 161 if (MatchPendingDeletePath(short_temp_file, iter->first)) 162 break; 163 } 164 165 // Check that each of the deletes we expect are there in order. 166 base::FilePath expected_paths[] = 167 { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ }; 168 for (int i = 0; i < arraysize(expected_paths); ++i) { 169 EXPECT_FALSE(iter == pending_moves.end()); 170 if (iter != pending_moves.end()) { 171 std::wstring short_path_name( 172 GetShortPathName(expected_paths[i].value().c_str())); 173 EXPECT_TRUE(MatchPendingDeletePath(short_path_name, iter->first)); 174 ++iter; 175 } 176 } 177 178 // Test that we can remove the pending deletes. 179 EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_.value().c_str())); 180 HRESULT hr = GetPendingMovesValue(&pending_moves); 181 EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); 182 183 std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin()); 184 for (; check_iter != pending_moves.end(); ++check_iter) { 185 EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, check_iter->first)); 186 } 187} 188 189TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteSchedulingWithActualDeletes) { 190 if (!IsUserAnAdmin()) { 191 return; 192 } 193 194 std::vector<PendingMove> initial_pending_moves; 195 GetPendingMovesValue(&initial_pending_moves); 196 size_t initial_pending_moves_size = initial_pending_moves.size(); 197 198 EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_.value().c_str())); 199 200 std::vector<PendingMove> pending_moves; 201 EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves))); 202 203 // We should see, somewhere in this key, deletion writs for 204 // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order. 205 EXPECT_TRUE(pending_moves.size() > 3); 206 207 // Get the short form of temp_file_ and use that to match. 208 std::wstring short_temp_file(GetShortPathName(temp_file_.value().c_str())); 209 210 // Scan for the first expected delete. 211 std::vector<PendingMove>::const_iterator iter(pending_moves.begin()); 212 for (; iter != pending_moves.end(); iter++) { 213 if (MatchPendingDeletePath(short_temp_file, iter->first)) 214 break; 215 } 216 217 // Check that each of the deletes we expect are there in order. 218 base::FilePath expected_paths[] = 219 { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ }; 220 for (int i = 0; i < arraysize(expected_paths); ++i) { 221 EXPECT_FALSE(iter == pending_moves.end()); 222 if (iter != pending_moves.end()) { 223 std::wstring short_path_name( 224 GetShortPathName(expected_paths[i].value().c_str())); 225 EXPECT_TRUE(MatchPendingDeletePath(short_path_name, iter->first)); 226 ++iter; 227 } 228 } 229 230 // Delete the temporary directory. 231 base::Delete(temp_dir_, true); 232 233 // Test that we can remove the pending deletes. 234 EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_.value().c_str())); 235 HRESULT hr = GetPendingMovesValue(&pending_moves); 236 EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); 237 238 EXPECT_EQ(initial_pending_moves_size, pending_moves.size()); 239 240 std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin()); 241 for (; check_iter != pending_moves.end(); ++check_iter) { 242 EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, check_iter->first)); 243 } 244} 245 246