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