summaryrefslogtreecommitdiffstats
path: root/testing/marionette/harness/marionette_harness/tests/unit/test_crash.py
blob: 7e74f0857a128cca3a6d8e515b15d487d3750fe1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import glob
import shutil

from marionette_driver.errors import MarionetteException
# Import runner module to monkey patch mozcrash module
from mozrunner.base import runner

from marionette_harness import MarionetteTestCase, expectedFailure, run_if_e10s


class MockMozCrash(object):
    """Mock object to replace original mozcrash methods."""

    def __init__(self, marionette):
        self.marionette = marionette

        with self.marionette.using_context('chrome'):
            self.crash_reporter_enabled = self.marionette.execute_script("""
                try {
                  Components.classes["@mozilla.org/toolkit/crash-reporter;1"].
                    getService(Components.interfaces.nsICrashReporter);
                  return true;
                } catch (exc) {
                  return false;
                }
            """)

    def check_for_crashes(self, dump_directory, *args, **kwargs):
        minidump_files = glob.glob('{}/*.dmp'.format(dump_directory))
        shutil.rmtree(dump_directory, ignore_errors=True)

        if self.crash_reporter_enabled:
            return len(minidump_files)
        else:
            return len(minidump_files) == 0

    def log_crashes(self, logger, dump_directory, *args, **kwargs):
        return self.check_for_crashes(dump_directory, *args, **kwargs)


class BaseCrashTestCase(MarionetteTestCase):

    # Reduce the timeout for faster processing of the tests
    socket_timeout = 10

    def setUp(self):
        super(BaseCrashTestCase, self).setUp()

        self.mozcrash_mock = MockMozCrash(self.marionette)
        self.crash_count = self.marionette.crashed
        self.pid = self.marionette.process_id
        self.remote_uri = self.marionette.absolute_url("javascriptPage.html")

    def tearDown(self):
        self.marionette.crashed = self.crash_count

        super(BaseCrashTestCase, self).tearDown()

    def crash(self, chrome=True):
        context = 'chrome' if chrome else 'content'
        sandbox = None if chrome else 'system'

        # Monkey patch mozcrash to avoid crash info output only for our triggered crashes.
        mozcrash = runner.mozcrash
        runner.mozcrash = self.mozcrash_mock

        socket_timeout = self.marionette.client.socket_timeout

        self.marionette.set_context(context)
        try:
            self.marionette.client.socket_timeout = self.socket_timeout
            self.marionette.execute_script("""
              // Copied from crash me simple
              Components.utils.import("resource://gre/modules/ctypes.jsm");

              // ctypes checks for NULL pointer derefs, so just go near-NULL.
              var zero = new ctypes.intptr_t(8);
              var badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
              var crash = badptr.contents;
            """, sandbox=sandbox)
        finally:
            runner.mozcrash = mozcrash
            self.marionette.client.socket_timeout = socket_timeout


class TestCrash(BaseCrashTestCase):

    def test_crash_chrome_process(self):
        self.assertRaisesRegexp(IOError, 'Process crashed',
                                self.crash, chrome=True)
        self.assertEqual(self.marionette.crashed, 1)
        self.assertIsNone(self.marionette.session)
        self.assertRaisesRegexp(MarionetteException, 'Please start a session',
                                self.marionette.get_url)

        self.marionette.start_session()
        self.assertNotEqual(self.marionette.process_id, self.pid)

        # TODO: Bug 1314594 - Causes a hang for the communication between the
        # chrome and frame script.
        # self.marionette.get_url()

    @run_if_e10s("Content crashes only exist in e10s mode")
    def test_crash_content_process(self):
        # If e10s is disabled the chrome process crashes
        self.marionette.navigate(self.remote_uri)

        self.assertRaisesRegexp(IOError, 'Content process crashed',
                                self.crash, chrome=False)
        self.assertEqual(self.marionette.crashed, 1)
        self.assertIsNone(self.marionette.session)
        self.assertRaisesRegexp(MarionetteException, 'Please start a session',
                                self.marionette.get_url)

        self.marionette.start_session()
        self.assertNotEqual(self.marionette.process_id, self.pid)
        self.marionette.get_url()

    @expectedFailure
    def test_unexpected_crash(self):
        self.crash(chrome=True)


class TestCrashInSetUp(BaseCrashTestCase):

    def setUp(self):
        super(TestCrashInSetUp, self).setUp()

        self.assertRaisesRegexp(IOError, 'Process crashed',
                                self.crash, chrome=True)
        self.assertEqual(self.marionette.crashed, 1)
        self.assertIsNone(self.marionette.session)

    def test_crash_in_setup(self):
        self.marionette.start_session()
        self.assertNotEqual(self.marionette.process_id, self.pid)


class TestCrashInTearDown(BaseCrashTestCase):

    def tearDown(self):
        try:
            self.assertRaisesRegexp(IOError, 'Process crashed',
                                    self.crash, chrome=True)
        finally:
            self.assertEqual(self.marionette.crashed, 1)
            self.assertIsNone(self.marionette.session)
            super(TestCrashInTearDown, self).tearDown()

    def test_crash_in_teardown(self):
        pass