~launchpad-pqm/launchpad/devel

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