/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/blackbox/test_breakin.py

  • Committer: John Arbash Meinel
  • Date: 2009-12-22 16:28:47 UTC
  • mto: This revision was merged to the branch mainline in revision 4922.
  • Revision ID: john@arbash-meinel.com-20091222162847-tvnsc69to4l4uf5r
Implement a permute_for_extension helper.

Use it for all of the 'simple' extension permutations.
It basically permutes all tests in the current module, by setting TestCase.module.
Which works well for most of our extension tests. Some had more advanced
handling of permutations (extra permutations, custom vars, etc.)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006, 2007, 2009 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Blackbox tests for debugger breakin"""
 
18
 
 
19
try:
 
20
    import ctypes
 
21
    have_ctypes = True
 
22
except ImportError:
 
23
    have_ctypes = False
 
24
import errno
 
25
import os
 
26
import signal
 
27
import subprocess
 
28
import sys
 
29
import time
 
30
 
 
31
from bzrlib import (
 
32
    breakin,
 
33
    errors,
 
34
    tests,
 
35
    )
 
36
 
 
37
 
 
38
class TestBreakin(tests.TestCase):
 
39
    # FIXME: If something is broken, these tests may just hang indefinitely in
 
40
    # wait() waiting for the child to exit when it's not going to.
 
41
 
 
42
    def setUp(self):
 
43
        super(TestBreakin, self).setUp()
 
44
        self.requireFeature(tests.BreakinFeature)
 
45
        if sys.platform == 'win32':
 
46
            self._send_signal = self._send_signal_win32
 
47
        else:
 
48
            self._send_signal = self._send_signal_via_kill
 
49
 
 
50
    def _send_signal_via_kill(self, pid, sig_type):
 
51
        if sig_type == 'break':
 
52
            sig_num = signal.SIGQUIT
 
53
        elif sig_type == 'kill':
 
54
            sig_num = signal.SIGKILL
 
55
        else:
 
56
            raise ValueError("unknown signal type: %s" % (sig_type,))
 
57
        try:
 
58
            os.kill(pid, sig_num)
 
59
        except OSError, e:
 
60
            if e.errno != errno.ESRCH:
 
61
                raise
 
62
 
 
63
    def _send_signal_win32(self, pid, sig_type):
 
64
        """Send a 'signal' on Windows.
 
65
 
 
66
        Windows doesn't really have signals in the same way. All it really
 
67
        supports is:
 
68
            1) Sending SIGINT to the *current* process group (so self, and all
 
69
                children of self)
 
70
            2) Sending SIGBREAK to a process that shares the current console,
 
71
                which can be in its own process group.
 
72
        So we have start_bzr_subprocess create a new process group for the
 
73
        spawned process (via a flag to Popen), and then we map
 
74
            SIGQUIT to GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT)
 
75
            SIGKILL to TerminateProcess
 
76
        """
 
77
        if sig_type == 'break':
 
78
            CTRL_BREAK_EVENT = 1
 
79
            # CTRL_C_EVENT = 0
 
80
            ret = ctypes.windll.kernel32.GenerateConsoleCtrlEvent(
 
81
                    CTRL_BREAK_EVENT, pid)
 
82
            if ret == 0: #error
 
83
                err = ctypes.FormatError()
 
84
                raise RuntimeError('failed to send CTRL_BREAK: %s'
 
85
                                   % (err,))
 
86
        elif sig_type == 'kill':
 
87
            # Does the exit code matter? For now we are just setting it to
 
88
            # something other than 0
 
89
            exit_code = breakin.determine_signal()
 
90
            ctypes.windll.kernel32.TerminateProcess(pid, exit_code)
 
91
 
 
92
    def _popen(self, *args, **kwargs):
 
93
        if sys.platform == 'win32':
 
94
            CREATE_NEW_PROCESS_GROUP = 512
 
95
            # This allows us to send a signal to the child, *without* also
 
96
            # sending it to ourselves
 
97
            kwargs['creationflags'] = CREATE_NEW_PROCESS_GROUP
 
98
        return super(TestBreakin, self)._popen(*args, **kwargs)
 
99
 
 
100
    def _dont_SIGQUIT_on_darwin(self):
 
101
        if sys.platform == 'darwin':
 
102
            # At least on Leopard and with python 2.6, this test will raise a
 
103
            # popup window asking if the python failure should be reported to
 
104
            # Apple... That's not the point of the test :) Marking the test as
 
105
            # not applicable Until we find a way to disable that intrusive
 
106
            # behavior... --vila20080611
 
107
            raise tests.TestNotApplicable(
 
108
                '%s raises a popup on OSX' % self.id())
 
109
 
 
110
    def _wait_for_process(self, pid, sig=None, count=100):
 
111
        # We don't know quite how long waiting for the process 'pid' will take,
 
112
        # but if it's more than 10s then it's probably not going to work.
 
113
        for i in range(count):
 
114
            if sig is not None:
 
115
                self._send_signal(pid, sig)
 
116
            # Use WNOHANG to ensure we don't get blocked, doing so, we may
 
117
            # leave the process continue after *we* die...
 
118
            # Win32 doesn't support WNOHANG, so we just pass 0
 
119
            opts = getattr(os, 'WNOHANG', 0)
 
120
            try:
 
121
                # TODO: waitpid doesn't work well on windows, we might consider
 
122
                #       using WaitForSingleObject(proc._handle, TIMEOUT)
 
123
                #       instead. Most notably, the WNOHANG isn't allowed, so
 
124
                #       this can hang indefinitely.
 
125
                pid_killed, returncode = os.waitpid(pid, opts)
 
126
                if pid_killed != 0 and returncode != 0:
 
127
                    if sig is not None:
 
128
                        # high bit in low byte says if core was dumped; we
 
129
                        # don't care
 
130
                        status, sig = (returncode >> 8, returncode & 0x7f)
 
131
                        return True, sig
 
132
            except OSError, e:
 
133
                if e.errno in (errno.ECHILD, errno.ESRCH):
 
134
                    # The process doesn't exist anymore
 
135
                    return True, None
 
136
                else:
 
137
                    raise
 
138
            if i + 1 != count:
 
139
                time.sleep(0.1)
 
140
 
 
141
        return False, None
 
142
 
 
143
    # port 0 means to allocate any port
 
144
    _test_process_args = ['serve', '--port', 'localhost:0']
 
145
 
 
146
    def test_breakin(self):
 
147
        # Break in to a debugger while bzr is running
 
148
        # we need to test against a command that will wait for
 
149
        # a while -- bzr serve should do
 
150
        proc = self.start_bzr_subprocess(self._test_process_args,
 
151
                env_changes=dict(BZR_SIGQUIT_PDB=None))
 
152
        # wait for it to get started, and print the 'listening' line
 
153
        proc.stderr.readline()
 
154
        # first sigquit pops into debugger
 
155
        self._send_signal(proc.pid, 'break')
 
156
        # Wait for the debugger to acknowledge the signal reception
 
157
        # Note that it is possible for this to deadlock if the child doesn't
 
158
        # acknowlege the signal and write to stderr. Perhaps we should try
 
159
        # os.read(proc.stderr.fileno())?
 
160
        err = proc.stderr.readline()
 
161
        self.assertContainsRe(err, r'entering debugger')
 
162
        # Try to shutdown cleanly;
 
163
        # Now that the debugger is entered, we can ask him to quit
 
164
        proc.stdin.write("q\n")
 
165
        # But we don't really care if it doesn't.
 
166
        dead, sig = self._wait_for_process(proc.pid, count=3)
 
167
        if not dead:
 
168
            # The process didn't finish, let's kill it.
 
169
            dead, sig = self._wait_for_process(proc.pid, 'kill', count=10)
 
170
            if not dead:
 
171
                # process isn't gone, user will have to hunt it down and kill
 
172
                # it.
 
173
                self.fail("subprocess %d wasn't terminated by repeated SIGKILL" %
 
174
                          proc.pid)
 
175
 
 
176
    def test_breakin_harder(self):
 
177
        """SIGQUITting twice ends the process."""
 
178
        self._dont_SIGQUIT_on_darwin()
 
179
        proc = self.start_bzr_subprocess(self._test_process_args,
 
180
                env_changes=dict(BZR_SIGQUIT_PDB=None))
 
181
        # wait for it to get started, and print the 'listening' line
 
182
        proc.stderr.readline()
 
183
        # break into the debugger
 
184
        self._send_signal(proc.pid, 'break')
 
185
        # Wait for the debugger to acknowledge the signal reception (since we
 
186
        # want to send a second signal, we ensure it doesn't get lost by
 
187
        # validating the first get received and produce its effect).
 
188
        err = proc.stderr.readline()
 
189
        self.assertContainsRe(err, r'entering debugger')
 
190
        dead, sig = self._wait_for_process(proc.pid, 'break')
 
191
        self.assertTrue(dead)
 
192
        # Either the child was dead before we could read its status, or the
 
193
        # child was dead from the signal we sent it.
 
194
        self.assertTrue(sig in (None, breakin.determine_signal()))
 
195
 
 
196
    def test_breakin_disabled(self):
 
197
        self._dont_SIGQUIT_on_darwin()
 
198
        proc = self.start_bzr_subprocess(self._test_process_args,
 
199
                env_changes=dict(BZR_SIGQUIT_PDB='0'))
 
200
        # wait for it to get started, and print the 'listening' line
 
201
        proc.stderr.readline()
 
202
        # first hit should just kill it
 
203
        self._send_signal(proc.pid, 'break')
 
204
        proc.wait()