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

root/branches/cherrypy-2.1/cherrypy/test/helper.py

Revision 699 (checked in by fumanchu, 3 years ago)

Final cleanups for [698] and #321. cherrypy.server is now an instance of cherrpy._cpserver.Server, instead of a module.

Line 
1 """
2 Copyright (c) 2004, CherryPy Team (team@cherrypy.org)
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7
8     * Redistributions of source code must retain the above copyright notice,
9       this list of conditions and the following disclaimer.
10     * Redistributions in binary form must reproduce the above copyright notice,
11       this list of conditions and the following disclaimer in the documentation
12       and/or other materials provided with the distribution.
13     * Neither the name of the CherryPy Team nor the names of its contributors
14       may be used to endorse or promote products derived from this software
15       without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 """
28
29 # This is a library of helper functions for the CherryPy test suite.
30 # The actual script that runs the entire CP test suite is called
31 # "test.py" (in this folder); test.py calls this module as a library.
32 #
33 # GREAT CARE has been taken to separate this module from test.py,
34 # because different consumers of each have mutually-exclusive import
35 # requirements. So don't go moving functions from here into test.py,
36 # or vice-versa, unless you *really* know what you're doing.
37 #
38 # Usage:
39 #   Each individual test_*.py module imports this module (helper),
40 #   usually to make an instance of CPWebCase, and then call testmain().
41 #   
42 #   The CP test suite script (test.py) imports this module and calls
43 #   run_test_suite, possibly more than once. CP applications may also
44 #   import test.py (to use TestHarness), which then calls helper.py.
45
46
47 import os, os.path
48 import re
49 import socket
50 import StringIO
51 import sys
52 import thread
53 import threading
54 import time
55 import types
56
57 import cherrypy
58 import webtest
59
60 for _x in dir(cherrypy):
61     y = getattr(cherrypy, _x)
62     if isinstance(y, types.ClassType) and issubclass(y, cherrypy.Error):
63         webtest.ignored_exceptions.append(y)
64
65
66 def onerror():
67     """Assign to _cpOnError to enable webtest server-side debugging."""
68     handled = webtest.server_error()
69     if not handled:
70         cherrypy._cputil._cpOnError()
71
72
73 class CPWebCase(webtest.WebCase):
74    
75     def exit(self):
76         sys.exit()
77    
78     def _getRequest(self, url, headers, method, body):
79         # Like getPage, but for serverless requests.
80         webtest.ServerError.on = False
81         self.url = url
82        
83         requestLine = "%s %s HTTP/1.1" % (method.upper(), url)
84         headers = webtest.cleanHeaders(headers, method, body,
85                                        self.HOST, self.PORT)
86         if body is not None:
87             body = StringIO.StringIO(body)
88        
89         cherrypy.request.purge__()
90         cherrypy.response.purge__()
91        
92         cherrypy.server.request((self.HOST, self.PORT), self.HOST,
93                                 requestLine, headers, body, "http")
94        
95         self.status = cherrypy.response.status
96         self.headers = cherrypy.response.headers
97        
98         # Build a list of request cookies from the previous response cookies.
99         self.cookies = [('Cookie', v) for k, v in self.headers
100                         if k.lower() == 'set-cookie']
101        
102         try:
103             self.body = []
104             for chunk in cherrypy.response.body:
105                 self.body.append(chunk)
106         except:
107             if cherrypy.config.get("streamResponse", False):
108                 # Pass the error through
109                 raise
110            
111             from cherrypy import _cphttptools
112             s, h, b = _cphttptools.bareError()
113             # Don't reset status or headers; we're emulating an error which
114             # occurs after status and headers have been written to the client.
115             for chunk in b:
116                 self.body.append(chunk)
117         self.body = "".join(self.body)
118        
119         if webtest.ServerError.on:
120             self.tearDown()
121             raise webtest.ServerError()
122    
123     def tearDown(self):
124         pass
125    
126     def getPage(self, url, headers=None, method="GET", body=None):
127         """Open the url with debugging support. Return status, headers, body."""
128         # Install a custom error handler, so errors in the server will:
129         # 1) show server tracebacks in the test output, and
130         # 2) stop the HTTP request (if any) and ignore further assertions.
131         cherrypy.root._cpOnError = onerror
132        
133         if cherrypy.server.httpserver is None:
134             self._getRequest(url, headers, method, body)
135         else:
136             webtest.WebCase.getPage(self, url, headers, method, body)
137    
138     def assertErrorPage(self, status, message=None, pattern=''):
139         """ Compare the response body with a built in error page.
140             The function will optionally look for the regexp pattern,
141             within the exception embedded in the error page.
142         """
143        
144         from cherrypy._cputil import getErrorPage
145         esc = re.escape
146         # This will never contain a traceback:
147         page = esc(getErrorPage(status, message=message))
148        
149         # First, test the response body without checking the traceback.
150         # Stick a match-all group (.*) in to grab the traceback.
151         page = page.replace(esc('<pre id="traceback"></pre>'),
152                             esc('<pre id="traceback">') + '(.*)' + esc('</pre>'))
153         m = re.match(page, self.body, re.DOTALL)
154         if not m:
155             self._handlewebError('Error page does not match\n' + page)
156             return
157        
158         # Now test the pattern against the traceback
159         if pattern is None:
160             # Special-case None to mean that there should be *no* traceback.
161             if m and m.group(1):
162                 self._handlewebError('Error page contains traceback')
163         else:
164             if (m is None) or (not re.search(pattern, m.group(1))):
165                 msg = 'Error page does not contain %s in traceback'
166                 self._handlewebError(msg % repr(pattern))
167
168
169 CPTestLoader = webtest.ReloadingTestLoader()
170 CPTestRunner = webtest.TerseTestRunner(verbosity=2)
171
172 def setConfig(conf):
173     """Set the config using a copy of conf."""
174     if isinstance(conf, basestring):
175         # assume it's a filename
176         cherrypy.config.update(file=conf)
177     else:
178         cherrypy.config.update(conf.copy())
179
180
181 def run_test_suite(moduleNames, server, conf):
182     """Run the given test modules using the given server and conf.
183     
184     The server is started and stopped once, regardless of the number
185     of test modules. The config, however, is reset for each module.
186     """
187     setConfig(conf)
188     cherrypy.server.start_with_callback(_run_test_suite_thread,
189                                         args=(moduleNames, conf),
190                                         serverClass=server)
191
192 def _run_test_suite_thread(moduleNames, conf):
193     cherrypy.server.wait()
194     for testmod in moduleNames:
195         # Must run each module in a separate suite,
196         # because each module uses/overwrites cherrypy globals.
197         cherrypy.config.reset()
198         setConfig(conf)
199         cherrypy._cputil._cpInitDefaultFilters()
200        
201         suite = CPTestLoader.loadTestsFromName(testmod)
202         CPTestRunner.run(suite)
203     thread.interrupt_main()
204
205 def testmain(server=None, conf=None):
206     """Run __main__ as a test module, with webtest debugging."""
207     if conf is None:
208         conf = {}
209     setConfig(conf)
210     cherrypy.server.start_with_callback(_test_main_thread, serverClass=server)
211
212 def _test_main_thread():
213     cherrypy.server.wait()
214     cherrypy._cputil._cpInitDefaultFilters()
215     try:
216         webtest.main()
217     finally:
218         thread.interrupt_main()
219
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets