/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-07-31 17:42:29 UTC
  • mto: This revision was merged to the branch mainline in revision 4611.
  • Revision ID: john@arbash-meinel.com-20090731174229-w2zdsdlfpeddk8gl
Now we got to the per-workingtree tests, etc.

The main causes seem to break down into:
  bzrdir.clone() is known to be broken wrt locking, this effects
  everything that tries to 'push'

  shelf code is not compatible with strict locking

  merge code seems to have an issue. This might actually be the
  root cause of the clone() problems.

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
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
19
import errno
25
20
import os
26
21
import signal
29
24
import time
30
25
 
31
26
from bzrlib import (
32
 
    breakin,
33
27
    errors,
34
28
    tests,
35
29
    )
40
34
    # wait() waiting for the child to exit when it's not going to.
41
35
 
42
36
    def setUp(self):
 
37
        if sys.platform == 'win32':
 
38
            raise tests.TestSkipped('breakin signal not tested on win32')
43
39
        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
40
 
100
41
    def _dont_SIGQUIT_on_darwin(self):
101
42
        if sys.platform == 'darwin':
107
48
            raise tests.TestNotApplicable(
108
49
                '%s raises a popup on OSX' % self.id())
109
50
 
110
 
    def _wait_for_process(self, pid, sig=None, count=100):
 
51
    def _wait_for_process(self, pid, sig=None):
111
52
        # We don't know quite how long waiting for the process 'pid' will take,
112
53
        # but if it's more than 10s then it's probably not going to work.
113
 
        for i in range(count):
 
54
        for i in range(100):
 
55
            time.sleep(0.1)
114
56
            if sig is not None:
115
 
                self._send_signal(pid, sig)
 
57
                os.kill(pid, sig)
116
58
            # Use WNOHANG to ensure we don't get blocked, doing so, we may
117
59
            # 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
60
            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:
 
61
                # note: waitpid is different on win32, but this test only runs
 
62
                # on unix
 
63
                pid_killed, returncode = os.waitpid(pid, os.WNOHANG)
 
64
                if (pid_killed, returncode) != (0, 0):
127
65
                    if sig is not None:
128
66
                        # high bit in low byte says if core was dumped; we
129
67
                        # don't care
135
73
                    return True, None
136
74
                else:
137
75
                    raise
138
 
            if i + 1 != count:
139
 
                time.sleep(0.1)
140
76
 
141
77
        return False, None
142
78
 
152
88
        # wait for it to get started, and print the 'listening' line
153
89
        proc.stderr.readline()
154
90
        # first sigquit pops into debugger
155
 
        self._send_signal(proc.pid, 'break')
 
91
        os.kill(proc.pid, signal.SIGQUIT)
156
92
        # 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
93
        err = proc.stderr.readline()
161
94
        self.assertContainsRe(err, r'entering debugger')
162
 
        # Try to shutdown cleanly;
163
95
        # Now that the debugger is entered, we can ask him to quit
164
96
        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)
 
97
        # We wait a bit to let the child process handles our query and avoid
 
98
        # triggering deadlocks leading to hangs on multi-core hosts...
 
99
        dead, sig = self._wait_for_process(proc.pid)
167
100
        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" %
 
101
            # The process didn't finish, let's kill it before reporting failure
 
102
            dead, sig = self._wait_for_process(proc.pid, signal.SIGKILL)
 
103
            if dead:
 
104
                raise tests.KnownFailure(
 
105
                    "subprocess wasn't terminated, it had to be killed")
 
106
            else:
 
107
                self.fail("subprocess %d wasn't terminated by repeated SIGKILL",
174
108
                          proc.pid)
175
109
 
176
110
    def test_breakin_harder(self):
181
115
        # wait for it to get started, and print the 'listening' line
182
116
        proc.stderr.readline()
183
117
        # break into the debugger
184
 
        self._send_signal(proc.pid, 'break')
 
118
        os.kill(proc.pid, signal.SIGQUIT)
185
119
        # Wait for the debugger to acknowledge the signal reception (since we
186
120
        # want to send a second signal, we ensure it doesn't get lost by
187
121
        # validating the first get received and produce its effect).
188
122
        err = proc.stderr.readline()
189
123
        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()))
 
124
        dead, sig = self._wait_for_process(proc.pid, signal.SIGQUIT)
 
125
        self.assertTrue((dead and sig == signal.SIGQUIT),
 
126
                        msg="subprocess wasn't terminated by repeated SIGQUIT")
195
127
 
196
128
    def test_breakin_disabled(self):
197
129
        self._dont_SIGQUIT_on_darwin()
200
132
        # wait for it to get started, and print the 'listening' line
201
133
        proc.stderr.readline()
202
134
        # first hit should just kill it
203
 
        self._send_signal(proc.pid, 'break')
 
135
        os.kill(proc.pid, signal.SIGQUIT)
204
136
        proc.wait()