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 <string> | |
17 #include "omaha/base/app_util.h" | |
18 #include "omaha/base/atl_regexp.h" | |
19 #include "omaha/base/constants.h" | |
20 #include "omaha/base/const_addresses.h" | |
21 #include "omaha/base/const_object_names.h" | |
22 #include "omaha/base/error.h" | |
23 #include "omaha/base/file.h" | |
24 #include "omaha/base/scoped_any.h" | |
25 #include "omaha/base/time.h" | |
26 #include "omaha/base/user_info.h" | |
27 #include "omaha/base/utils.h" | |
28 #include "omaha/base/vistautil.h" | |
29 #include "omaha/common/config_manager.h" | |
30 #include "omaha/common/event_logger.h" | |
31 #include "omaha/common/goopdate_utils.h" | |
32 #include "omaha/goopdate/crash.h" | |
33 #include "omaha/testing/unit_test.h" | |
34 | |
35 // TODO(omaha): Modify the tests to avoid writing files to the staging | |
36 // directory, which should not be modified after building. | |
37 | |
38 using google_breakpad::ClientInfo; | |
39 using google_breakpad::CustomClientInfo; | |
40 using google_breakpad::CustomInfoEntry; | |
41 using google_breakpad::ExceptionHandler; | |
42 | |
43 namespace omaha { | |
44 | |
45 namespace { | |
46 | |
47 const TCHAR kMiniDumpFilename[] = _T("minidump.dmp"); | |
48 const TCHAR kCustomInfoFilename[] = _T("minidump.txt"); | |
49 const TCHAR kTestFilenamePattern[] = _T("minidump.*"); | |
50 | |
51 } // namespace | |
52 | |
53 class CrashTest : public testing::Test { | |
54 protected: | |
55 // Initialize the crash reporting for the machine case. The user case is | |
56 // simpler and specific tests can reinitialize for the user case if needed. | |
57 virtual void SetUp() { | |
58 module_dir_ = app_util::GetModuleDirectory(NULL); | |
59 EXPECT_HRESULT_SUCCEEDED(Crash::Initialize(true)); | |
60 } | |
61 | |
62 virtual void TearDown() { | |
63 EXPECT_HRESULT_SUCCEEDED(DeleteDirectory(Crash::crash_dir_)); | |
64 } | |
65 | |
66 static void CallbackHelper(const wchar_t* dump_path, | |
67 const wchar_t* minidump_id) { | |
68 CString postfix_string(kCrashVersionPostfixString); | |
69 postfix_string.Append(_T(".ut")); | |
70 CString crash_filename; | |
71 crash_filename.Format(_T("%s\\%s.dmp"), dump_path, minidump_id); | |
72 Crash::set_max_reports_per_day(kMaxReportsPerDayFromUnittests); | |
73 Crash::set_version_postfix(postfix_string); | |
74 Crash::set_crash_report_url(kUrlCrashReport); | |
75 EXPECT_SUCCEEDED(Crash::Report(true, crash_filename, CString(), false)); | |
76 } | |
77 | |
78 static bool MinidumpCallback(const wchar_t* dump_path, | |
79 const wchar_t* minidump_id, | |
80 void* context, | |
81 EXCEPTION_POINTERS*, | |
82 MDRawAssertionInfo*, | |
83 bool succeeded) { | |
84 EXPECT_TRUE(dump_path); | |
85 EXPECT_TRUE(minidump_id); | |
86 EXPECT_TRUE(!context); | |
87 EXPECT_SUCCEEDED(succeeded); | |
88 | |
89 CallbackHelper(dump_path, minidump_id); | |
90 return true; | |
91 } | |
92 | |
93 static void BuildPipeSecurityAttributesTest(bool is_machine) { | |
94 CSecurityAttributes pipe_sec_attrs; | |
95 EXPECT_TRUE( | |
96 Crash::BuildPipeSecurityAttributes(is_machine, &pipe_sec_attrs)); | |
97 LPVOID sec_desc(pipe_sec_attrs.lpSecurityDescriptor); | |
98 CSecurityDesc sd1(*reinterpret_cast<SECURITY_DESCRIPTOR*>(sec_desc)); | |
99 CString sddl1; | |
100 | |
101 sd1.ToString(&sddl1, OWNER_SECURITY_INFORMATION | | |
102 GROUP_SECURITY_INFORMATION | | |
103 DACL_SECURITY_INFORMATION | | |
104 SACL_SECURITY_INFORMATION | | |
105 LABEL_SECURITY_INFORMATION); | |
106 | |
107 CSecurityDesc sd2; | |
108 EXPECT_TRUE(Crash::AddPipeSecurityDaclToDesc(is_machine, &sd2)); | |
109 EXPECT_HRESULT_SUCCEEDED( | |
110 vista_util::AddLowIntegritySaclToExistingDesc(&sd2)); | |
111 | |
112 CString sddl2; | |
113 sd2.ToString(&sddl2, OWNER_SECURITY_INFORMATION | | |
114 GROUP_SECURITY_INFORMATION | | |
115 DACL_SECURITY_INFORMATION | | |
116 SACL_SECURITY_INFORMATION | | |
117 LABEL_SECURITY_INFORMATION); | |
118 | |
119 EXPECT_STREQ(sddl2, sddl1); | |
120 | |
121 if (vista_util::IsVistaOrLater()) { | |
122 // The low integrity SACL is at the end of the SDDL string. | |
123 EXPECT_STREQ(LOW_INTEGRITY_SDDL_SACL, | |
124 sddl1.Right(arraysize(LOW_INTEGRITY_SDDL_SACL) - 1)); | |
125 } | |
126 } | |
127 | |
128 static HRESULT StartSenderWithCommandLine(CString* cmd_line) { | |
129 return Crash::StartSenderWithCommandLine(cmd_line); | |
130 } | |
131 | |
132 // Returns the strings of the last Update2 event in the event log. | |
133 static CString GetLastCrashEventStrings() { | |
134 const size_t kBufferSize = 1024; | |
135 uint8 buffer[kBufferSize] = {0}; | |
136 EVENTLOGRECORD* rec = reinterpret_cast<EVENTLOGRECORD*>(buffer); | |
137 | |
138 rec->Length = kBufferSize; | |
139 EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(_T("Update2"), rec)); | |
140 EXPECT_EQ(kCrashUploadEventId, rec->EventID); | |
141 | |
142 const TCHAR* strings = reinterpret_cast<const TCHAR*>( | |
143 (reinterpret_cast<uint8*>(buffer + rec->StringOffset))); | |
144 | |
145 return CString(strings); | |
146 } | |
147 | |
148 CString module_dir_; | |
149 | |
150 static const int kMaxReportsPerDayFromUnittests = INT_MAX; | |
151 }; | |
152 | |
153 TEST_F(CrashTest, CreateCustomInfoFile) { | |
154 CString expected_custom_info_file_path; | |
155 expected_custom_info_file_path.Format(_T("%s\\%s"), | |
156 module_dir_, kCustomInfoFilename); | |
157 | |
158 CString crash_filename; | |
159 crash_filename.Format(_T("%s\\%s"), module_dir_, kMiniDumpFilename); | |
160 CustomInfoEntry info_entry(_T("foo"), _T("bar")); | |
161 CustomClientInfo custom_client_info = {&info_entry, 1}; | |
162 | |
163 CString actual_custom_info_filepath; | |
164 EXPECT_SUCCEEDED(Crash::CreateCustomInfoFile(crash_filename, | |
165 custom_client_info, | |
166 &actual_custom_info_filepath)); | |
167 EXPECT_STREQ(expected_custom_info_file_path, actual_custom_info_filepath); | |
168 EXPECT_TRUE(File::Exists(actual_custom_info_filepath)); | |
169 EXPECT_TRUE(::DeleteFile(actual_custom_info_filepath)); | |
170 | |
171 // Tests an invalid file name. | |
172 EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_NAME), | |
173 Crash::CreateCustomInfoFile(_T("C:\\\"minidump.dmp"), | |
174 custom_client_info, | |
175 &actual_custom_info_filepath)); | |
176 } | |
177 | |
178 // Tests sending an Omaha crash. | |
179 TEST_F(CrashTest, Report_OmahaCrash) { | |
180 CString crash_filename; | |
181 crash_filename.Format(_T("%s\\%s"), module_dir_, kMiniDumpFilename); | |
182 | |
183 ::DeleteFile(crash_filename); | |
184 | |
185 EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), | |
186 Crash::Report(true, crash_filename, _T(""), _T(""))); | |
187 | |
188 // Copy the minidump and the corresponding info file. | |
189 CString test_dir; | |
190 test_dir.Format(_T("%s\\unittest_support"), module_dir_); | |
191 ASSERT_SUCCEEDED(File::CopyWildcards(test_dir, // From. | |
192 module_dir_, // To. | |
193 kTestFilenamePattern, | |
194 true)); | |
195 | |
196 ASSERT_TRUE(File::Exists(crash_filename)); | |
197 | |
198 ASSERT_SUCCEEDED(Crash::Report(true, crash_filename, _T(""), _T(""))); | |
199 | |
200 // The crash artifacts should be deleted after the crash is reported. | |
201 EXPECT_FALSE(File::Exists(crash_filename)); | |
202 } | |
203 | |
204 // Tests sending an out-of-process crash. | |
205 // This test will write an entry with the source "Update2" in the Event Log. | |
206 TEST_F(CrashTest, Report_ProductCrash) { | |
207 CString crash_filename; | |
208 CString custom_info_filename; | |
209 crash_filename.Format(_T("%s\\%s"), module_dir_, kMiniDumpFilename); | |
210 custom_info_filename.Format(_T("%s\\%s"), module_dir_, kCustomInfoFilename); | |
211 | |
212 ::DeleteFile(crash_filename); | |
213 ::DeleteFile(custom_info_filename); | |
214 | |
215 EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), | |
216 Crash::Report(true, crash_filename, custom_info_filename, _T(""))); | |
217 | |
218 // Copy the minidump and the corresponding info file. | |
219 CString test_dir; | |
220 test_dir.Format(_T("%s\\unittest_support"), module_dir_); | |
221 ASSERT_SUCCEEDED(File::CopyWildcards(test_dir, // From. | |
222 module_dir_, // To. | |
223 kTestFilenamePattern, | |
224 true)); | |
225 | |
226 ASSERT_TRUE(File::Exists(crash_filename)); | |
227 ASSERT_TRUE(File::Exists(custom_info_filename)); | |
228 | |
229 ASSERT_SUCCEEDED(Crash::Report(true, crash_filename, | |
230 custom_info_filename, _T(""))); | |
231 | |
232 // Check the 'crash uploaded' event log. | |
233 const CString strings = GetLastCrashEventStrings(); | |
234 | |
235 // Verify that the strings include the Id token. | |
236 AtlRE crash_id_regex(_T("Id={\\h+}.")); | |
237 CString crash_id; | |
238 EXPECT_TRUE(AtlRE::PartialMatch(strings, crash_id_regex, &crash_id)); | |
239 | |
240 // The crash artifacts should be deleted after the crash is reported. | |
241 EXPECT_FALSE(File::Exists(crash_filename)); | |
242 EXPECT_FALSE(File::Exists(custom_info_filename)); | |
243 } | |
244 | |
245 // Tests generation of a minidump and uploading it to the staging server. | |
246 TEST_F(CrashTest, WriteMinidump) { | |
247 ASSERT_TRUE(!Crash::crash_dir_.IsEmpty()); | |
248 | |
249 MINIDUMP_TYPE dump_type = MiniDumpNormal; | |
250 ExceptionHandler handler(Crash::crash_dir_.GetString(), NULL, | |
251 &MinidumpCallback, NULL, | |
252 ExceptionHandler::HANDLER_NONE, | |
253 dump_type, NULL, NULL); | |
254 ASSERT_TRUE(handler.WriteMinidump()); | |
255 } | |
256 | |
257 // Tests the retrieval of the exception information from an existing mini dump. | |
258 TEST_F(CrashTest, GetExceptionInfo) { | |
259 const uint32 kExceptionAddress = 0x12345670; | |
260 const uint32 kExceptionCode = 0xc0000005; | |
261 | |
262 CString filename; | |
263 filename.AppendFormat(_T("%s\\unittest_support\\%s"), | |
264 module_dir_, kMiniDumpFilename); | |
265 MINIDUMP_EXCEPTION ex_info = {0}; | |
266 ASSERT_SUCCEEDED(Crash::GetExceptionInfo(filename, &ex_info)); | |
267 EXPECT_EQ(kExceptionAddress, ex_info.ExceptionAddress); | |
268 EXPECT_EQ(kExceptionCode, ex_info.ExceptionCode); | |
269 } | |
270 | |
271 TEST_F(CrashTest, IsCrashReportProcess) { | |
272 // Clear the environment variable. | |
273 ::SetEnvironmentVariable(kNoCrashHandlerEnvVariableName, NULL); | |
274 | |
275 bool is_crash_report_process = false; | |
276 EXPECT_SUCCEEDED(Crash::IsCrashReportProcess(&is_crash_report_process)); | |
277 EXPECT_FALSE(is_crash_report_process); | |
278 | |
279 EXPECT_TRUE(::SetEnvironmentVariable(kNoCrashHandlerEnvVariableName, | |
280 _T("1"))); | |
281 is_crash_report_process = false; | |
282 EXPECT_SUCCEEDED(Crash::IsCrashReportProcess(&is_crash_report_process)); | |
283 EXPECT_TRUE(is_crash_report_process); | |
284 | |
285 // Clear the environment variable. | |
286 EXPECT_TRUE(::SetEnvironmentVariable(kNoCrashHandlerEnvVariableName, NULL)); | |
287 } | |
288 | |
289 TEST_F(CrashTest, GetProductName) { | |
290 Crash::ParameterMap parameters; | |
291 EXPECT_STREQ(SHORT_COMPANY_NAME _T(" Error Reporting"), | |
292 Crash::GetProductName(parameters)); | |
293 | |
294 parameters[_T("prod")] = _T("Update2"); | |
295 EXPECT_STREQ(_T("Update2"), Crash::GetProductName(parameters)); | |
296 } | |
297 | |
298 TEST_F(CrashTest, SaveLastCrash) { | |
299 // Copy a test file into the module directory to use as a crash file. | |
300 CString test_file; // The unit test support file. | |
301 CString crash_file; // The crash file to be backed up. | |
302 test_file.AppendFormat(_T("%s\\unittest_support\\%s"), | |
303 module_dir_, kMiniDumpFilename); | |
304 crash_file.AppendFormat(_T("%s\\%s"), Crash::crash_dir_, kMiniDumpFilename); | |
305 EXPECT_TRUE(File::Exists(test_file)); | |
306 EXPECT_TRUE(::CopyFile(test_file, crash_file, false)); | |
307 EXPECT_TRUE(File::Exists(crash_file)); | |
308 | |
309 EXPECT_HRESULT_SUCCEEDED(Crash::SaveLastCrash(crash_file, _T("test"))); | |
310 | |
311 CString saved_crash_file; // The name of backup crash file. | |
312 saved_crash_file.AppendFormat(_T("%s\\test-last.dmp"), Crash::crash_dir_); | |
313 EXPECT_TRUE(File::Exists(saved_crash_file)); | |
314 | |
315 EXPECT_TRUE(::DeleteFile(saved_crash_file)); | |
316 } | |
317 | |
318 TEST_F(CrashTest, StartServer) { | |
319 // Terminate all processes to avoid conflicts on the crash services pipe. | |
320 TerminateAllGoogleUpdateProcesses(); | |
321 | |
322 EXPECT_HRESULT_SUCCEEDED(Crash::StartServer()); | |
323 | |
324 // Try opening the crash services pipe. | |
325 CString user_sid; | |
326 EXPECT_HRESULT_SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid)); | |
327 CString pipe_name; | |
328 pipe_name.AppendFormat(_T("\\\\.\\pipe\\%sCrashServices\\%s"), | |
329 SHORT_COMPANY_NAME, user_sid); | |
330 scoped_pipe pipe_handle(::CreateFile(pipe_name, | |
331 GENERIC_READ | GENERIC_WRITE, | |
332 FILE_SHARE_READ | FILE_SHARE_WRITE, | |
333 NULL, | |
334 OPEN_EXISTING, | |
335 0, | |
336 NULL)); | |
337 EXPECT_TRUE(pipe_handle); | |
338 | |
339 Crash::StopServer(); | |
340 } | |
341 | |
342 TEST_F(CrashTest, CleanStaleCrashes) { | |
343 // Copy a test file into the module directory to use as a crash file. | |
344 CString test_file; // The unit test support file. | |
345 CString crash_file; // The crash file to be backed up. | |
346 test_file.AppendFormat(_T("%s\\unittest_support\\%s"), | |
347 module_dir_, kMiniDumpFilename); | |
348 crash_file.AppendFormat(_T("%s\\%s.dmp"), | |
349 Crash::crash_dir_, | |
350 _T("5695F1E0-95BD-4bc2-99C0-E9DCC0AC5274")); | |
351 EXPECT_TRUE(File::Exists(test_file)); | |
352 EXPECT_TRUE(::CopyFile(test_file, crash_file, false)); | |
353 EXPECT_TRUE(File::Exists(crash_file)); | |
354 | |
355 FILETIME time_created = {0}; | |
356 time64 now = GetCurrent100NSTime(); | |
357 | |
358 // Create a time value 23 hours in the past. Expect the crash file remains. | |
359 Time64ToFileTime(now - 23 * kHoursTo100ns, &time_created); | |
360 EXPECT_HRESULT_SUCCEEDED(File::SetFileTime(crash_file, &time_created, | |
361 NULL, NULL)); | |
362 Crash::CleanStaleCrashes(); | |
363 EXPECT_TRUE(File::Exists(crash_file)); | |
364 | |
365 // Create a time value 25 hours in the past. Expect the crash file is deleted. | |
366 Time64ToFileTime(now - 25 * kHoursTo100ns, &time_created); | |
367 EXPECT_HRESULT_SUCCEEDED(File::SetFileTime(crash_file, &time_created, | |
368 NULL, NULL)); | |
369 Crash::CleanStaleCrashes(); | |
370 EXPECT_FALSE(File::Exists(crash_file)); | |
371 } | |
372 | |
373 // Installs and uninstalls the crash handler in the user case. | |
374 TEST_F(CrashTest, InstallCrashHandler) { | |
375 EXPECT_HRESULT_SUCCEEDED(Crash::InstallCrashHandler(false)); | |
376 Crash::UninstallCrashHandler(); | |
377 } | |
378 | |
379 // Makes sure that the security descriptor that BuildPipeSecurityAttributes | |
380 // creates matches the security descriptor built by adding the DACL first, and | |
381 // then using AddLowIntegritySaclToExistingDesc(). The latter method uses an | |
382 // approach similar to what is documented in MSDN: | |
383 // http://msdn.microsoft.com/en-us/library/bb625960.aspx | |
384 // | |
385 // Also, makes sure that the security descriptor that | |
386 // BuildPipeSecurityAttributes creates has the low integrity SACL within it. | |
387 TEST_F(CrashTest, BuildPipeSecurityAttributes) { | |
388 BuildPipeSecurityAttributesTest(true); | |
389 BuildPipeSecurityAttributesTest(false); | |
390 } | |
391 | |
392 TEST_F(CrashTest, StartSenderWithCommandLine) { | |
393 // Negative test. | |
394 CString filename(_T("DoesNotExist.exe")); | |
395 EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), | |
396 StartSenderWithCommandLine(&filename)); | |
397 } | |
398 | |
399 } // namespace omaha | |
400 | |
OLD | NEW |