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