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 'modwsgi': "modwsgi",
91 }
92 default_server = "wsgi"
93 scheme = "http"
94 protocol = "HTTP/1.1"
95 port = 8080
96 host = '127.0.0.1'
97 cover = False
98 profile = False
99 validate = False
100 conquer = False
101 server = None
102 basedir = None
103 interactive = True
104
105 - def __init__(self, available_tests, args=sys.argv[1:]):
106 """Constructor to populate the TestHarness instance.
107
108 available_tests should be a list of module names (strings).
109
110 args defaults to sys.argv[1:], but you can provide a different
111 set of args if you like.
112 """
113 self.available_tests = available_tests
114
115 longopts = ['cover', 'profile', 'validate', 'conquer', 'dumb',
116 '1.0', 'ssl', 'help',
117 'basedir=', 'port=', 'server=', 'host=']
118 longopts.extend(self.available_tests)
119 try:
120 opts, args = getopt.getopt(args, "", longopts)
121 except getopt.GetoptError:
122
123 self.help()
124 sys.exit(2)
125
126 self.tests = []
127
128 for o, a in opts:
129 if o == '--help':
130 self.help()
131 sys.exit()
132 elif o == "--cover":
133 self.cover = True
134 elif o == "--profile":
135 self.profile = True
136 elif o == "--validate":
137 self.validate = True
138 elif o == "--conquer":
139 self.conquer = True
140 elif o == "--dumb":
141 self.interactive = False
142 elif o == "--1.0":
143 self.protocol = "HTTP/1.0"
144 elif o == "--ssl":
145 self.scheme = "https"
146 elif o == "--basedir":
147 self.basedir = a
148 elif o == "--port":
149 self.port = int(a)
150 elif o == "--host":
151 self.host = a
152 elif o == "--server":
153 if a in self.available_servers:
154 a = self.available_servers[a]
155 self.server = a
156 else:
157 o = o[2:]
158 if o in self.available_tests and o not in self.tests:
159 self.tests.append(o)
160
161 if self.cover and self.profile:
162
163 print ('Error: you cannot run the profiler and the '
164 'coverage tool at the same time.')
165 sys.exit(2)
166
167 if not self.server:
168 self.server = self.available_servers[self.default_server]
169
170 if not self.tests:
171 self.tests = self.available_tests[:]
172
174 """Print help for test.py command-line options."""
175
176 print """CherryPy Test Program
177 Usage:
178 test.py --server=* --host=%s --port=%s --1.0 --cover --basedir=path --profile --validate --conquer --dumb --tests**
179
180 """ % (self.__class__.host, self.__class__.port)
181 print ' * servers:'
182 for name, val in self.available_servers.iteritems():
183 if name == self.default_server:
184 print ' --server=%s: %s (default)' % (name, val)
185 else:
186 print ' --server=%s: %s' % (name, val)
187
188 print """
189
190 --host=<name or IP addr>: use a host other than the default (%s).
191 Not yet available with mod_python servers.
192 --port=<int>: use a port other than the default (%s)
193 --1.0: use HTTP/1.0 servers instead of default HTTP/1.1
194
195 --cover: turn on code-coverage tool
196 --basedir=path: display coverage stats for some path other than cherrypy.
197
198 --profile: turn on profiling tool
199 --validate: use wsgiref.validate (builtin in Python 2.5).
200 --conquer: use wsgiconq (which uses pyconquer) to trace calls.
201 --dumb: turn off the interactive output features.
202 """ % (self.__class__.host, self.__class__.port)
203
204 print ' ** tests:'
205 for name in self.available_tests:
206 print ' --' + name
207
209 """Start the coverage tool.
210
211 To use this feature, you need to download 'coverage.py',
212 either Gareth Rees' original implementation:
213 http://www.garethrees.org/2001/12/04/python-coverage/
214
215 or Ned Batchelder's enhanced version:
216 http://www.nedbatchelder.com/code/modules/coverage.html
217
218 If neither module is found in PYTHONPATH,
219 coverage is silently(!) disabled.
220 """
221 try:
222 from coverage import the_coverage as coverage
223 c = os.path.join(os.path.dirname(__file__), "../lib/coverage.cache")
224 coverage.cache_default = c
225 if c and os.path.exists(c):
226 os.remove(c)
227 coverage.start()
228 import cherrypy
229 from cherrypy.lib import covercp
230 cherrypy.engine.on_start_engine_list.insert(0, covercp.start)
231 cherrypy.engine.on_start_thread_list.insert(0, covercp.start)
232 except ImportError:
233 coverage = None
234 self.coverage = coverage
235
249
251 """Print a summary from the code coverage tool."""
252
253 basedir = self.basedir
254 if basedir is None:
255
256 basedir = os.path.normpath(os.path.join(os.getcwd(), localDir, '../'))
257 else:
258 if not os.path.isabs(basedir):
259 basedir = os.path.normpath(os.path.join(os.getcwd(), basedir))
260 basedir = basedir.lower()
261
262 self.coverage.get_ready()
263 morfs = [x for x in self.coverage.cexecuted
264 if x.lower().startswith(basedir)]
265
266 total_statements = 0
267 total_executed = 0
268
269 print
270 print "CODE COVERAGE (this might take a while)",
271 for morf in morfs:
272 sys.stdout.write(".")
273 sys.stdout.flush()
274
275 if morf.find('test') != -1:
276 continue
277 try:
278 _, statements, _, missing, readable = self.coverage.analysis2(morf)
279 n = len(statements)
280 m = n - len(missing)
281 total_statements = total_statements + n
282 total_executed = total_executed + m
283 except KeyboardInterrupt:
284 raise
285 except:
286
287 pass
288
289 pc = 100.0
290 if total_statements > 0:
291 pc = 100.0 * total_executed / total_statements
292
293 print ("\nTotal: %s Covered: %s Percent: %2d%%"
294 % (total_statements, total_executed, pc))
295
296 - def run(self, conf=None):
297 """Run the test harness."""
298
299
300 if self.cover:
301 self.start_coverage()
302
303 if self.profile:
304 conf = conf or {}
305 conf['profiling.on'] = True
306
307 if self.validate:
308 conf = conf or {}
309 conf['validator.on'] = True
310
311 if self.conquer:
312 conf = conf or {}
313 conf['conquer.on'] = True
314
315 if self.server == 'cpmodpy':
316 from cherrypy.test import modpy
317 h = modpy.ModPythonTestHarness(self.tests, self.server,
318 self.protocol, self.port,
319 "http", self.interactive)
320 h.use_wsgi = False
321 elif self.server == 'modpygw':
322 from cherrypy.test import modpy
323 h = modpy.ModPythonTestHarness(self.tests, self.server,
324 self.protocol, self.port,
325 "http", self.interactive)
326 h.use_wsgi = True
327 elif self.server == 'modwsgi':
328 from cherrypy.test import modwsgi
329 h = modwsgi.ModWSGITestHarness(self.tests, self.server,
330 self.protocol, self.port,
331 "http", self.interactive)
332 h.use_wsgi = True
333 else:
334 h = TestHarness(self.tests, self.server, self.protocol,
335 self.port, self.scheme, self.interactive,
336