Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/trunk/cherrypy/test/test.py

Revision 2018 (checked in by fumanchu, 3 months ago)

Almost fcgi support in tests.

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

Hosted by WebFaction

Log in as guest/cpguest to create tickets