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