~launchpad-pqm/launchpad/devel

14564.1.1 by Jeroen Vermeulen
lpserve lint.
1
# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
3
11869.11.2 by John Arbash Meinel
stop_service() now waits for the service to actually exit, and gets increasingly demanding about it.
4
import errno
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
5
import os
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
6
import shutil
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
7
import signal
8
import socket
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
9
import subprocess
11149.12.25 by John Arbash Meinel
Trying to spawn multiple sessions seems to show some failures.
10
import tempfile
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
11
import threading
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
12
import time
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
13
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
14
from bzrlib import (
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
15
    errors,
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
16
    osutils,
17
    tests,
18
    trace,
19
    )
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
20
from bzrlib.plugins import lpserve
14564.1.1 by Jeroen Vermeulen
lpserve lint.
21
from testtools import content
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
22
14564.1.1 by Jeroen Vermeulen
lpserve lint.
23
from lp.codehosting import (
24
    get_bzr_path,
25
    get_BZR_PLUGIN_PATH_for_subprocess,
26
    )
14612.2.9 by William Grant
Other bits and pieces.
27
from lp.services.config import config
14564.1.1 by Jeroen Vermeulen
lpserve lint.
28
from lp.testing.fakemethod import FakeMethod
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
29
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
30
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
31
class TestingLPForkingServiceInAThread(lpserve.LPForkingService):
11149.12.83 by John Arbash Meinel
Review feedback from Michael Hudson.
32
    """A test-double to run a "forking service" in a thread.
33
14564.1.1 by Jeroen Vermeulen
lpserve lint.
34
    Note that we don't allow actually forking, but it does allow us to
35
    interact with the service for other operations.
11149.12.83 by John Arbash Meinel
Review feedback from Michael Hudson.
36
    """
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
37
14564.1.1 by Jeroen Vermeulen
lpserve lint.
38
    # For testing we set the timeouts much lower, because we want the
39
    # tests to run quickly.
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
40
    WAIT_FOR_CHILDREN_TIMEOUT = 0.5
41
    SOCKET_TIMEOUT = 0.01
42
    SLEEP_FOR_CHILDREN_TIMEOUT = 0.01
11149.12.62 by John Arbash Meinel
test that incomplete messages timeout quickly, rather than hanging the server.
43
    WAIT_FOR_REQUEST_TIMEOUT = 0.1
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
44
14564.1.1 by Jeroen Vermeulen
lpserve lint.
45
    # We're running in a thread as part of the test suite.  Blow up at
46
    # any attempt to fork.
11149.12.18 by John Arbash Meinel
A command string => argv parser
47
    _fork_function = None
48
11149.12.78 by John Arbash Meinel
Change LPForkingService to use a unix domain socket.
49
    def __init__(self, path, perms=None):
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
50
        self.service_started = threading.Event()
51
        self.service_stopped = threading.Event()
52
        self.this_thread = None
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
53
        self.fork_log = []
11149.12.78 by John Arbash Meinel
Change LPForkingService to use a unix domain socket.
54
        super(TestingLPForkingServiceInAThread, self).__init__(
55
            path=path, perms=None)
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
56
11149.12.66 by John Arbash Meinel
SIGTERM is now handled gracefully as a normal shutdown, rather than crashing.
57
    def _register_signals(self):
14564.1.1 by Jeroen Vermeulen
lpserve lint.
58
        # Don't register it for the test suite.
59
        pass
11149.12.48 by John Arbash Meinel
Create a SIGCHLD handler.
60
11149.12.66 by John Arbash Meinel
SIGTERM is now handled gracefully as a normal shutdown, rather than crashing.
61
    def _unregister_signals(self):
14564.1.1 by Jeroen Vermeulen
lpserve lint.
62
        # We don't fork, and didn't register, so don't unregister.
63
        pass
11149.12.48 by John Arbash Meinel
Create a SIGCHLD handler.
64
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
65
    def _create_master_socket(self):
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
66
        super(TestingLPForkingServiceInAThread, self)._create_master_socket()
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
67
        self.service_started.set()
68
69
    def main_loop(self):
70
        self.service_stopped.clear()
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
71
        super(TestingLPForkingServiceInAThread, self).main_loop()
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
72
        self.service_stopped.set()
73
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
74
    def fork_one_request(self, conn, client_addr, command, env):
11149.12.14 by John Arbash Meinel
try to move the test subprocess code into a common class.
75
        # We intentionally don't allow the test suite to request a fork, as
76
        # threads + forks and everything else don't exactly play well together
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
77
        self.fork_log.append((command, env))
78
        conn.sendall('ok\nfake forking\n')
79
        conn.close()
11149.12.14 by John Arbash Meinel
try to move the test subprocess code into a common class.
80
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
81
    @staticmethod
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
82
    def start_service(test):
11149.12.78 by John Arbash Meinel
Change LPForkingService to use a unix domain socket.
83
        """Start a new LPForkingService in a thread at a random path.
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
84
85
        This will block until the service has created its socket, and is ready
86
        to communicate.
87
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
88
        :return: A new TestingLPForkingServiceInAThread instance
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
89
        """
11149.12.78 by John Arbash Meinel
Change LPForkingService to use a unix domain socket.
90
        fd, path = tempfile.mkstemp(prefix='tmp-lp-forking-service-',
91
                                    suffix='.sock')
92
        # We don't want a temp file, we want a temp socket
93
        os.close(fd)
94
        os.remove(path)
95
        new_service = TestingLPForkingServiceInAThread(path=path)
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
96
        thread = threading.Thread(target=new_service.main_loop,
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
97
                                  name='TestingLPForkingServiceInAThread')
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
98
        new_service.this_thread = thread
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
99
        # should we be doing thread.setDaemon(True) ?
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
100
        thread.start()
101
        new_service.service_started.wait(10.0)
102
        if not new_service.service_started.isSet():
103
            raise RuntimeError(
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
104
                'Failed to start the TestingLPForkingServiceInAThread')
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
105
        test.addCleanup(new_service.stop_service)
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
106
        # what about returning new_service._sockname ?
107
        return new_service
108
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
109
    def stop_service(self):
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
110
        """Stop the test-server thread. This can be called multiple times."""
111
        if self.this_thread is None:
112
            # We already stopped the process
113
            return
114
        self._should_terminate.set()
115
        self.service_stopped.wait(10.0)
116
        if not self.service_stopped.isSet():
117
            raise RuntimeError(
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
118
                'Failed to stop the TestingLPForkingServiceInAThread')
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
119
        self.this_thread.join()
120
        # Break any refcycles
121
        self.this_thread = None
122
123
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
124
class TestTestingLPForkingServiceInAThread(tests.TestCaseWithTransport):
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
125
126
    def test_start_and_stop_service(self):
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
127
        service = TestingLPForkingServiceInAThread.start_service(self)
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
128
        service.stop_service()
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
129
130
    def test_multiple_stops(self):
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
131
        service = TestingLPForkingServiceInAThread.start_service(self)
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
132
        service.stop_service()
11149.12.83 by John Arbash Meinel
Review feedback from Michael Hudson.
133
        # calling stop_service repeatedly is a no-op (and not an error)
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
134
        service.stop_service()
11149.12.10 by John Arbash Meinel
Start building the testing infrastructure.
135
136
    def test_autostop(self):
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
137
        # We shouldn't leak a thread here, as it should be part of the test
138
        # case teardown.
14564.1.1 by Jeroen Vermeulen
lpserve lint.
139
        TestingLPForkingServiceInAThread.start_service(self)
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
140
141
142
class TestCaseWithLPForkingService(tests.TestCaseWithTransport):
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
143
144
    def setUp(self):
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
145
        super(TestCaseWithLPForkingService, self).setUp()
146
        self.service = TestingLPForkingServiceInAThread.start_service(self)
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
147
11149.12.61 by John Arbash Meinel
Handle messages that take a while to get sent.
148
    def send_message_to_service(self, message, one_byte_at_a_time=False):
11149.12.78 by John Arbash Meinel
Change LPForkingService to use a unix domain socket.
149
        client_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
150
        client_sock.connect(self.service.master_socket_path)
11149.12.66 by John Arbash Meinel
SIGTERM is now handled gracefully as a normal shutdown, rather than crashing.
151
        if one_byte_at_a_time:
152
            for byte in message:
153
                client_sock.send(byte)
154
        else:
155
            client_sock.sendall(message)
156
        response = client_sock.recv(1024)
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
157
        return response
158
159
11149.12.18 by John Arbash Meinel
A command string => argv parser
160
class TestLPForkingServiceCommandToArgv(tests.TestCase):
161
162
    def assertAsArgv(self, argv, command_str):
163
        self.assertEqual(argv,
164
            lpserve.LPForkingService.command_to_argv(command_str))
165
166
    def test_simple(self):
167
        self.assertAsArgv([u'foo'], 'foo')
168
        self.assertAsArgv([u'foo', u'bar'], 'foo bar')
169
170
    def test_quoted(self):
171
        self.assertAsArgv([u'foo'], 'foo')
172
        self.assertAsArgv([u'foo bar'], '"foo bar"')
173
174
    def test_unicode(self):
175
        self.assertAsArgv([u'command', u'\xe5'], 'command \xc3\xa5')
176
177
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
178
class TestLPForkingServiceParseEnv(tests.TestCase):
179
180
    def assertEnv(self, env, env_str):
181
        self.assertEqual(env, lpserve.LPForkingService.parse_env(env_str))
182
11149.12.60 by John Arbash Meinel
Change it so that we use a different main command.
183
    def assertInvalid(self, env_str):
184
        self.assertRaises(ValueError, lpserve.LPForkingService.parse_env,
185
                                      env_str)
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
186
187
    def test_no_entries(self):
11149.12.60 by John Arbash Meinel
Change it so that we use a different main command.
188
        self.assertEnv({}, 'end\n')
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
189
190
    def test_one_entries(self):
191
        self.assertEnv({'BZR_EMAIL': 'joe@foo.com'},
192
                       'BZR_EMAIL: joe@foo.com\n'
193
                       'end\n')
194
195
    def test_two_entries(self):
196
        self.assertEnv({'BZR_EMAIL': 'joe@foo.com', 'BAR': 'foo'},
197
                       'BZR_EMAIL: joe@foo.com\n'
198
                       'BAR: foo\n'
199
                       'end\n')
200
11149.12.60 by John Arbash Meinel
Change it so that we use a different main command.
201
    def test_invalid_empty(self):
202
        self.assertInvalid('')
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
203
204
    def test_invalid_end(self):
11149.12.60 by John Arbash Meinel
Change it so that we use a different main command.
205
        self.assertInvalid("BZR_EMAIL: joe@foo.com\n")
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
206
207
    def test_invalid_entry(self):
11149.12.60 by John Arbash Meinel
Change it so that we use a different main command.
208
        self.assertInvalid("BZR_EMAIL joe@foo.com\nend\n")
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
209
210
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
211
class TestLPForkingService(TestCaseWithLPForkingService):
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
212
213
    def test_send_quit_message(self):
214
        response = self.send_message_to_service('quit\n')
11149.12.31 by John Arbash Meinel
Change the returned information to always start with 'ok' or 'FAILURE'.
215
        self.assertEqual('ok\nquit command requested... exiting\n', response)
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
216
        self.service.service_stopped.wait(10.0)
217
        self.assertTrue(self.service.service_stopped.isSet())
218
219
    def test_send_invalid_message_fails(self):
220
        response = self.send_message_to_service('unknown\n')
221
        self.assertStartsWith(response, 'FAILURE')
222
11149.12.13 by John Arbash Meinel
Add a test for the 'hello' heartbeat request.
223
    def test_send_hello_heartbeat(self):
224
        response = self.send_message_to_service('hello\n')
11149.12.31 by John Arbash Meinel
Change the returned information to always start with 'ok' or 'FAILURE'.
225
        self.assertEqual('ok\nyep, still alive\n', response)
11149.12.13 by John Arbash Meinel
Add a test for the 'hello' heartbeat request.
226
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
227
    def test_send_simple_fork(self):
228
        response = self.send_message_to_service('fork rocks\n')
229
        self.assertEqual('ok\nfake forking\n', response)
230
        self.assertEqual([(['rocks'], {})], self.service.fork_log)
231
11149.12.61 by John Arbash Meinel
Handle messages that take a while to get sent.
232
    def test_send_fork_env_with_empty_env(self):
11149.12.60 by John Arbash Meinel
Change it so that we use a different main command.
233
        response = self.send_message_to_service(
234
            'fork-env rocks\n'
235
            'end\n')
236
        self.assertEqual('ok\nfake forking\n', response)
237
        self.assertEqual([(['rocks'], {})], self.service.fork_log)
238
11149.12.61 by John Arbash Meinel
Handle messages that take a while to get sent.
239
    def test_send_fork_env_with_env(self):
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
240
        response = self.send_message_to_service(
11149.12.60 by John Arbash Meinel
Change it so that we use a different main command.
241
            'fork-env rocks\n'
11149.12.59 by John Arbash Meinel
Enable passing env vars to the 'fork' request.
242
            'BZR_EMAIL: joe@example.com\n'
243
            'end\n')
244
        self.assertEqual('ok\nfake forking\n', response)
245
        self.assertEqual([(['rocks'], {'BZR_EMAIL': 'joe@example.com'})],
246
                         self.service.fork_log)
247
11149.12.61 by John Arbash Meinel
Handle messages that take a while to get sent.
248
    def test_send_fork_env_slowly(self):
249
        response = self.send_message_to_service(
250
            'fork-env rocks\n'
251
            'BZR_EMAIL: joe@example.com\n'
252
            'end\n', one_byte_at_a_time=True)
253
        self.assertEqual('ok\nfake forking\n', response)
254
        self.assertEqual([(['rocks'], {'BZR_EMAIL': 'joe@example.com'})],
255
                         self.service.fork_log)
256
11149.12.62 by John Arbash Meinel
test that incomplete messages timeout quickly, rather than hanging the server.
257
    def test_send_incomplete_fork_env_timeout(self):
258
        # We should get a failure message if we can't quickly read the whole
259
        # content
260
        response = self.send_message_to_service(
261
            'fork-env rocks\n'
262
            'BZR_EMAIL: joe@example.com\n',
263
            one_byte_at_a_time=True)
264
        # Note that we *don't* send a final 'end\n'
265
        self.assertStartsWith(response, 'FAILURE\n')
266
267
    def test_send_incomplete_request_timeout(self):
268
        # Requests end with '\n', send one without it
269
        response = self.send_message_to_service('hello',
270
                                                one_byte_at_a_time=True)
271
        self.assertStartsWith(response, 'FAILURE\n')
272
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
273
    def test_child_connection_timeout(self):
274
        self.assertEqual(self.service.CHILD_CONNECT_TIMEOUT,
275
                         self.service._child_connect_timeout)
12344.5.4 by John Arbash Meinel
Change the code to use signal.alarm() instead of another python thread.
276
        response = self.send_message_to_service('child_connect_timeout 1\n')
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
277
        self.assertEqual('ok\n', response)
12344.5.4 by John Arbash Meinel
Change the code to use signal.alarm() instead of another python thread.
278
        self.assertEqual(1, self.service._child_connect_timeout)
279
280
    def test_child_connection_timeout_bad_float(self):
281
        self.assertEqual(self.service.CHILD_CONNECT_TIMEOUT,
282
                         self.service._child_connect_timeout)
283
        response = self.send_message_to_service('child_connect_timeout 1.2\n')
284
        self.assertStartsWith(response, 'FAILURE:')
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
285
286
    def test_child_connection_timeout_no_val(self):
287
        response = self.send_message_to_service('child_connect_timeout \n')
288
        self.assertStartsWith(response, 'FAILURE:')
289
290
    def test_child_connection_timeout_bad_val(self):
291
        response = self.send_message_to_service('child_connect_timeout b\n')
292
        self.assertStartsWith(response, 'FAILURE:')
293
294
    def test__open_handles_will_timeout(self):
12344.5.4 by John Arbash Meinel
Change the code to use signal.alarm() instead of another python thread.
295
        # signal.alarm() has only 1-second granularity. :(
296
        self.service._child_connect_timeout = 1
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
297
        tempdir = tempfile.mkdtemp(prefix='testlpserve-')
298
        self.addCleanup(shutil.rmtree, tempdir, ignore_errors=True)
299
        os.mkfifo(os.path.join(tempdir, 'stdin'))
300
        os.mkfifo(os.path.join(tempdir, 'stdout'))
301
        os.mkfifo(os.path.join(tempdir, 'stderr'))
14564.1.1 by Jeroen Vermeulen
lpserve lint.
302
12344.5.5 by John Arbash Meinel
Review changes from Gavin Panella.
303
        # catch SIGALRM so we don't stop the test suite. It will still
304
        # interupt the blocking open() calls.
14564.1.1 by Jeroen Vermeulen
lpserve lint.
305
        signal.signal(signal.SIGALRM, FakeMethod())
306
12344.5.4 by John Arbash Meinel
Change the code to use signal.alarm() instead of another python thread.
307
        self.addCleanup(signal.signal, signal.SIGALRM, signal.SIG_DFL)
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
308
        e = self.assertRaises(errors.BzrError,
309
            self.service._open_handles, tempdir)
310
        self.assertContainsRe(str(e), r'After \d+.\d+s we failed to open.*')
311
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
312
11149.12.14 by John Arbash Meinel
try to move the test subprocess code into a common class.
313
class TestCaseWithSubprocess(tests.TestCaseWithTransport):
314
    """Override the bzr start_bzr_subprocess command.
315
14564.1.1 by Jeroen Vermeulen
lpserve lint.
316
    The launchpad infrastructure requires a fair amount of configuration to
317
    get paths, etc correct. This provides a "start_bzr_subprocess" command
318
    that has all of those paths appropriately set, but otherwise functions the
319
    same as the bzrlib.tests.TestCase version.
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
320
    """
321
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
322
    def get_python_path(self):
323
        """Return the path to the Python interpreter."""
324
        return '%s/bin/py' % config.root
325
326
    def start_bzr_subprocess(self, process_args, env_changes=None,
327
                             working_dir=None):
328
        """Start bzr in a subprocess for testing.
329
330
        Copied and modified from `bzrlib.tests.TestCase.start_bzr_subprocess`.
331
        This version removes some of the skipping stuff, some of the
332
        irrelevant comments (e.g. about win32) and uses Launchpad's own
333
        mechanisms for getting the path to 'bzr'.
334
335
        Comments starting with 'LAUNCHPAD' are comments about our
336
        modifications.
337
        """
338
        if env_changes is None:
339
            env_changes = {}
340
        env_changes['BZR_PLUGIN_PATH'] = get_BZR_PLUGIN_PATH_for_subprocess()
341
        old_env = {}
342
343
        def cleanup_environment():
344
            for env_var, value in env_changes.iteritems():
345
                old_env[env_var] = osutils.set_or_unset_env(env_var, value)
346
347
        def restore_environment():
348
            for env_var, value in old_env.iteritems():
349
                osutils.set_or_unset_env(env_var, value)
350
351
        cwd = None
352
        if working_dir is not None:
353
            cwd = osutils.getcwd()
354
            os.chdir(working_dir)
355
356
        # LAUNCHPAD: Because of buildout, we need to get a custom Python
357
        # binary, not sys.executable.
358
        python_path = self.get_python_path()
359
        # LAUNCHPAD: We can't use self.get_bzr_path(), since it'll find
360
        # lib/bzrlib, rather than the path to sourcecode/bzr/bzr.
361
        bzr_path = get_bzr_path()
362
        try:
363
            cleanup_environment()
364
            command = [python_path, bzr_path]
365
            command.extend(process_args)
366
            process = self._popen(
367
                command, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
368
                stderr=subprocess.PIPE)
369
        finally:
370
            restore_environment()
371
            if cwd is not None:
372
                os.chdir(cwd)
373
374
        return process
375
11149.12.14 by John Arbash Meinel
try to move the test subprocess code into a common class.
376
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
377
class TestCaseWithLPForkingServiceSubprocess(TestCaseWithSubprocess):
11149.12.14 by John Arbash Meinel
try to move the test subprocess code into a common class.
378
    """Tests will get a separate process to communicate to.
379
14564.1.1 by Jeroen Vermeulen
lpserve lint.
380
    The number of these tests should be small, because it is expensive to
381
    start and stop the daemon.
11149.12.14 by John Arbash Meinel
try to move the test subprocess code into a common class.
382
383
    TODO: This should probably use testresources, or layers somehow...
384
    """
385
386
    def setUp(self):
11149.12.17 by John Arbash Meinel
change the name a bit, prepare for changing the 'fork' request.
387
        super(TestCaseWithLPForkingServiceSubprocess, self).setUp()
11149.12.84 by John Arbash Meinel
Cleanup some 'make lint' warnings.
388
        (self.service_process,
389
         self.service_path) = self.start_service_subprocess()
11149.12.14 by John Arbash Meinel
try to move the test subprocess code into a common class.
390
        self.addCleanup(self.stop_service)
391
11149.12.61 by John Arbash Meinel
Handle messages that take a while to get sent.
392
    def start_conversation(self, message, one_byte_at_a_time=False):
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
393
        """Start talking to the service, and get the initial response."""
11149.12.78 by John Arbash Meinel
Change LPForkingService to use a unix domain socket.
394
        client_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
395
        trace.mutter('sending %r to socket %s' % (message, self.service_path))
396
        client_sock.connect(self.service_path)
11149.12.66 by John Arbash Meinel
SIGTERM is now handled gracefully as a normal shutdown, rather than crashing.
397
        if one_byte_at_a_time:
398
            for byte in message:
399
                client_sock.send(byte)
400
        else:
401
            client_sock.sendall(message)
402
        response = client_sock.recv(1024)
11149.12.19 by John Arbash Meinel
I think I have it hooked up, but the test is failing.
403
        trace.mutter('response: %r' % (response,))
404
        if response.startswith("FAILURE"):
405
            raise RuntimeError('Failed to send message: %r' % (response,))
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
406
        return response, client_sock
407
11149.12.61 by John Arbash Meinel
Handle messages that take a while to get sent.
408
    def send_message_to_service(self, message, one_byte_at_a_time=False):
409
        response, client_sock = self.start_conversation(message,
410
            one_byte_at_a_time=one_byte_at_a_time)
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
411
        client_sock.close()
11149.12.14 by John Arbash Meinel
try to move the test subprocess code into a common class.
412
        return response
413
11149.12.61 by John Arbash Meinel
Handle messages that take a while to get sent.
414
    def send_fork_request(self, command, env=None):
415
        if env is not None:
416
            request_lines = ['fork-env %s\n' % (command,)]
417
            for key, value in env.iteritems():
418
                request_lines.append('%s: %s\n' % (key, value))
419
            request_lines.append('end\n')
420
            request = ''.join(request_lines)
421
        else:
422
            request = 'fork %s\n' % (command,)
423
        response, sock = self.start_conversation(request)
11149.12.31 by John Arbash Meinel
Change the returned information to always start with 'ok' or 'FAILURE'.
424
        ok, pid, path, tail = response.split('\n')
425
        self.assertEqual('ok', ok)
426
        self.assertEqual('', tail)
427
        # Don't really care what it is, but should be an integer
428
        pid = int(pid)
429
        path = path.strip()
430
        self.assertContainsRe(path, '/lp-forking-service-child-')
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
431
        return path, pid, sock
11149.12.31 by John Arbash Meinel
Change the returned information to always start with 'ok' or 'FAILURE'.
432
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
433
    def _start_subprocess(self, path, env_changes):
434
        proc = self.start_bzr_subprocess(
435
            ['lp-service', '--path', path, '--no-preload',
436
             '--children-timeout=1'],
437
            env_changes=env_changes)
438
        trace.mutter('started lp-service subprocess')
439
        expected = 'Listening on socket: %s\n' % (path,)
440
        path_line = proc.stderr.readline()
441
        trace.mutter(path_line)
442
        self.assertEqual(expected, path_line)
443
        return proc
444
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
445
    def start_service_subprocess(self):
446
        # Make sure this plugin is exposed to the subprocess
11149.12.78 by John Arbash Meinel
Change LPForkingService to use a unix domain socket.
447
        # SLOOWWW (~2 seconds, which is why we are doing the work anyway)
11149.12.25 by John Arbash Meinel
Trying to spawn multiple sessions seems to show some failures.
448
        fd, tempname = tempfile.mkstemp(prefix='tmp-log-bzr-lp-forking-')
11149.12.47 by John Arbash Meinel
Remove the 'status' tracking and command.
449
        # I'm not 100% sure about when cleanup runs versus addDetail, but I
450
        # think this will work.
451
        self.addCleanup(os.remove, tempname)
11149.12.84 by John Arbash Meinel
Cleanup some 'make lint' warnings.
452
11149.12.25 by John Arbash Meinel
Trying to spawn multiple sessions seems to show some failures.
453
        def read_log():
454
            f = os.fdopen(fd)
455
            f.seek(0)
456
            content = f.read()
457
            f.close()
458
            return [content]
459
        self.addDetail('server-log', content.Content(
460
            content.ContentType('text', 'plain', {"charset": "utf8"}),
461
            read_log))
11149.12.78 by John Arbash Meinel
Change LPForkingService to use a unix domain socket.
462
        service_fd, path = tempfile.mkstemp(prefix='tmp-lp-service-',
463
                                            suffix='.sock')
464
        os.close(service_fd)
14564.1.1 by Jeroen Vermeulen
lpserve lint.
465
        # The service wants to create this file as a socket.
466
        os.remove(path)
467
        env_changes = {
468
            'BZR_PLUGIN_PATH': lpserve.__path__[0],
469
            'BZR_LOG': tempname,
470
            }
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
471
        proc = self._start_subprocess(path, env_changes)
11149.12.78 by John Arbash Meinel
Change LPForkingService to use a unix domain socket.
472
        return proc, path
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
473
474
    def stop_service(self):
475
        if self.service_process is None:
14564.1.1 by Jeroen Vermeulen
lpserve lint.
476
            # Already stopped.
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
477
            return
478
        # First, try to stop the service gracefully, by sending a 'quit'
14564.1.1 by Jeroen Vermeulen
lpserve lint.
479
        # message.
11149.12.66 by John Arbash Meinel
SIGTERM is now handled gracefully as a normal shutdown, rather than crashing.
480
        try:
481
            response = self.send_message_to_service('quit\n')
14564.1.1 by Jeroen Vermeulen
lpserve lint.
482
        except socket.error:
483
            # Ignore a failure to connect; the service must be
484
            # stopping/stopped already.
11149.12.66 by John Arbash Meinel
SIGTERM is now handled gracefully as a normal shutdown, rather than crashing.
485
            response = None
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
486
        tend = time.time() + 10.0
487
        while self.service_process.poll() is None:
488
            if time.time() > tend:
489
                self.finish_bzr_subprocess(process=self.service_process,
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
490
                    send_signal=signal.SIGINT, retcode=3)
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
491
                self.fail('Failed to quit gracefully after 10.0 seconds')
492
            time.sleep(0.1)
11149.12.66 by John Arbash Meinel
SIGTERM is now handled gracefully as a normal shutdown, rather than crashing.
493
        if response is not None:
494
            self.assertEqual('ok\nquit command requested... exiting\n',
495
                             response)
11149.12.11 by John Arbash Meinel
Start trying to get some subprocess style testing for the service as well.
496
11149.12.23 by John Arbash Meinel
Create a command that just replays content that it read back to its output.
497
    def _get_fork_handles(self, path):
11149.12.25 by John Arbash Meinel
Trying to spawn multiple sessions seems to show some failures.
498
        trace.mutter('getting handles for: %s' % (path,))
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
499
        stdin_path = os.path.join(path, 'stdin')
500
        stdout_path = os.path.join(path, 'stdout')
501
        stderr_path = os.path.join(path, 'stderr')
11149.12.83 by John Arbash Meinel
Review feedback from Michael Hudson.
502
        # The ordering must match the ordering of the service or we get a
503
        # deadlock.
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
504
        child_stdin = open(stdin_path, 'wb', 0)
505
        child_stdout = open(stdout_path, 'rb', 0)
506
        child_stderr = open(stderr_path, 'rb', 0)
11149.12.23 by John Arbash Meinel
Create a command that just replays content that it read back to its output.
507
        return child_stdin, child_stdout, child_stderr
508
509
    def communicate_with_fork(self, path, stdin=None):
510
        child_stdin, child_stdout, child_stderr = self._get_fork_handles(path)
11149.12.21 by John Arbash Meinel
Unfortunately 'bzr rocks' test still takes 2s, though lp-serve takes 3.6s.
511
        if stdin is not None:
512
            child_stdin.write(stdin)
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
513
        child_stdin.close()
514
        stdout_content = child_stdout.read()
515
        stderr_content = child_stderr.read()
11149.12.21 by John Arbash Meinel
Unfortunately 'bzr rocks' test still takes 2s, though lp-serve takes 3.6s.
516
        return stdout_content, stderr_content
517
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
518
    def assertReturnCode(self, expected_code, sock):
519
        """Assert that we get the expected return code as a message."""
520
        response = sock.recv(1024)
11149.12.41 by John Arbash Meinel
Test suite passing again, this time with the master process
521
        self.assertStartsWith(response, 'exited\n')
522
        code = int(response.split('\n', 1)[1])
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
523
        self.assertEqual(expected_code, code)
524
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
525
526
class TestLPServiceInSubprocess(TestCaseWithLPForkingServiceSubprocess):
527
11149.12.23 by John Arbash Meinel
Create a command that just replays content that it read back to its output.
528
    def test_fork_lp_serve_hello(self):
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
529
        path, _, sock = self.send_fork_request('lp-serve --inet 2')
11149.12.21 by John Arbash Meinel
Unfortunately 'bzr rocks' test still takes 2s, though lp-serve takes 3.6s.
530
        stdout_content, stderr_content = self.communicate_with_fork(path,
531
            'hello\n')
532
        self.assertEqual('ok\x012\n', stdout_content)
11149.12.12 by John Arbash Meinel
Do lots of dancing to get it to work.
533
        self.assertEqual('', stderr_content)
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
534
        self.assertReturnCode(0, sock)
11149.12.23 by John Arbash Meinel
Create a command that just replays content that it read back to its output.
535
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
536
    def DONT_test_fork_lp_serve_multiple_hello(self):
537
        # This ensures that the fifos are all set to blocking mode
538
        # We can't actually run this test, because by default 'bzr serve
539
        # --inet' does not flush after each message. So we end up blocking
540
        # forever waiting for the server to finish responding to the first
541
        # request.
542
        path, _, sock = self.send_fork_request('lp-serve --inet 2')
543
        child_stdin, child_stdout, child_stderr = self._get_fork_handles(path)
544
        child_stdin.write('hello\n')
545
        child_stdin.flush()
546
        self.assertEqual('ok\x012\n', child_stdout.read())
547
        child_stdin.write('hello\n')
548
        self.assertEqual('ok\x012\n', child_stdout.read())
549
        child_stdin.close()
550
        self.assertEqual('', child_stderr.read())
551
        child_stdout.close()
552
        child_stderr.close()
553
11149.12.23 by John Arbash Meinel
Create a command that just replays content that it read back to its output.
554
    def test_fork_replay(self):
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
555
        path, _, sock = self.send_fork_request('launchpad-replay')
11149.12.23 by John Arbash Meinel
Create a command that just replays content that it read back to its output.
556
        stdout_content, stderr_content = self.communicate_with_fork(path,
557
            '1 hello\n2 goodbye\n1 maybe\n')
558
        self.assertEqualDiff('hello\nmaybe\n', stdout_content)
559
        self.assertEqualDiff('goodbye\n', stderr_content)
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
560
        self.assertReturnCode(0, sock)
11149.12.24 by John Arbash Meinel
it only takes 907ms to start and stop the service, we may need to look closer at
561
562
    def test_just_run_service(self):
563
        # Start and stop are defined in setUp()
564
        pass
11149.12.25 by John Arbash Meinel
Trying to spawn multiple sessions seems to show some failures.
565
566
    def test_fork_multiple_children(self):
567
        paths = []
568
        for idx in range(4):
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
569
            paths.append(self.send_fork_request('launchpad-replay'))
11149.12.81 by John Arbash Meinel
Respond to Andrew's feedback.
570
        # Do them out of order, as order shouldn't matter.
11149.12.28 by John Arbash Meinel
Just make sure we can handle out-of-order communication as well.
571
        for idx in [3, 2, 0, 1]:
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
572
            p, pid, sock = paths[idx]
11149.12.25 by John Arbash Meinel
Trying to spawn multiple sessions seems to show some failures.
573
            stdout_msg = 'hello %d\n' % (idx,)
14564.1.1 by Jeroen Vermeulen
lpserve lint.
574
            stderr_msg = 'goodbye %d\n' % (idx + 1,)
11149.12.25 by John Arbash Meinel
Trying to spawn multiple sessions seems to show some failures.
575
            stdout, stderr = self.communicate_with_fork(p,
576
                '1 %s2 %s' % (stdout_msg, stderr_msg))
577
            self.assertEqualDiff(stdout_msg, stdout)
578
            self.assertEqualDiff(stderr_msg, stderr)
11149.12.40 by John Arbash Meinel
Start checking the returncode message.
579
            self.assertReturnCode(0, sock)
11149.12.61 by John Arbash Meinel
Handle messages that take a while to get sent.
580
581
    def test_fork_respects_env_vars(self):
582
        path, pid, sock = self.send_fork_request('whoami',
583
            env={'BZR_EMAIL': 'this_test@example.com'})
584
        stdout_content, stderr_content = self.communicate_with_fork(path)
585
        self.assertEqual('', stderr_content)
586
        self.assertEqual('this_test@example.com\n', stdout_content)
11149.12.66 by John Arbash Meinel
SIGTERM is now handled gracefully as a normal shutdown, rather than crashing.
587
11149.12.69 by John Arbash Meinel
Set the default configuration to not use the forking daemon, but the development version to use it.
588
    def _check_exits_nicely(self, sig_id):
11149.12.66 by John Arbash Meinel
SIGTERM is now handled gracefully as a normal shutdown, rather than crashing.
589
        path, _, sock = self.send_fork_request('rocks')
590
        self.assertEqual(None, self.service_process.poll())
591
        # Now when we send SIGTERM, it should wait for the child to exit,
592
        # before it tries to exit itself.
593
        # In python2.6+ we could use self.service_process.terminate()
11149.12.69 by John Arbash Meinel
Set the default configuration to not use the forking daemon, but the development version to use it.
594
        os.kill(self.service_process.pid, sig_id)
11149.12.66 by John Arbash Meinel
SIGTERM is now handled gracefully as a normal shutdown, rather than crashing.
595
        self.assertEqual(None, self.service_process.poll())
596
        # Now talk to the child, so the service can close
597
        stdout_content, stderr_content = self.communicate_with_fork(path)
598
        self.assertEqual('It sure does!\n', stdout_content)
599
        self.assertEqual('', stderr_content)
600
        self.assertReturnCode(0, sock)
601
        # And the process should exit cleanly
602
        self.assertEqual(0, self.service_process.wait())
11149.12.69 by John Arbash Meinel
Set the default configuration to not use the forking daemon, but the development version to use it.
603
604
    def test_sigterm_exits_nicely(self):
605
        self._check_exits_nicely(signal.SIGTERM)
606
607
    def test_sigint_exits_nicely(self):
608
        self._check_exits_nicely(signal.SIGINT)
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
609
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
610
    def test_child_exits_eventually(self):
611
        # We won't ever bind to the socket the child wants, and after some
612
        # time, the child should exit cleanly.
613
        # First, tell the subprocess that we want children to exit quickly.
12344.5.4 by John Arbash Meinel
Change the code to use signal.alarm() instead of another python thread.
614
        # *sigh* signal.alarm only has 1s resolution, so this test is slow.
615
        response = self.send_message_to_service('child_connect_timeout 1\n')
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
616
        self.assertEqual('ok\n', response)
14564.1.1 by Jeroen Vermeulen
lpserve lint.
617
        # Now request a fork.
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
618
        path, pid, sock = self.send_fork_request('rocks')
12344.5.4 by John Arbash Meinel
Change the code to use signal.alarm() instead of another python thread.
619
        # We started opening the child, but stop before we get all handles
620
        # open. After 1 second, the child should get signaled and die.
621
        # The master process should notice, and tell us the status of the
622
        # exited child.
623
        val = sock.recv(4096)
624
        self.assertEqual('exited\n%s\n' % (signal.SIGALRM,), val)
625
        # The master process should clean up after the now deceased child.
14363.2.2 by Jelmer Vernooij
Avoid deprecated failUnlessExists.
626
        self.assertPathDoesNotExist(path)
12344.5.1 by John Arbash Meinel
Change how the children connect to their fifos.
627
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
628
629
class TestCaseWithLPForkingServiceDaemon(
630
    TestCaseWithLPForkingServiceSubprocess):
631
    """Test LPForkingService interaction, when run in daemon mode."""
632
633
    def _cleanup_daemon(self, pid, pid_filename):
634
        try:
635
            os.kill(pid, signal.SIGKILL)
636
        except (OSError, IOError), e:
637
            trace.mutter('failed to kill pid %d, might be already dead: %s'
638
                         % (pid, e))
639
        try:
640
            os.remove(pid_filename)
641
        except (OSError, IOError), e:
642
            if e.errno != errno.ENOENT:
643
                trace.mutter('failed to remove %r: %s'
644
                             % (pid_filename, e))
645
646
    def _start_subprocess(self, path, env_changes):
647
        fd, pid_filename = tempfile.mkstemp(prefix='tmp-lp-forking-service-',
648
                                            suffix='.pid')
11869.11.2 by John Arbash Meinel
stop_service() now waits for the service to actually exit, and gets increasingly demanding about it.
649
        self.service_pid_filename = pid_filename
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
650
        os.close(fd)
651
        proc = self.start_bzr_subprocess(
652
            ['lp-service', '--path', path, '--no-preload',
653
             '--children-timeout=1', '--pid-file', pid_filename],
654
            env_changes=env_changes)
655
        trace.mutter('started lp-service daemon')
656
        # We wait for the spawned process to exit, expecting it to report the
657
        # final pid into the pid_filename.
658
        tnow = time.time()
659
        tstop_waiting = tnow + 1.0
11869.11.4 by John Arbash Meinel
Reduce the chance of a race condition by waiting for the socket, rather than the pid file.
660
        # When this returns, the first fork has completed and the parent has
661
        # exited.
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
662
        proc.wait()
663
        while tnow < tstop_waiting:
11869.11.4 by John Arbash Meinel
Reduce the chance of a race condition by waiting for the socket, rather than the pid file.
664
            # Wait for the socket to become available
665
            if os.path.exists(path):
666
                # The service has created the socket for us to communicate
667
                break
668
            time.sleep(0.1)
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
669
            tnow = time.time()
11869.11.4 by John Arbash Meinel
Reduce the chance of a race condition by waiting for the socket, rather than the pid file.
670
671
        with open(pid_filename, 'rb') as f:
672
            pid = f.read()
673
            trace.mutter('found pid: %r' % (pid,))
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
674
        pid = int(pid.strip())
675
        # This is now the pid of the final daemon
676
        trace.mutter('lp-forking-service daemon at pid %s' % (pid,))
677
        # Because nothing else will clean this up, add this final handler to
678
        # clean up if all else fails.
679
        self.addCleanup(self._cleanup_daemon, pid, pid_filename)
14564.1.1 by Jeroen Vermeulen
lpserve lint.
680
        # self.service_process will now be the pid of the daemon,
681
        # rather than a Popen object.
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
682
        return pid
683
684
    def stop_service(self):
685
        if self.service_process is None:
686
            # Already stopped
687
            return
688
        # First, try to stop the service gracefully, by sending a 'quit'
689
        # message
690
        try:
691
            response = self.send_message_to_service('quit\n')
692
        except socket.error, e:
14564.1.1 by Jeroen Vermeulen
lpserve lint.
693
            # Ignore a failure to connect; the service must be
694
            # stopping/stopped already.
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
695
            response = None
11869.11.2 by John Arbash Meinel
stop_service() now waits for the service to actually exit, and gets increasingly demanding about it.
696
        if response is not None:
697
            self.assertEqual('ok\nquit command requested... exiting\n',
698
                             response)
699
        # Wait for the process to actually exit, or force it if necessary.
700
        tnow = time.time()
701
        tend = tnow + 2.0
702
        # We'll be nice a couple of times, and then get mean
703
        attempts = [None, None, None, signal.SIGTERM, signal.SIGKILL]
704
        stopped = False
705
        unclean = False
706
        while tnow < tend:
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
707
            try:
11869.11.2 by John Arbash Meinel
stop_service() now waits for the service to actually exit, and gets increasingly demanding about it.
708
                os.kill(self.service_process, 0)
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
709
            except (OSError, IOError), e:
11869.11.2 by John Arbash Meinel
stop_service() now waits for the service to actually exit, and gets increasingly demanding about it.
710
                if e.errno == errno.ESRCH:
711
                    # The process has successfully exited
712
                    stopped = True
713
                    break
714
                raise
715
            else:
716
                # The process has not exited yet
717
                time.sleep(0.1)
718
                if attempts:
719
                    sig = attempts.pop(0)
720
                    if sig is not None:
721
                        unclean = True
722
                        try:
723
                            os.kill(self.service_process, sig)
724
                        except (OSError, IOError), e:
725
                            if e.errno == errno.ESRCH:
726
                                stopped = True
727
                                break
728
                            raise
729
        if not stopped:
730
            self.fail('Unable to stop the daemon process (pid %s) after 2.0s'
731
                      % (self.service_process,))
732
        elif unclean:
733
            self.fail('Process (pid %s) had to be shut-down'
734
                      % (self.service_process,))
11869.11.1 by John Arbash Meinel
Start working on a --pid-file option.
735
        self.service_process = None
736
737
    def test_simple_start_and_stop(self):
14564.1.1 by Jeroen Vermeulen
lpserve lint.
738
        # All the work is done in setUp().
739
        pass
11869.11.2 by John Arbash Meinel
stop_service() now waits for the service to actually exit, and gets increasingly demanding about it.
740
741
    def test_starts_and_cleans_up(self):
14564.1.1 by Jeroen Vermeulen
lpserve lint.
742
        # The service should be up and responsive.
11869.11.2 by John Arbash Meinel
stop_service() now waits for the service to actually exit, and gets increasingly demanding about it.
743
        response = self.send_message_to_service('hello\n')
744
        self.assertEqual('ok\nyep, still alive\n', response)
745
        self.failUnless(os.path.isfile(self.service_pid_filename))
746
        with open(self.service_pid_filename, 'rb') as f:
747
            content = f.read()
748
        self.assertEqualDiff('%d\n' % (self.service_process,), content)
14564.1.1 by Jeroen Vermeulen
lpserve lint.
749
        # We're done.  Shut it down.
11869.11.2 by John Arbash Meinel
stop_service() now waits for the service to actually exit, and gets increasingly demanding about it.
750
        self.stop_service()
751
        self.failIf(os.path.isfile(self.service_pid_filename))