process_test.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 
00004 from __future__ import absolute_import, division, with_statement
00005 import logging
00006 import os
00007 import signal
00008 import sys
00009 from tornado.httpclient import HTTPClient, HTTPError
00010 from tornado.httpserver import HTTPServer
00011 from tornado.ioloop import IOLoop
00012 from tornado.netutil import bind_sockets
00013 from tornado.process import fork_processes, task_id
00014 from tornado.simple_httpclient import SimpleAsyncHTTPClient
00015 from tornado.testing import LogTrapTestCase, get_unused_port
00016 from tornado.web import RequestHandler, Application
00017 
00018 # Not using AsyncHTTPTestCase because we need control over the IOLoop.
00019 # Logging is tricky here so you may want to replace LogTrapTestCase
00020 # with unittest.TestCase when debugging.
00021 
00022 
00023 class ProcessTest(LogTrapTestCase):
00024     def get_app(self):
00025         class ProcessHandler(RequestHandler):
00026             def get(self):
00027                 if self.get_argument("exit", None):
00028                     # must use os._exit instead of sys.exit so unittest's
00029                     # exception handler doesn't catch it
00030                     os._exit(int(self.get_argument("exit")))
00031                 if self.get_argument("signal", None):
00032                     os.kill(os.getpid(),
00033                             int(self.get_argument("signal")))
00034                 self.write(str(os.getpid()))
00035         return Application([("/", ProcessHandler)])
00036 
00037     def tearDown(self):
00038         if task_id() is not None:
00039             # We're in a child process, and probably got to this point
00040             # via an uncaught exception.  If we return now, both
00041             # processes will continue with the rest of the test suite.
00042             # Exit now so the parent process will restart the child
00043             # (since we don't have a clean way to signal failure to
00044             # the parent that won't restart)
00045             logging.error("aborting child process from tearDown")
00046             logging.shutdown()
00047             os._exit(1)
00048         # In the surviving process, clear the alarm we set earlier
00049         signal.alarm(0)
00050         super(ProcessTest, self).tearDown()
00051 
00052     def test_multi_process(self):
00053         self.assertFalse(IOLoop.initialized())
00054         port = get_unused_port()
00055 
00056         def get_url(path):
00057             return "http://127.0.0.1:%d%s" % (port, path)
00058         sockets = bind_sockets(port, "127.0.0.1")
00059         # ensure that none of these processes live too long
00060         signal.alarm(5)
00061         try:
00062             id = fork_processes(3, max_restarts=3)
00063         except SystemExit, e:
00064             # if we exit cleanly from fork_processes, all the child processes
00065             # finished with status 0
00066             self.assertEqual(e.code, 0)
00067             self.assertTrue(task_id() is None)
00068             for sock in sockets:
00069                 sock.close()
00070             return
00071         try:
00072             if id in (0, 1):
00073                 self.assertEqual(id, task_id())
00074                 server = HTTPServer(self.get_app())
00075                 server.add_sockets(sockets)
00076                 IOLoop.instance().start()
00077             elif id == 2:
00078                 self.assertEqual(id, task_id())
00079                 for sock in sockets:
00080                     sock.close()
00081                 # Always use SimpleAsyncHTTPClient here; the curl
00082                 # version appears to get confused sometimes if the
00083                 # connection gets closed before it's had a chance to
00084                 # switch from writing mode to reading mode.
00085                 client = HTTPClient(SimpleAsyncHTTPClient)
00086 
00087                 def fetch(url, fail_ok=False):
00088                     try:
00089                         return client.fetch(get_url(url))
00090                     except HTTPError, e:
00091                         if not (fail_ok and e.code == 599):
00092                             raise
00093 
00094                 # Make two processes exit abnormally
00095                 fetch("/?exit=2", fail_ok=True)
00096                 fetch("/?exit=3", fail_ok=True)
00097 
00098                 # They've been restarted, so a new fetch will work
00099                 int(fetch("/").body)
00100 
00101                 # Now the same with signals
00102                 # Disabled because on the mac a process dying with a signal
00103                 # can trigger an "Application exited abnormally; send error
00104                 # report to Apple?" prompt.
00105                 #fetch("/?signal=%d" % signal.SIGTERM, fail_ok=True)
00106                 #fetch("/?signal=%d" % signal.SIGABRT, fail_ok=True)
00107                 #int(fetch("/").body)
00108 
00109                 # Now kill them normally so they won't be restarted
00110                 fetch("/?exit=0", fail_ok=True)
00111                 # One process left; watch it's pid change
00112                 pid = int(fetch("/").body)
00113                 fetch("/?exit=4", fail_ok=True)
00114                 pid2 = int(fetch("/").body)
00115                 self.assertNotEqual(pid, pid2)
00116 
00117                 # Kill the last one so we shut down cleanly
00118                 fetch("/?exit=0", fail_ok=True)
00119 
00120                 os._exit(0)
00121         except Exception:
00122             logging.error("exception in child process %d", id, exc_info=True)
00123             raise
00124 
00125 
00126 if os.name != 'posix' or sys.platform == 'cygwin':
00127     # All sorts of unixisms here
00128     del ProcessTest


rosbridge_server
Author(s): Jonathan Mace
autogenerated on Thu Jan 2 2014 11:53:55