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
8
9
10
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
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'):
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
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):
90
91
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
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
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
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
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
257
259 """Print a summary from the code coverage tool."""
260
261 basedir = self.basedir
262 if basedir is None:
263
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
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
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
309
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
364
365
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
372
373 prefer_parent_path()
374
375 testList = [
376 'test_proxy',
377 'test_caching',
378 'test_config',
379
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
397 'test_tidy',
398 'test_xmlrpc',
399 'test_wsgiapps',
400 'test_wsgi_ns',
401 'test_wsgi_vhost',
402
403
404
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