~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to bzrplugins/lpserve/test_lpserve.py

  • Committer: Jeroen Vermeulen
  • Date: 2011-12-21 06:14:46 UTC
  • mto: This revision was merged to the branch mainline in revision 14571.
  • Revision ID: jeroen.vermeulen@canonical.com-20111221061446-6qyshymj9r6nfxyl
lpserveĀ lint.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2010 Canonical Ltd.  This software is licensed under the
 
1
# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
2
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
3
 
4
4
import errno
11
11
import threading
12
12
import time
13
13
 
14
 
from testtools import content
15
 
 
16
14
from bzrlib import (
17
15
    errors,
18
16
    osutils,
20
18
    trace,
21
19
    )
22
20
from bzrlib.plugins import lpserve
 
21
from testtools import content
23
22
 
24
23
from canonical.config import config
25
 
from lp.codehosting import get_bzr_path, get_BZR_PLUGIN_PATH_for_subprocess
 
24
from lp.codehosting import (
 
25
    get_bzr_path,
 
26
    get_BZR_PLUGIN_PATH_for_subprocess,
 
27
    )
 
28
from lp.testing.fakemethod import FakeMethod
26
29
 
27
30
 
28
31
class TestingLPForkingServiceInAThread(lpserve.LPForkingService):
29
32
    """A test-double to run a "forking service" in a thread.
30
33
 
31
 
    Note that we don't allow actually forking, but it does allow us to interact
32
 
    with the service for other operations.
 
34
    Note that we don't allow actually forking, but it does allow us to
 
35
    interact with the service for other operations.
33
36
    """
34
37
 
35
 
    # For testing, we set the timeouts much lower, because we want the tests to
36
 
    # run quickly
 
38
    # For testing we set the timeouts much lower, because we want the
 
39
    # tests to run quickly.
37
40
    WAIT_FOR_CHILDREN_TIMEOUT = 0.5
38
41
    SOCKET_TIMEOUT = 0.01
39
42
    SLEEP_FOR_CHILDREN_TIMEOUT = 0.01
40
43
    WAIT_FOR_REQUEST_TIMEOUT = 0.1
41
44
 
42
 
    # We're running in a thread as part of the test suite, blow up if we try to
43
 
    # fork
 
45
    # We're running in a thread as part of the test suite.  Blow up at
 
46
    # any attempt to fork.
44
47
    _fork_function = None
45
48
 
46
49
    def __init__(self, path, perms=None):
52
55
            path=path, perms=None)
53
56
 
54
57
    def _register_signals(self):
55
 
        pass # Don't register it for the test suite
 
58
        # Don't register it for the test suite.
 
59
        pass
56
60
 
57
61
    def _unregister_signals(self):
58
 
        pass # We don't fork, and didn't register, so don't unregister
 
62
        # We don't fork, and didn't register, so don't unregister.
 
63
        pass
59
64
 
60
65
    def _create_master_socket(self):
61
66
        super(TestingLPForkingServiceInAThread, self)._create_master_socket()
131
136
    def test_autostop(self):
132
137
        # We shouldn't leak a thread here, as it should be part of the test
133
138
        # case teardown.
134
 
        service = TestingLPForkingServiceInAThread.start_service(self)
 
139
        TestingLPForkingServiceInAThread.start_service(self)
135
140
 
136
141
 
137
142
class TestCaseWithLPForkingService(tests.TestCaseWithTransport):
294
299
        os.mkfifo(os.path.join(tempdir, 'stdin'))
295
300
        os.mkfifo(os.path.join(tempdir, 'stdout'))
296
301
        os.mkfifo(os.path.join(tempdir, 'stderr'))
 
302
 
297
303
        # catch SIGALRM so we don't stop the test suite. It will still
298
304
        # interupt the blocking open() calls.
299
 
        def noop_on_alarm(signal, frame):
300
 
            return
301
 
        signal.signal(signal.SIGALRM, noop_on_alarm)
 
305
        signal.signal(signal.SIGALRM, FakeMethod())
 
306
 
302
307
        self.addCleanup(signal.signal, signal.SIGALRM, signal.SIG_DFL)
303
308
        e = self.assertRaises(errors.BzrError,
304
309
            self.service._open_handles, tempdir)
305
310
        self.assertContainsRe(str(e), r'After \d+.\d+s we failed to open.*')
306
311
 
307
312
 
308
 
 
309
313
class TestCaseWithSubprocess(tests.TestCaseWithTransport):
310
314
    """Override the bzr start_bzr_subprocess command.
311
315
 
312
 
    The launchpad infrastructure requires a fair amount of configuration to get
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.
 
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.
316
320
    """
317
321
 
318
322
    def get_python_path(self):
373
377
class TestCaseWithLPForkingServiceSubprocess(TestCaseWithSubprocess):
374
378
    """Tests will get a separate process to communicate to.
375
379
 
376
 
    The number of these tests should be small, because it is expensive to start
377
 
    and stop the daemon.
 
380
    The number of these tests should be small, because it is expensive to
 
381
    start and stop the daemon.
378
382
 
379
383
    TODO: This should probably use testresources, or layers somehow...
380
384
    """
458
462
        service_fd, path = tempfile.mkstemp(prefix='tmp-lp-service-',
459
463
                                            suffix='.sock')
460
464
        os.close(service_fd)
461
 
        os.remove(path) # service wants create it as a socket
462
 
        env_changes = {'BZR_PLUGIN_PATH': lpserve.__path__[0],
463
 
                       'BZR_LOG': tempname}
 
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
            }
464
471
        proc = self._start_subprocess(path, env_changes)
465
472
        return proc, path
466
473
 
467
474
    def stop_service(self):
468
475
        if self.service_process is None:
469
 
            # Already stopped
 
476
            # Already stopped.
470
477
            return
471
478
        # First, try to stop the service gracefully, by sending a 'quit'
472
 
        # message
 
479
        # message.
473
480
        try:
474
481
            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
 
482
        except socket.error:
 
483
            # Ignore a failure to connect; the service must be
 
484
            # stopping/stopped already.
478
485
            response = None
479
486
        tend = time.time() + 10.0
480
487
        while self.service_process.poll() is None:
564
571
        for idx in [3, 2, 0, 1]:
565
572
            p, pid, sock = paths[idx]
566
573
            stdout_msg = 'hello %d\n' % (idx,)
567
 
            stderr_msg = 'goodbye %d\n' % (idx+1,)
 
574
            stderr_msg = 'goodbye %d\n' % (idx + 1,)
568
575
            stdout, stderr = self.communicate_with_fork(p,
569
576
                '1 %s2 %s' % (stdout_msg, stderr_msg))
570
577
            self.assertEqualDiff(stdout_msg, stdout)
607
614
        # *sigh* signal.alarm only has 1s resolution, so this test is slow.
608
615
        response = self.send_message_to_service('child_connect_timeout 1\n')
609
616
        self.assertEqual('ok\n', response)
610
 
        # Now request a fork
 
617
        # Now request a fork.
611
618
        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')
617
619
        # We started opening the child, but stop before we get all handles
618
620
        # open. After 1 second, the child should get signaled and die.
619
621
        # The master process should notice, and tell us the status of the
675
677
        # Because nothing else will clean this up, add this final handler to
676
678
        # clean up if all else fails.
677
679
        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
        # self.service_process will now be the pid of the daemon,
 
681
        # rather than a Popen object.
680
682
        return pid
681
683
 
682
684
    def stop_service(self):
688
690
        try:
689
691
            response = self.send_message_to_service('quit\n')
690
692
        except socket.error, e:
691
 
            # Ignore a failure to connect, the service must be stopping/stopped
692
 
            # already
 
693
            # Ignore a failure to connect; the service must be
 
694
            # stopping/stopped already.
693
695
            response = None
694
696
        if response is not None:
695
697
            self.assertEqual('ok\nquit command requested... exiting\n',
733
735
        self.service_process = None
734
736
 
735
737
    def test_simple_start_and_stop(self):
736
 
        pass # All the work is done in setUp()
 
738
        # All the work is done in setUp().
 
739
        pass
737
740
 
738
741
    def test_starts_and_cleans_up(self):
739
 
        # The service should be up and responsive
 
742
        # The service should be up and responsive.
740
743
        response = self.send_message_to_service('hello\n')
741
744
        self.assertEqual('ok\nyep, still alive\n', response)
742
745
        self.failUnless(os.path.isfile(self.service_pid_filename))
743
746
        with open(self.service_pid_filename, 'rb') as f:
744
747
            content = f.read()
745
748
        self.assertEqualDiff('%d\n' % (self.service_process,), content)
746
 
        # We're done, shut it down
 
749
        # We're done.  Shut it down.
747
750
        self.stop_service()
748
751
        self.failIf(os.path.isfile(self.service_pid_filename))