Package cherrypy :: Package test :: Module modpy
[hide private]
[frames] | no frames]

Source Code for Module cherrypy.test.modpy

  1  """Wrapper for mod_python, for use as a CherryPy HTTP server when testing. 
  2   
  3  To autostart modpython, the "apache" executable or script must be 
  4  on your system path, or you must override the global APACHE_PATH. 
  5  On some platforms, "apache" may be called "apachectl" or "apache2ctl"-- 
  6  create a symlink to them if needed. 
  7   
  8  If you wish to test the WSGI interface instead of our _cpmodpy interface, 
  9  you also need the 'modpython_gateway' module at: 
 10  http://projects.amor.org/misc/wiki/ModPythonGateway 
 11   
 12   
 13  KNOWN BUGS 
 14  ========== 
 15   
 16  1. Apache processes Range headers automatically; CherryPy's truncated 
 17      output is then truncated again by Apache. See test_core.testRanges. 
 18      This was worked around in http://www.cherrypy.org/changeset/1319. 
 19  2. Apache does not allow custom HTTP methods like CONNECT as per the spec. 
 20      See test_core.testHTTPMethods. 
 21  3. Max request header and body settings do not work with Apache. 
 22  4. Apache replaces status "reason phrases" automatically. For example, 
 23      CherryPy may set "304 Not modified" but Apache will write out 
 24      "304 Not Modified" (capital "M"). 
 25  5. Apache does not allow custom error codes as per the spec. 
 26  6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the 
 27      Request-URI too early. 
 28  7. mod_python will not read request bodies which use the "chunked" 
 29      transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block 
 30      instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and 
 31      mod_python's requestobject.c). 
 32  8. Apache will output a "Content-Length: 0" response header even if there's 
 33      no response entity body. This isn't really a bug; it just differs from 
 34      the CherryPy default. 
 35  """ 
 36   
 37  import os 
 38  curdir = os.path.join(os.getcwd(), os.path.dirname(__file__)) 
 39  import re 
 40  import time 
 41   
 42  from cherrypy.test import test 
 43   
 44   
45 -def read_process(cmd, args=""):
46 pipein, pipeout = os.popen4("%s %s" % (cmd, args)) 47 try: 48 firstline = pipeout.readline() 49 if (re.search(r"(not recognized|No such file|not found)", firstline, 50 re.IGNORECASE)): 51 raise IOError('%s must be on your system path.' % cmd) 52 output = firstline + pipeout.read() 53 finally: 54 pipeout.close() 55 return output
56 57 58 APACHE_PATH = "httpd" 59 CONF_PATH = "test_mp.conf" 60 61 conf_modpython_gateway = """ 62 # Apache2 server conf file for testing CherryPy with modpython_gateway. 63 64 ServerName 127.0.0.1 65 DocumentRoot "/" 66 Listen %s 67 LoadModule python_module modules/mod_python.so 68 69 SetHandler python-program 70 PythonFixupHandler cherrypy.test.modpy::wsgisetup 71 PythonOption testmod %s 72 PythonHandler modpython_gateway::handler 73 PythonOption wsgi.application cherrypy::tree 74 PythonOption socket_host %s 75 PythonDebug On 76 """ 77 78 conf_cpmodpy = """ 79 # Apache2 server conf file for testing CherryPy with _cpmodpy. 80 81 ServerName 127.0.0.1 82 DocumentRoot "/" 83 Listen %s 84 LoadModule python_module modules/mod_python.so 85 86 SetHandler python-program 87 PythonFixupHandler cherrypy.test.modpy::cpmodpysetup 88 PythonHandler cherrypy._cpmodpy::handler 89 PythonOption cherrypy.setup cherrypy.test.%s::setup_server 90 PythonOption socket_host %s 91 PythonDebug On 92 """ 93
94 -def start(testmod, host, port, conf_template):
95 mpconf = CONF_PATH 96 if not os.path.isabs(mpconf): 97 mpconf = os.path.join(curdir, mpconf) 98 99 f = open(mpconf, 'wb') 100 try: 101 f.write(conf_template % (port, testmod, host)) 102 finally: 103 f.close() 104 105 result = read_process(APACHE_PATH, "-k start -f %s" % mpconf) 106 if result: 107 print result
108
109 -def stop():
110 """Gracefully shutdown a server that is serving forever.""" 111 read_process(APACHE_PATH, "-k stop")
112 113 114 loaded = False
115 -def wsgisetup(req):
116 global loaded 117 if not loaded: 118 loaded = True 119 options = req.get_options() 120 121 import cherrypy 122 cherrypy.config.update({ 123 "log.error_file": os.path.join(curdir, "test.log"), 124 "environment": "test_suite", 125 "server.socket_host": options['socket_host'], 126 }) 127 128 modname = options['testmod'] 129 mod = __import__(modname, globals(), locals(), ['']) 130 mod.setup_server() 131 132 cherrypy.server.unsubscribe() 133 cherrypy.engine.start() 134 from mod_python import apache 135 return apache.OK
136 137
138 -def cpmodpysetup(req):
139 global loaded 140 if not loaded: 141 loaded = True 142 options = req.get_options() 143 144 import cherrypy 145 cherrypy.config.update({ 146 "log.error_file": os.path.join(curdir, "test.log"), 147 "environment": "test_suite", 148 "server.socket_host": options['socket_host'], 149 }) 150 from mod_python import apache 151 return apache.OK
152 153
154 -class ModPythonTestHarness(test.TestHarness):
155 """TestHarness for ModPython and CherryPy.""" 156 157 use_wsgi = False 158
159 - def _run(self, conf):
160 import cherrypy 161 cherrypy.server.using_apache = True 162 163 from cherrypy.test import webtest 164 webtest.WebCase.PORT = self.port 165 webtest.WebCase.harness = self 166 webtest.WebCase.scheme = "http" 167 webtest.WebCase.interactive = self.interactive 168 print 169 print "Running tests:", self.server 170 171 if self.use_wsgi: 172 cherrypy.server.using_wsgi = True 173 conf_template = conf_modpython_gateway 174 else: 175 cherrypy.server.using_wsgi = False 176 conf_template = conf_cpmodpy 177 178 # mod_python, since it runs in the Apache process, must be 179 # started separately for each test, and then *that* process 180 # must run the setup_server() function for the test. 181 # Then our process can run the actual test. 182 success = True 183 for testmod in self.tests: 184 try: 185 start(testmod, self.host, self.port, conf_template) 186 suite = webtest.ReloadingTestLoader().loadTestsFromName(testmod) 187 result = webtest.TerseTestRunner(verbosity=2).run(suite) 188 success &= result.wasSuccessful() 189 finally: 190 stop() 191 if success: 192 return 0 193 else: 194 return 1
195