~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_server.py

  • Committer: Vincent Ladeuil
  • Date: 2010-06-07 12:55:58 UTC
  • mto: (5247.6.1 http-leaks)
  • mto: This revision was merged to the branch mainline in revision 5396.
  • Revision ID: v.ladeuil+lp@free.fr-20100607125558-5rdbfk21podzot0x
Implement an execption handling mechanism that can be injected in ThreadWithException.

* bzrlib/tests/test_test_server.py:
(TestTCPServerInAThread.test_exception_swallowed_while_serving):
Check that the expected exception is swallowed.

* bzrlib/tests/test_server.py:
(ThreadWithException.set_ignored_exceptions): Allows exception
handlers to be installed.
(ThreadWithException.join): Respect ignored_exceptions.
(TestingTCPServerMixin.set_ignored_exceptions)
(TestingTCPServerMixin._pending_exections): New helpers.
(TestingThreadingTCPServer.process_request)
(TestingThreadingTCPServer.set_ignored_exceptions): Propagate
exception_handler.
(TestingTCPServerInAThread.set_ignored_exceptions): New helper.

Show diffs side-by-side

added added

removed removed

Lines of Context:
252
252
        super(ThreadWithException, self).__init__(*args, **kwargs)
253
253
        self.set_event(event)
254
254
        self.exception = None
 
255
        self.ignored_exceptions = None # see set_ignored_exceptions
255
256
 
256
257
    def set_event(self, event):
257
258
        self.ready = event
258
259
 
 
260
    def set_ignored_exceptions(self, ignored):
 
261
        """Declare which exceptions will be ignored.
 
262
 
 
263
        :param ignored: Can be either:
 
264
           - None: all exceptions will be raised,
 
265
           - an exception class: the instances of this class will be ignored,
 
266
           - a tuple of exception classes: the instances of any class of the
 
267
             list will be ignored,
 
268
           - a callable: that will be passed exc_class, exc_value
 
269
             and should return True if the exception should be ignored
 
270
        """
 
271
        if ignored is None:
 
272
            self.ignored_exceptions = None
 
273
        elif isinstance(ignored, Exception):
 
274
            self.ignored_exceptions = lambda c, v: c is ignored
 
275
        elif isinstance(ignored, tuple):
 
276
            self.ignored_exceptions = lambda c, v: isinstance(v, ignored)
 
277
        else:
 
278
            self.ignored_exceptions = ignored
 
279
 
259
280
    def run(self):
260
281
        """Overrides Thread.run to capture any exception."""
261
282
        self.ready.clear()
282
303
        if self.exception is not None:
283
304
            exc_class, exc_value, exc_tb = self.exception
284
305
            self.exception = None # The exception should be raised only once
285
 
            raise exc_class, exc_value, exc_tb
 
306
            if (self.ignored_exceptions is None
 
307
                or not self.ignored_exceptions(exc_class, exc_value)):
 
308
                # Raise non ignored exceptions
 
309
                raise exc_class, exc_value, exc_tb
286
310
        if timeout and self.isAlive():
287
311
            # The timeout expired without joining the thread, the thread is
288
312
            # therefore stucked and that's a failure as far as the test is
312
336
        # We collect the resources used by the clients so we can release them
313
337
        # when shutting down
314
338
        self.clients = []
 
339
        self.ignored_exceptions = None
315
340
 
316
341
    def server_bind(self):
317
342
        # We need to override the SocketServer bind, yet, we still want to use
378
403
            else:
379
404
                raise
380
405
 
 
406
    # The following methods are called by the main thread
 
407
 
 
408
    def set_ignored_exceptions(self, thread, ignored_exceptions):
 
409
        self.ignored_exceptions = ignored_exceptions
 
410
        thread.set_ignored_exceptions(self.ignored_exceptions)
 
411
 
 
412
    def _pending_exception(self, thread):
 
413
        """Raise server uncaught exception.
 
414
 
 
415
        Daughter classes can override this if they use daughter threads.
 
416
        """
 
417
        thread.pending_exception()
 
418
 
381
419
 
382
420
class TestingTCPServer(TestingTCPServerMixin, SocketServer.TCPServer):
383
421
 
398
436
        sock, addr = client
399
437
        self.shutdown_client_socket(sock)
400
438
 
401
 
    def _pending_exception(self, thread):
402
 
        """Raise server uncaught exception.
403
 
 
404
 
        Daughter classes can override this if they use daughter threads.
405
 
        """
406
 
        thread.pending_exception()
407
 
 
408
439
 
409
440
class TestingThreadingTCPServer(TestingTCPServerMixin,
410
441
                                SocketServer.ThreadingTCPServer):
436
467
            event=stopped,
437
468
            target = self.process_request_thread,
438
469
            args = (started, stopped, request, client_address))
439
 
        t.name = '%s -> %s' % (client_address, self.server_address)
440
470
        # Update the client description
441
471
        self.clients.pop()
442
472
        self.clients.append((request, client_address, t))
 
473
        # Propagate the exception handler since we must the same one for
 
474
        # connections running in their own threads than TestingTCPServer.
 
475
        t.set_ignored_exceptions(self.ignored_exceptions)
 
476
        t.name = '%s -> %s' % (client_address, self.server_address)
443
477
        t.start()
444
478
        started.wait()
445
479
        # If an exception occured during the thread start, it will get raised.
457
491
            # re-raised
458
492
            connection_thread.join()
459
493
 
 
494
    def set_ignored_exceptions(self, thread, ignored_exceptions):
 
495
        TestingTCPServerMixin.set_ignored_exceptions(self, thread,
 
496
                                                     ignored_exceptions)
 
497
        for sock, addr, connection_thread in self.clients:
 
498
            if connection_thread is not None:
 
499
                connection_thread.set_ignored_exceptions(
 
500
                    self.ignored_exceptions)
 
501
 
460
502
    def _pending_exception(self, thread):
461
503
        for sock, addr, connection_thread in self.clients:
462
504
            if connection_thread is not None:
463
505
                connection_thread.pending_exception()
464
 
        super(TestingThreadingTCPServer, self)._pending_exception(thread)
 
506
        TestingTCPServerMixin._pending_exception(self, thread)
465
507
 
466
508
 
467
509
class TestingTCPServerInAThread(transport.Server):
472
514
        self.request_handler_class = request_handler_class
473
515
        self.server_address = server_address
474
516
        self.server = None
 
517
        self._server_thread = None
475
518
 
476
519
    def __repr__(self):
477
520
        return "%s%r" % (self.__class__.__name__, self.server_address)
536
579
            # the various threads involved.
537
580
            self.server = None
538
581
 
 
582
    def set_ignored_exceptions(self, ignored_exceptions):
 
583
        """Install an exception handler for the server."""
 
584
        self.server.set_ignored_exceptions(self._server_thread,
 
585
                                           ignored_exceptions)
 
586
 
539
587
    def pending_exception(self):
540
588
        """Raise uncaught exception in the server."""
541
589
        self.server._pending_exception(self._server_thread)