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

root/branches/cp3-wsgi-remix/test/test_states.py

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

New deadlock monitor which sets Response.timed_out to True if Response.time < now - config.get("deadlock_timeout"). The request thread periodically checkes Response.timed_out and raises TimeoutError? if it is True. Current checks are in HookMap?.run, Request.respond, Body.set, and Response.finalize, more can be added later if needed.

  • Property svn:eol-style set to native
Line 
1 import os
2 import sys
3 import threading
4 import time
5
6 import test
7 test.prefer_parent_path()
8
9 import cherrypy
10
11
12 class Root:
13     def index(self):
14         return "Hello World"
15     index.exposed = True
16    
17     def ctrlc(self):
18         raise KeyboardInterrupt()
19     ctrlc.exposed = True
20    
21     def restart(self):
22         cherrypy.engine.restart()
23         return "app was restarted succesfully"
24     restart.exposed = True
25    
26     def block_explicit(self):
27         while True:
28             if cherrypy.response.timed_out:
29                 cherrypy.response.timed_out = False
30                 return "broken!"
31             time.sleep(0.1)
32     block_explicit.exposed = True
33    
34     def block_implicit(self):
35         raise cherrypy.InternalRedirect("/block_implicit")
36     block_implicit.exposed = True
37     block_implicit._cp_config = {'recursive_redirect': True}
38
39 cherrypy.tree.mount(Root())
40 cherrypy.config.update({
41     'log_to_screen': False,
42     'environment': 'production',
43     'deadlock_poll_freq': 1,
44     'deadlock_timeout': 2,
45     })
46
47 class Dependency:
48    
49     def __init__(self):
50         self.running = False
51         self.startcount = 0
52         self.threads = {}
53    
54     def start(self):
55         self.running = True
56         self.startcount += 1
57    
58     def stop(self):
59         self.running = False
60    
61     def startthread(self, thread_id):
62         self.threads[thread_id] = None
63    
64     def stopthread(self, thread_id):
65         del self.threads[thread_id]
66
67
68 import helper
69
70 class ServerStateTests(helper.CPWebCase):
71    
72     def test_0_NormalStateFlow(self):
73         if not self.server_class:
74             # Without having called "cherrypy.engine.start()", we should
75             # get a 503 Service Unavailable response.
76             self.getPage("/")
77             self.assertStatus(503)
78        
79         # And our db_connection should not be running
80         self.assertEqual(db_connection.running, False)
81         self.assertEqual(db_connection.startcount, 0)
82         self.assertEqual(len(db_connection.threads), 0)
83        
84         # Test server start
85         cherrypy.server.quickstart(self.server_class)
86         cherrypy.engine.start(blocking=False)
87         self.assertEqual(cherrypy.engine.state, 1)
88        
89         if self.server_class:
90             host = cherrypy.config.get('server.socket_host')
91             port = cherrypy.config.get('server.socket_port')
92             self.assertRaises(IOError, cherrypy._cpserver.check_port, host, port)
93        
94         # The db_connection should be running now
95         self.assertEqual(db_connection.running, True)
96         self.assertEqual(db_connection.startcount, 1)
97         self.assertEqual(len(db_connection.threads), 0)
98        
99         self.getPage("/")
100         self.assertBody("Hello World")
101         self.assertEqual(len(db_connection.threads), 1)
102        
103         # Test engine stop
104         cherrypy.engine.stop()
105         self.assertEqual(cherrypy.engine.state, 0)
106        
107         # Verify that the on_stop_engine function was called
108         self.assertEqual(db_connection.running, False)
109         self.assertEqual(len(db_connection.threads), 0)
110        
111         if not self.server_class:
112             # Once the engine has stopped, we should get a 503
113             # error again. (If we were running an HTTP server,
114             # then the connection should not even be processed).
115             self.getPage("/")
116             self.assertStatus(503)
117        
118         # Block the main thread now and verify that stop() works.
119         def stoptest():
120             self.getPage("/")
121             self.assertBody("Hello World")
122             cherrypy.engine.stop()
123         cherrypy.engine.start_with_callback(stoptest)
124         self.assertEqual(cherrypy.engine.state, 0)
125         cherrypy.server.stop()
126    
127     def test_1_Restart(self):
128         cherrypy.server.start()
129         cherrypy.engine.start(blocking=False)
130        
131         # The db_connection should be running now
132         self.assertEqual(db_connection.running, True)
133         sc = db_connection.startcount
134        
135         self.getPage("/")
136         self.assertBody("Hello World")
137         self.assertEqual(len(db_connection.threads), 1)
138        
139         # Test server restart from this thread
140         cherrypy.engine.restart()
141         self.assertEqual(cherrypy.engine.state, 1)
142         self.getPage("/")
143         self.assertBody("Hello World")
144         self.assertEqual(db_connection.running, True)
145         self.assertEqual(db_connection.startcount, sc + 1)
146         self.assertEqual(len(db_connection.threads), 1)
147        
148         # Test server restart from inside a page handler
149         self.getPage("/restart")
150         self.assertEqual(cherrypy.engine.state, 1)
151         self.assertBody("app was restarted succesfully")
152         self.assertEqual(db_connection.running, True)
153         self.assertEqual(db_connection.startcount, sc + 2)
154         # Since we are requesting synchronously, is only one thread used?
155         # Note that the "/restart" request has been flushed.
156         self.assertEqual(len(db_connection.threads), 0)
157        
158         cherrypy.engine.stop()
159         self.assertEqual(cherrypy.engine.state, 0)
160         self.assertEqual(db_connection.running, False)
161         self.assertEqual(len(db_connection.threads), 0)
162         cherrypy.server.stop()
163    
164     def test_2_KeyboardInterrupt(self):
165         if self.server_class:
166            
167             # Raise a keyboard interrupt in the HTTP server's main thread.
168             # We must start the server in this, the main thread
169             cherrypy.engine.start(blocking=False)
170             cherrypy.server.start()
171             cherrypy.server.httpservers.keys()[0].interrupt = KeyboardInterrupt
172             while cherrypy.engine.state != 0:
173                 time.sleep(0.1)
174            
175             self.assertEqual(db_connection.running, False)
176             self.assertEqual(len(db_connection.threads), 0)
177             self.assertEqual(cherrypy.engine.state, 0)
178            
179             # Raise a keyboard interrupt in a page handler; on multithreaded
180             # servers, this should occur in one of the worker threads.
181             # This should raise a BadStatusLine error, since the worker
182             # thread will just die without writing a response.
183             cherrypy.engine.start(blocking=False)
184             cherrypy.server.start()
185            
186             from httplib import BadStatusLine
187             try:
188                 self.getPage("/ctrlc")
189             except BadStatusLine:
190                 pass
191             else:
192                 print self.body
193                 self.fail("AssertionError: BadStatusLine not raised")
194            
195             while cherrypy.engine.state != 0:
196                 time.sleep(0.1)
197             self.assertEqual(db_connection.running, False)
198             self.assertEqual(len(db_connection.threads), 0)
199    
200     def test_3_Deadlocks(self):
201         cherrypy.engine.start(blocking=False)
202         cherrypy.server.start()
203         try:
204             self.assertNotEqual(cherrypy.engine.monitor_thread, None)
205            
206             # Request a "normal" page.
207             self.assertEqual(cherrypy.engine.servings, [])
208             self.getPage("/")
209             self.assertBody("Hello World")
210             # request.close is called async.
211             while cherrypy.engine.servings:
212                 time.sleep(0.1)
213            
214             # Request a page that explicitly checks itself for deadlock.
215             # The deadlock_timeout should be 2 secs.
216             self.getPage("/block_explicit")
217             self.assertBody("broken!")
218            
219             # Request a page that implicitly breaks deadlock.
220             # If we deadlock, we want to touch as little code as possible,
221             # so we won't even call handle_error, just bail ASAP.
222             self.getPage("/block_implicit")
223             self.assertStatus(500)
224             self.assertInBody("raise cherrypy.TimeoutError()")
225         finally:
226             cherrypy.engine.stop()
227             cherrypy.server.stop()
228    
229     def test_4_Autoreload(self):
230         if self.server_class:
231             demoscript = os.path.join(os.getcwd(), os.path.dirname(__file__),
232                                       "test_states_demo.py")
233            
234             # Start the demo script in a new process
235             host = cherrypy.config.get("server.socket_host")
236             port = cherrypy.config.get("server.socket_port")
237             cherrypy._cpserver.wait_for_free_port(host, port)
238             os.spawnl(os.P_NOWAIT, sys.executable, sys.executable,
239                       demoscript, host, str(port))
240             cherrypy._cpserver.wait_for_occupied_port(host, port)
241            
242             try:
243                 self.getPage("/pid")
244                 pid = self.body
245                
246                 # Give the autoreloader time to cache the file time.
247                 time.sleep(2)
248                
249                 # Touch the file
250                 f = open(demoscript, 'ab')
251                 f.write(" ")
252                 f.close()
253                
254                 # Give the autoreloader time to re-exec the process
255                 time.sleep(2)
256                 cherrypy._cpserver.wait_for_occupied_port(host, port)
257                
258                 self.getPage("/pid")
259                 self.assertNotEqual(self.body, pid)
260             finally:
261                 # Shut down the spawned process
262                 self.getPage("/stop")
263
264
265 db_connection = None
266
267 def run(server, conf):
268     helper.setConfig(conf)
269     ServerStateTests.server_class = server
270     suite = helper.CPTestLoader.loadTestsFromTestCase(ServerStateTests)
271     try:
272         global db_connection
273         db_connection = Dependency()
274         cherrypy.engine.on_start_engine_list.append(db_connection.start)
275         cherrypy.engine.on_stop_engine_list.append(db_connection.stop)
276         cherrypy.engine.on_start_thread_list.append(db_connection.startthread)
277         cherrypy.engine.on_stop_thread_list.append(db_connection.stopthread)
278        
279         try:
280             import pyconquer
281         except ImportError:
282             helper.CPTestRunner.run(suite)
283         else:
284             tr = pyconquer.Logger("cherrypy")
285             tr.out = open(os.path.join(os.path.dirname(__file__), "state.log"), "wb")
286             try:
287                 tr.start()
288                 helper.CPTestRunner.run(suite)
289             finally:
290                 tr.stop()
291                 tr.out.close()
292     finally:
293         cherrypy.server.stop()
294         cherrypy.engine.stop()
295
296
297 def run_all(host, port):
298     conf = {'server.socket_host': host,
299             'server.socket_port': port,
300             'server.thread_pool': 10,
301             'log_to_screen': False,
302             'log_config': False,
303             'environment': "production",
304             'show_tracebacks': True,
305             }
306     def _run(server):
307         print
308         print "Testing %s on %s:%s..." % (server, host, port)
309         run(server, conf)
310     _run("cherrypy._cpwsgi.WSGIServer")
311
312
313 def run_localhosts(port):
314     for host in ("", "127.0.0.1", "localhost"):
315         conf = {'server.socket_host': host,
316                 'server.socket_port': port,
317                 'server.thread_pool': 10,
318                 'log_to_screen': False,
319                 'log_config': False,
320                 'environment': "production",
321                 'show_tracebacks': True,
322                 }
323         def _run(server):
324             print
325             print "Testing %s on %s:%s..." % (server, host, port)
326             run(server, conf)
327         _run("cherrypy._cpwsgi.WSGIServer")
328
329
330 if __name__ == "__main__":
331     import sys
332     host = '127.0.0.1'
333     port = 8000
334     if len(sys.argv) > 1:
335         cmd = sys.argv[1]
336         if cmd in [prefix + atom for atom in ("?", "h", "help")
337                    for prefix in ("", "-", "--", "\\")]:
338             print
339             print "test_states.py -?             -> this help page"
340             print "test_states.py [host] [port]  -> run the tests on the given host/port"
341             print "test_states.py -localhosts [port]  -> try various localhost strings"
342             sys.exit(0)
343         if len(sys.argv) > 2:
344             port = int(sys.argv[2])
345         if cmd == "-localhosts":
346             run_localhosts(port)
347             sys.exit(0)
348         host = sys.argv[1].strip("\"'")
349     run_all(host, port)
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets