OLD | NEW |
| (Empty) |
1 // Copyright 2007-2010 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 #include "testing/unit_test.h" | |
17 #include "omaha/base/app_util.h" | |
18 #include "omaha/base/constants.h" | |
19 #include "omaha/base/path.h" | |
20 #include "omaha/base/process.h" | |
21 #include "omaha/base/reg_key.h" | |
22 #include "omaha/base/scoped_any.h" | |
23 #include "omaha/base/system.h" | |
24 #include "omaha/base/utils.h" | |
25 #include "omaha/base/user_info.h" | |
26 #include "omaha/base/vistautil.h" | |
27 #include "omaha/common/command_line.h" | |
28 #include "omaha/common/command_line_builder.h" | |
29 #include "omaha/common/const_goopdate.h" | |
30 | |
31 namespace omaha { | |
32 | |
33 namespace { | |
34 | |
35 static bool is_buildsystem = false; | |
36 static TCHAR psexec_dir[MAX_PATH] = {0}; | |
37 | |
38 // Returns whether all unit tests should be run. | |
39 bool ShouldRunAllTests() { | |
40 if (is_buildsystem) { | |
41 return true; | |
42 } | |
43 | |
44 EXPECT_FALSE(IsEnvironmentVariableSet(_T("OMAHA_RUN_ALL_TESTS"))) | |
45 << _T("Use OMAHA_TEST_RUN_ALL instead of OMAHA_RUN_ALL_TESTS."); | |
46 | |
47 return IsEnvironmentVariableSet(_T("OMAHA_TEST_RUN_ALL")); | |
48 } | |
49 | |
50 } // namespace | |
51 | |
52 bool IsEnvironmentVariableSet(const TCHAR* name) { | |
53 ASSERT1(name); | |
54 TCHAR var[100] = {0}; | |
55 DWORD res = ::GetEnvironmentVariable(name, var, arraysize(var)); | |
56 if (0 == res) { | |
57 ASSERT1(ERROR_ENVVAR_NOT_FOUND == ::GetLastError()); | |
58 return false; | |
59 } else { | |
60 return true; | |
61 } | |
62 } | |
63 | |
64 bool IsTestRunByLocalSystem() { | |
65 return user_info::IsRunningAsSystem(); | |
66 } | |
67 | |
68 CString GetLocalAppDataPath() { | |
69 CString expected_local_app_data_path; | |
70 EXPECT_SUCCEEDED(GetFolderPath(CSIDL_LOCAL_APPDATA | CSIDL_FLAG_DONT_VERIFY, | |
71 &expected_local_app_data_path)); | |
72 expected_local_app_data_path.Append(_T("\\")); | |
73 return expected_local_app_data_path; | |
74 } | |
75 | |
76 CString GetGoogleUserPath() { | |
77 return GetLocalAppDataPath() + SHORT_COMPANY_NAME + _T("\\"); | |
78 } | |
79 | |
80 // TODO(omaha): make GetGoogleUpdateUserPath and GetGoogleUpdateMachinePath | |
81 // consistent. They should end with \ or not. | |
82 CString GetGoogleUpdateUserPath() { | |
83 return GetGoogleUserPath() + PRODUCT_NAME + _T("\\"); | |
84 } | |
85 | |
86 CString GetGoogleUpdateMachinePath() { | |
87 CString program_files; | |
88 GetFolderPath(CSIDL_PROGRAM_FILES, &program_files); | |
89 return program_files + _T("\\") + SHORT_COMPANY_NAME | |
90 + _T("\\") + PRODUCT_NAME; | |
91 } | |
92 | |
93 DWORD GetDwordValue(const CString& full_key_name, const CString& value_name) { | |
94 DWORD value = 0; | |
95 EXPECT_SUCCEEDED(RegKey::GetValue(full_key_name, value_name, &value)); | |
96 return value; | |
97 } | |
98 | |
99 CString GetSzValue(const CString& full_key_name, const CString& value_name) { | |
100 CString value; | |
101 EXPECT_SUCCEEDED(RegKey::GetValue(full_key_name, value_name, &value)); | |
102 return value; | |
103 } | |
104 | |
105 GUID StringToGuid(const CString& str) { | |
106 GUID guid(GUID_NULL); | |
107 VERIFY(SUCCEEDED(StringToGuidSafe(str, &guid)), (_T("guid '%s'"), str)); | |
108 return guid; | |
109 } | |
110 | |
111 void OverrideRegistryHives(const CString& hive_override_key_name) { | |
112 OverrideSpecifiedRegistryHives(hive_override_key_name, true, true); | |
113 } | |
114 | |
115 void OverrideSpecifiedRegistryHives(const CString& hive_override_key_name, | |
116 bool override_hklm, | |
117 bool override_hkcu) { | |
118 // Override the destinations of HKLM and HKCU to use a special location | |
119 // for the unit tests so that we don't disturb the actual Omaha state. | |
120 RegKey machine_key; | |
121 RegKey user_key; | |
122 ASSERT_SUCCEEDED(machine_key.Create(hive_override_key_name + MACHINE_KEY)); | |
123 ASSERT_SUCCEEDED(user_key.Create(hive_override_key_name + USER_KEY)); | |
124 if (override_hklm) { | |
125 ASSERT_SUCCEEDED(::RegOverridePredefKey(HKEY_LOCAL_MACHINE, | |
126 machine_key.Key())); | |
127 } | |
128 if (override_hkcu) { | |
129 ASSERT_SUCCEEDED(::RegOverridePredefKey(HKEY_CURRENT_USER, | |
130 user_key.Key())); | |
131 } | |
132 } | |
133 | |
134 // When tests execute programs (i.e. with ShellExecute or indirectly), Windows | |
135 // looks at kMyComputerSecurityZoneKeyPathL:kMiscSecurityZonesValueName | |
136 // to see if it should run the program. | |
137 // Normally, these reads are not redirected to the override key even though | |
138 // it seems like they should be. In this case, the execution succeeds. | |
139 // In certain cases, the reads are redirected and the program execution | |
140 // fails due to permission denied errors. | |
141 // This has been observed when XmlUtilsTest::LoadSave() is run before such | |
142 // tests. Specifically, the my_xmldoc->load() call in LoadXMLFromFile() | |
143 // appears to somehow cause the redirection to occur. | |
144 // | |
145 // See http://support.microsoft.com/kb/182569 for information on security | |
146 // zones. | |
147 void OverrideRegistryHivesWithExecutionPermissions( | |
148 const CString& hive_override_key_name) { | |
149 OverrideRegistryHives(hive_override_key_name); | |
150 | |
151 const TCHAR kMyComputerSecurityZoneKeyPath[] = | |
152 _T("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\") | |
153 _T("Internet Settings\\Zones\\0"); | |
154 const TCHAR kMiscSecurityZonesValueName[] = _T("1806"); | |
155 const DWORD kPermitAction = 0; | |
156 | |
157 RegKey my_computer_zone_key; | |
158 ASSERT_SUCCEEDED(my_computer_zone_key.Create( | |
159 kMyComputerSecurityZoneKeyPath)); | |
160 ASSERT_SUCCEEDED(my_computer_zone_key.SetValue(kMiscSecurityZonesValueName, | |
161 kPermitAction)); | |
162 } | |
163 | |
164 void RestoreRegistryHives() { | |
165 ASSERT_SUCCEEDED(::RegOverridePredefKey(HKEY_LOCAL_MACHINE, NULL)); | |
166 ASSERT_SUCCEEDED(::RegOverridePredefKey(HKEY_CURRENT_USER, NULL)); | |
167 } | |
168 | |
169 void SetPsexecDir(const CString& dir) { | |
170 _tcscpy_s(psexec_dir, arraysize(psexec_dir), dir.GetString()); | |
171 } | |
172 | |
173 CString GetPsexecDir() { | |
174 EXPECT_TRUE(_tcsnlen(psexec_dir, arraysize(psexec_dir))); | |
175 return psexec_dir; | |
176 } | |
177 | |
178 // Must be called after SetPsexecDir(). | |
179 // Does not wait for the EULA accepting process to complete. | |
180 bool AcceptPsexecEula() { | |
181 CString psexec_dir = GetPsexecDir(); | |
182 if (psexec_dir.IsEmpty()) { | |
183 return false; | |
184 } | |
185 | |
186 CString psexec_path = ConcatenatePath(psexec_dir, _T("psexec.exe")); | |
187 return SUCCEEDED(System::StartProcessWithArgs(psexec_path, | |
188 _T("/accepteula"))); | |
189 } | |
190 | |
191 void SetIsBuildSystem() { | |
192 is_buildsystem = true; | |
193 } | |
194 | |
195 bool IsBuildSystem() { | |
196 return is_buildsystem; | |
197 } | |
198 | |
199 void SetBuildSystemTestSource() { | |
200 EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV, | |
201 kRegValueTestSource, | |
202 _T("buildsystem"))); | |
203 } | |
204 | |
205 bool ShouldRunLargeTest() { | |
206 if (ShouldRunAllTests()) { | |
207 return true; | |
208 } | |
209 | |
210 if (IsEnvironmentVariableSet(_T("OMAHA_TEST_RUN_LARGE"))) { | |
211 return true; | |
212 } else { | |
213 std::wcout << _T("\tThis large test did not run because neither ") | |
214 _T("'OMAHA_TEST_RUN_LARGE' or 'OMAHA_TEST_RUN_ALL' is set ") | |
215 _T("in the environment.") << std::endl; | |
216 return false; | |
217 } | |
218 } | |
219 | |
220 bool ShouldRunEnormousTest() { | |
221 if (ShouldRunAllTests()) { | |
222 return true; | |
223 } | |
224 | |
225 std::wcout << _T("\tThis large test did not run because ") | |
226 _T("'OMAHA_TEST_RUN_ALL' is not set in the environment.") | |
227 << std::endl; | |
228 return false; | |
229 } | |
230 | |
231 void TerminateAllProcessesByName(const TCHAR* process_name) { | |
232 std::vector<uint32> process_pids; | |
233 ASSERT_SUCCEEDED(Process::FindProcesses(0, // No flags. | |
234 process_name, | |
235 true, | |
236 &process_pids)); | |
237 | |
238 for (size_t i = 0; i < process_pids.size(); ++i) { | |
239 scoped_process process(::OpenProcess(PROCESS_TERMINATE, | |
240 FALSE, | |
241 process_pids[i])); | |
242 EXPECT_TRUE(process); | |
243 EXPECT_TRUE(::TerminateProcess(get(process), static_cast<uint32>(-3))); | |
244 } | |
245 } | |
246 | |
247 void TerminateAllGoogleUpdateProcesses() { | |
248 TerminateAllProcessesByName(kOmahaShellFileName); | |
249 TerminateAllProcessesByName(kCrashHandlerFileName); | |
250 } | |
251 | |
252 // The exit code of psexec is the pid it started when -d is used. | |
253 // Wait for psexec to exit, get the exit code, and use it to get a handle | |
254 // to the GoogleUpdate.exe instance. | |
255 void LaunchProcessAsSystem(const CString& launch_cmd, HANDLE* process) { | |
256 ASSERT_TRUE(process); | |
257 | |
258 CString app_launcher = ConcatenatePath(GetPsexecDir(), _T("psexec.exe")); | |
259 CString cmd_line_args; | |
260 cmd_line_args.Format(_T("-s -d %s"), launch_cmd); | |
261 | |
262 PROCESS_INFORMATION pi = {0}; | |
263 EXPECT_SUCCEEDED(System::StartProcessWithArgsAndInfo(app_launcher, | |
264 cmd_line_args, | |
265 &pi)); | |
266 ::CloseHandle(pi.hThread); | |
267 scoped_handle started_process(pi.hProcess); | |
268 ASSERT_TRUE(started_process); | |
269 | |
270 DWORD google_update_pid = 0; | |
271 EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(started_process), 30000)); | |
272 EXPECT_TRUE(::GetExitCodeProcess(get(started_process), &google_update_pid)); | |
273 DWORD desired_access = | |
274 PROCESS_QUERY_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE; | |
275 *process = ::OpenProcess(desired_access, false, google_update_pid); | |
276 DWORD last_error(::GetLastError()); | |
277 | |
278 // psexec sometimes returns errors instead of PIDs and there is no way to | |
279 // tell the difference. We see ERROR_SERVICE_MARKED_FOR_DELETE (1072) | |
280 // intermittently on the build server, but do not expect any other errors. | |
281 EXPECT_TRUE(*process || | |
282 ERROR_SERVICE_MARKED_FOR_DELETE == google_update_pid) << | |
283 _T("::OpenProcess failed in a case where psexec did not return ") | |
284 _T("ERROR_SERVICE_MARKED_FOR_DELETE.") << _T(" The error was ") | |
285 << last_error << _T("."); | |
286 } | |
287 | |
288 void LaunchProcess(const CString& exe_path, | |
289 const CString& args, | |
290 bool as_system, | |
291 HANDLE* process) { | |
292 ASSERT_TRUE(process); | |
293 *process = NULL; | |
294 | |
295 CString launch_cmd = exe_path; | |
296 EnclosePath(&launch_cmd); | |
297 launch_cmd += args.IsEmpty() ? _T("") : _T(" ") + args; | |
298 | |
299 if (as_system) { | |
300 // Retry the process launch if the process handle is invalid. Hopefully this | |
301 // is robust against intermittent ERROR_SERVICE_MARKED_FOR_DELETE errors. | |
302 for (int tries = 0; tries < 10 && !*process; ++tries) { | |
303 LaunchProcessAsSystem(launch_cmd, process); | |
304 if (!*process) { | |
305 ::Sleep(1000); | |
306 } | |
307 } | |
308 } else { | |
309 PROCESS_INFORMATION pi = {0}; | |
310 EXPECT_SUCCEEDED(System::StartProcess(NULL, launch_cmd.GetBuffer(), &pi)); | |
311 ::CloseHandle(pi.hThread); | |
312 *process = pi.hProcess; | |
313 } | |
314 | |
315 ASSERT_TRUE(*process); | |
316 } | |
317 | |
318 void RegistryProtectedTest::SetUp() { | |
319 RegKey::DeleteKey(hive_override_key_name_, true); | |
320 OverrideRegistryHives(hive_override_key_name_); | |
321 } | |
322 | |
323 void RegistryProtectedTest::TearDown() { | |
324 RestoreRegistryHives(); | |
325 ASSERT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true)); | |
326 } | |
327 | |
328 CString GetUniqueTempDirectoryName() { | |
329 CString guid; | |
330 EXPECT_HRESULT_SUCCEEDED(GetGuid(&guid)); | |
331 return ConcatenatePath(app_util::GetTempDir(), guid); | |
332 } | |
333 | |
334 void RunAsAdmin(const CString& exe_path, const CString& cmd_line) { | |
335 if (vista_util::IsUserAdmin()) { | |
336 EXPECT_SUCCEEDED(RegisterOrUnregisterExe(exe_path, cmd_line)); | |
337 return; | |
338 } | |
339 | |
340 // Elevate for medium integrity users on Vista and above. | |
341 DWORD exit_code(S_OK); | |
342 EXPECT_SUCCEEDED(vista_util::RunElevated(exe_path, | |
343 cmd_line, | |
344 SW_SHOWNORMAL, | |
345 &exit_code)); | |
346 EXPECT_SUCCEEDED(exit_code); | |
347 } | |
348 | |
349 void RegisterOrUnregisterGoopdateLocalServer(bool reg) { | |
350 CString server_path = ConcatenatePath(GetGoogleUpdateMachinePath(), | |
351 kOmahaShellFileName); | |
352 EnclosePath(&server_path); | |
353 | |
354 CommandLineBuilder builder(reg ? COMMANDLINE_MODE_REGSERVER : | |
355 COMMANDLINE_MODE_UNREGSERVER); | |
356 CString cmd_line = builder.GetCommandLineArgs(); | |
357 RunAsAdmin(server_path, cmd_line); | |
358 } | |
359 | |
360 void RegisterOrUnregisterGoopdateService(bool reg) { | |
361 CString service_path = ConcatenatePath(GetGoogleUpdateMachinePath(), | |
362 kServiceFileName); | |
363 EnclosePath(&service_path); | |
364 | |
365 CommandLineBuilder builder(reg ? COMMANDLINE_MODE_SERVICE_REGISTER : | |
366 COMMANDLINE_MODE_SERVICE_UNREGISTER); | |
367 CString cmd_line = builder.GetCommandLineArgs(); | |
368 RunAsAdmin(service_path, cmd_line); | |
369 } | |
370 | |
371 } // namespace omaha | |
OLD | NEW |