1# Copyright (c) 2012 The Chromium OS 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 5import contextlib 6import fcntl 7import glob 8import logging 9import os 10import re 11import shutil 12 13import common 14from autotest_lib.client.bin import test, utils 15from autotest_lib.client.common_lib import error 16from autotest_lib.client.cros import constants, cros_logging 17 18 19class CrashTest(test.test): 20 """ 21 This class deals with running crash tests, which are tests which crash a 22 user-space program (or the whole machine) and generate a core dump. We 23 want to check that the correct crash dump is available and can be 24 retrieved. 25 26 Chromium OS has a crash sender which checks for new crash data and sends 27 it to a server. This crash data is used to track software quality and find 28 bugs. The system crash sender normally is always running, but can be paused 29 by creating _PAUSE_FILE. When crash sender sees this, it pauses operation. 30 31 For testing purposes we sometimes want to run the crash sender manually. 32 In this case we can set 'OVERRIDE_PAUSE_SENDING=1' in the environment and 33 run the crash sender manually (as a child process). 34 35 Also for testing we sometimes want to mock out the crash sender, and just 36 have it pretend to succeed or fail. The _MOCK_CRASH_SENDING file is used 37 for this. If it doesn't exist, then the crash sender runs normally. If 38 it exists but is empty, the crash sender will succeed (but actually do 39 nothing). If the file contains something, then the crash sender will fail. 40 41 If the user consents to sending crash tests, then the _CONSENT_FILE will 42 exist in the home directory. This test needs to create this file for the 43 crash sending to work. 44 45 Crash reports are rate limited to a certain number of reports each 24 46 hours. If the maximum number has already been sent then reports are held 47 until later. This is administered by a directory _CRASH_SENDER_RATE_DIR 48 which contains one temporary file for each time a report is sent. 49 50 The class provides the ability to push a consent file. This disables 51 consent for this test but allows it to be popped back at later. This 52 makes nested tests easier. If _automatic_consent_saving is True (the 53 default) then consent will be pushed at the start and popped at the end. 54 55 Interesting variables: 56 _log_reader: the log reader used for reading log files 57 _leave_crash_sending: True to enable crash sending on exit from the 58 test, False to disable it. (Default True). 59 _automatic_consent_saving: True to push the consent at the start of 60 the test and pop it afterwards. (Default True). 61 62 Useful places to look for more information are: 63 64 chromeos/src/platform/crash-reporter/crash_sender 65 - sender script which crash crash reporter to create reports, then 66 67 chromeos/src/platform/crash-reporter/ 68 - crash reporter program 69 """ 70 71 72 _CONSENT_FILE = '/home/chronos/Consent To Send Stats' 73 _CORE_PATTERN = '/proc/sys/kernel/core_pattern' 74 _CRASH_REPORTER_PATH = '/sbin/crash_reporter' 75 _CRASH_SENDER_PATH = '/sbin/crash_sender' 76 _CRASH_SENDER_RATE_DIR = '/var/lib/crash_sender' 77 _CRASH_SENDER_LOCK_PATH = '/run/lock/crash_sender' 78 _CRASH_RUN_STATE_DIR = '/run/crash_reporter' 79 _CRASH_TEST_IN_PROGRESS = _CRASH_RUN_STATE_DIR + '/crash-test-in-progress' 80 _MOCK_CRASH_SENDING = _CRASH_RUN_STATE_DIR + '/mock-crash-sending' 81 _PAUSE_FILE = '/var/lib/crash_sender_paused' 82 _SYSTEM_CRASH_DIR = '/var/spool/crash' 83 _FALLBACK_USER_CRASH_DIR = '/home/chronos/crash' 84 _USER_CRASH_DIRS = '/home/chronos/u-*/crash' 85 _USER_CRASH_DIR_REGEX = re.compile('/home/chronos/u-([a-f0-9]+)/crash') 86 87 # Use the same file format as crash does normally: 88 # <basename>.#.#.#.meta 89 _FAKE_TEST_BASENAME = 'fake.1.2.3' 90 91 def _set_system_sending(self, is_enabled): 92 """Sets whether or not the system crash_sender is allowed to run. 93 94 This is done by creating or removing _PAUSE_FILE. 95 96 crash_sender may still be allowed to run if _set_child_sending is 97 called with True and it is run as a child process. 98 99 @param is_enabled: True to enable crash_sender, False to disable it. 100 """ 101 if is_enabled: 102 if os.path.exists(self._PAUSE_FILE): 103 os.remove(self._PAUSE_FILE) 104 else: 105 utils.system('touch ' + self._PAUSE_FILE) 106 107 108 def _set_child_sending(self, is_enabled): 109 """Overrides crash sending enabling for child processes. 110 111 When the system crash sender is disabled this test can manually run 112 the crash sender as a child process. Normally this would do nothing, 113 but this function sets up crash_sender to ignore its disabled status 114 and do its job. 115 116 @param is_enabled: True to enable crash sending for child processes. 117 """ 118 if is_enabled: 119 os.environ['OVERRIDE_PAUSE_SENDING'] = "1" 120 else: 121 del os.environ['OVERRIDE_PAUSE_SENDING'] 122 123 124 def _set_force_official(self, is_enabled): 125 """Sets whether or not reports will upload for unofficial versions. 126 127 Normally, crash reports are only uploaded for official build 128 versions. If the override is set, however, they will also be 129 uploaded for unofficial versions. 130 131 @param is_enabled: True to enable uploading for unofficial versions. 132 """ 133 if is_enabled: 134 os.environ['FORCE_OFFICIAL'] = "1" 135 elif os.environ.get('FORCE_OFFICIAL'): 136 del os.environ['FORCE_OFFICIAL'] 137 138 139 def _set_mock_developer_mode(self, is_enabled): 140 """Sets whether or not we should pretend we booted in developer mode. 141 142 @param is_enabled: True to pretend we are in developer mode. 143 """ 144 if is_enabled: 145 os.environ['MOCK_DEVELOPER_MODE'] = "1" 146 elif os.environ.get('MOCK_DEVELOPER_MODE'): 147 del os.environ['MOCK_DEVELOPER_MODE'] 148 149 150 def _reset_rate_limiting(self): 151 """Reset the count of crash reports sent today. 152 153 This clears the contents of the rate limiting directory which has 154 the effect of reseting our count of crash reports sent. 155 """ 156 utils.system('rm -rf ' + self._CRASH_SENDER_RATE_DIR) 157 158 159 def _clear_spooled_crashes(self): 160 """Clears system and user crash directories. 161 162 This will remove all crash reports which are waiting to be sent. 163 """ 164 utils.system('rm -rf ' + self._SYSTEM_CRASH_DIR) 165 utils.system('rm -rf %s %s' % (self._USER_CRASH_DIRS, 166 self._FALLBACK_USER_CRASH_DIR)) 167 168 169 def _kill_running_sender(self): 170 """Kill the the crash_sender process if running.""" 171 utils.system('pkill -9 -e crash_sender', ignore_status=True) 172 173 174 def _set_sending_mock(self, mock_enabled, send_success=True): 175 """Enables / disables mocking of the sending process. 176 177 This uses the _MOCK_CRASH_SENDING file to achieve its aims. See notes 178 at the top. 179 180 @param mock_enabled: If True, mocking is enabled, else it is disabled. 181 @param send_success: If mock_enabled this is True for the mocking to 182 indicate success, False to indicate failure. 183 """ 184 if mock_enabled: 185 if send_success: 186 data = '' 187 else: 188 data = '1' 189 logging.info('Setting sending mock') 190 utils.open_write_close(self._MOCK_CRASH_SENDING, data) 191 else: 192 utils.system('rm -f ' + self._MOCK_CRASH_SENDING) 193 194 195 def _set_consent(self, has_consent): 196 """Sets whether or not we have consent to send crash reports. 197 198 This creates or deletes the _CONSENT_FILE to control whether 199 crash_sender will consider that it has consent to send crash reports. 200 It also copies a policy blob with the proper policy setting. 201 202 @param has_consent: True to indicate consent, False otherwise 203 """ 204 autotest_cros_dir = os.path.join(os.path.dirname(__file__), '..') 205 if has_consent: 206 if os.path.isdir(constants.WHITELIST_DIR): 207 # Create policy file that enables metrics/consent. 208 shutil.copy('%s/mock_metrics_on.policy' % autotest_cros_dir, 209 constants.SIGNED_POLICY_FILE) 210 shutil.copy('%s/mock_metrics_owner.key' % autotest_cros_dir, 211 constants.OWNER_KEY_FILE) 212 # Create deprecated consent file. This is created *after* the 213 # policy file in order to avoid a race condition where chrome 214 # might remove the consent file if the policy's not set yet. 215 # We create it as a temp file first in order to make the creation 216 # of the consent file, owned by chronos, atomic. 217 # See crosbug.com/18413. 218 temp_file = self._CONSENT_FILE + '.tmp'; 219 utils.open_write_close(temp_file, 'test-consent') 220 utils.system('chown chronos:chronos "%s"' % (temp_file)) 221 shutil.move(temp_file, self._CONSENT_FILE) 222 logging.info('Created ' + self._CONSENT_FILE) 223 else: 224 if os.path.isdir(constants.WHITELIST_DIR): 225 # Create policy file that disables metrics/consent. 226 shutil.copy('%s/mock_metrics_off.policy' % autotest_cros_dir, 227 constants.SIGNED_POLICY_FILE) 228 shutil.copy('%s/mock_metrics_owner.key' % autotest_cros_dir, 229 constants.OWNER_KEY_FILE) 230 # Remove deprecated consent file. 231 utils.system('rm -f "%s"' % (self._CONSENT_FILE)) 232 233 234 def _set_crash_test_in_progress(self, in_progress): 235 if in_progress: 236 utils.open_write_close(self._CRASH_TEST_IN_PROGRESS, 'in-progress') 237 logging.info('Created ' + self._CRASH_TEST_IN_PROGRESS) 238 else: 239 utils.system('rm -f "%s"' % (self._CRASH_TEST_IN_PROGRESS)) 240 241 242 def _get_pushed_consent_file_path(self): 243 """Returns filename of the pushed consent file.""" 244 return os.path.join(self.bindir, 'pushed_consent') 245 246 247 def _get_pushed_policy_file_path(self): 248 """Returns filename of the pushed policy file.""" 249 return os.path.join(self.bindir, 'pushed_policy') 250 251 252 def _get_pushed_owner_key_file_path(self): 253 """Returns filename of the pushed owner.key file.""" 254 return os.path.join(self.bindir, 'pushed_owner_key') 255 256 257 def _push_consent(self): 258 """Push the consent file, thus disabling consent. 259 260 The consent files can be created in the new test if required. Call 261 _pop_consent() to restore the original state. 262 """ 263 if os.path.exists(self._CONSENT_FILE): 264 shutil.move(self._CONSENT_FILE, 265 self._get_pushed_consent_file_path()) 266 if os.path.exists(constants.SIGNED_POLICY_FILE): 267 shutil.move(constants.SIGNED_POLICY_FILE, 268 self._get_pushed_policy_file_path()) 269 if os.path.exists(constants.OWNER_KEY_FILE): 270 shutil.move(constants.OWNER_KEY_FILE, 271 self._get_pushed_owner_key_file_path()) 272 273 274 def _pop_consent(self): 275 """Pop the consent files, enabling/disabling consent as it was before 276 we pushed the consent.""" 277 if os.path.exists(self._get_pushed_consent_file_path()): 278 shutil.move(self._get_pushed_consent_file_path(), 279 self._CONSENT_FILE) 280 else: 281 utils.system('rm -f "%s"' % self._CONSENT_FILE) 282 if os.path.exists(self._get_pushed_policy_file_path()): 283 shutil.move(self._get_pushed_policy_file_path(), 284 constants.SIGNED_POLICY_FILE) 285 else: 286 utils.system('rm -f "%s"' % constants.SIGNED_POLICY_FILE) 287 if os.path.exists(self._get_pushed_owner_key_file_path()): 288 shutil.move(self._get_pushed_owner_key_file_path(), 289 constants.OWNER_KEY_FILE) 290 else: 291 utils.system('rm -f "%s"' % constants.OWNER_KEY_FILE) 292 293 294 def _get_crash_dir(self, username, force_user_crash_dir=False): 295 """Returns crash directory for process running as the given user. 296 297 @param username: Unix user of the crashing process. 298 @param force_user_crash_dir: Regardless of |username|, return the crash 299 directory of the current user session, or 300 the fallback directory if no sessions. 301 """ 302 if username == 'root' and not force_user_crash_dir: 303 return self._SYSTEM_CRASH_DIR 304 else: 305 dirs = glob.glob(self._USER_CRASH_DIRS) 306 return dirs[0] if dirs else self._FALLBACK_USER_CRASH_DIR 307 308 309 def _canonicalize_crash_dir(self, crash_dir): 310 """Converts /home/chronos crash directory to /home/user counterpart. 311 312 @param crash_dir: A path of the form /home/chronos/u-<hash>/crash. 313 @returns /home/user/<hash>/crash, or |crash_dir| on form mismatch. 314 """ 315 match = re.match(self._USER_CRASH_DIR_REGEX, crash_dir) 316 return ('/home/user/%s/crash' % match.group(1)) if match else crash_dir 317 318 319 def _initialize_crash_reporter(self): 320 """Start up the crash reporter.""" 321 utils.system('%s --init' % self._CRASH_REPORTER_PATH) 322 # Completely disable crash_reporter from generating crash dumps 323 # while any tests are running, otherwise a crashy system can make 324 # these tests flaky. 325 self.enable_crash_filtering('none') 326 327 328 def get_crash_dir_name(self, name): 329 """Return the full path for |name| inside the system crash directory.""" 330 return os.path.join(self._SYSTEM_CRASH_DIR, name) 331 332 333 def write_crash_dir_entry(self, name, contents): 334 """Writes an empty file to the system crash directory. 335 336 This writes a file to _SYSTEM_CRASH_DIR with the given name. This is 337 used to insert new crash dump files for testing purposes. 338 339 @param name: Name of file to write. 340 @param contents: String to write to the file. 341 """ 342 entry = self.get_crash_dir_name(name) 343 if not os.path.exists(self._SYSTEM_CRASH_DIR): 344 os.makedirs(self._SYSTEM_CRASH_DIR) 345 utils.open_write_close(entry, contents) 346 return entry 347 348 349 def write_fake_meta(self, name, exec_name, payload, log=None, 350 complete=True): 351 """Writes a fake meta entry to the system crash directory. 352 353 @param name: Name of file to write. 354 @param exec_name: Value for exec_name item. 355 @param payload: Value for payload item. 356 @param log: Value for log item. 357 @param complete: True to close off the record, otherwise leave it 358 incomplete. 359 """ 360 last_line = '' 361 if complete: 362 last_line = 'done=1\n' 363 contents = ('exec_name=%s\n' 364 'ver=my_ver\n' 365 'payload=%s\n' 366 '%s' % (exec_name, payload, 367 last_line)) 368 if log: 369 contents = ('log=%s\n' % log) + contents 370 return self.write_crash_dir_entry(name, contents) 371 372 373 def _prepare_sender_one_crash(self, 374 send_success, 375 reports_enabled, 376 report): 377 """Create metadata for a fake crash report. 378 379 This enabled mocking of the crash sender, then creates a fake 380 crash report for testing purposes. 381 382 @param send_success: True to make the crash_sender success, False to 383 make it fail. 384 @param reports_enabled: True to enable consent to that reports will be 385 sent. 386 @param report: Report to use for crash, if None we create one. 387 """ 388 self._set_sending_mock(mock_enabled=True, send_success=send_success) 389 self._set_consent(reports_enabled) 390 if report is None: 391 # Use the same file format as crash does normally: 392 # <basename>.#.#.#.meta 393 payload = self.write_crash_dir_entry( 394 '%s.dmp' % self._FAKE_TEST_BASENAME, '') 395 report = self.write_fake_meta( 396 '%s.meta' % self._FAKE_TEST_BASENAME, 'fake', payload) 397 return report 398 399 400 def _parse_sender_output(self, output): 401 """Parse the log output from the crash_sender script. 402 403 This script can run on the logs from either a mocked or true 404 crash send. It looks for one and only one crash from output. 405 Non-crash anomalies should be ignored since there're just noise 406 during running the test. 407 408 @param output: output from the script 409 410 @returns A dictionary with these values: 411 error_type: an error type, if given 412 exec_name: name of executable which crashed 413 image_type: type of image ("dev","force-official",...), if given 414 boot_mode: current boot mode ("dev",...), if given 415 meta_path: path to the report metadata file 416 output: the output from the script, copied 417 report_kind: kind of report sent (minidump vs kernel) 418 send_attempt: did the script attempt to send a crash. 419 send_success: if it attempted, was the crash send successful. 420 sig: signature of the report, if given. 421 sleep_time: if it attempted, how long did it sleep before 422 sending (if mocked, how long would it have slept) 423 """ 424 anomaly_types = ( 425 'kernel_suspend_warning', 426 'kernel_warning', 427 'kernel_wifi_warning', 428 'selinux_violation', 429 'service_failure', 430 ) 431 432 # TODO(crbug.com/923200): clean up and make more robust. 433 def crash_sender_search(regexp, output): 434 """Narrow search to lines from crash_sender.""" 435 return re.search(r'crash_sender.*' + regexp, output) 436 437 before_first_crash = None 438 while True: 439 crash_header = crash_sender_search( 440 'Considering metadata (\S+)', 441 output 442 ) 443 if not crash_header: 444 break 445 if before_first_crash is None: 446 before_first_crash = output[:crash_header.start()] 447 meta_considered = crash_header.group(1) 448 is_anomaly = any(x in meta_considered for x in anomaly_types) 449 if is_anomaly: 450 # If it's an anomaly, skip this header, and look for next 451 # one. 452 output = output[crash_header.end():] 453 else: 454 # If it's not an anomaly, skip everything before this 455 # header. 456 output = output[crash_header.start():] 457 break 458 if before_first_crash: 459 output = before_first_crash + output 460 logging.debug('Filtered sender output to parse:\n%s', output) 461 462 sleep_match = crash_sender_search('Scheduled to send in (\d+)s', output) 463 send_attempt = sleep_match is not None 464 if send_attempt: 465 sleep_time = int(sleep_match.group(1)) 466 else: 467 sleep_time = None 468 469 meta_match = crash_sender_search('Metadata: (\S+) \((\S+)\)', output) 470 if meta_match: 471 meta_path = meta_match.group(1) 472 report_kind = meta_match.group(2) 473 else: 474 meta_path = None 475 report_kind = None 476 477 payload_match = crash_sender_search('Payload: (\S+)', output) 478 if payload_match: 479 report_payload = payload_match.group(1) 480 else: 481 report_payload = None 482 483 exec_name_match = crash_sender_search('Exec name: (\S+)', output) 484 if exec_name_match: 485 exec_name = exec_name_match.group(1) 486 else: 487 exec_name = None 488 489 sig_match = crash_sender_search('sig: (\S+)', output) 490 if sig_match: 491 sig = sig_match.group(1) 492 else: 493 sig = None 494 495 error_type_match = crash_sender_search('Error type: (\S+)', output) 496 if error_type_match: 497 error_type = error_type_match.group(1) 498 else: 499 error_type = None 500 501 image_type_match = crash_sender_search('Image type: (\S+)', output) 502 if image_type_match: 503 image_type = image_type_match.group(1) 504 else: 505 image_type = None 506 507 boot_mode_match = crash_sender_search('Boot mode: (\S+)', output) 508 if boot_mode_match: 509 boot_mode = boot_mode_match.group(1) 510 else: 511 boot_mode = None 512 513 send_success = 'Mocking successful send' in output 514 return {'exec_name': exec_name, 515 'report_kind': report_kind, 516 'meta_path': meta_path, 517 'report_payload': report_payload, 518 'send_attempt': send_attempt, 519 'send_success': send_success, 520 'sig': sig, 521 'error_type': error_type, 522 'image_type': image_type, 523 'boot_mode': boot_mode, 524 'sleep_time': sleep_time, 525 'output': output} 526 527 528 def wait_for_sender_completion(self): 529 """Wait for crash_sender to complete. 530 531 Wait for no crash_sender's last message to be placed in the 532 system log before continuing and for the process to finish. 533 Otherwise we might get only part of the output.""" 534 utils.poll_for_condition( 535 lambda: self._log_reader.can_find('crash_sender done.'), 536 timeout=60, 537 exception=error.TestError( 538 'Timeout waiting for crash_sender to emit done: ' + 539 self._log_reader.get_logs())) 540 utils.poll_for_condition( 541 lambda: utils.system('pgrep crash_sender', 542 ignore_status=True) != 0, 543 timeout=60, 544 exception=error.TestError( 545 'Timeout waiting for crash_sender to finish: ' + 546 self._log_reader.get_logs())) 547 548 549 def _call_sender_one_crash(self, 550 send_success=True, 551 reports_enabled=True, 552 report=None, 553 should_fail=False): 554 """Call the crash sender script to mock upload one crash. 555 556 @param send_success: Mock a successful send if true 557 @param reports_enabled: Has the user consented to sending crash reports. 558 @param report: report to use for crash, if None we create one. 559 560 @returns a dictionary describing the result with the keys 561 from _parse_sender_output, as well as: 562 report_exists: does the minidump still exist after calling 563 send script 564 rate_count: how many crashes have been uploaded in the past 565 24 hours. 566 """ 567 report = self._prepare_sender_one_crash(send_success, 568 reports_enabled, 569 report) 570 self._log_reader.set_start_by_current() 571 script_output = "" 572 try: 573 script_output = utils.system_output( 574 '%s 2>&1' % self._CRASH_SENDER_PATH, 575 ignore_status=should_fail) 576 except error.CmdError as err: 577 raise error.TestFail('"%s" returned an unexpected non-zero ' 578 'value (%s).' 579 % (err.command, err.result_obj.exit_status)) 580 581 self.wait_for_sender_completion() 582 output = self._log_reader.get_logs() 583 logging.debug('Crash sender message output:\n' + output) 584 585 if script_output != '': 586 logging.debug('crash_sender stdout/stderr: ' + script_output) 587 588 if os.path.exists(report): 589 report_exists = True 590 os.remove(report) 591 else: 592 report_exists = False 593 if os.path.exists(self._CRASH_SENDER_RATE_DIR): 594 rate_count = len(os.listdir(self._CRASH_SENDER_RATE_DIR)) 595 else: 596 rate_count = 0 597 598 result = self._parse_sender_output(output) 599 result['report_exists'] = report_exists 600 result['rate_count'] = rate_count 601 602 # Show the result for debugging but remove 'output' key 603 # since it's large and earlier in debug output. 604 debug_result = dict(result) 605 del debug_result['output'] 606 logging.debug('Result of send (besides output): %s', debug_result) 607 608 return result 609 610 611 def _replace_crash_reporter_filter_in(self, new_parameter): 612 """Replaces the --filter_in= parameter of the crash reporter. 613 614 The kernel is set up to call the crash reporter with the core dump 615 as stdin when a process dies. This function adds a filter to the 616 command line used to call the crash reporter. This is used to ignore 617 crashes in which we have no interest. 618 619 This removes any --filter_in= parameter and optionally replaces it 620 with a new one. 621 622 @param new_parameter: This is parameter to add to the command line 623 instead of the --filter_in=... that was there. 624 """ 625 core_pattern = utils.read_file(self._CORE_PATTERN)[:-1] 626 core_pattern = re.sub('--filter_in=\S*\s*', '', 627 core_pattern).rstrip() 628 if new_parameter: 629 core_pattern += ' ' + new_parameter 630 utils.system('echo "%s" > %s' % (core_pattern, self._CORE_PATTERN)) 631 632 633 def enable_crash_filtering(self, name): 634 """Add a --filter_in argument to the kernel core dump cmdline. 635 636 @param name: Filter text to use. This is passed as a --filter_in 637 argument to the crash reporter. 638 """ 639 self._replace_crash_reporter_filter_in('--filter_in=' + name) 640 641 642 def disable_crash_filtering(self): 643 """Remove the --filter_in argument from the kernel core dump cmdline. 644 645 Next time the crash reporter is invoked (due to a crash) it will not 646 receive a --filter_in paramter.""" 647 self._replace_crash_reporter_filter_in('') 648 649 650 @contextlib.contextmanager 651 def hold_crash_lock(self): 652 """A context manager to hold the crash sender lock.""" 653 with open(self._CRASH_SENDER_LOCK_PATH, 'w+') as f: 654 fcntl.flock(f.fileno(), fcntl.LOCK_EX) 655 try: 656 yield 657 finally: 658 fcntl.flock(f.fileno(), fcntl.LOCK_UN) 659 660 661 def initialize(self): 662 """Initalize the test.""" 663 test.test.initialize(self) 664 self._log_reader = cros_logging.make_system_log_reader() 665 self._leave_crash_sending = True 666 self._automatic_consent_saving = True 667 self.enable_crash_filtering('none') 668 self._set_crash_test_in_progress(True) 669 670 671 def cleanup(self): 672 """Cleanup after the test. 673 674 We reset things back to the way we think they should be. This is 675 intended to allow the system to continue normal operation. 676 677 Some variables silently change the behavior: 678 _automatic_consent_saving: if True, we pop the consent file. 679 _leave_crash_sending: True to enable crash sending, False to 680 disable it 681 """ 682 self._reset_rate_limiting() 683 self._clear_spooled_crashes() 684 self._set_system_sending(self._leave_crash_sending) 685 self._set_sending_mock(mock_enabled=False) 686 if self._automatic_consent_saving: 687 self._pop_consent() 688 self.disable_crash_filtering() 689 self._set_crash_test_in_progress(False) 690 test.test.cleanup(self) 691 692 693 def run_crash_tests(self, 694 test_names, 695 initialize_crash_reporter=False, 696 clear_spool_first=True, 697 must_run_all=True): 698 """Run crash tests defined in this class. 699 700 @param test_names: Array of test names. 701 @param initialize_crash_reporter: Should set up crash reporter for every 702 run. 703 @param clear_spool_first: Clear all spooled user/system crashes before 704 starting the test. 705 @param must_run_all: Should make sure every test in this class is 706 mentioned in test_names. 707 """ 708 if self._automatic_consent_saving: 709 self._push_consent() 710 711 if must_run_all: 712 # Sanity check test_names is complete 713 for attr in dir(self): 714 if attr.find('_test_') == 0: 715 test_name = attr[6:] 716 if not test_name in test_names: 717 raise error.TestError('Test %s is missing' % test_name) 718 719 for test_name in test_names: 720 logging.info(('=' * 20) + ('Running %s' % test_name) + ('=' * 20)) 721 if initialize_crash_reporter: 722 self._initialize_crash_reporter() 723 # Disable crash_sender from running, kill off any running ones, but 724 # set environment so crash_sender may run as a child process. 725 self._set_system_sending(False) 726 self._set_child_sending(True) 727 self._kill_running_sender() 728 self._reset_rate_limiting() 729 # Default to not overriding for unofficial versions. 730 self._set_force_official(False) 731 # Default to not pretending we're in developer mode. 732 self._set_mock_developer_mode(False) 733 if clear_spool_first: 734 self._clear_spooled_crashes() 735 736 # Call the test function 737 getattr(self, '_test_' + test_name)() 738 739 # Clear the intentional crashes, so that the server won't automatically 740 # report crash as failure. 741 self._clear_spooled_crashes() 742