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."""
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):
84
85
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
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
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
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
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
248
250 """Print a summary from the code coverage tool."""
251
252 basedir = self.basedir
253 if basedir is None:
254
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
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
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
298
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
345
346
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
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
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