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

root/tags/cherrypy-3.0.0/cherrypy/test/test.py

Revision 1528 (checked in by fumanchu, 2 years ago)

Changes to socket_host:

  1. wsgiserver now treats a host of "" as an alias for INADDR_ANY. The getaddrinfo call now passes host=None and sets AI_PASSIVE in this case.
  2. Server.httpserver_from_self doesn't change an empty host ("") to localhost anymore.
  3. The test suite has a new --host=<name or IP> flag.
  4. The webtest module now allows WebCase?.HOST to be "", and will connect on '127.0.0.1' if so.
  5. Lots of comments throughout to explain that the server's treatment of socket_host="" (all IPs) is different than the client's (pick any IP).
  6. Hopefully, this will fix #619 (long delay on startup).
  • 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."""
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):
67         # helper must be imported lazily so the coverage tool
68         # can run against module-level statements within cherrypy.
69         # Also, we have to do "from cherrypy.test import helper",
70         # exactly like each test module does, because a relative import
71         # would stick a second instance of webtest in sys.modules,
72         # and we wouldn't be able to globally override the port anymore.
73         from cherrypy.test import helper, webtest
74         webtest.WebCase.PORT = self.port
75         webtest.WebCase.HOST = self.host
76         webtest.WebCase.harness = self
77         helper.CPWebCase.scheme = self.scheme
78         webtest.WebCase.interactive = self.interactive
79         if self.scheme == "https":
80             webtest.WebCase.HTTP_CONN = httplib.HTTPSConnection
81         print
82         print "Running tests:", self.server
83         return helper.run_test_suite(self.tests, self.server, conf)
84
85
86 class CommandLineParser(object):
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             # print help information and exit
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             # Print error message and exit
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    
172     def help(self):
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    
207     def start_coverage(self):
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    
235     def stop_coverage(self):
236         """Stop the coverage tool, save results, and report."""
237         import cherrypy
238         from cherrypy.lib import covercp
239         while covercp.start in cherrypy.engine.on_start_engine_list:
240             cherrypy.engine.on_start_engine_list.remove(covercp.start)
241         while covercp.start in cherrypy.engine.on_start_thread_list:
242             cherrypy.engine.on_start_thread_list.remove(covercp.start)
243         if self.coverage:
244             self.coverage.save()
245             self.report_coverage()
246             print ("run cherrypy/lib/covercp.py as a script to serve "
247                    "coverage results on port 8080")
248    
249     def report_coverage(self):
250         """Print a summary from the code coverage tool."""
251        
252         basedir = self.basedir
253         if basedir is None:
254             # Assume we want to cover everything in "../../cherrypy/"
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 ##            name = os.path.split(morf)[1]
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                 # No, really! We truly want to ignore any other errors.
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         # Start the coverage tool before importing cherrypy,
298         # so module-level global statements are covered.
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
344 def prefer_parent_path():
345     # Place this __file__'s grandparent (../../) at the start of sys.path,
346     # so that all cherrypy/* imports are from this __file__'s package.
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
352 def run():
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 ##        'test_states',
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()
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets