/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: Robert Collins
  • Date: 2007-07-15 15:40:37 UTC
  • mto: (2592.3.33 repository)
  • mto: This revision was merged to the branch mainline in revision 2624.
  • Revision ID: robertc@robertcollins.net-20070715154037-3ar8g89decddc9su
Make GraphIndex accept nodes as key, value, references, so that the method
signature is closer to what a simple key->value index delivers. Also
change the behaviour when the reference list count is zero to accept
key, value as nodes, and emit key, value to make it identical in that case
to a simple key->value index. This may not be a good idea, but for now it
seems ok.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2009 Canonical Ltd
 
1
# Copyright (C) 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
"""Blackbox tests for debugger breakin"""
18
18
 
19
 
try:
20
 
    import ctypes
21
 
    have_ctypes = True
22
 
except ImportError:
23
 
    have_ctypes = False
24
 
import errno
25
19
import os
26
20
import signal
27
21
import subprocess
28
22
import sys
29
23
import time
30
24
 
31
 
from bzrlib import (
32
 
    breakin,
33
 
    errors,
34
 
    tests,
35
 
    )
36
 
 
37
 
 
38
 
class TestBreakin(tests.TestCase):
 
25
from bzrlib.tests import TestCase, TestSkipped
 
26
 
 
27
 
 
28
class TestBreakin(TestCase):
39
29
    # FIXME: If something is broken, these tests may just hang indefinitely in
40
30
    # wait() waiting for the child to exit when it's not going to.
41
31
 
42
32
    def setUp(self):
 
33
        if sys.platform == 'win32':
 
34
            raise TestSkipped('breakin signal not tested on win32')
43
35
        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
36
 
143
37
    # port 0 means to allocate any port
144
38
    _test_process_args = ['serve', '--port', 'localhost:0']
145
39
 
146
40
    def test_breakin(self):
147
41
        # Break in to a debugger while bzr is running
148
 
        # we need to test against a command that will wait for
 
42
        # we need to test against a command that will wait for 
149
43
        # a while -- bzr serve should do
150
44
        proc = self.start_bzr_subprocess(self._test_process_args,
151
45
                env_changes=dict(BZR_SIGQUIT_PDB=None))
152
46
        # wait for it to get started, and print the 'listening' line
153
 
        proc.stderr.readline()
 
47
        proc.stdout.readline()
154
48
        # 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
 
49
        os.kill(proc.pid, signal.SIGQUIT)
164
50
        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)
 
51
        time.sleep(.5)
 
52
        err = proc.stderr.readline()
 
53
        self.assertContainsRe(err, r'entering debugger')
175
54
 
176
55
    def test_breakin_harder(self):
177
 
        """SIGQUITting twice ends the process."""
178
 
        self._dont_SIGQUIT_on_darwin()
179
56
        proc = self.start_bzr_subprocess(self._test_process_args,
180
57
                env_changes=dict(BZR_SIGQUIT_PDB=None))
181
58
        # wait for it to get started, and print the 'listening' line
182
 
        proc.stderr.readline()
 
59
        proc.stdout.readline()
183
60
        # 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()))
 
61
        os.kill(proc.pid, signal.SIGQUIT)
 
62
        # now send a second sigquit, which should cause it to exit.  That
 
63
        # won't happen until the original signal has been noticed by the
 
64
        # child and it's run its signal handler.  We don't know quite how long
 
65
        # this will take, but if it's more than 10s then it's probably not
 
66
        # going to work.
 
67
        for i in range(100):
 
68
            time.sleep(0.1)
 
69
            os.kill(proc.pid, signal.SIGQUIT)
 
70
            # note: waitpid is different on win32, but this test only runs on
 
71
            # unix
 
72
            r = os.waitpid(proc.pid, os.WNOHANG)
 
73
            if r != (0, 0):
 
74
                # high bit says if core was dumped; we don't care
 
75
                self.assertEquals(r[1] & 0x7f, signal.SIGQUIT)
 
76
                break
 
77
        else:
 
78
            self.fail("subprocess wasn't terminated by repeated SIGQUIT")
195
79
 
196
80
    def test_breakin_disabled(self):
197
 
        self._dont_SIGQUIT_on_darwin()
198
81
        proc = self.start_bzr_subprocess(self._test_process_args,
199
82
                env_changes=dict(BZR_SIGQUIT_PDB='0'))
200
83
        # wait for it to get started, and print the 'listening' line
201
 
        proc.stderr.readline()
 
84
        proc.stdout.readline()
202
85
        # first hit should just kill it
203
 
        self._send_signal(proc.pid, 'break')
 
86
        os.kill(proc.pid, signal.SIGQUIT)
204
87
        proc.wait()