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

root/branches/cherrypy-2.x/cherrypy/test/test.py

Revision 1582 (checked in by dowski, 2 years ago)

2.x backport of fixes in [1388] and [1530] (safer WSGI and request close). See #567.

Also buglet fixes in test.py and helper.py.

  • 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 sys
14 import os, os.path
15 import webtest
16 import getopt
17
18
19 class TestHarness(object):
20     """A test harness for the CherryPy framework and CherryPy applications."""
21    
22     def __init__(self, tests=None, server=None, protocol="HTTP/1.1",
23                  port=8000, interactive=True):
24         """Constructor to populate the TestHarness instance.
25         
26         tests should be a list of module names (strings).
27         """
28         self.protocol = protocol
29         self.port = port
30         self.interactive = interactive
31         self.server = server
32         self.tests = tests or []
33    
34     def run(self, conf=None):
35         """Run the test harness."""
36         import cherrypy
37         v = sys.version.split()[0]
38         print "Python version used to run this test script:", v
39         print "CherryPy version", cherrypy.__version__
40         print
41        
42         if isinstance(conf, basestring):
43             conf = cherrypy.config.dict_from_config_file(conf)
44         baseconf = {'server.socket_host': '127.0.0.1',
45                     'server.socket_port': self.port,
46                     'server.thread_pool': 10,
47                     'server.log_to_screen': False,
48                     'server.environment': "production",
49                     'server.show_tracebacks': True,
50                     }
51         baseconf.update(conf or {})
52        
53         baseconf['server.protocol_version'] = self.protocol
54         return self._run(baseconf)
55    
56     def _run(self, conf):
57         # helper must be imported lazily so the coverage tool
58         # can run against module-level statements within cherrypy.
59         # Also, we have to do a relative import here, not
60         # "from cherrypy.test import helper", because the latter
61         # would stick a second instance of webtest in sys.modules,
62         # and we wouldn't be able to globally override the port anymore.
63         import helper
64         webtest.WebCase.PORT = self.port
65         webtest.WebCase.interactive = self.interactive
66         print
67         print "Running tests:", self.server
68         return helper.run_test_suite(self.tests, self.server, conf)
69
70
71 class CommandLineParser(object):
72     available_servers = {'wsgi': "cherrypy._cpwsgi.WSGIServer",
73                          'wsgi3': "cherrypy._cpwsgi.CPWSGIServer3",
74                          'modpy': "modpy",
75                          }
76     default_server = "wsgi"
77     port = 8080
78     interactive = True
79    
80     def __init__(self, available_tests, args=sys.argv[1:]):
81         """Constructor to populate the TestHarness instance.
82         
83         available_tests should be a list of module names (strings).
84         
85         args defaults to sys.argv[1:], but you can provide a different
86             set of args if you like.
87         """
88         self.available_tests = available_tests
89         self.cover = False
90         self.profile = False
91         self.server = None
92         self.protocol = "HTTP/1.1"
93        
94         longopts = ['cover', 'profile', 'dumb', '1.0', 'help',
95                     'basedir=', 'port=', 'server=']
96         longopts.extend(self.available_tests)
97         try:
98             opts, args = getopt.getopt(args, "", longopts)
99         except getopt.GetoptError:
100             # print help information and exit
101             self.help()
102             sys.exit(2)
103        
104         self.tests = []
105        
106         for o, a in opts:
107             if o == '--help':
108                 self.help()
109                 sys.exit()
110             elif o == "--cover":
111                 self.cover = True
112             elif o == "--profile":
113                 self.profile = True
114             elif o == "--dumb":
115                 self.interactive = False
116             elif o == "--1.0":
117                 self.protocol = "HTTP/1.0"
118             elif o == "--basedir":
119                 self.basedir = a
120             elif o == "--port":
121                 self.port = int(a)
122             elif o == "--server":
123                 if a in self.available_servers:
124                     a = self.available_servers[a]
125                 self.server = a
126             else:
127                 o = o[2:]
128                 if o in self.available_tests and o not in self.tests:
129                     self.tests.append(o)
130        
131         if self.cover and self.profile:
132             # Print error message and exit
133             print ('Error: you cannot run the profiler and the '
134                    'coverage tool at the same time.')
135             sys.exit(2)
136        
137         if not self.server:
138             self.server = self.available_servers[self.default_server]
139        
140         if not self.tests:
141             self.tests = self.available_tests[:]
142    
143     def help(self):
144         """Print help for test.py command-line options."""
145        
146         print """CherryPy Test Program
147     Usage:
148         test.py --server=* --port=%s --1.1 --cover --basedir=path --profile --dumb --tests**
149         
150     """ % self.__class__.port
151         print '    * servers:'
152         for name, val in self.available_servers.iteritems():
153             if name == self.default_server:
154                 print '        --%s: %s (default)' % (name, val)
155             else:
156                 print '        --%s: %s' % (name, val)
157        
158         print """
159     
160     --port=<int>: use a port other than the default (%s).
161     --1.0: use HTTP/1.0 servers instead of default HTTP/1.1.
162     
163     --cover: turn on the code-coverage tool.
164     --basedir=path: display coverage stats for some path other than cherrypy.
165     
166     --profile: turn on the profiling tool.
167     --dumb: turn off the interactive output features.
168     """ % self.__class__.port
169        
170         print '    ** tests:'
171         for name in self.available_tests:
172             print '        --' + name
173    
174     def start_coverage(self):
175         """Start the coverage tool.
176         
177         To use this feature, you need to download 'coverage.py',
178         either Gareth Rees' original implementation:
179         http://www.garethrees.org/2001/12/04/python-coverage/
180         
181         or Ned Batchelder's enhanced version:
182         http://www.nedbatchelder.com/code/modules/coverage.html
183         
184         If neither module is found in PYTHONPATH, coverage is disabled.
185         """
186         try:
187             from coverage import the_coverage as coverage
188             c = os.path.join(os.path.dirname(__file__), "../lib/coverage.cache")
189             coverage.cache_default = c
190             if c and os.path.exists(c):
191                 os.remove(c)
192             coverage.start()
193             import cherrypy
194             cherrypy.codecoverage = True
195         except ImportError:
196             coverage = None
197         self.coverage = coverage
198    
199     def stop_coverage(self):
200         """Stop the coverage tool, save results, and report."""
201         import cherrypy
202         cherrypy.codecoverage = False
203         if self.coverage:
204             self.coverage.save()
205             self.report_coverage()
206             print ("run cherrypy/lib/covercp.py as a script to serve "
207                    "coverage results on port 8080")
208    
209     def report_coverage(self):
210         """Print a summary from the code coverage tool."""
211        
212         basedir = self.basedir
213         if basedir is None:
214             # Assume we want to cover everything in "../../cherrypy/"
215             localDir = os.path.dirname(__file__)
216             basedir = os.path.normpath(os.path.join(os.getcwd(), localDir, '../'))
217         else:
218             if not os.path.isabs(basedir):
219                 basedir = os.path.normpath(os.path.join(os.getcwd(), basedir))
220         basedir = basedir.lower()
221        
222         self.coverage.get_ready()
223         morfs = [x for x in self.coverage.cexecuted
224                  if x.lower().startswith(basedir)]
225        
226         total_statements = 0
227         total_executed = 0
228        
229         print
230         print "CODE COVERAGE (this might take a while)",
231         for morf in morfs:
232             sys.stdout.write(".")
233             sys.stdout.flush()
234             name = os.path.split(morf)[1]
235             if morf.find('test') != -1:
236                 continue
237             try:
238                 _, statements, _, missing, readable  = self.coverage.analysis2(morf)
239                 n = len(statements)
240                 m = n - len(missing)
241                 total_statements = total_statements + n
242                 total_executed = total_executed + m
243             except KeyboardInterrupt:
244                 raise
245             except:
246                 # No, really! We truly want to ignore any other errors.
247                 pass
248        
249         pc = 100.0
250         if total_statements > 0:
251             pc = 100.0 * total_executed / total_statements
252        
253         print ("\nTotal: %s Covered: %s Percent: %2d%%"
254                % (total_statements, total_executed, pc))
255    
256     def run(self, conf=None):
257         """Run the test harness."""
258         # Start the coverage tool before importing cherrypy,
259         # so module-level global statements are covered.
260         if self.cover:
261             self.start_coverage()
262        
263         if self.profile:
264             conf = conf or {}
265             conf['profiling.on'] = True
266        
267         if self.server == 'modpy':
268             import modpy
269             h = modpy.ModPythonTestHarness(self.tests, self.server,
270                                            self.protocol, self.port,
271                                            self.interactive)
272         else:
273             h = TestHarness(self.tests, self.server,
274                             self.protocol, self.port,
275                             self.interactive)
276        
277         success = h.run(conf)
278        
279         if self.profile:
280             del conf['profiling.on']
281             print
282             print ("run /cherrypy/lib/profiler.py as a script to serve "
283                    "profiling results on port 8080")
284        
285         if self.cover:
286             self.stop_coverage()
287        
288         return success
289
290
291 def prefer_parent_path():
292     # Place this __file__'s grandparent (../../) at the start of sys.path,
293     # so that all cherrypy/* imports are from this __file__'s package.
294     localDir = os.path.dirname(__file__)
295     curpath = os.path.normpath(os.path.join(os.getcwd(), localDir))
296     grandparent = os.path.normpath(os.path.join(curpath, '../../'))
297     if grandparent not in sys.path:
298         sys.path.insert(0, grandparent)
299
300 def run():
301    
302     prefer_parent_path()
303    
304     testList = [
305         'test_baseurl_filter',
306         'test_cache_filter',
307         'test_combinedfilters',
308         'test_config',
309         'test_core',
310         'test_custom_filters',
311         'test_decodingencoding_filter',
312         'test_etags',
313         'test_gzip_filter',
314         'test_logdebuginfo_filter',
315         'test_misc_tools',
316         'test_objectmapping',
317         'test_response_headers_filter',
318         'test_static_filter',
319         'test_tutorials',
320         'test_virtualhost_filter',
321         'test_session_filter',
322         'test_sessionauthenticate_filter',
323 ##        'test_states',
324         'test_xmlrpc_filter',
325         'test_wsgiapp_filter',
326     ]
327     clp = CommandLineParser(testList)
328     success = clp.run()
329     if clp.interactive:
330         print
331         raw_input('hit enter')
332     sys.exit(success)
333
334
335 if __name__ == '__main__':
336     run()
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets