diff options
author | Picca Frédéric-Emmanuel <picca@synchrotron-soleil.fr> | 2018-07-31 16:22:25 +0200 |
---|---|---|
committer | Picca Frédéric-Emmanuel <picca@synchrotron-soleil.fr> | 2018-07-31 16:22:25 +0200 |
commit | 159ef14fb9e198bb0066ea14e6b980f065de63dd (patch) | |
tree | bc37c7d4ba09ee59deb708897fa0571709aec293 /silx/gui/test | |
parent | 270d5ddc31c26b62379e3caa9044dd75ccc71847 (diff) |
New upstream version 0.8.0+dfsg
Diffstat (limited to 'silx/gui/test')
-rw-r--r-- | silx/gui/test/__init__.py | 11 | ||||
-rw-r--r-- | silx/gui/test/test_colors.py | 401 | ||||
-rw-r--r-- | silx/gui/test/test_utils.py | 166 |
3 files changed, 408 insertions, 170 deletions
diff --git a/silx/gui/test/__init__.py b/silx/gui/test/__init__.py index 0d0805f..8a9a949 100644 --- a/silx/gui/test/__init__.py +++ b/silx/gui/test/__init__.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility +# Copyright (c) 2016-2018 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 @@ -24,15 +24,15 @@ # ###########################################################################*/ __authors__ = ["T. Vincent", "P. Knobel"] __license__ = "MIT" -__date__ = "28/11/2017" +__date__ = "24/04/2018" import logging import os import sys import unittest -from silx.test.utils import test_options +from silx.test.utils import test_options _logger = logging.getLogger(__name__) @@ -73,12 +73,14 @@ def suite(): from ..widgets import test as test_widgets from ..data import test as test_data from ..dialog import test as test_dialog + from ..utils import test as test_utils + from . import test_qt # Console tests disabled due to corruption of python environment # (see issue #538 on github) # from . import test_console from . import test_icons - from . import test_utils + from . import test_colors try: from ..plot3d.test import suite as test_plot3d_suite @@ -102,6 +104,7 @@ def suite(): test_suite.addTest(test_widgets.suite()) # test_suite.addTest(test_console.suite()) # see issue #538 on github test_suite.addTest(test_icons.suite()) + test_suite.addTest(test_colors.suite()) test_suite.addTest(test_data.suite()) test_suite.addTest(test_utils.suite()) test_suite.addTest(test_plot3d_suite()) diff --git a/silx/gui/test/test_colors.py b/silx/gui/test/test_colors.py new file mode 100644 index 0000000..d7c205e --- /dev/null +++ b/silx/gui/test/test_colors.py @@ -0,0 +1,401 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2015-2018 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. +# +# ###########################################################################*/ +"""This module provides the Colormap object +""" + +from __future__ import absolute_import + +__authors__ = ["H.Payno"] +__license__ = "MIT" +__date__ = "24/04/2018" + +import unittest +import numpy +from silx.utils.testutils import ParametricTestCase +from silx.gui import colors +from silx.gui.colors import Colormap +from silx.gui.colors import preferredColormaps, setPreferredColormaps +from silx.utils.exceptions import NotEditableError + + +class TestRGBA(ParametricTestCase): + """Basic tests of rgba function""" + + def testRGBA(self): + """"Test rgba function with accepted values""" + tests = { # name: (colors, expected values) + 'blue': ('blue', (0., 0., 1., 1.)), + '#010203': ('#010203', (1. / 255., 2. / 255., 3. / 255., 1.)), + '#01020304': ('#01020304', (1. / 255., 2. / 255., 3. / 255., 4. / 255.)), + '3 x uint8': (numpy.array((1, 255, 0), dtype=numpy.uint8), + (1 / 255., 1., 0., 1.)), + '4 x uint8': (numpy.array((1, 255, 0, 1), dtype=numpy.uint8), + (1 / 255., 1., 0., 1 / 255.)), + '3 x float overflow': ((3., 0.5, 1.), (1., 0.5, 1., 1.)), + } + + for name, test in tests.items(): + color, expected = test + with self.subTest(msg=name): + result = colors.rgba(color) + self.assertEqual(result, expected) + + +class TestApplyColormapToData(ParametricTestCase): + """Tests of applyColormapToData function""" + + def testApplyColormapToData(self): + """Simple test of applyColormapToData function""" + colormap = Colormap(name='gray', normalization='linear', + vmin=0, vmax=255) + + size = 10 + expected = numpy.empty((size, 4), dtype='uint8') + expected[:, 0] = numpy.arange(size, dtype='uint8') + expected[:, 1] = expected[:, 0] + expected[:, 2] = expected[:, 0] + expected[:, 3] = 255 + + for dtype in ('uint8', 'int32', 'float32', 'float64'): + with self.subTest(dtype=dtype): + array = numpy.arange(size, dtype=dtype) + result = colormap.applyToData(data=array) + self.assertTrue(numpy.all(numpy.equal(result, expected))) + + +class TestDictAPI(unittest.TestCase): + """Make sure the old dictionary API is working + """ + + def setUp(self): + self.vmin = -1.0 + self.vmax = 12 + + def testGetItem(self): + """test the item getter API ([xxx])""" + colormap = Colormap(name='viridis', + normalization=Colormap.LINEAR, + vmin=self.vmin, + vmax=self.vmax) + self.assertTrue(colormap['name'] == 'viridis') + self.assertTrue(colormap['normalization'] == Colormap.LINEAR) + self.assertTrue(colormap['vmin'] == self.vmin) + self.assertTrue(colormap['vmax'] == self.vmax) + with self.assertRaises(KeyError): + colormap['toto'] + + def testGetDict(self): + """Test the getDict function API""" + clmObject = Colormap(name='viridis', + normalization=Colormap.LINEAR, + vmin=self.vmin, + vmax=self.vmax) + clmDict = clmObject._toDict() + self.assertTrue(clmDict['name'] == 'viridis') + self.assertTrue(clmDict['autoscale'] is False) + self.assertTrue(clmDict['vmin'] == self.vmin) + self.assertTrue(clmDict['vmax'] == self.vmax) + self.assertTrue(clmDict['normalization'] == Colormap.LINEAR) + + clmObject.setVRange(None, None) + self.assertTrue(clmObject._toDict()['autoscale'] is True) + + def testSetValidDict(self): + """Test that if a colormap is created from a dict then it is correctly + created and the values are copied (so if some values from the dict + is changing, this won't affect the Colormap object""" + clm_dict = { + 'name': 'temperature', + 'vmin': 1.0, + 'vmax': 2.0, + 'normalization': 'linear', + 'colors': None, + 'autoscale': False + } + + # Test that the colormap is correctly created + colormapObject = Colormap._fromDict(clm_dict) + self.assertTrue(colormapObject.getName() == clm_dict['name']) + self.assertTrue(colormapObject.getColormapLUT() == clm_dict['colors']) + self.assertTrue(colormapObject.getVMin() == clm_dict['vmin']) + self.assertTrue(colormapObject.getVMax() == clm_dict['vmax']) + self.assertTrue(colormapObject.isAutoscale() == clm_dict['autoscale']) + + # Check that the colormap has copied the values + clm_dict['vmin'] = None + clm_dict['vmax'] = None + clm_dict['colors'] = [1.0, 2.0] + clm_dict['autoscale'] = True + clm_dict['normalization'] = Colormap.LOGARITHM + clm_dict['name'] = 'viridis' + + self.assertFalse(colormapObject.getName() == clm_dict['name']) + self.assertFalse(colormapObject.getColormapLUT() == clm_dict['colors']) + self.assertFalse(colormapObject.getVMin() == clm_dict['vmin']) + self.assertFalse(colormapObject.getVMax() == clm_dict['vmax']) + self.assertFalse(colormapObject.isAutoscale() == clm_dict['autoscale']) + + def testMissingKeysFromDict(self): + """Make sure we can create a Colormap object from a dictionnary even if + there is missing keys excepts if those keys are 'colors' or 'name' + """ + colormap = Colormap._fromDict({'name': 'toto'}) + self.assertTrue(colormap.getVMin() is None) + colormap = Colormap._fromDict({'colors': numpy.zeros(10)}) + self.assertTrue(colormap.getName() is None) + + with self.assertRaises(ValueError): + Colormap._fromDict({}) + + def testUnknowNorm(self): + """Make sure an error is raised if the given normalization is not + knowed + """ + clm_dict = { + 'name': 'temperature', + 'vmin': 1.0, + 'vmax': 2.0, + 'normalization': 'toto', + 'colors': None, + 'autoscale': False + } + with self.assertRaises(ValueError): + Colormap._fromDict(clm_dict) + + +class TestObjectAPI(ParametricTestCase): + """Test the new Object API of the colormap""" + def testVMinVMax(self): + """Test getter and setter associated to vmin and vmax values""" + vmin = 1.0 + vmax = 2.0 + + colormapObject = Colormap(name='viridis', + vmin=vmin, + vmax=vmax, + normalization=Colormap.LINEAR) + + with self.assertRaises(ValueError): + colormapObject.setVMin(3) + + with self.assertRaises(ValueError): + colormapObject.setVMax(-2) + + with self.assertRaises(ValueError): + colormapObject.setVRange(3, -2) + + self.assertTrue(colormapObject.getColormapRange() == (1.0, 2.0)) + self.assertTrue(colormapObject.isAutoscale() is False) + colormapObject.setVRange(None, None) + self.assertTrue(colormapObject.getVMin() is None) + self.assertTrue(colormapObject.getVMax() is None) + self.assertTrue(colormapObject.isAutoscale() is True) + + def testCopy(self): + """Make sure the copy function is correctly processing + """ + colormapObject = Colormap(name='toto', + colors=numpy.array([12, 13, 14]), + vmin=None, + vmax=None, + normalization=Colormap.LOGARITHM) + + colormapObject2 = colormapObject.copy() + self.assertTrue(colormapObject == colormapObject2) + colormapObject.setColormapLUT(numpy.array([0, 1])) + self.assertFalse(colormapObject == colormapObject2) + + colormapObject2 = colormapObject.copy() + self.assertTrue(colormapObject == colormapObject2) + colormapObject.setNormalization(Colormap.LINEAR) + self.assertFalse(colormapObject == colormapObject2) + + def testGetColorMapRange(self): + """Make sure the getColormapRange function of colormap is correctly + applying + """ + # test linear scale + data = numpy.array([-1, 1, 2, 3, float('nan')]) + cl1 = Colormap(name='gray', + normalization=Colormap.LINEAR, + vmin=0, + vmax=2) + cl2 = Colormap(name='gray', + normalization=Colormap.LINEAR, + vmin=None, + vmax=2) + cl3 = Colormap(name='gray', + normalization=Colormap.LINEAR, + vmin=0, + vmax=None) + cl4 = Colormap(name='gray', + normalization=Colormap.LINEAR, + vmin=None, + vmax=None) + + self.assertTrue(cl1.getColormapRange(data) == (0, 2)) + self.assertTrue(cl2.getColormapRange(data) == (-1, 2)) + self.assertTrue(cl3.getColormapRange(data) == (0, 3)) + self.assertTrue(cl4.getColormapRange(data) == (-1, 3)) + + # test linear with annoying cases + self.assertEqual(cl3.getColormapRange((-1, -2)), (0, 0)) + self.assertEqual(cl4.getColormapRange(()), (0., 1.)) + self.assertEqual(cl4.getColormapRange( + (float('nan'), float('inf'), 1., -float('inf'), 2)), (1., 2.)) + self.assertEqual(cl4.getColormapRange( + (float('nan'), float('inf'))), (0., 1.)) + + # test log scale + data = numpy.array([float('nan'), -1, 1, 10, 100, 1000]) + cl1 = Colormap(name='gray', + normalization=Colormap.LOGARITHM, + vmin=1, + vmax=100) + cl2 = Colormap(name='gray', + normalization=Colormap.LOGARITHM, + vmin=None, + vmax=100) + cl3 = Colormap(name='gray', + normalization=Colormap.LOGARITHM, + vmin=1, + vmax=None) + cl4 = Colormap(name='gray', + normalization=Colormap.LOGARITHM, + vmin=None, + vmax=None) + + self.assertTrue(cl1.getColormapRange(data) == (1, 100)) + self.assertTrue(cl2.getColormapRange(data) == (1, 100)) + self.assertTrue(cl3.getColormapRange(data) == (1, 1000)) + self.assertTrue(cl4.getColormapRange(data) == (1, 1000)) + + # test log with annoying cases + self.assertEqual(cl3.getColormapRange((0.1, 0.2)), (1, 1)) + self.assertEqual(cl4.getColormapRange((-2., -1.)), (1., 1.)) + self.assertEqual(cl4.getColormapRange(()), (1., 10.)) + self.assertEqual(cl4.getColormapRange( + (float('nan'), float('inf'), 1., -float('inf'), 2)), (1., 2.)) + self.assertEqual(cl4.getColormapRange( + (float('nan'), float('inf'))), (1., 10.)) + + def testApplyToData(self): + """Test applyToData on different datasets""" + datasets = [ + numpy.zeros((0, 0)), # Empty array + numpy.array((numpy.nan, numpy.inf)), # All non-finite + numpy.array((-numpy.inf, numpy.inf, 1.0, 2.0)), # Some infinite + ] + + for normalization in ('linear', 'log'): + colormap = Colormap(name='gray', + normalization=normalization, + vmin=None, + vmax=None) + + for data in datasets: + with self.subTest(data=data): + image = colormap.applyToData(data) + self.assertEqual(image.dtype, numpy.uint8) + self.assertEqual(image.shape[-1], 4) + self.assertEqual(image.shape[:-1], data.shape) + + def testGetNColors(self): + """Test getNColors method""" + # specific LUT + colormap = Colormap(name=None, + colors=((0., 0., 0.), (1., 1., 1.)), + vmin=1000, + vmax=2000) + colors = colormap.getNColors() + self.assertTrue(numpy.all(numpy.equal( + colors, + ((0, 0, 0, 255), (255, 255, 255, 255))))) + + def testEditableMode(self): + """Make sure the colormap will raise NotEditableError when try to + change a colormap not editable""" + colormap = Colormap() + colormap.setEditable(False) + with self.assertRaises(NotEditableError): + colormap.setVRange(0., 1.) + with self.assertRaises(NotEditableError): + colormap.setVMin(1.) + with self.assertRaises(NotEditableError): + colormap.setVMax(1.) + with self.assertRaises(NotEditableError): + colormap.setNormalization(Colormap.LOGARITHM) + with self.assertRaises(NotEditableError): + colormap.setName('magma') + with self.assertRaises(NotEditableError): + colormap.setColormapLUT(numpy.array([0, 1])) + with self.assertRaises(NotEditableError): + colormap._setFromDict(colormap._toDict()) + state = colormap.saveState() + with self.assertRaises(NotEditableError): + colormap.restoreState(state) + + +class TestPreferredColormaps(unittest.TestCase): + """Test get|setPreferredColormaps functions""" + + def setUp(self): + # Save preferred colormaps + self._colormaps = preferredColormaps() + + def tearDown(self): + # Restore saved preferred colormaps + setPreferredColormaps(self._colormaps) + + def test(self): + colormaps = 'viridis', 'magma' + + setPreferredColormaps(colormaps) + self.assertEqual(preferredColormaps(), colormaps) + + with self.assertRaises(ValueError): + setPreferredColormaps(()) + + with self.assertRaises(ValueError): + setPreferredColormaps(('This is not a colormap',)) + + colormaps = 'red', 'green' + setPreferredColormaps(('This is not a colormap',) + colormaps) + self.assertEqual(preferredColormaps(), colormaps) + + +def suite(): + test_suite = unittest.TestSuite() + loadTests = unittest.defaultTestLoader.loadTestsFromTestCase + test_suite.addTest(loadTests(TestApplyColormapToData)) + test_suite.addTest(loadTests(TestRGBA)) + test_suite.addTest(loadTests(TestDictAPI)) + test_suite.addTest(loadTests(TestObjectAPI)) + test_suite.addTest(loadTests(TestPreferredColormaps)) + return test_suite + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') diff --git a/silx/gui/test/test_utils.py b/silx/gui/test/test_utils.py deleted file mode 100644 index b1cdf0f..0000000 --- a/silx/gui/test/test_utils.py +++ /dev/null @@ -1,166 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2017-2018 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 of utils module.""" - -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "16/01/2017" - - -import threading -import unittest - -import numpy - -from silx.third_party.concurrent_futures import wait -from silx.gui import qt -from silx.gui.test.utils import TestCaseQt - -from silx.gui import _utils - - -class TestQImageConversion(TestCaseQt): - """Tests conversion of QImage to/from numpy array.""" - - def testConvertArrayToQImage(self): - """Test conversion of numpy array to QImage""" - image = numpy.ones((3, 3, 3), dtype=numpy.uint8) - qimage = _utils.convertArrayToQImage(image) - - self.assertEqual(qimage.height(), image.shape[0]) - self.assertEqual(qimage.width(), image.shape[1]) - self.assertEqual(qimage.format(), qt.QImage.Format_RGB888) - - color = qt.QColor(1, 1, 1).rgb() - self.assertEqual(qimage.pixel(1, 1), color) - - def testConvertQImageToArray(self): - """Test conversion of QImage to numpy array""" - qimage = qt.QImage(3, 3, qt.QImage.Format_RGB888) - qimage.fill(0x010101) - image = _utils.convertQImageToArray(qimage) - - self.assertEqual(qimage.height(), image.shape[0]) - self.assertEqual(qimage.width(), image.shape[1]) - self.assertEqual(image.shape[2], 3) - self.assertTrue(numpy.all(numpy.equal(image, 1))) - - -class TestSubmitToQtThread(TestCaseQt): - """Test submission of tasks to Qt main thread""" - - def setUp(self): - # Reset executor to test lazy-loading in different conditions - _utils._executor = None - super(TestSubmitToQtThread, self).setUp() - - def _task(self, value1, value2): - return value1, value2 - - def _taskWithException(self, *args, **kwargs): - raise RuntimeError('task exception') - - def testFromMainThread(self): - """Call submitToQtMainThread from the main thread""" - value1, value2 = 0, 1 - future = _utils.submitToQtMainThread(self._task, value1, value2=value2) - self.assertTrue(future.done()) - self.assertEqual(future.result(1), (value1, value2)) - self.assertIsNone(future.exception(1)) - - future = _utils.submitToQtMainThread(self._taskWithException) - self.assertTrue(future.done()) - with self.assertRaises(RuntimeError): - future.result(1) - self.assertIsInstance(future.exception(1), RuntimeError) - - def _threadedTest(self): - """Function run in a thread for the tests""" - value1, value2 = 0, 1 - future = _utils.submitToQtMainThread(self._task, value1, value2=value2) - - wait([future], 3) - - self.assertTrue(future.done()) - self.assertEqual(future.result(1), (value1, value2)) - self.assertIsNone(future.exception(1)) - - future = _utils.submitToQtMainThread(self._taskWithException) - - wait([future], 3) - - self.assertTrue(future.done()) - with self.assertRaises(RuntimeError): - future.result(1) - self.assertIsInstance(future.exception(1), RuntimeError) - - def testFromPythonThread(self): - """Call submitToQtMainThread from a Python thread""" - thread = threading.Thread(target=self._threadedTest) - thread.start() - for i in range(100): # Loop over for 10 seconds - self.qapp.processEvents() - thread.join(0.1) - if not thread.is_alive(): - break - else: - self.fail(('Thread task still running')) - - def testFromQtThread(self): - """Call submitToQtMainThread from a Qt thread pool""" - class Runner(qt.QRunnable): - def __init__(self, fn): - super(Runner, self).__init__() - self._fn = fn - - def run(self): - self._fn() - - def autoDelete(self): - return True - - threadPool = qt.silxGlobalThreadPool() - runner = Runner(self._threadedTest) - threadPool.start(runner) - for i in range(100): # Loop over for 10 seconds - self.qapp.processEvents() - done = threadPool.waitForDone(100) - if done: - break - else: - self.fail('Thread pool task still running') - - -def suite(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase( - TestQImageConversion)) - test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase( - TestSubmitToQtThread)) - return test_suite - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') |