| 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 |