summaryrefslogtreecommitdiff
path: root/silx/test
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2017-08-18 14:48:52 +0200
committerPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2017-08-18 14:48:52 +0200
commitf7bdc2acff3c13a6d632c28c4569690ab106eed7 (patch)
tree9d67cdb7152ee4e711379e03fe0546c7c3b97303 /silx/test
Import Upstream version 0.5.0+dfsg
Diffstat (limited to 'silx/test')
-rw-r--r--silx/test/__init__.py80
-rw-r--r--silx/test/test_resources.py97
-rw-r--r--silx/test/test_sx.py174
-rw-r--r--silx/test/test_version.py49
-rw-r--r--silx/test/utils.py284
5 files changed, 684 insertions, 0 deletions
diff --git a/silx/test/__init__.py b/silx/test/__init__.py
new file mode 100644
index 0000000..01ee8d1
--- /dev/null
+++ b/silx/test/__init__.py
@@ -0,0 +1,80 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2015-2017 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""Full silx test suite.
+
+
+It is possible to disable tests depending on Qt by setting
+:envvar:`WITH_QT_TEST` environment variable to 'False'.
+It will skip all tests from :mod:`silx.test.gui`.
+"""
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "20/04/2017"
+
+
+import logging
+import os
+import unittest
+
+
+logger = logging.getLogger(__name__)
+
+
+def suite():
+ from . import test_version
+ from . import test_resources
+ from . import test_sx
+ from ..io import test as test_io
+ from ..math import test as test_math
+ from ..image import test as test_image
+ from ..gui import test as test_gui
+ from ..utils import test as test_utils
+ from ..opencl import test as test_ocl
+ test_suite = unittest.TestSuite()
+ # test sx first cause qui tests load ipython module
+ test_suite.addTest(test_sx.suite())
+ test_suite.addTest(test_gui.suite())
+ # then test no-gui tests
+ test_suite.addTest(test_utils.suite())
+ test_suite.addTest(test_version.suite())
+ test_suite.addTest(test_resources.suite())
+ test_suite.addTest(test_utils.suite())
+ test_suite.addTest(test_io.suite())
+ test_suite.addTest(test_math.suite())
+ test_suite.addTest(test_image.suite())
+ test_suite.addTest(test_ocl.suite())
+ return test_suite
+
+
+def run_tests():
+ """Run test complete test_suite"""
+ runner = unittest.TextTestRunner()
+ if not runner.run(suite()).wasSuccessful():
+ print("Test suite failed")
+ return 1
+ else:
+ print("Test suite succeeded")
+ return 0
diff --git a/silx/test/test_resources.py b/silx/test/test_resources.py
new file mode 100644
index 0000000..9d3e277
--- /dev/null
+++ b/silx/test/test_resources.py
@@ -0,0 +1,97 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""Test for resource files management."""
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "20/04/2017"
+
+
+import os
+import unittest
+
+import silx.resources
+from .utils import utilstest
+
+
+class TestResources(unittest.TestCase):
+ def test_resource_dir(self):
+ """Get a resource directory"""
+ icons_dirname = silx.resources.resource_filename('gui/icons/')
+ self.assertTrue(os.path.isdir(icons_dirname))
+
+ def test_resource_file(self):
+ """Get a resource file name"""
+ filename = silx.resources.resource_filename('gui/icons/colormap.png')
+ self.assertTrue(os.path.isfile(filename))
+
+ def test_resource_nonexistent(self):
+ """Get a non existent resource"""
+ filename = silx.resources.resource_filename('non_existent_file.txt')
+ self.assertFalse(os.path.exists(filename))
+
+
+class TestExternalResources(unittest.TestCase):
+ "This is a test for the TestResources"
+ def test_tempdir(self):
+ "test the temporary directory creation"
+ myutilstest = silx.resources.ExternalResources("toto", "http://www.silx.org")
+ d = myutilstest.tempdir
+ self.assertTrue(os.path.isdir(d))
+ self.assertEqual(d, myutilstest.tempdir, 'tmpdir is stable')
+ myutilstest.clean_up()
+ self.assertFalse(os.path.isdir(d))
+ e = myutilstest.tempdir
+ self.assertTrue(os.path.isdir(e))
+ self.assertEqual(e, myutilstest.tempdir, 'tmpdir is stable')
+ self.assertNotEqual(d, e, "tempdir changed")
+ myutilstest.clean_up()
+
+ def test_download(self):
+ "test the download from silx.org"
+ f = utilstest.getfile("lena.png")
+ self.assertTrue(os.path.exists(f))
+ f = utilstest.getdir("source.tar.gz")
+ self.assertTrue(os.path.isfile(f))
+ self.assertTrue(os.path.isdir(f[:-7]))
+
+ def test_dowload_all(self):
+ "test the download of all files from silx.org"
+ l = utilstest.download_all()
+ self.assertGreater(len(l), 1, "At least 2 items were downloaded")
+
+
+def suite():
+ test_suite = unittest.TestSuite()
+ test_suite.addTest(
+ unittest.defaultTestLoader.loadTestsFromTestCase(TestResources))
+ test_suite.addTest(TestExternalResources("test_tempdir"))
+ test_suite.addTest(TestExternalResources("test_download")) # order matters !
+ test_suite.addTest(TestExternalResources("test_dowload_all"))
+ return test_suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
diff --git a/silx/test/test_sx.py b/silx/test/test_sx.py
new file mode 100644
index 0000000..0de3b35
--- /dev/null
+++ b/silx/test/test_sx.py
@@ -0,0 +1,174 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+__authors__ = ["T. Vincent", "P. Knobel"]
+__license__ = "MIT"
+__date__ = "05/12/2016"
+
+
+import logging
+import os
+import sys
+import unittest
+
+import numpy
+
+
+_logger = logging.getLogger(__name__)
+
+
+if sys.platform.startswith('linux') and not os.environ.get('DISPLAY', ''):
+ # On linux and no DISPLAY available (e.g., ssh without -X)
+ _logger.warning('silx.sx tests disabled (DISPLAY env. variable not set)')
+
+ class SkipSXTest(unittest.TestCase):
+ def runTest(self):
+ self.skipTest(
+ 'silx.sx tests disabled (DISPLAY env. variable not set)')
+
+ def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(SkipSXTest())
+ return suite
+
+elif os.environ.get('WITH_QT_TEST', 'True') == 'False':
+ # Explicitly disabled tests
+ _logger.warning(
+ "silx.sx tests disabled (env. variable WITH_QT_TEST=False)")
+
+ class SkipSXTest(unittest.TestCase):
+ def runTest(self):
+ self.skipTest(
+ "silx.sx tests disabled (env. variable WITH_QT_TEST=False)")
+
+ def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(SkipSXTest())
+ return suite
+
+else:
+ # Import here to avoid loading QT if tests are disabled
+
+ from silx.gui import qt
+ # load TestCaseQt before sx
+ from silx.gui.test.utils import TestCaseQt
+ from silx import sx
+
+ class SXTest(TestCaseQt):
+ """Test the sx module"""
+
+ def _expose_and_close(self, plot):
+ self.qWaitForWindowExposed(plot)
+ self.qapp.processEvents()
+ plot.setAttribute(qt.Qt.WA_DeleteOnClose)
+ plot.close()
+
+ def test_plot(self):
+ """Test plot function"""
+ y = numpy.random.random(100)
+ x = numpy.arange(len(y)) * 0.5
+
+ # Nothing
+ plt = sx.plot()
+ self._expose_and_close(plt)
+
+ # y
+ plt = sx.plot(y, title='y')
+ self._expose_and_close(plt)
+
+ # y, style
+ plt = sx.plot(y, 'blued ', title='y, "blued "')
+ self._expose_and_close(plt)
+
+ # x, y
+ plt = sx.plot(x, y, title='x, y')
+ self._expose_and_close(plt)
+
+ # x, y, style
+ plt = sx.plot(x, y, 'ro-', xlabel='x', title='x, y, "ro-"')
+ self._expose_and_close(plt)
+
+ # x, y, style, y
+ plt = sx.plot(x, y, 'ro-', y ** 2, xlabel='x', ylabel='y',
+ title='x, y, "ro-", y ** 2')
+ self._expose_and_close(plt)
+
+ # x, y, style, y, style
+ plt = sx.plot(x, y, 'ro-', y ** 2, 'b--',
+ title='x, y, "ro-", y ** 2, "b--"')
+ self._expose_and_close(plt)
+
+ # x, y, style, x, y, style
+ plt = sx.plot(x, y, 'ro-', x, y ** 2, 'b--',
+ title='x, y, "ro-", x, y ** 2, "b--"')
+ self._expose_and_close(plt)
+
+ # x, y, x, y
+ plt = sx.plot(x, y, x, y ** 2, title='x, y, x, y ** 2')
+ self._expose_and_close(plt)
+
+ def test_imshow(self):
+ """Test imshow function"""
+ img = numpy.arange(100.).reshape(10, 10) + 1
+
+ # Nothing
+ plt = sx.imshow()
+ self._expose_and_close(plt)
+
+ # image
+ plt = sx.imshow(img)
+ self._expose_and_close(plt)
+
+ # image, gray cmap
+ plt = sx.imshow(img, cmap='jet', title='jet cmap')
+ self._expose_and_close(plt)
+
+ # image, log cmap
+ plt = sx.imshow(img, norm='log', title='log cmap')
+ self._expose_and_close(plt)
+
+ # image, fixed range
+ plt = sx.imshow(img, vmin=10, vmax=20,
+ title='[10,20] cmap')
+ self._expose_and_close(plt)
+
+ # image, keep ratio
+ plt = sx.imshow(img, aspect=True,
+ title='keep ratio')
+ self._expose_and_close(plt)
+
+ # image, change origin and scale
+ plt = sx.imshow(img, origin=(10, 10), scale=(2, 2),
+ title='origin=(10, 10), scale=(2, 2)')
+ self._expose_and_close(plt)
+
+ def suite():
+ test_suite = unittest.TestSuite()
+ test_suite.addTest(
+ unittest.defaultTestLoader.loadTestsFromTestCase(SXTest))
+ return test_suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
diff --git a/silx/test/test_version.py b/silx/test/test_version.py
new file mode 100644
index 0000000..bb91e4e
--- /dev/null
+++ b/silx/test/test_version.py
@@ -0,0 +1,49 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2015-2016 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""Basic test of top-level package import and existence of version info."""
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "26/02/2016"
+
+import unittest
+
+import silx
+
+
+class TestVersion(unittest.TestCase):
+ def test_version(self):
+ self.assertTrue(isinstance(silx.version, str))
+
+
+def suite():
+ test_suite = unittest.TestSuite()
+ test_suite.addTest(
+ unittest.defaultTestLoader.loadTestsFromTestCase(TestVersion))
+ return test_suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
diff --git a/silx/test/utils.py b/silx/test/utils.py
new file mode 100644
index 0000000..ec86a2a
--- /dev/null
+++ b/silx/test/utils.py
@@ -0,0 +1,284 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""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__ = "20/04/2017"
+
+
+import os
+import contextlib
+import functools
+import logging
+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/",
+ env_key="SILX_DATA",
+ 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.
+
+ Add subTest method to support parametric tests.
+ API is the same, but behavior differs:
+ If a subTest fails, the following ones are not run.
+ """
+
+ _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.
+ """
+
+ 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
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ """Context (i.e., with) support"""
+ self.logger.removeHandler(self)
+ self.logger.propagate = True
+
+ 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)
+
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs):
+ with test_context:
+ result = func(*args, **kwargs)
+ return result
+ return wrapper
+ return decorator
+
+
+
+
+
+
+# Temporary directory context #################################################
+
+@contextlib.contextmanager
+def temp_dir():
+ """with context providing a temporary directory.
+
+ >>> import os.path
+ >>> with temp_dir() as tmp:
+ ... print(os.path.isdir(tmp)) # Use tmp directory
+ """
+ tmp_dir = tempfile.mkdtemp()
+ try:
+ yield tmp_dir
+ finally:
+ shutil.rmtree(tmp_dir)
+
+
+# Synthetic data and random noise #############################################
+def add_gaussian_noise(y, stdev=1., mean=0.):
+ """Add random gaussian noise to synthetic data.
+
+ :param ndarray y: Array of synthetic data
+ :param float mean: Mean of the gaussian distribution of noise.
+ :param float stdev: Standard deviation of the gaussian distribution of
+ noise.
+ :return: Array of data with noise added
+ """
+ noise = numpy.random.normal(mean, stdev, size=y.size)
+ noise.shape = y.shape
+ return y + noise
+
+
+def add_poisson_noise(y):
+ """Add random noise from a poisson distribution to synthetic data.
+
+ :param ndarray y: Array of synthetic data
+ :return: Array of data with noise added
+ """
+ yn = numpy.random.poisson(y)
+ yn.shape = y.shape
+ return yn
+
+
+def add_relative_noise(y, max_noise=5.):
+ """Add relative random noise to synthetic data. The maximum noise level
+ is given in percents.
+
+ An array of noise in the interval [-max_noise, max_noise] (continuous
+ uniform distribution) is generated, and applied to the data the
+ following way:
+
+ :math:`yn = y * (1. + noise / 100.)`
+
+ :param ndarray y: Array of synthetic data
+ :param float max_noise: Maximum percentage of noise
+ :return: Array of data with noise added
+ """
+ noise = max_noise * (2 * numpy.random.random(size=y.size) - 1)
+ noise.shape = y.shape
+ return y * (1. + noise / 100.)