1
+ import collections
1
2
from contextlib import contextmanager
2
3
import functools
3
4
import sys
6
7
7
8
import pytest
8
9
9
- from pytestqt .qt_compat import QtCore , QtTest , QApplication , QT_API
10
+ from pytestqt .qt_compat import QtCore , QtTest , QApplication , QT_API , \
11
+ qInstallMsgHandler , QtDebugMsg , QtWarningMsg , QtCriticalMsg , QtFatalMsg
10
12
11
13
12
14
def _inject_qtest_methods (cls ):
@@ -421,6 +423,78 @@ def pytest_configure(config):
421
423
"qt_no_exception_capture: Disables pytest-qt's automatic exception "
422
424
'capture for just one test item.' )
423
425
426
+ config .pluginmanager .register (QtLoggingPlugin (config ), '_qt_logging' )
427
+
424
428
425
429
def pytest_report_header ():
426
- return ['qt-api: %s' % QT_API ]
430
+ return ['qt-api: %s' % QT_API ]
431
+
432
+
433
+ class QtLoggingPlugin (object ):
434
+ """
435
+ Pluging responsible for installing a QtMessageHandler before each
436
+ test and augment reporting if the test failed with the messages captured.
437
+ """
438
+
439
+ def __init__ (self , config ):
440
+ self .config = config
441
+
442
+ def pytest_runtest_setup (self , item ):
443
+ item .qt_log_handler = _QtMessageCapture ()
444
+ previous_handler = qInstallMsgHandler (item .qt_log_handler .handle )
445
+ item .qt_previous_handler = previous_handler
446
+
447
+ @pytest .mark .hookwrapper
448
+ def pytest_runtest_makereport (self , item , call ):
449
+ """Add captured Qt messages to test item report if the call failed."""
450
+
451
+ outcome = yield
452
+ report = outcome .result
453
+
454
+ if call .when == 'call' :
455
+
456
+ if not report .passed :
457
+ long_repr = getattr (report , 'longrepr' , None )
458
+ if hasattr (long_repr , 'addsection' ):
459
+ lines = []
460
+ for msg_type , msg in item .qt_log_handler .messages :
461
+ msg_name = _QtMessageCapture .get_msg_name (msg_type )
462
+ lines .append ('{0}: {1}' .format (msg_name , msg ))
463
+ if lines :
464
+ long_repr .addsection ('Captured Qt messages' ,
465
+ '\n ' .join (lines ))
466
+ # Release the handler resources.
467
+ qInstallMsgHandler (item .qt_previous_handler )
468
+ del item .qt_previous_handler
469
+ del item .qt_log_handler
470
+
471
+
472
+ class _QtMessageCapture (object ):
473
+ """
474
+ Captures Qt messages when its `handle` method is installed using
475
+ qInstallMsgHandler, and stores them into `messages` attribute.
476
+
477
+ :attr messages: list of Message named-tuples.
478
+ """
479
+
480
+ Message = collections .namedtuple ('Message' , 'msg_type, msg' )
481
+
482
+ def __init__ (self ):
483
+ self .messages = []
484
+
485
+ def handle (self , msg_type , msg ):
486
+ """
487
+ Method to be installed using qInstallMsgHandler, stores each message
488
+ into the `messages` attribute.
489
+ """
490
+ self .messages .append (self .Message (msg_type , msg ))
491
+
492
+ @classmethod
493
+ def get_msg_name (cls , msg_type ):
494
+ """Returns a string representation of the given QtMsgType enum value."""
495
+ return {
496
+ QtDebugMsg : 'QtDebugMsg' ,
497
+ QtWarningMsg : 'QtWarningMsg' ,
498
+ QtCriticalMsg : 'QtCriticalMsg' ,
499
+ QtFatalMsg : 'QtFatalMsg' ,
500
+ }[msg_type ]
0 commit comments