- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / installer / util / delete_after_reboot_helper_unittest.cc
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
15 namespace {
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.
23 class 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::DeleteFile(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 }  // namespace
101
102 TEST_F(DeleteAfterRebootHelperTest, TestStringListToMultiSZConversions) {
103   struct StringTest {
104     wchar_t* test_name;
105     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 },
112   };
113
114   for (int i = 0; i < arraysize(tests); i++) {
115     std::vector<PendingMove> string_list;
116     EXPECT_TRUE(SUCCEEDED(
117         MultiSZBytesToStringArray(reinterpret_cast<char*>(tests[i].str),
118                                   tests[i].length, &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(&buffer[0],
125                 reinterpret_cast<char*>(tests[i].str),
126                 tests[i].length)) << tests[i].test_name;
127   }
128
129   StringTest failures[] =
130     { L"malformed", reinterpret_cast<wchar_t*>("oddnumb\0\0"), 9, 1 };
131
132   for (int i = 0; i < arraysize(failures); i++) {
133     std::vector<PendingMove> string_list;
134     EXPECT_FALSE(SUCCEEDED(
135         MultiSZBytesToStringArray(reinterpret_cast<char*>(failures[i].str),
136                                   failures[i].length, &string_list)))
137         << failures[i].test_name;
138   }
139 }
140
141
142 TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteScheduleAndUnschedule) {
143   if (!IsUserAnAdmin()) {
144     return;
145   }
146
147   EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_));
148
149   std::vector<PendingMove> pending_moves;
150   EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves)));
151
152   // We should see, somewhere in this key, deletion writs for
153   // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order.
154   EXPECT_GT(pending_moves.size(), 3U);
155
156   // Get the short form of temp_file_ and use that to match.
157   base::FilePath short_temp_file(GetShortPathName(temp_file_));
158
159   // Scan for the first expected delete.
160   std::vector<PendingMove>::const_iterator iter(pending_moves.begin());
161   for (; iter != pending_moves.end(); iter++) {
162     base::FilePath move_path(iter->first);
163     if (MatchPendingDeletePath(short_temp_file, move_path))
164       break;
165   }
166
167   // Check that each of the deletes we expect are there in order.
168   base::FilePath expected_paths[] =
169       { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ };
170   for (int i = 0; i < arraysize(expected_paths); ++i) {
171     EXPECT_FALSE(iter == pending_moves.end());
172     if (iter != pending_moves.end()) {
173       base::FilePath short_path_name(GetShortPathName(expected_paths[i]));
174       base::FilePath move_path(iter->first);
175       EXPECT_TRUE(MatchPendingDeletePath(short_path_name, move_path));
176       ++iter;
177     }
178   }
179
180   // Test that we can remove the pending deletes.
181   EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_));
182   HRESULT hr = GetPendingMovesValue(&pending_moves);
183   EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
184
185   std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin());
186   for (; check_iter != pending_moves.end(); ++check_iter) {
187     base::FilePath move_path(check_iter->first);
188     EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, move_path));
189   }
190 }
191
192 TEST_F(DeleteAfterRebootHelperTest, TestFileDeleteSchedulingWithActualDeletes) {
193   if (!IsUserAnAdmin()) {
194     return;
195   }
196
197   std::vector<PendingMove> initial_pending_moves;
198   GetPendingMovesValue(&initial_pending_moves);
199   size_t initial_pending_moves_size = initial_pending_moves.size();
200
201   EXPECT_TRUE(ScheduleDirectoryForDeletion(temp_dir_));
202
203   std::vector<PendingMove> pending_moves;
204   EXPECT_TRUE(SUCCEEDED(GetPendingMovesValue(&pending_moves)));
205
206   // We should see, somewhere in this key, deletion writs for
207   // temp_file_, temp_subdir_file_, temp_subdir_ and temp_dir_ in that order.
208   EXPECT_TRUE(pending_moves.size() > 3);
209
210   // Get the short form of temp_file_ and use that to match.
211   base::FilePath short_temp_file(GetShortPathName(temp_file_));
212
213   // Scan for the first expected delete.
214   std::vector<PendingMove>::const_iterator iter(pending_moves.begin());
215   for (; iter != pending_moves.end(); iter++) {
216     base::FilePath move_path(iter->first);
217     if (MatchPendingDeletePath(short_temp_file, move_path))
218       break;
219   }
220
221   // Check that each of the deletes we expect are there in order.
222   base::FilePath expected_paths[] =
223       { temp_file_, temp_subdir_file_, temp_subdir_, temp_dir_ };
224   for (int i = 0; i < arraysize(expected_paths); ++i) {
225     EXPECT_FALSE(iter == pending_moves.end());
226     if (iter != pending_moves.end()) {
227       base::FilePath short_path_name(GetShortPathName(expected_paths[i]));
228       base::FilePath move_path(iter->first);
229       EXPECT_TRUE(MatchPendingDeletePath(short_path_name, move_path));
230       ++iter;
231     }
232   }
233
234   // Delete the temporary directory.
235   base::DeleteFile(temp_dir_, true);
236
237   // Test that we can remove the pending deletes.
238   EXPECT_TRUE(RemoveFromMovesPendingReboot(temp_dir_));
239   HRESULT hr = GetPendingMovesValue(&pending_moves);
240   EXPECT_TRUE(hr == S_OK || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
241
242   EXPECT_EQ(initial_pending_moves_size, pending_moves.size());
243
244   std::vector<PendingMove>::const_iterator check_iter(pending_moves.begin());
245   for (; check_iter != pending_moves.end(); ++check_iter) {
246     base::FilePath move_path(check_iter->first);
247     EXPECT_FALSE(MatchPendingDeletePath(short_temp_file, move_path));
248   }
249 }
250