~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to bzrplugins/lpserve/test_lpserve.py

  • Committer: John Arbash Meinel
  • Date: 2011-03-02 13:04:02 UTC
  • mto: This revision was merged to the branch mainline in revision 12534.
  • Revision ID: john@arbash-meinel.com-20110302130402-o0dzzd7e893qvh9j
Change the code to use signal.alarm() instead of another python thread.

We lose sub-second granularity, but gain some security in knowing that a GIL locked
thread won't prevent the timer from actually firing. threads and signals don't usually
interact well, anyway.

Show diffs side-by-side

added added

removed removed

Lines of Context:
268
268
    def test_child_connection_timeout(self):
269
269
        self.assertEqual(self.service.CHILD_CONNECT_TIMEOUT,
270
270
                         self.service._child_connect_timeout)
271
 
        response = self.send_message_to_service('child_connect_timeout 1.0\n')
 
271
        response = self.send_message_to_service('child_connect_timeout 1\n')
272
272
        self.assertEqual('ok\n', response)
273
 
        self.assertEqual(1.0, self.service._child_connect_timeout)
 
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:')
274
280
 
275
281
    def test_child_connection_timeout_no_val(self):
276
282
        response = self.send_message_to_service('child_connect_timeout \n')
281
287
        self.assertStartsWith(response, 'FAILURE:')
282
288
 
283
289
    def test__open_handles_will_timeout(self):
284
 
        self.service._child_connect_timeout = 0.1
 
290
        # signal.alarm() has only 1-second granularity. :(
 
291
        self.service._child_connect_timeout = 1
285
292
        tempdir = tempfile.mkdtemp(prefix='testlpserve-')
286
293
        self.addCleanup(shutil.rmtree, tempdir, ignore_errors=True)
287
294
        os.mkfifo(os.path.join(tempdir, 'stdin'))
288
295
        os.mkfifo(os.path.join(tempdir, 'stdout'))
289
296
        os.mkfifo(os.path.join(tempdir, 'stderr'))
 
297
        def noop_on_alarm(signal, frame):
 
298
            return
 
299
        signal.signal(signal.SIGALRM, noop_on_alarm)
 
300
        self.addCleanup(signal.signal, signal.SIGALRM, signal.SIG_DFL)
290
301
        e = self.assertRaises(errors.BzrError,
291
302
            self.service._open_handles, tempdir)
292
303
        self.assertContainsRe(str(e), r'After \d+.\d+s we failed to open.*')
593
604
        # We won't ever bind to the socket the child wants, and after some
594
605
        # time, the child should exit cleanly.
595
606
        # First, tell the subprocess that we want children to exit quickly.
596
 
        response = self.send_message_to_service('child_connect_timeout 0.05\n')
 
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')
597
609
        self.assertEqual('ok\n', response)
598
610
        # Now request a fork
599
611
        path, pid, sock = self.send_fork_request('rocks')
602
614
        stdout_path = os.path.join(path, 'stdout')
603
615
        stderr_path = os.path.join(path, 'stderr')
604
616
        child_stdin = open(stdin_path, 'wb')
605
 
        # # I hate adding time.sleep here, but I don't see a better way, yet
606
 
        for i in xrange(10):
607
 
            if not os.path.exists(path):
608
 
                break
609
 
            time.sleep(0.01)
610
 
        else:
611
 
            self.fail('Child process failed to cleanup after timeout.')
 
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)
612
625
 
613
626
 
614
627
class TestCaseWithLPForkingServiceDaemon(
733
746
        # We're done, shut it down
734
747
        self.stop_service()
735
748
        self.failIf(os.path.isfile(self.service_pid_filename))
736
 
 
737
 
 
738
 
class Test_WakeUp(tests.TestCaseInTempDir):
739
 
 
740
 
    def test_wakeup_interrupts_fifo_open(self):
741
 
        os.mkfifo('test-fifo')
742
 
        self.addCleanup(os.remove, 'test-fifo')
743
 
        cancel, t = lpserve._wake_me_up_in_a_few(0.01)
744
 
        e = self.assertRaises(OSError, os.open, 'test-fifo', os.O_RDONLY)
745
 
        self.assertEqual(errno.EINTR, e.errno)
746
 
        t.join()
747
 
 
748
 
    def test_custom_callback_called(self):
749
 
        called = []
750
 
        def _sigusr1_called(sig, frame):
751
 
            called.append(sig)
752
 
            signal.signal(signal.SIGUSR1, signal.SIG_DFL)
753
 
        cancel, t = lpserve._wake_me_up_in_a_few(0.01, _sigusr1_called)
754
 
        time.sleep(0.1)
755
 
        self.assertEqual([signal.SIGUSR1], called)
756
 
        t.join()
757
 
 
758
 
    def test_cancel_aborts_interrupt(self):
759
 
        called = []
760
 
        def _sigusr1_called(sig, frame):
761
 
            called.append(sig, frame)
762
 
        cancel, t = lpserve._wake_me_up_in_a_few(0.01)
763
 
        cancel()
764
 
        time.sleep(0.1)
765
 
        # The signal should not have been fired, and we should have reset the
766
 
        # signal handler
767
 
        self.assertEqual([], called)
768
 
        self.assertEqual(signal.SIG_DFL,
769
 
                         signal.signal(signal.SIGUSR1, signal.SIG_DFL))
770
 
        # Should have already been joined in cancel()
771
 
        t.join()