diff options
Diffstat (limited to 'silx/test/utils.py')
-rw-r--r-- | silx/test/utils.py | 243 |
1 files changed, 73 insertions, 170 deletions
diff --git a/silx/test/utils.py b/silx/test/utils.py index 44eb899..bac415b 100644 --- a/silx/test/utils.py +++ b/silx/test/utils.py @@ -24,31 +24,23 @@ # ###########################################################################*/ """Utilities for writing tests. -- :class:`ParametricTestCase` provides a :meth:`TestCase.subTest` replacement - for Python < 3.4 -- :class:`TestLogging` with context or the :func:`test_logging` decorator - enables testing the number of logging messages of different levels. - :func:`temp_dir` provides a with context to create/delete a temporary directory. """ __authors__ = ["T. Vincent"] __license__ = "MIT" -__date__ = "03/08/2017" +__date__ = "17/01/2018" -import os +import sys import contextlib -import functools -import logging +import os import numpy import shutil -import sys import tempfile -import unittest from ..resources import ExternalResources -_logger = logging.getLogger(__name__) utilstest = ExternalResources(project="silx", url_base="http://www.silx.org/pub/silx/", @@ -56,174 +48,85 @@ utilstest = ExternalResources(project="silx", timeout=60) "This is the instance to be used. Singleton-like feature provided by module" -# Parametric Test Base Class ################################################## - -if sys.hexversion >= 0x030400F0: # Python >= 3.4 - class ParametricTestCase(unittest.TestCase): - pass -else: - class ParametricTestCase(unittest.TestCase): - """TestCase with subTest support for Python < 3.4. +class _TestOptions(object): - Add subTest method to support parametric tests. - API is the same, but behavior differs: - If a subTest fails, the following ones are not run. - """ + def __init__(self): + self.WITH_QT_TEST = True + """Qt tests are included""" - _subtest_msg = None # Class attribute to provide a default value - - @contextlib.contextmanager - def subTest(self, msg=None, **params): - """Use as unittest.TestCase.subTest method in Python >= 3.4.""" - # Format arguments as: '[msg] (key=value, ...)' - param_str = ', '.join(['%s=%s' % (k, v) for k, v in params.items()]) - self._subtest_msg = '[%s] (%s)' % (msg or '', param_str) - yield - self._subtest_msg = None - - def shortDescription(self): - short_desc = super(ParametricTestCase, self).shortDescription() - if self._subtest_msg is not None: - # Append subTest message to shortDescription - short_desc = ' '.join( - [msg for msg in (short_desc, self._subtest_msg) if msg]) - - return short_desc if short_desc else None - - -# Test logging messages ####################################################### - -class TestLogging(logging.Handler): - """Context checking the number of logging messages from a specified Logger. - - It disables propagation of logging message while running. - - This is meant to be used as a with statement, for example: - - >>> with TestLogging(logger, error=2, warning=0): - >>> pass # Run tests here expecting 2 ERROR and no WARNING from logger - ... - - :param logger: Name or instance of the logger to test. - (Default: root logger) - :type logger: str or :class:`logging.Logger` - :param int critical: Expected number of CRITICAL messages. - Default: Do not check. - :param int error: Expected number of ERROR messages. - Default: Do not check. - :param int warning: Expected number of WARNING messages. - Default: Do not check. - :param int info: Expected number of INFO messages. - Default: Do not check. - :param int debug: Expected number of DEBUG messages. - Default: Do not check. - :param int notset: Expected number of NOTSET messages. - Default: Do not check. - :raises RuntimeError: If the message counts are the expected ones. - """ + self.WITH_QT_TEST_REASON = "" + """Reason for Qt tests are disabled if any""" - def __init__(self, logger=None, critical=None, error=None, - warning=None, info=None, debug=None, notset=None): - if logger is None: - logger = logging.getLogger() - elif not isinstance(logger, logging.Logger): - logger = logging.getLogger(logger) - self.logger = logger - - self.records = [] - - self.count_by_level = { - logging.CRITICAL: critical, - logging.ERROR: error, - logging.WARNING: warning, - logging.INFO: info, - logging.DEBUG: debug, - logging.NOTSET: notset - } - - super(TestLogging, self).__init__() - - def __enter__(self): - """Context (i.e., with) support""" - self.records = [] # Reset recorded LogRecords - self.logger.addHandler(self) - self.logger.propagate = False - # ensure no log message is ignored - self.entry_level = self.logger.level * 1 - self.logger.setLevel(logging.DEBUG) - - def __exit__(self, exc_type, exc_value, traceback): - """Context (i.e., with) support""" - self.logger.removeHandler(self) - self.logger.propagate = True - self.logger.setLevel(self.entry_level) - - for level, expected_count in self.count_by_level.items(): - if expected_count is None: - continue - - # Number of records for the specified level_str - count = len([r for r in self.records if r.levelno == level]) - if count != expected_count: # That's an error - # Resend record logs through logger as they where masked - # to help debug - for record in self.records: - self.logger.handle(record) - raise RuntimeError( - 'Expected %d %s logging messages, got %d' % ( - expected_count, logging.getLevelName(level), count)) - - def emit(self, record): - """Override :meth:`logging.Handler.emit`""" - self.records.append(record) - - -def test_logging(logger=None, critical=None, error=None, - warning=None, info=None, debug=None, notset=None): - """Decorator checking number of logging messages. - - Propagation of logging messages is disabled by this decorator. - - In case the expected number of logging messages is not found, it raises - a RuntimeError. - - >>> class Test(unittest.TestCase): - ... @test_logging('module_logger_name', error=2, warning=0) - ... def test(self): - ... pass # Test expecting 2 ERROR and 0 WARNING messages - - :param logger: Name or instance of the logger to test. - (Default: root logger) - :type logger: str or :class:`logging.Logger` - :param int critical: Expected number of CRITICAL messages. - Default: Do not check. - :param int error: Expected number of ERROR messages. - Default: Do not check. - :param int warning: Expected number of WARNING messages. - Default: Do not check. - :param int info: Expected number of INFO messages. - Default: Do not check. - :param int debug: Expected number of DEBUG messages. - Default: Do not check. - :param int notset: Expected number of NOTSET messages. - Default: Do not check. - """ - def decorator(func): - test_context = TestLogging(logger, critical, error, - warning, info, debug, notset) + self.WITH_OPENCL_TEST = True + """OpenCL tests are included""" - @functools.wraps(func) - def wrapper(*args, **kwargs): - with test_context: - result = func(*args, **kwargs) - return result - return wrapper - return decorator + self.WITH_GL_TEST = True + """OpenGL tests are included""" + self.WITH_GL_TEST_REASON = "" + """Reason for OpenGL tests are disabled if any""" + self.TEST_LOW_MEM = False + """Skip tests using too much memory""" + def configure(self, parsed_options): + """Configure the TestOptions class from the command line arguments and the + environment variables + """ + if not parsed_options.gui: + self.WITH_QT_TEST = False + self.WITH_QT_TEST_REASON = "Skipped by command line" + elif os.environ.get('WITH_QT_TEST', 'True') == 'False': + self.WITH_QT_TEST = False + self.WITH_QT_TEST_REASON = "Skipped by WITH_QT_TEST env var" + elif sys.platform.startswith('linux') and not os.environ.get('DISPLAY', ''): + self.WITH_QT_TEST = False + self.WITH_QT_TEST_REASON = "DISPLAY env variable not set" + + if not parsed_options.opencl or os.environ.get('SILX_OPENCL', 'True') == 'False': + self.WITH_OPENCL_TEST = False + # That's an easy way to skip OpenCL tests + # It disable the use of OpenCL on the full silx project + os.environ['SILX_OPENCL'] = "False" + + if not parsed_options.opengl: + self.WITH_GL_TEST = False + self.WITH_GL_TEST_REASON = "Skipped by command line" + elif os.environ.get('WITH_GL_TEST', 'True') == 'False': + self.WITH_GL_TEST = False + self.WITH_GL_TEST_REASON = "Skipped by WITH_GL_TEST env var" + + if parsed_options.low_mem or os.environ.get('SILX_TEST_LOW_MEM', 'True') == 'False': + self.TEST_LOW_MEM = True + + if self.WITH_QT_TEST: + from silx.gui import qt + if sys.platform == "win32" and qt.qVersion() == "5.9.2": + self.SKIP_TEST_FOR_ISSUE_936 = True + + def add_parser_argument(self, parser): + """Add extrat arguments to the test argument parser + + :param ArgumentParser parser: An argument parser + """ + parser.add_argument("-x", "--no-gui", dest="gui", default=True, + action="store_false", + help="Disable the test of the graphical use interface") + parser.add_argument("-g", "--no-opengl", dest="opengl", default=True, + action="store_false", + help="Disable tests using OpenGL") + parser.add_argument("-o", "--no-opencl", dest="opencl", default=True, + action="store_false", + help="Disable the test of the OpenCL part") + parser.add_argument("-l", "--low-mem", dest="low_mem", default=False, + action="store_true", + help="Disable test with large memory consumption (>100Mbyte") + + +test_options = _TestOptions() +"""Singleton providing configuration information for all the tests""" # Temporary directory context ################################################# |