Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Changeset 2004

Show
Ignore:
Timestamp:
06/30/08 12:00:35
Author:
fumanchu
Message:

New helper.CPProcess class (moved from test_states), plus a bugfix to allow Ctrl-C to stop CP when running tests in --server mode.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/cherrypy/test/helper.py

    r2002 r2004  
    1919# or vice-versa, unless you *really* know what you're doing. 
    2020 
     21import os 
     22thisdir = os.path.abspath(os.path.dirname(__file__)) 
    2123import re 
    2224import sys 
    2325import thread 
     26import time 
    2427import warnings 
    2528 
     
    180183def testmain(conf=None): 
    181184    """Run __main__ as a test module, with webtest debugging.""" 
     185    engine = cherrypy.engine 
    182186    if '--server' in sys.argv: 
    183187        # Run the test module server-side only; wait for Ctrl-C to break. 
     
    185189        conf['server.socket_host'] = '0.0.0.0' 
    186190        setConfig(conf) 
    187         cherrypy.engine.start() 
    188         cherrypy.engine.block() 
     191        if hasattr(engine, "signal_handler"): 
     192            engine.signal_handler.subscribe() 
     193        if hasattr(engine, "console_control_handler"): 
     194            engine.console_control_handler.subscribe() 
     195        engine.start() 
     196        engine.block() 
    189197    else: 
    190198        for arg in sys.argv: 
     
    204212            conf['server.socket_host'] = '127.0.0.1' 
    205213            setConfig(conf) 
    206             cherrypy.engine.start_with_callback(_test_main_thread) 
    207             cherrypy.engine.block() 
     214            engine.start_with_callback(_test_main_thread) 
     215            engine.block() 
    208216 
    209217def _test_main_thread(): 
     
    214222        cherrypy.engine.exit() 
    215223 
     224 
     225 
     226# --------------------------- Spawning helpers --------------------------- # 
     227 
     228 
     229class CPProcess(object): 
     230     
     231    pid_file = os.path.join(thisdir, 'test.pid') 
     232    config_file = os.path.join(thisdir, 'test.conf') 
     233    config_template = """[global] 
     234server.socket_host: '%(host)s' 
     235server.socket_port: %(port)s 
     236log.screen: False 
     237log.error_file: r'%(error_log)s' 
     238log.access_file: r'%(access_log)s' 
     239%(ssl)s 
     240%(extra)s 
     241""" 
     242    error_log = os.path.join(thisdir, 'test.error.log') 
     243    access_log = os.path.join(thisdir, 'test.access.log') 
     244     
     245    def __init__(self, wait=False, daemonize=False, ssl=False): 
     246        self.wait = wait 
     247        self.daemonize = daemonize 
     248        self.ssl = ssl 
     249        self.host = cherrypy.server.socket_host 
     250        self.port = cherrypy.server.socket_port 
     251     
     252    def write_conf(self, extra=""): 
     253        if self.ssl: 
     254            serverpem = os.path.join(thisdir, 'test.pem') 
     255            ssl = """ 
     256server.ssl_certificate: r'%s' 
     257server.ssl_private_key: r'%s' 
     258""" % (serverpem, serverpem) 
     259        else: 
     260            ssl = "" 
     261         
     262        f = open(self.config_file, 'wb') 
     263        f.write(self.config_template % 
     264                {'host': self.host, 
     265                 'port': self.port, 
     266                 'error_log': self.error_log, 
     267                 'access_log': self.access_log, 
     268                 'ssl': ssl, 
     269                 'extra': extra, 
     270                 }) 
     271        f.close() 
     272     
     273    def start(self, imports=None): 
     274        """Start cherryd in a subprocess.""" 
     275        cherrypy._cpserver.wait_for_free_port(self.host, self.port) 
     276         
     277        args = [sys.executable, os.path.join(thisdir, '..', 'cherryd'), 
     278                '-c', self.config_file, '-p', self.pid_file] 
     279         
     280        if not isinstance(imports, (list, tuple)): 
     281            imports = [imports] 
     282        for i in imports: 
     283            if i: 
     284                args.append('-i') 
     285                args.append(i) 
     286         
     287        if self.daemonize: 
     288            args.append('-d') 
     289         
     290        if self.wait: 
     291            self.exit_code = os.spawnl(os.P_WAIT, sys.executable, *args) 
     292        else: 
     293            os.spawnl(os.P_NOWAIT, sys.executable, *args) 
     294            cherrypy._cpserver.wait_for_occupied_port(self.host, self.port) 
     295         
     296        # Give the engine a wee bit more time to finish STARTING 
     297        if self.daemonize: 
     298            time.sleep(2) 
     299        else: 
     300            time.sleep(1) 
     301     
     302    def get_pid(self): 
     303        return int(open(self.pid_file, 'rb').read()) 
     304     
     305    def join(self): 
     306        """Wait for the process to exit.""" 
     307        try: 
     308            try: 
     309                # Mac, UNIX 
     310                os.wait() 
     311            except AttributeError: 
     312                # Windows 
     313                try: 
     314                    pid = self.get_pid() 
     315                except IOError: 
     316                    # Assume the subprocess deleted the pidfile on shutdown. 
     317                    pass 
     318                else: 
     319                    os.waitpid(pid, 0) 
     320        except OSError, x: 
     321            if x.args != (10, 'No child processes'): 
     322                raise 
     323 
  • trunk/cherrypy/test/test_states.py

    r1999 r2004  
    1313engine = cherrypy.engine 
    1414thisdir = os.path.join(os.getcwd(), os.path.dirname(__file__)) 
    15 PID_file_path = os.path.join(thisdir, 'pid_for_test_daemonize') 
    16  
    17  
    18 def write_conf(scheme='http', extra=""): 
    19     if scheme.lower() == 'https': 
    20         serverpem = os.path.join(thisdir, 'test.pem') 
    21         ssl = """ 
    22 server.ssl_certificate: r'%s' 
    23 server.ssl_private_key: r'%s' 
    24 """ % (serverpem, serverpem) 
    25     else: 
    26         ssl = "" 
    27      
    28     conffile = open(os.path.join(thisdir, 'test_states.conf'), 'wb') 
    29     conffile.write("""[global] 
    30 server.socket_host: '%(host)s' 
    31 server.socket_port: %(port)s 
    32 log.screen: False 
    33 log.error_file: r'%(error_log)s' 
    34 log.access_file: r'%(access_log)s' 
    35 %(ssl)s 
    36 %(extra)s 
    37 """ % {'host': host, 
    38        'port': port, 
    39        'error_log': os.path.join(thisdir, 'test_states_demo.error.log'), 
    40        'access_log': os.path.join(thisdir, 'test_states_demo.access.log'), 
    41        'ssl': ssl, 
    42        'extra': extra, 
    43        }) 
    44     conffile.close() 
    45  
    46  
    47 def spawn_cp(configfile=os.path.join(thisdir, 'test_states.conf'), 
    48              wait=False, daemonize=False): 
    49     """Start cherryd in a subprocess.""" 
    50     host = cherrypy.server.socket_host 
    51     port = cherrypy.server.socket_port 
    52     cherrypy._cpserver.wait_for_free_port(host, port) 
    53      
    54     args = [sys.executable, os.path.join(thisdir, '..', 'cherryd'), 
    55             '-c', configfile, '-i', 'cherrypy.test.test_states_demo'] 
    56      
    57     if sys.platform != 'win32': 
    58         args.append('-p') 
    59         args.append(PID_file_path) 
    60      
    61     # Spawn the process and wait, when this returns, the original process 
    62     # is finished.  If it daemonized properly, we should still be able 
    63     # to access pages. 
    64     if daemonize: 
    65         args.append('-d') 
    66      
    67     if wait: 
    68         result = os.spawnl(os.P_WAIT, sys.executable, *args) 
    69     else: 
    70         result = os.spawnl(os.P_NOWAIT, sys.executable, *args) 
    71         cherrypy._cpserver.wait_for_occupied_port(host, port) 
    72      
    73     # Give the engine a wee bit more time to finish STARTING 
    74     if daemonize: 
    75         time.sleep(2) 
    76     else: 
    77         time.sleep(1) 
    78      
    79     return result 
    80  
    81 def wait(pid): 
    82     """Wait for the process with the given pid to exit.""" 
    83     try: 
    84         try: 
    85             # Mac, UNIX 
    86             os.wait() 
    87         except AttributeError: 
    88             # Windows 
    89             os.waitpid(pid, 0) 
    90     except OSError, x: 
    91         if x.args != (10, 'No child processes'): 
    92             raise 
    9315 
    9416 
     
    342264         
    343265        # Start the demo script in a new process 
    344         write_conf(scheme=self.scheme) 
    345         pid = spawn_cp() 
     266        p = helper.CPProcess(ssl=(self.scheme.lower()=='https')) 
     267        p.write_conf() 
     268        p.start(imports='cherrypy.test.test_states_demo') 
    346269        try: 
    347270            self.getPage("/start") 
     
    357280            time.sleep(2) 
    358281            cherrypy._cpserver.wait_for_occupied_port(host, port) 
    359              
    360             self.getPage("/pid") 
    361             pid = int(self.body) 
    362282             
    363283            self.getPage("/start") 
     
    366286            # Shut down the spawned process 
    367287            self.getPage("/exit") 
    368         wait(pid
     288        p.join(
    369289     
    370290    def test_5_Start_Error(self): 
     
    375295        # If a process errors during start, it should stop the engine 
    376296        # and exit with a non-zero exit code. 
    377         write_conf(scheme=self.scheme, extra="starterror: True") 
    378         exit_code = spawn_cp(wait=True) 
    379         if exit_code == 0: 
     297        p = helper.CPProcess(ssl=(self.scheme.lower()=='https'), 
     298                             wait=True) 
     299        p.write_conf(extra="starterror: True") 
     300        p.start(imports='cherrypy.test.test_states_demo') 
     301        if p.exit_code == 0: 
    380302            self.fail("Process failed to return nonzero exit code.") 
    381303 
     
    394316        # is finished.  If it daemonized properly, we should still be able 
    395317        # to access pages. 
    396         write_conf(scheme=self.scheme) 
    397         exit_code = spawn_cp(wait=True, daemonize=True) 
    398          
    399         # Get the PID from the file. 
    400         pid = int(open(PID_file_path).read()) 
     318        p = helper.CPProcess(ssl=(self.scheme.lower()=='https'), 
     319                             wait=True, daemonize=True) 
     320        p.write_conf() 
     321        p.start(imports='cherrypy.test.test_states_demo') 
    401322        try: 
    402323            # Just get the pid of the daemonization process. 
     
    404325            self.assertStatus(200) 
    405326            page_pid = int(self.body) 
    406             self.assertEqual(page_pid, pid
     327            self.assertEqual(page_pid, p.get_pid()
    407328        finally: 
    408329            # Shut down the spawned process 
    409330            self.getPage("/exit") 
    410         wait(pid
     331        p.join(
    411332         
    412333        # Wait until here to test the exit code because we want to ensure 
    413334        # that we wait for the daemon to finish running before we fail. 
    414         if exit_code != 0: 
     335        if p.exit_code != 0: 
    415336            self.fail("Daemonized parent process failed to exit cleanly.") 
    416337 
     
    431352         
    432353        # Spawn the process. 
    433         write_conf(scheme=self.scheme) 
    434         pid = spawn_cp(wait=False, daemonize=False) 
     354        p = helper.CPProcess(ssl=(self.scheme.lower()=='https')) 
     355        p.write_conf() 
     356        p.start(imports='cherrypy.test.test_states_demo') 
    435357        # Send a SIGHUP 
    436         os.kill(pid, SIGHUP) 
     358        os.kill(p.get_pid(), SIGHUP) 
    437359        # This might hang if things aren't working right, but meh. 
    438         wait(pid
     360        p.join(
    439361     
    440362    def test_SIGHUP_daemonized(self): 
     
    457379        # is finished.  If it daemonized properly, we should still be able 
    458380        # to access pages. 
    459         write_conf(scheme=self.scheme) 
    460         exit_code = spawn_cp(wait=True, daemonize=True) 
    461          
    462         # Get the PID from the file. 
    463         pid = int(open(PID_file_path).read()) 
     381        p = helper.CPProcess(ssl=(self.scheme.lower()=='https'), 
     382                             wait=True, daemonize=True) 
     383        p.write_conf() 
     384        p.start(imports='cherrypy.test.test_states_demo') 
     385         
     386        pid = p.get_pid() 
    464387        try: 
    465388            # Send a SIGHUP 
     
    474397            # Shut down the spawned process 
    475398            self.getPage("/exit") 
    476         wait(new_pid
     399        p.join(
    477400     
    478401    def test_SIGTERM(self): 
     
    495418         
    496419        # Spawn a normal, undaemonized process. 
    497         write_conf(scheme=self.scheme) 
    498         pid = spawn_cp(wait=False, daemonize=False) 
     420        p = helper.CPProcess(ssl=(self.scheme.lower()=='https')) 
     421        p.write_conf() 
     422        p.start(imports='cherrypy.test.test_states_demo') 
    499423        # Send a SIGTERM 
    500         os.kill(pid, SIGTERM) 
     424        os.kill(p.get_pid(), SIGTERM) 
    501425        # This might hang if things aren't working right, but meh. 
    502         wait(pid
     426        p.join(
    503427         
    504428        if os.name in ['posix']:  
    505429            # Spawn a daemonized process and test again. 
    506             exit_code = spawn_cp(wait=True, daemonize=True) 
     430            p = helper.CPProcess(ssl=(self.scheme.lower()=='https'), 
     431                                 wait=True, daemonize=True) 
     432            p.write_conf() 
     433            p.start(imports='cherrypy.test.test_states_demo') 
    507434            # Send a SIGTERM 
    508             pid = int(open(PID_file_path).read()) 
    509             os.kill(pid, SIGTERM) 
     435            os.kill(p.get_pid(), SIGTERM) 
    510436            # This might hang if things aren't working right, but meh. 
    511             wait(pid
     437            p.join(
    512438     
    513439    def test_signal_handler_unsubscribe(self): 
     
    529455         
    530456        # Spawn a normal, undaemonized process. 
    531         write_conf(scheme=self.scheme, extra="unsubsig: True") 
    532         pid = spawn_cp(wait=False, daemonize=False) 
    533         os.kill(pid, SIGTERM) 
    534         wait(pid) 
     457        p = helper.CPProcess(ssl=(self.scheme.lower()=='https')) 
     458        p.write_conf(extra="unsubsig: True") 
     459        p.start(imports='cherrypy.test.test_states_demo') 
     460        # Send a SIGTERM 
     461        os.kill(p.get_pid(), SIGTERM) 
     462        # This might hang if things aren't working right, but meh. 
     463        p.join() 
     464         
    535465        # Assert the old handler ran. 
    536         errlog = os.path.join(thisdir, 'test_states_demo.error.log') 
    537         target_line = open(errlog, 'rb').readlines()[-10] 
     466        target_line = open(p.error_log, 'rb').readlines()[-10] 
    538467        if not "I am an old SIGTERM handler." in target_line: 
    539468            self.fail("Old SIGTERM handler did not run.\n%r" % target_line) 

Hosted by WebFaction

Log in as guest/cpguest to create tickets