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

Source Code for Module cherrypy.test.test

  1  """The actual script that runs the entire CP test suite. 
  2   
  3  There is a library of helper functions for the CherryPy test suite, 
  4  called "helper.py" (in this folder); this module calls that as a library. 
  5  """ 
  6   
  7  # GREAT CARE has been taken to separate this module from helper.py, 
  8  # because different consumers of each have mutually-exclusive import 
  9  # requirements. So don't go moving functions from here into helper.py, 
 10  # or vice-versa, unless you *really* know what you're doing. 
 11   
 12   
 13  import getopt 
 14  import httplib 
 15  import os 
 16  localDir = os.path.dirname(__file__) 
 17  serverpem = os.path.join(os.getcwd(), localDir, 'test.pem') 
 18  import sys 
 19   
 20   
21 -class TestHarness(object):
22 """A test harness for the CherryPy framework and CherryPy applications.""" 23
24 - def __init__(self, tests=None, server=None, protocol="HTTP/1.1", 25 port=8000, scheme="http", interactive=True, host='127.0.0.1'):
26 """Constructor to populate the TestHarness instance. 27 28 tests should be a list of module names (strings). 29 """ 30 self.tests = tests or [] 31 self.server = server 32 self.protocol = protocol 33 self.port = port 34 self.host = host 35 self.scheme = scheme 36 self.interactive = interactive
37
38 - def run(self, conf=None):
39 """Run the test harness (using the given [global] conf).""" 40 import cherrypy 41 v = sys.version.split()[0] 42 print "Python version used to run this test script:", v 43 print "CherryPy version", cherrypy.__version__ 44 if self.scheme == "https": 45 ssl = "(ssl)" 46 else: 47 ssl = "" 48 print "HTTP server version", self.protocol, ssl 49 print "PID:", os.getpid() 50 print 51 52 # Override these in _run as needed: 53 cherrypy.server.using_wsgi = True 54 cherrypy.server.using_apache = False 55 56 if isinstance(conf, basestring): 57 parser = cherrypy.config._Parser() 58 conf = parser.dict_from_file(conf).get('global', {}) 59 else: 60 conf = conf or {} 61 baseconf = conf.copy() 62 baseconf.update({'server.socket_host': self.host, 63 'server.socket_port': self.port, 64 'server.protocol_version': self.protocol, 65 'environment': "test_suite", 66 }) 67 if self.scheme == "https": 68 baseconf['server.ssl_certificate'] = serverpem 69 baseconf['server.ssl_private_key'] = serverpem 70 return self._run(baseconf)
71
72 - def _run(self, conf):
73 # helper must be imported lazily so the coverage tool 74 # can run against module-level statements within cherrypy. 75 # Also, we have to do "from cherrypy.test import helper", 76 # exactly like each test module does, because a relative import 77 # would stick a second instance of webtest in sys.modules, 78 # and we wouldn't be able to globally override the port anymore. 79 from cherrypy.test import helper, webtest 80 webtest.WebCase.PORT = self.port 81 webtest.WebCase.HOST = self.host 82 webtest.WebCase.harness = self 83 helper.CPWebCase.scheme = self.scheme 84 webtest.WebCase.interactive = self.interactive 85 if self.scheme == "https": 86 webtest.WebCase.HTTP_CONN = httplib.HTTPSConnection 87 print 88 print "Running tests:", self.server 89 return helper.run_test_suite(self.tests, self.server, conf)
90 91
92 -class CommandLineParser(object):
93 available_servers = {'wsgi': "cherrypy._cpwsgi.CPWSGIServer", 94 'cpmodpy': "cpmodpy", 95 'modpygw': "modpygw", 96 'modwsgi': "modwsgi", 97 'modfcgid': "modfcgid", 98 } 99 default_server = "wsgi" 100 scheme = "http" 101 protocol = "HTTP/1.1" 102 port = 8080 103 host = '127.0.0.1' 104 cover = False 105 profile = False 106 validate = False 107 conquer = False 108 server = None 109 basedir = None 110 interactive = True 111 112 shortopts = [] 113 longopts = ['cover', 'profile', 'validate', 'conquer', 'dumb', '1.0', 114 'ssl', 'help', 'basedir=', 'port=', 'server=', 'host='] 115
116 - def __init__(self, available_tests, args=sys.argv[1:]):
117 """Constructor to populate the TestHarness instance. 118 119 available_tests should be a list of module names (strings). 120 121 args defaults to sys.argv[1:], but you can provide a different 122 set of args if you like. 123 """ 124 self.available_tests = available_tests 125 126 longopts = self.longopts[:] 127 longopts.extend(self.available_tests) 128 try: 129 opts, args = getopt.getopt(args, self.shortopts, longopts) 130 except getopt.GetoptError: 131 # print help information and exit 132 self.help() 133 sys.exit(2) 134 135 self.tests = [] 136 137 for o, a in opts: 138 if o == '--help': 139 self.help() 140 sys.exit() 141 elif o == "--cover": 142 self.cover = True 143 elif o == "--profile": 144 self.profile = True 145 elif o == "--validate": 146 self.validate = True 147 elif o == "--conquer": 148 self.conquer = True 149 elif o == "--dumb": 150 self.interactive = False 151 elif o == "--1.0": 152 self.protocol = "HTTP/1.0" 153 elif o == "--ssl": 154 self.scheme = "https" 155 elif o == "--basedir": 156 self.basedir = a 157 elif o == "--port": 158 self.port = int(a) 159 elif o == "--host": 160 self.host = a 161 elif o == "--server": 162 if a in self.available_servers: 163 a = self.available_servers[a] 164 self.server = a 165 else: 166 o = o[2:] 167 if o in self.available_tests and o not in self.tests: 168 self.tests.append(o) 169 170 if self.cover and self.profile: 171 # Print error message and exit 172 print ('Error: you cannot run the profiler and the ' 173 'coverage tool at the same time.') 174 sys.exit(2) 175 176 if not self.server: 177 self.server = self.available_servers[self.default_server] 178 179 if not self.tests: 180 self.tests = self.available_tests[:]
181
182 - def help(self):
183 """Print help for test.py command-line options.""" 184 185 print """CherryPy Test Program 186 Usage: 187 test.py --help --server=* --host=%s --port=%s --1.0 --ssl --cover 188 --basedir=path --profile --validate --conquer --dumb --tests** 189 190 """ % (self.__class__.host, self.__class__.port) 191 print ' * servers:' 192 for name, val in self.available_servers.iteritems(): 193 if name == self.default_server: 194 print ' --server=%s: %s (default)' % (name, val) 195 else: 196 print ' --server=%s: %s' % (name, val) 197 198 print """ 199 200 --host=<name or IP addr>: use a host other than the default (%s). 201 Not yet available with mod_python servers. 202 --port=<int>: use a port other than the default (%s) 203 --1.0: use HTTP/1.0 servers instead of default HTTP/1.1 204 205 --cover: turn on code-coverage tool 206 --basedir=path: display coverage stats for some path other than cherrypy. 207 208 --profile: turn on profiling tool 209 --validate: use wsgiref.validate (builtin in Python 2.5). 210 --conquer: use wsgiconq (which uses pyconquer) to trace calls. 211 --dumb: turn off the interactive output features. 212 """ % (self.__class__.host, self.__class__.port) 213 214 print ' ** tests:' 215 for name in self.available_tests: 216 print ' --' + name
217
218 - def start_coverage(self):
219 """Start the coverage tool. 220 221 To use this feature, you need to download 'coverage.py', 222 either Gareth Rees' original implementation: 223 http://www.garethrees.org/2001/12/04/python-coverage/ 224 225 or Ned Batchelder's enhanced version: 226 http://www.nedbatchelder.com/code/modules/coverage.html 227 228 If neither module is found in PYTHONPATH, 229 coverage is silently(!) disabled. 230 """ 231 try: 232 from coverage import the_coverage as coverage 233 c = os.path.join(os.path.dirname(__file__), "../lib/coverage.cache") 234 coverage.cache_default = c 235 if c and os.path.exists(c): 236 os.remove(c) 237 coverage.start() 238 import cherrypy 239 from cherrypy.lib import covercp 240 cherrypy.engine.subscribe('start', covercp.start) 241 cherrypy.engine.subscribe('start_thread', covercp.start) 242 except ImportError: 243 coverage = None 244 self.coverage = coverage
245
246 - def stop_coverage(self):
247 """Stop the coverage tool, save results, and report.""" 248 import cherrypy 249 from cherrypy.lib import covercp 250 cherrypy.engine.unsubscribe('start', covercp.start) 251 cherrypy.engine.unsubscribe('start_thread', covercp.start) 252 if self.coverage: 253 self.coverage.save() 254 self.report_coverage() 255 print ("run cherrypy/lib/covercp.py as a script to serve " 256 "coverage results on port 8080")
257
258 - def report_coverage(self):
259 """Print a summary from the code coverage tool.""" 260 261 basedir = self.basedir 262 if basedir is None: 263 # Assume we want to cover everything in "../../cherrypy/" 264 basedir = os.path.normpath(os.path.join(os.getcwd(), localDir, '../')) 265 else: 266 if not os.path.isabs(basedir): 267 basedir = os.path.normpath(os.path.join(os.getcwd(), basedir)) 268 basedir = basedir.lower() 269 270 self.coverage.get_ready() 271 morfs = [x for x in self.coverage.cexecuted 272 if x.lower().startswith(basedir)] 273 274 total_statements = 0 275 total_executed = 0 276 277 print 278 print "CODE COVERAGE (this might take a while)", 279 for morf in morfs: 280 sys.stdout.write(".") 281 sys.stdout.flush() 282 ## name = os.path.split(morf)[1] 283 if morf.find('test') != -1: 284 continue 285 try: 286 _, statements, _, missing, readable = self.coverage.analysis2(morf) 287 n = len(statements) 288 m = n - len(missing) 289 total_statements = total_statements + n 290 total_executed = total_executed + m 291 except KeyboardInterrupt: 292 raise 293 except: 294 # No, really! We truly want to ignore any other errors. 295 pass 296 297 pc = 100.0 298 if total_statements > 0: 299 pc = 100.0 * total_executed / total_statements 300 301 print ("\nTotal: %s Covered: %s Percent: %2d%%" 302 % (total_statements, total_executed, pc))
303
304 - def run(self, conf=None):
305 """Run the test harness (using the given [global] conf).""" 306 conf = conf or {} 307 308 # Start the coverage tool before importing cherrypy, 309 # so module-level global statements are covered. 310 if self.cover: 311 self.start_coverage() 312 313 if self.profile: 314 conf['profiling.on'] = True 315 316 if self.validate: 317 conf['validator.on'] = True 318 319 if self.conquer: 320 conf['conquer.on'] = True 321 322 if self.server == 'cpmodpy': 323 from cherrypy.test import modpy 324 h = modpy.ModPythonTestHarness(self.tests, self.server, 325 self.protocol, self.port, 326 "http", self.interactive) 327 h.use_wsgi = False 328 elif self.server == 'modpygw': 329 from cherrypy.test import modpy 330 h = modpy.ModPythonTestHarness(self.tests, self.server, 331 self.protocol, self.port, 332 "http", self.interactive) 333 h.use_wsgi = True 334 elif self.server == 'modwsgi': 335 from cherrypy.test import modwsgi 336 h = modwsgi.ModWSGITestHarness(self.tests, self.server, 337 self.protocol, self.port, 338 "http", self.interactive) 339 h.use_wsgi = True 340 elif self.server == 'modfcgid': 341 from cherrypy.test import modfcgid 342 h = modfcgid.FCGITestHarness(self.tests, self.server, 343 self.protocol, self.port, 344 "http", self.interactive) 345 else: 346 h = TestHarness(self.tests, self.server, self.protocol, 347 self.port, self.scheme, self.interactive, 348 self.host) 349 350 success = h.run(conf) 351 352 if self.profile: 353 print 354 print ("run /cherrypy/lib/profiler.py as a script to serve " 355 "profiling results on port 8080") 356 357 if self.cover: 358 self.stop_coverage() 359 360 return success
361 362
363 -def prefer_parent_path():
364 # Place this __file__'s grandparent (../../) at the start of sys.path, 365 # so that all cherrypy/* imports are from this __file__'s package. 366 curpath = os.path.normpath(os.path.join(os.getcwd(), localDir)) 367 grandparent = os.path.normpath(os.path.join(curpath, '../../')) 368 if grandparent not in sys.path: 369 sys.path.insert(0, grandparent)
370
371 -def run():
372 373 prefer_parent_path() 374 375 testList = [ 376 'test_proxy', 377 'test_caching', 378 'test_config', 379 ## 'test_conn', 380 'test_core', 381 'test_tools', 382 'test_encoding', 383 'test_etags', 384 'test_http', 385 'test_httpauth', 386 'test_httplib', 387 'test_logging', 388 'test_objectmapping', 389 'test_misc_tools', 390 'test_static', 391 'test_tutorials', 392 'test_virtualhost', 393 'test_safe_multipart', 394 'test_session', 395 'test_sessionauthenticate', 396 ## 'test_states', 397 'test_tidy', 398 'test_xmlrpc', 399 'test_wsgiapps', 400 'test_wsgi_ns', 401 'test_wsgi_vhost', 402 403 # Run refleak test as late as possible to 404 # catch refleaks from all exercised tests. 405 'test_refleaks', 406 ] 407 408 try: 409 import routes 410 testList.append('test_routes') 411 except ImportError: 412 pass 413 414 clp = CommandLineParser(testList) 415 success = clp.run() 416 if clp.interactive: 417 print 418 raw_input('hit enter') 419 sys.exit(success)
420 421 422 if __name__ == '__main__': 423 run() 424