summaryrefslogtreecommitdiff
path: root/src/silx/utils/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/silx/utils/test')
-rwxr-xr-xsrc/silx/utils/test/__init__.py24
-rw-r--r--src/silx/utils/test/test_array_like.py430
-rw-r--r--src/silx/utils/test/test_debug.py88
-rw-r--r--src/silx/utils/test/test_deprecation.py96
-rw-r--r--src/silx/utils/test/test_enum.py85
-rw-r--r--src/silx/utils/test/test_external_resources.py89
-rw-r--r--src/silx/utils/test/test_launcher.py191
-rw-r--r--src/silx/utils/test/test_launcher_command.py47
-rw-r--r--src/silx/utils/test/test_number.py175
-rw-r--r--src/silx/utils/test/test_proxy.py330
-rw-r--r--src/silx/utils/test/test_retry.py169
-rwxr-xr-xsrc/silx/utils/test/test_testutils.py94
-rw-r--r--src/silx/utils/test/test_weakref.py315
13 files changed, 2133 insertions, 0 deletions
diff --git a/src/silx/utils/test/__init__.py b/src/silx/utils/test/__init__.py
new file mode 100755
index 0000000..14fd940
--- /dev/null
+++ b/src/silx/utils/test/__init__.py
@@ -0,0 +1,24 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016-2021 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.
+#
+# ###########################################################################*/
diff --git a/src/silx/utils/test/test_array_like.py b/src/silx/utils/test/test_array_like.py
new file mode 100644
index 0000000..a0b4b7b
--- /dev/null
+++ b/src/silx/utils/test/test_array_like.py
@@ -0,0 +1,430 @@
+# 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.
+#
+# ###########################################################################*/
+"""Tests for array_like module"""
+
+__authors__ = ["P. Knobel"]
+__license__ = "MIT"
+__date__ = "09/01/2017"
+
+import h5py
+import numpy
+import os
+import tempfile
+import unittest
+
+from ..array_like import DatasetView, ListOfImages
+from ..array_like import get_dtype, get_concatenated_dtype, get_shape,\
+ is_array, is_nested_sequence, is_list_of_arrays
+
+
+class TestTransposedDatasetView(unittest.TestCase):
+
+ def setUp(self):
+ # dataset attributes
+ self.ndim = 3
+ self.original_shape = (5, 10, 20)
+ self.size = 1
+ for dim in self.original_shape:
+ self.size *= dim
+
+ self.volume = numpy.arange(self.size).reshape(self.original_shape)
+
+ self.tempdir = tempfile.mkdtemp()
+ self.h5_fname = os.path.join(self.tempdir, "tempfile.h5")
+ with h5py.File(self.h5_fname, "w") as f:
+ f["volume"] = self.volume
+
+ self.h5f = h5py.File(self.h5_fname, "r")
+
+ self.all_permutations = [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0),
+ (2, 0, 1), (2, 1, 0)]
+
+ def tearDown(self):
+ self.h5f.close()
+ os.unlink(self.h5_fname)
+ os.rmdir(self.tempdir)
+
+ def _testSize(self, obj):
+ """These assertions apply to all following test cases"""
+ self.assertEqual(obj.ndim, self.ndim)
+ self.assertEqual(obj.size, self.size)
+ size_from_shape = 1
+ for dim in obj.shape:
+ size_from_shape *= dim
+ self.assertEqual(size_from_shape, self.size)
+
+ for dim in self.original_shape:
+ self.assertIn(dim, obj.shape)
+
+ def testNoTransposition(self):
+ """no transposition (transposition = (0, 1, 2))"""
+ a = DatasetView(self.h5f["volume"])
+
+ self.assertEqual(a.shape, self.original_shape)
+ self._testSize(a)
+
+ # reversing the dimensions twice results in no change
+ rtrans = list(reversed(range(self.ndim)))
+ self.assertTrue(numpy.array_equal(
+ a,
+ a.transpose(rtrans).transpose(rtrans)))
+
+ for i in range(a.shape[0]):
+ for j in range(a.shape[1]):
+ for k in range(a.shape[2]):
+ self.assertEqual(self.h5f["volume"][i, j, k],
+ a[i, j, k])
+
+ def _testTransposition(self, transposition):
+ """test transposed dataset
+
+ :param tuple transposition: List of dimensions (0... n-1) sorted
+ in the desired order
+ """
+ a = DatasetView(self.h5f["volume"],
+ transposition=transposition)
+ self._testSize(a)
+
+ # sort shape of transposed object, to hopefully find the original shape
+ sorted_shape = tuple(dim_size for (_, dim_size) in
+ sorted(zip(transposition, a.shape)))
+ self.assertEqual(sorted_shape, self.original_shape)
+
+ a_as_array = numpy.array(self.h5f["volume"]).transpose(transposition)
+
+ # test the __array__ method
+ self.assertTrue(numpy.array_equal(
+ numpy.array(a),
+ a_as_array))
+
+ # test slicing
+ for selection in [(2, slice(None), slice(None)),
+ (slice(None), 1, slice(0, 8)),
+ (slice(0, 3), slice(None), 3),
+ (1, 3, slice(None)),
+ (slice(None), 2, 1),
+ (4, slice(1, 9, 2), 2)]:
+ self.assertIsInstance(a[selection], numpy.ndarray)
+ self.assertTrue(numpy.array_equal(
+ a[selection],
+ a_as_array[selection]))
+
+ # test the DatasetView.__getitem__ for single values
+ # (step adjusted to test at least 3 indices in each dimension)
+ for i in range(0, a.shape[0], a.shape[0] // 3):
+ for j in range(0, a.shape[1], a.shape[1] // 3):
+ for k in range(0, a.shape[2], a.shape[2] // 3):
+ sorted_indices = tuple(idx for (_, idx) in
+ sorted(zip(transposition, [i, j, k])))
+ viewed_value = a[i, j, k]
+ corresponding_original_value = self.h5f["volume"][sorted_indices]
+ self.assertEqual(viewed_value,
+ corresponding_original_value)
+
+ # reversing the dimensions twice results in no change
+ rtrans = list(reversed(range(self.ndim)))
+ self.assertTrue(numpy.array_equal(
+ a,
+ a.transpose(rtrans).transpose(rtrans)))
+
+ # test .T property
+ self.assertTrue(numpy.array_equal(
+ a.T,
+ a.transpose(rtrans)))
+
+ def testTransposition012(self):
+ """transposition = (0, 1, 2)
+ (should be the same as testNoTransposition)"""
+ self._testTransposition((0, 1, 2))
+
+ def testTransposition021(self):
+ """transposition = (0, 2, 1)"""
+ self._testTransposition((0, 2, 1))
+
+ def testTransposition102(self):
+ """transposition = (1, 0, 2)"""
+ self._testTransposition((1, 0, 2))
+
+ def testTransposition120(self):
+ """transposition = (1, 2, 0)"""
+ self._testTransposition((1, 2, 0))
+
+ def testTransposition201(self):
+ """transposition = (2, 0, 1)"""
+ self._testTransposition((2, 0, 1))
+
+ def testTransposition210(self):
+ """transposition = (2, 1, 0)"""
+ self._testTransposition((2, 1, 0))
+
+ def testAllDoubleTranspositions(self):
+ for trans1 in self.all_permutations:
+ for trans2 in self.all_permutations:
+ self._testDoubleTransposition(trans1, trans2)
+
+ def _testDoubleTransposition(self, transposition1, transposition2):
+ a = DatasetView(self.h5f["volume"],
+ transposition=transposition1).transpose(transposition2)
+
+ b = self.volume.transpose(transposition1).transpose(transposition2)
+
+ self.assertTrue(numpy.array_equal(a, b),
+ "failed with double transposition %s %s" % (transposition1, transposition2))
+
+ def test1DIndex(self):
+ a = DatasetView(self.h5f["volume"])
+ self.assertTrue(numpy.array_equal(self.volume[1],
+ a[1]))
+
+ b = DatasetView(self.h5f["volume"], transposition=(1, 0, 2))
+ self.assertTrue(numpy.array_equal(self.volume[:, 1, :],
+ b[1]))
+
+
+class TestTransposedListOfImages(unittest.TestCase):
+ def setUp(self):
+ # images attributes
+ self.ndim = 3
+ self.original_shape = (5, 10, 20)
+ self.size = 1
+ for dim in self.original_shape:
+ self.size *= dim
+
+ volume = numpy.arange(self.size).reshape(self.original_shape)
+
+ self.images = []
+ for i in range(self.original_shape[0]):
+ self.images.append(
+ volume[i])
+
+ self.images_as_3D_array = numpy.array(self.images)
+
+ self.all_permutations = [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0),
+ (2, 0, 1), (2, 1, 0)]
+
+ def tearDown(self):
+ pass
+
+ def _testSize(self, obj):
+ """These assertions apply to all following test cases"""
+ self.assertEqual(obj.ndim, self.ndim)
+ self.assertEqual(obj.size, self.size)
+ size_from_shape = 1
+ for dim in obj.shape:
+ size_from_shape *= dim
+ self.assertEqual(size_from_shape, self.size)
+
+ for dim in self.original_shape:
+ self.assertIn(dim, obj.shape)
+
+ def testNoTransposition(self):
+ """no transposition (transposition = (0, 1, 2))"""
+ a = ListOfImages(self.images)
+
+ self.assertEqual(a.shape, self.original_shape)
+ self._testSize(a)
+
+ for i in range(a.shape[0]):
+ for j in range(a.shape[1]):
+ for k in range(a.shape[2]):
+ self.assertEqual(self.images[i][j, k],
+ a[i, j, k])
+
+ # reversing the dimensions twice results in no change
+ rtrans = list(reversed(range(self.ndim)))
+ self.assertTrue(numpy.array_equal(
+ a,
+ a.transpose(rtrans).transpose(rtrans)))
+
+ # test .T property
+ self.assertTrue(numpy.array_equal(
+ a.T,
+ a.transpose(rtrans)))
+
+ def _testTransposition(self, transposition):
+ """test transposed dataset
+
+ :param tuple transposition: List of dimensions (0... n-1) sorted
+ in the desired order
+ """
+ a = ListOfImages(self.images,
+ transposition=transposition)
+ self._testSize(a)
+
+ # sort shape of transposed object, to hopefully find the original shape
+ sorted_shape = tuple(dim_size for (_, dim_size) in
+ sorted(zip(transposition, a.shape)))
+ self.assertEqual(sorted_shape, self.original_shape)
+
+ a_as_array = numpy.array(self.images).transpose(transposition)
+
+ # test the DatasetView.__array__ method
+ self.assertTrue(numpy.array_equal(
+ numpy.array(a),
+ a_as_array))
+
+ # test slicing
+ for selection in [(2, slice(None), slice(None)),
+ (slice(None), 1, slice(0, 8)),
+ (slice(0, 3), slice(None), 3),
+ (1, 3, slice(None)),
+ (slice(None), 2, 1),
+ (4, slice(1, 9, 2), 2)]:
+ self.assertIsInstance(a[selection], numpy.ndarray)
+ self.assertTrue(numpy.array_equal(
+ a[selection],
+ a_as_array[selection]))
+
+ # test the DatasetView.__getitem__ for single values
+ # (step adjusted to test at least 3 indices in each dimension)
+ for i in range(0, a.shape[0], a.shape[0] // 3):
+ for j in range(0, a.shape[1], a.shape[1] // 3):
+ for k in range(0, a.shape[2], a.shape[2] // 3):
+ viewed_value = a[i, j, k]
+ sorted_indices = tuple(idx for (_, idx) in
+ sorted(zip(transposition, [i, j, k])))
+ corresponding_original_value = self.images[sorted_indices[0]][sorted_indices[1:]]
+ self.assertEqual(viewed_value,
+ corresponding_original_value)
+
+ # reversing the dimensions twice results in no change
+ rtrans = list(reversed(range(self.ndim)))
+ self.assertTrue(numpy.array_equal(
+ a,
+ a.transpose(rtrans).transpose(rtrans)))
+
+ # test .T property
+ self.assertTrue(numpy.array_equal(
+ a.T,
+ a.transpose(rtrans)))
+
+ def _testDoubleTransposition(self, transposition1, transposition2):
+ a = ListOfImages(self.images,
+ transposition=transposition1).transpose(transposition2)
+
+ b = self.images_as_3D_array.transpose(transposition1).transpose(transposition2)
+
+ self.assertTrue(numpy.array_equal(a, b),
+ "failed with double transposition %s %s" % (transposition1, transposition2))
+
+ def testTransposition012(self):
+ """transposition = (0, 1, 2)
+ (should be the same as testNoTransposition)"""
+ self._testTransposition((0, 1, 2))
+
+ def testTransposition021(self):
+ """transposition = (0, 2, 1)"""
+ self._testTransposition((0, 2, 1))
+
+ def testTransposition102(self):
+ """transposition = (1, 0, 2)"""
+ self._testTransposition((1, 0, 2))
+
+ def testTransposition120(self):
+ """transposition = (1, 2, 0)"""
+ self._testTransposition((1, 2, 0))
+
+ def testTransposition201(self):
+ """transposition = (2, 0, 1)"""
+ self._testTransposition((2, 0, 1))
+
+ def testTransposition210(self):
+ """transposition = (2, 1, 0)"""
+ self._testTransposition((2, 1, 0))
+
+ def testAllDoubleTranspositions(self):
+ for trans1 in self.all_permutations:
+ for trans2 in self.all_permutations:
+ self._testDoubleTransposition(trans1, trans2)
+
+ def test1DIndex(self):
+ a = ListOfImages(self.images)
+ self.assertTrue(numpy.array_equal(self.images[1],
+ a[1]))
+
+ b = ListOfImages(self.images, transposition=(1, 0, 2))
+ self.assertTrue(numpy.array_equal(self.images_as_3D_array[:, 1, :],
+ b[1]))
+
+
+class TestFunctions(unittest.TestCase):
+ """Test functions to guess the dtype and shape of an array_like
+ object"""
+ def testListOfLists(self):
+ l = [[0, 1, 2], [2, 3, 4]]
+ self.assertEqual(get_dtype(l),
+ numpy.dtype(int))
+ self.assertEqual(get_shape(l),
+ (2, 3))
+ self.assertTrue(is_nested_sequence(l))
+ self.assertFalse(is_array(l))
+ self.assertFalse(is_list_of_arrays(l))
+
+ l = [[0., 1.], [2., 3.]]
+ self.assertEqual(get_dtype(l),
+ numpy.dtype(float))
+ self.assertEqual(get_shape(l),
+ (2, 2))
+ self.assertTrue(is_nested_sequence(l))
+ self.assertFalse(is_array(l))
+ self.assertFalse(is_list_of_arrays(l))
+
+ # concatenated dtype of int and float
+ l = [numpy.array([[0, 1, 2], [2, 3, 4]]),
+ numpy.array([[0., 1., 2.], [2., 3., 4.]])]
+
+ self.assertEqual(get_concatenated_dtype(l),
+ numpy.array(l).dtype)
+ self.assertEqual(get_shape(l),
+ (2, 2, 3))
+ self.assertFalse(is_nested_sequence(l))
+ self.assertFalse(is_array(l))
+ self.assertTrue(is_list_of_arrays(l))
+
+ def testNumpyArray(self):
+ a = numpy.array([[0, 1], [2, 3]])
+ self.assertEqual(get_dtype(a),
+ a.dtype)
+ self.assertFalse(is_nested_sequence(a))
+ self.assertTrue(is_array(a))
+ self.assertFalse(is_list_of_arrays(a))
+
+ def testH5pyDataset(self):
+ a = numpy.array([[0, 1], [2, 3]])
+
+ tempdir = tempfile.mkdtemp()
+ h5_fname = os.path.join(tempdir, "tempfile.h5")
+ with h5py.File(h5_fname, "w") as h5f:
+ h5f["dataset"] = a
+ d = h5f["dataset"]
+
+ self.assertEqual(get_dtype(d),
+ numpy.dtype(int))
+ self.assertFalse(is_nested_sequence(d))
+ self.assertTrue(is_array(d))
+ self.assertFalse(is_list_of_arrays(d))
+
+ os.unlink(h5_fname)
+ os.rmdir(tempdir)
diff --git a/src/silx/utils/test/test_debug.py b/src/silx/utils/test/test_debug.py
new file mode 100644
index 0000000..09f4b01
--- /dev/null
+++ b/src/silx/utils/test/test_debug.py
@@ -0,0 +1,88 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016-2021 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.
+#
+# ###########################################################################*/
+"""Tests for debug module"""
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "27/02/2018"
+
+
+import unittest
+from silx.utils import debug
+from silx.utils import testutils
+
+
+@debug.log_all_methods
+class _Foobar(object):
+
+ def a(self):
+ return None
+
+ def b(self):
+ return self.a()
+
+ def random_args(self, *args, **kwargs):
+ return args, kwargs
+
+ def named_args(self, a, b):
+ return a + 1, b + 1
+
+
+class TestDebug(unittest.TestCase):
+ """Tests for debug module."""
+
+ def logB(self):
+ """
+ Can be used to check the log output using:
+ `./run_tests.py silx.utils.test.test_debug.TestDebug.logB -v`
+ """
+ print()
+ test = _Foobar()
+ test.b()
+
+ @testutils.validate_logging(debug.debug_logger.name, warning=2)
+ def testMethod(self):
+ test = _Foobar()
+ test.a()
+
+ @testutils.validate_logging(debug.debug_logger.name, warning=4)
+ def testInterleavedMethod(self):
+ test = _Foobar()
+ test.b()
+
+ @testutils.validate_logging(debug.debug_logger.name, warning=2)
+ def testNamedArgument(self):
+ # Arguments arre still provided to the patched method
+ test = _Foobar()
+ result = test.named_args(10, 11)
+ self.assertEqual(result, (11, 12))
+
+ @testutils.validate_logging(debug.debug_logger.name, warning=2)
+ def testRandomArguments(self):
+ # Arguments arre still provided to the patched method
+ test = _Foobar()
+ result = test.random_args("foo", 50, a=10, b=100)
+ self.assertEqual(result[0], ("foo", 50))
+ self.assertEqual(result[1], {"a": 10, "b": 100})
diff --git a/src/silx/utils/test/test_deprecation.py b/src/silx/utils/test/test_deprecation.py
new file mode 100644
index 0000000..d52cb26
--- /dev/null
+++ b/src/silx/utils/test/test_deprecation.py
@@ -0,0 +1,96 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016-2021 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.
+#
+# ###########################################################################*/
+"""Tests for html module"""
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "17/01/2018"
+
+
+import unittest
+from .. import deprecation
+from silx.utils import testutils
+
+
+class TestDeprecation(unittest.TestCase):
+ """Tests for deprecation module."""
+
+ @deprecation.deprecated
+ def deprecatedWithoutParam(self):
+ pass
+
+ @deprecation.deprecated(reason="r", replacement="r", since_version="v")
+ def deprecatedWithParams(self):
+ pass
+
+ @deprecation.deprecated(reason="r", replacement="r", since_version="v", only_once=True)
+ def deprecatedOnlyOnce(self):
+ pass
+
+ @deprecation.deprecated(reason="r", replacement="r", since_version="v", only_once=False)
+ def deprecatedEveryTime(self):
+ pass
+
+ @testutils.validate_logging(deprecation.depreclog.name, warning=1)
+ def testAnnotationWithoutParam(self):
+ self.deprecatedWithoutParam()
+
+ @testutils.validate_logging(deprecation.depreclog.name, warning=1)
+ def testAnnotationWithParams(self):
+ self.deprecatedWithParams()
+
+ @testutils.validate_logging(deprecation.depreclog.name, warning=3)
+ def testLoggedEveryTime(self):
+ """Logged everytime cause it is 3 different locations"""
+ self.deprecatedOnlyOnce()
+ self.deprecatedOnlyOnce()
+ self.deprecatedOnlyOnce()
+
+ @testutils.validate_logging(deprecation.depreclog.name, warning=1)
+ def testLoggedSingleTime(self):
+ def log():
+ self.deprecatedOnlyOnce()
+ log()
+ log()
+ log()
+
+ @testutils.validate_logging(deprecation.depreclog.name, warning=3)
+ def testLoggedEveryTime2(self):
+ self.deprecatedEveryTime()
+ self.deprecatedEveryTime()
+ self.deprecatedEveryTime()
+
+ @testutils.validate_logging(deprecation.depreclog.name, warning=1)
+ def testWarning(self):
+ deprecation.deprecated_warning(type_="t", name="n")
+
+ def testBacktrace(self):
+ loggingValidator = testutils.LoggingValidator(deprecation.depreclog.name)
+ with loggingValidator:
+ self.deprecatedEveryTime()
+ message = loggingValidator.records[0].getMessage()
+ filename = __file__.replace(".pyc", ".py")
+ self.assertTrue(filename in message)
+ self.assertTrue("testBacktrace" in message)
diff --git a/src/silx/utils/test/test_enum.py b/src/silx/utils/test/test_enum.py
new file mode 100644
index 0000000..808304a
--- /dev/null
+++ b/src/silx/utils/test/test_enum.py
@@ -0,0 +1,85 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2019 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.
+#
+# ###########################################################################*/
+"""Tests of Enum class with extra class methods"""
+
+from __future__ import absolute_import
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "29/04/2019"
+
+
+import sys
+import unittest
+
+import enum
+from silx.utils.enum import Enum
+
+
+class TestEnum(unittest.TestCase):
+ """Tests for enum module."""
+
+ def test(self):
+ """Test with Enum"""
+ class Success(Enum):
+ A = 1
+ B = 'B'
+ self._check_enum_content(Success)
+
+ @unittest.skipIf(sys.version_info.major <= 2, 'Python3 only')
+ def test(self):
+ """Test Enum with member redefinition"""
+ with self.assertRaises(TypeError):
+ class Failure(Enum):
+ A = 1
+ A = 'B'
+
+ def test_unique(self):
+ """Test with enum.unique"""
+ with self.assertRaises(ValueError):
+ @enum.unique
+ class Failure(Enum):
+ A = 1
+ B = 1
+
+ @enum.unique
+ class Success(Enum):
+ A = 1
+ B = 'B'
+ self._check_enum_content(Success)
+
+ def _check_enum_content(self, enum_):
+ """Check that the content of an enum is: <A: 1, B: 2>.
+
+ :param Enum enum_:
+ """
+ self.assertEqual(enum_.members(), (enum_.A, enum_.B))
+ self.assertEqual(enum_.names(), ('A', 'B'))
+ self.assertEqual(enum_.values(), (1, 'B'))
+
+ self.assertEqual(enum_.from_value(1), enum_.A)
+ self.assertEqual(enum_.from_value('B'), enum_.B)
+ with self.assertRaises(ValueError):
+ enum_.from_value(3)
diff --git a/src/silx/utils/test/test_external_resources.py b/src/silx/utils/test/test_external_resources.py
new file mode 100644
index 0000000..1fedda3
--- /dev/null
+++ b/src/silx/utils/test/test_external_resources.py
@@ -0,0 +1,89 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016-2021 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__ = "08/03/2019"
+
+
+import os
+import unittest
+import shutil
+import socket
+import urllib.request
+import urllib.error
+
+from silx.utils.ExternalResources import ExternalResources
+
+
+def isSilxWebsiteAvailable():
+ try:
+ urllib.request.urlopen('http://www.silx.org', timeout=1)
+ return True
+ except urllib.error.URLError:
+ return False
+ except socket.timeout:
+ # This exception is still received in Python 2.7
+ return False
+
+
+class TestExternalResources(unittest.TestCase):
+ """This is a test for the ExternalResources"""
+
+ @classmethod
+ def setUpClass(cls):
+ if not isSilxWebsiteAvailable():
+ raise unittest.SkipTest("Network or silx website not available")
+
+ def setUp(self):
+ self.resources = ExternalResources("toto", "http://www.silx.org/pub/silx/")
+
+ def tearDown(self):
+ if self.resources.data_home:
+ shutil.rmtree(self.resources.data_home)
+ self.resources = None
+
+ def test_download(self):
+ "test the download from silx.org"
+ f = self.resources.getfile("lena.png")
+ self.assertTrue(os.path.exists(f))
+ di = self.resources.getdir("source.tar.gz")
+ for fi in di:
+ self.assertTrue(os.path.exists(fi))
+
+ def test_download_all(self):
+ "test the download of all files from silx.org"
+ filename = self.resources.getfile("lena.png")
+ directory = "source.tar.gz"
+ filelist = self.resources.getdir(directory)
+ # download file and remove it to create a json mapping file
+ os.remove(filename)
+ directory_path = os.path.commonprefix(filelist)
+ # Make sure we will rmtree a dangerous path like "/"
+ self.assertIn(self.resources.data_home, directory_path)
+ shutil.rmtree(directory_path)
+ filelist = self.resources.download_all()
+ self.assertGreater(len(filelist), 1, "At least 2 items were downloaded")
diff --git a/src/silx/utils/test/test_launcher.py b/src/silx/utils/test/test_launcher.py
new file mode 100644
index 0000000..9e9024c
--- /dev/null
+++ b/src/silx/utils/test/test_launcher.py
@@ -0,0 +1,191 @@
+# 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.
+#
+# ###########################################################################*/
+"""Tests for html module"""
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "17/01/2018"
+
+
+import sys
+import unittest
+from silx.utils.testutils import ParametricTestCase
+from .. import launcher
+
+
+class CallbackMock():
+
+ def __init__(self, result=None):
+ self._execute_count = 0
+ self._execute_argv = None
+ self._result = result
+
+ def execute(self, argv):
+ self._execute_count = self._execute_count + 1
+ self._execute_argv = argv
+ return self._result
+
+ def __call__(self, argv):
+ return self.execute(argv)
+
+
+class TestLauncherCommand(unittest.TestCase):
+ """Tests for launcher class."""
+
+ def testEnv(self):
+ command = launcher.LauncherCommand("foo")
+ old = sys.argv
+ params = ["foo", "bar"]
+ with command.get_env(params):
+ self.assertEqual(params, sys.argv)
+ self.assertEqual(sys.argv, old)
+
+ def testEnvWhileException(self):
+ command = launcher.LauncherCommand("foo")
+ old = sys.argv
+ params = ["foo", "bar"]
+ try:
+ with command.get_env(params):
+ raise RuntimeError()
+ except RuntimeError:
+ pass
+ self.assertEqual(sys.argv, old)
+
+ def testExecute(self):
+ params = ["foo", "bar"]
+ callback = CallbackMock(result=42)
+ command = launcher.LauncherCommand("foo", function=callback)
+ status = command.execute(params)
+ self.assertEqual(callback._execute_count, 1)
+ self.assertEqual(callback._execute_argv, params)
+ self.assertEqual(status, 42)
+
+
+class TestModuleCommand(ParametricTestCase):
+
+ def setUp(self):
+ module_name = "silx.utils.test.test_launcher_command"
+ command = launcher.LauncherCommand("foo", module_name=module_name)
+ self.command = command
+
+ def testHelp(self):
+ status = self.command.execute(["--help"])
+ self.assertEqual(status, 0)
+
+ def testException(self):
+ try:
+ self.command.execute(["exception"])
+ self.fail()
+ except RuntimeError:
+ pass
+
+ def testCall(self):
+ status = self.command.execute([])
+ self.assertEqual(status, 0)
+
+ def testError(self):
+ status = self.command.execute(["error"])
+ self.assertEqual(status, -1)
+
+
+class TestLauncher(ParametricTestCase):
+ """Tests for launcher class."""
+
+ def testCallCommand(self):
+ callback = CallbackMock(result=42)
+ runner = launcher.Launcher(prog="prog")
+ command = launcher.LauncherCommand("foo", function=callback)
+ runner.add_command(command=command)
+ status = runner.execute(["prog", "foo", "param1", "param2"])
+ self.assertEqual(status, 42)
+ self.assertEqual(callback._execute_argv, ["prog foo", "param1", "param2"])
+ self.assertEqual(callback._execute_count, 1)
+
+ def testAddCommand(self):
+ runner = launcher.Launcher(prog="prog")
+ module_name = "silx.utils.test.test_launcher_command"
+ runner.add_command("foo", module_name=module_name)
+ status = runner.execute(["prog", "foo"])
+ self.assertEqual(status, 0)
+
+ def testCallHelpOnCommand(self):
+ callback = CallbackMock(result=42)
+ runner = launcher.Launcher(prog="prog")
+ command = launcher.LauncherCommand("foo", function=callback)
+ runner.add_command(command=command)
+ status = runner.execute(["prog", "--help", "foo"])
+ self.assertEqual(status, 42)
+ self.assertEqual(callback._execute_argv, ["prog foo", "--help"])
+ self.assertEqual(callback._execute_count, 1)
+
+ def testCallHelpOnCommand2(self):
+ callback = CallbackMock(result=42)
+ runner = launcher.Launcher(prog="prog")
+ command = launcher.LauncherCommand("foo", function=callback)
+ runner.add_command(command=command)
+ status = runner.execute(["prog", "help", "foo"])
+ self.assertEqual(status, 42)
+ self.assertEqual(callback._execute_argv, ["prog foo", "--help"])
+ self.assertEqual(callback._execute_count, 1)
+
+ def testCallHelpOnUnknownCommand(self):
+ callback = CallbackMock(result=42)
+ runner = launcher.Launcher(prog="prog")
+ command = launcher.LauncherCommand("foo", function=callback)
+ runner.add_command(command=command)
+ status = runner.execute(["prog", "help", "foo2"])
+ self.assertEqual(status, -1)
+
+ def testNotAvailableCommand(self):
+ callback = CallbackMock(result=42)
+ runner = launcher.Launcher(prog="prog")
+ command = launcher.LauncherCommand("foo", function=callback)
+ runner.add_command(command=command)
+ status = runner.execute(["prog", "foo2", "param1", "param2"])
+ self.assertEqual(status, -1)
+ self.assertEqual(callback._execute_count, 0)
+
+ def testCallHelp(self):
+ callback = CallbackMock(result=42)
+ runner = launcher.Launcher(prog="prog")
+ command = launcher.LauncherCommand("foo", function=callback)
+ runner.add_command(command=command)
+ status = runner.execute(["prog", "help"])
+ self.assertEqual(status, 0)
+ self.assertEqual(callback._execute_count, 0)
+
+ def testCommonCommands(self):
+ runner = launcher.Launcher()
+ tests = [
+ ["prog"],
+ ["prog", "--help"],
+ ["prog", "--version"],
+ ["prog", "help", "--help"],
+ ["prog", "help", "help"],
+ ]
+ for arguments in tests:
+ with self.subTest(args=tests):
+ status = runner.execute(arguments)
+ self.assertEqual(status, 0)
diff --git a/src/silx/utils/test/test_launcher_command.py b/src/silx/utils/test/test_launcher_command.py
new file mode 100644
index 0000000..ccf4601
--- /dev/null
+++ b/src/silx/utils/test/test_launcher_command.py
@@ -0,0 +1,47 @@
+# 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.
+#
+# ###########################################################################*/
+"""Tests for html module"""
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "03/04/2017"
+
+
+import sys
+
+
+def main(argv):
+
+ if "--help" in argv:
+ # Common behaviour of ArgumentParser
+ sys.exit(0)
+
+ if "exception" in argv:
+ raise RuntimeError("Simulated exception")
+
+ if "error" in argv:
+ return -1
+
+ return 0
diff --git a/src/silx/utils/test/test_number.py b/src/silx/utils/test/test_number.py
new file mode 100644
index 0000000..3eb8e91
--- /dev/null
+++ b/src/silx/utils/test/test_number.py
@@ -0,0 +1,175 @@
+# coding: utf-8
+# /*##########################################################################
+# Copyright (C) 2016-2021 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.
+#
+# ############################################################################*/
+"""Tests for silx.uitls.number module"""
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "05/06/2018"
+
+import logging
+import numpy
+import unittest
+import pkg_resources
+from silx.utils import number
+from silx.utils import testutils
+
+_logger = logging.getLogger(__name__)
+
+
+class TestConversionTypes(testutils.ParametricTestCase):
+
+ def testEmptyFail(self):
+ self.assertRaises(ValueError, number.min_numerical_convertible_type, "")
+
+ def testStringFail(self):
+ self.assertRaises(ValueError, number.min_numerical_convertible_type, "a")
+
+ def testInteger(self):
+ dtype = number.min_numerical_convertible_type("1456")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.unsignedinteger))
+
+ def testTrailledInteger(self):
+ dtype = number.min_numerical_convertible_type(" \t\n\r1456\t\n\r")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.unsignedinteger))
+
+ def testPositiveInteger(self):
+ dtype = number.min_numerical_convertible_type("+1456")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.unsignedinteger))
+
+ def testNegativeInteger(self):
+ dtype = number.min_numerical_convertible_type("-1456")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.signedinteger))
+
+ def testIntegerExponential(self):
+ dtype = number.min_numerical_convertible_type("14e10")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testIntegerPositiveExponential(self):
+ dtype = number.min_numerical_convertible_type("14e+10")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testIntegerNegativeExponential(self):
+ dtype = number.min_numerical_convertible_type("14e-10")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testNumberDecimal(self):
+ dtype = number.min_numerical_convertible_type("14.5")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testPositiveNumberDecimal(self):
+ dtype = number.min_numerical_convertible_type("+14.5")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testNegativeNumberDecimal(self):
+ dtype = number.min_numerical_convertible_type("-14.5")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testDecimal(self):
+ dtype = number.min_numerical_convertible_type(".50")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testPositiveDecimal(self):
+ dtype = number.min_numerical_convertible_type("+.5")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testNegativeDecimal(self):
+ dtype = number.min_numerical_convertible_type("-.5")
+ self.assertTrue(numpy.issubdtype(dtype, numpy.floating))
+
+ def testMantissa16(self):
+ dtype = number.min_numerical_convertible_type("1.50")
+ self.assertEqual(dtype, numpy.float16)
+
+ def testFloat32(self):
+ dtype = number.min_numerical_convertible_type("-23.172")
+ self.assertEqual(dtype, numpy.float32)
+
+ def testMantissa32(self):
+ dtype = number.min_numerical_convertible_type("1400.50")
+ self.assertEqual(dtype, numpy.float32)
+
+ def testMantissa64(self):
+ dtype = number.min_numerical_convertible_type("10000.000010")
+ self.assertEqual(dtype, numpy.float64)
+
+ def testMantissa80(self):
+ self.skipIfFloat80NotSupported()
+ dtype = number.min_numerical_convertible_type("1000000000.00001013")
+
+ if pkg_resources.parse_version(numpy.version.version) <= pkg_resources.parse_version("1.10.4"):
+ # numpy 1.8.2 -> Debian 8
+ # Checking a float128 precision with numpy 1.8.2 using abs(diff) is not working.
+ # It looks like the difference is done using float64 (diff == 0.0)
+ expected = (numpy.longdouble, numpy.float64)
+ else:
+ expected = (numpy.longdouble, )
+ self.assertIn(dtype, expected)
+
+ def testExponent32(self):
+ dtype = number.min_numerical_convertible_type("14.0e30")
+ self.assertEqual(dtype, numpy.float32)
+
+ def testExponent64(self):
+ dtype = number.min_numerical_convertible_type("14.0e300")
+ self.assertEqual(dtype, numpy.float64)
+
+ def testExponent80(self):
+ self.skipIfFloat80NotSupported()
+ dtype = number.min_numerical_convertible_type("14.0e3000")
+ self.assertEqual(dtype, numpy.longdouble)
+
+ def testFloat32ToString(self):
+ value = str(numpy.float32(numpy.pi))
+ dtype = number.min_numerical_convertible_type(value)
+ self.assertIn(dtype, (numpy.float32, numpy.float64))
+
+ def skipIfFloat80NotSupported(self):
+ if number.is_longdouble_64bits():
+ self.skipTest("float-80bits not supported")
+
+ def testLosePrecisionUsingFloat80(self):
+ self.skipIfFloat80NotSupported()
+ if pkg_resources.parse_version(numpy.version.version) <= pkg_resources.parse_version("1.10.4"):
+ self.skipTest("numpy > 1.10.4 expected")
+ # value does not fit even in a 128 bits mantissa
+ value = "1.0340282366920938463463374607431768211456"
+ func = testutils.validate_logging(number._logger.name, warning=1)
+ func = func(number.min_numerical_convertible_type)
+ dtype = func(value)
+ self.assertIn(dtype, (numpy.longdouble, ))
+
+ def testMillisecondEpochTime(self):
+ datetimes = ['1465803236.495412',
+ '1465803236.999362',
+ '1465803237.504311',
+ '1465803238.009261',
+ '1465803238.512211',
+ '1465803239.016160',
+ '1465803239.520110',
+ '1465803240.026059',
+ '1465803240.529009']
+ for datetime in datetimes:
+ with self.subTest(datetime=datetime):
+ dtype = number.min_numerical_convertible_type(datetime)
+ self.assertEqual(dtype, numpy.float64)
diff --git a/src/silx/utils/test/test_proxy.py b/src/silx/utils/test/test_proxy.py
new file mode 100644
index 0000000..e165267
--- /dev/null
+++ b/src/silx/utils/test/test_proxy.py
@@ -0,0 +1,330 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016-2019 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.
+#
+# ###########################################################################*/
+"""Tests for weakref module"""
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "02/10/2017"
+
+
+import unittest
+import pickle
+import numpy
+from silx.utils.proxy import Proxy, docstring
+
+
+class Thing(object):
+
+ def __init__(self, value):
+ self.value = value
+
+ def __getitem__(self, selection):
+ return selection + 1
+
+ def method(self, value):
+ return value + 2
+
+
+class InheritedProxy(Proxy):
+ """Inheriting the proxy allow to specialisze methods"""
+
+ def __init__(self, obj, value):
+ Proxy.__init__(self, obj)
+ self.value = value + 2
+
+ def __getitem__(self, selection):
+ return selection + 3
+
+ def method(self, value):
+ return value + 4
+
+
+class TestProxy(unittest.TestCase):
+ """Test that the proxy behave as expected"""
+
+ def text_init(self):
+ obj = Thing(10)
+ p = Proxy(obj)
+ self.assertTrue(isinstance(p, Thing))
+ self.assertTrue(isinstance(p, Proxy))
+
+ # methods and properties
+
+ def test_has_special_method(self):
+ obj = Thing(10)
+ p = Proxy(obj)
+ self.assertTrue(hasattr(p, "__getitem__"))
+
+ def test_missing_special_method(self):
+ obj = Thing(10)
+ p = Proxy(obj)
+ self.assertFalse(hasattr(p, "__and__"))
+
+ def test_method(self):
+ obj = Thing(10)
+ p = Proxy(obj)
+ self.assertEqual(p.method(10), obj.method(10))
+
+ def test_property(self):
+ obj = Thing(10)
+ p = Proxy(obj)
+ self.assertEqual(p.value, obj.value)
+
+ # special functions
+
+ def test_getitem(self):
+ obj = Thing(10)
+ p = Proxy(obj)
+ self.assertEqual(p[10], obj[10])
+
+ def test_setitem(self):
+ obj = numpy.array([10, 20, 30])
+ p = Proxy(obj)
+ p[0] = 20
+ self.assertEqual(obj[0], 20)
+
+ def test_slice(self):
+ obj = numpy.arange(20)
+ p = Proxy(obj)
+ expected = obj[0:10:2]
+ result = p[0:10:2]
+ self.assertEqual(list(result), list(expected))
+
+ # binary comparator methods
+
+ def test_lt(self):
+ obj = numpy.array([20])
+ p = Proxy(obj)
+ expected = obj < obj
+ result = p < p
+ self.assertEqual(result, expected)
+
+ # binary numeric methods
+
+ def test_add(self):
+ obj = numpy.array([20])
+ proxy = Proxy(obj)
+ expected = obj + obj
+ result = proxy + proxy
+ self.assertEqual(result, expected)
+
+ def test_iadd(self):
+ expected = numpy.array([20])
+ expected += 10
+ obj = numpy.array([20])
+ result = Proxy(obj)
+ result += 10
+ self.assertEqual(result, expected)
+
+ def test_radd(self):
+ obj = numpy.array([20])
+ p = Proxy(obj)
+ expected = 10 + obj
+ result = 10 + p
+ self.assertEqual(result, expected)
+
+ # binary logical methods
+
+ def test_and(self):
+ obj = numpy.array([20])
+ p = Proxy(obj)
+ expected = obj & obj
+ result = p & p
+ self.assertEqual(result, expected)
+
+ def test_iand(self):
+ expected = numpy.array([20])
+ expected &= 10
+ obj = numpy.array([20])
+ result = Proxy(obj)
+ result &= 10
+ self.assertEqual(result, expected)
+
+ def test_rand(self):
+ obj = numpy.array([20])
+ p = Proxy(obj)
+ expected = 10 & obj
+ result = 10 & p
+ self.assertEqual(result, expected)
+
+ # unary methods
+
+ def test_neg(self):
+ obj = numpy.array([20])
+ p = Proxy(obj)
+ expected = -obj
+ result = -p
+ self.assertEqual(result, expected)
+
+ def test_round(self):
+ obj = 20.5
+ p = Proxy(obj)
+ expected = round(obj)
+ result = round(p)
+ self.assertEqual(result, expected)
+
+ # cast
+
+ def test_bool(self):
+ obj = True
+ p = Proxy(obj)
+ if p:
+ pass
+ else:
+ self.fail()
+
+ def test_str(self):
+ obj = Thing(10)
+ p = Proxy(obj)
+ expected = str(obj)
+ result = str(p)
+ self.assertEqual(result, expected)
+
+ def test_repr(self):
+ obj = Thing(10)
+ p = Proxy(obj)
+ expected = repr(obj)
+ result = repr(p)
+ self.assertEqual(result, expected)
+
+ def test_text_bool(self):
+ obj = ""
+ p = Proxy(obj)
+ if p:
+ self.fail()
+ else:
+ pass
+
+ def test_text_str(self):
+ obj = "a"
+ p = Proxy(obj)
+ expected = str(obj)
+ result = str(p)
+ self.assertEqual(result, expected)
+
+ def test_text_repr(self):
+ obj = "a"
+ p = Proxy(obj)
+ expected = repr(obj)
+ result = repr(p)
+ self.assertEqual(result, expected)
+
+ def test_hash(self):
+ obj = [0, 1, 2]
+ p = Proxy(obj)
+ with self.assertRaises(TypeError):
+ hash(p)
+ obj = (0, 1, 2)
+ p = Proxy(obj)
+ hash(p)
+
+
+class TestInheritedProxy(unittest.TestCase):
+ """Test that inheriting the Proxy class behave as expected"""
+
+ # methods and properties
+
+ def test_method(self):
+ obj = Thing(10)
+ p = InheritedProxy(obj, 11)
+ self.assertEqual(p.method(10), 11 + 3)
+
+ def test_property(self):
+ obj = Thing(10)
+ p = InheritedProxy(obj, 11)
+ self.assertEqual(p.value, 11 + 2)
+
+ # special functions
+
+ def test_getitem(self):
+ obj = Thing(10)
+ p = InheritedProxy(obj, 11)
+ self.assertEqual(p[12], 12 + 3)
+
+
+class TestPickle(unittest.TestCase):
+
+ def test_dumps(self):
+ obj = Thing(10)
+ p = Proxy(obj)
+ expected = pickle.dumps(obj)
+ result = pickle.dumps(p)
+ self.assertEqual(result, expected)
+
+ def test_loads(self):
+ obj = Thing(10)
+ p = Proxy(obj)
+ obj2 = pickle.loads(pickle.dumps(p))
+ self.assertTrue(isinstance(obj2, Thing))
+ self.assertFalse(isinstance(obj2, Proxy))
+ self.assertEqual(obj.value, obj2.value)
+
+
+class TestDocstring(unittest.TestCase):
+ """Test docstring decorator"""
+
+ class Base(object):
+ def method(self):
+ """Docstring"""
+ pass
+
+ def test_inheritance(self):
+ class Derived(TestDocstring.Base):
+ @docstring(TestDocstring.Base)
+ def method(self):
+ pass
+
+ self.assertEqual(Derived.method.__doc__,
+ TestDocstring.Base.method.__doc__)
+
+ def test_composition(self):
+ class Composed(object):
+ def __init__(self):
+ self._base = TestDocstring.Base()
+
+ @docstring(TestDocstring.Base)
+ def method(self):
+ return self._base.method()
+
+ @docstring(TestDocstring.Base.method)
+ def renamed(self):
+ return self._base.method()
+
+ self.assertEqual(Composed.method.__doc__,
+ TestDocstring.Base.method.__doc__)
+
+ self.assertEqual(Composed.renamed.__doc__,
+ TestDocstring.Base.method.__doc__)
+
+ def test_function(self):
+ def f():
+ """Docstring"""
+ pass
+
+ @docstring(f)
+ def g():
+ pass
+
+ self.assertEqual(f.__doc__, g.__doc__)
diff --git a/src/silx/utils/test/test_retry.py b/src/silx/utils/test/test_retry.py
new file mode 100644
index 0000000..39bfdcf
--- /dev/null
+++ b/src/silx/utils/test/test_retry.py
@@ -0,0 +1,169 @@
+# 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.
+#
+# ############################################################################*/
+"""Tests for retry utilities"""
+
+__authors__ = ["W. de Nolf"]
+__license__ = "MIT"
+__date__ = "05/02/2020"
+
+
+import unittest
+import os
+import sys
+import time
+import tempfile
+
+from .. import retry
+
+
+def _cause_segfault():
+ import ctypes
+
+ i = ctypes.c_char(b"a")
+ j = ctypes.pointer(i)
+ c = 0
+ while True:
+ j[c] = b"a"
+ c += 1
+
+
+def _submain(filename, kwcheck=None, ncausefailure=0, faildelay=0):
+ assert filename
+ assert kwcheck
+ sys.stderr = open(os.devnull, "w")
+
+ with open(filename, mode="r") as f:
+ failcounter = int(f.readline().strip())
+
+ if failcounter < ncausefailure:
+ time.sleep(faildelay)
+ failcounter += 1
+ with open(filename, mode="w") as f:
+ f.write(str(failcounter))
+ if failcounter % 2:
+ raise retry.RetryError
+ else:
+ _cause_segfault()
+ return True
+
+
+_wsubmain = retry.retry_in_subprocess()(_submain)
+
+
+class TestRetry(unittest.TestCase):
+ def setUp(self):
+ self.test_dir = tempfile.mkdtemp()
+ self.ctr_file = os.path.join(self.test_dir, "failcounter.txt")
+
+ def tearDown(self):
+ if os.path.exists(self.ctr_file):
+ os.unlink(self.ctr_file)
+ os.rmdir(self.test_dir)
+
+ def test_retry(self):
+ ncausefailure = 3
+ faildelay = 0.1
+ sufficient_timeout = ncausefailure * (faildelay + 10)
+ insufficient_timeout = ncausefailure * faildelay * 0.5
+
+ @retry.retry()
+ def method(check, kwcheck=None):
+ assert check
+ assert kwcheck
+ nonlocal failcounter
+ if failcounter < ncausefailure:
+ time.sleep(faildelay)
+ failcounter += 1
+ raise retry.RetryError
+ return True
+
+ failcounter = 0
+ kw = {
+ "kwcheck": True,
+ "retry_timeout": sufficient_timeout,
+ }
+ self.assertTrue(method(True, **kw))
+
+ failcounter = 0
+ kw = {
+ "kwcheck": True,
+ "retry_timeout": insufficient_timeout,
+ }
+ with self.assertRaises(retry.RetryTimeoutError):
+ method(True, **kw)
+
+ def test_retry_contextmanager(self):
+ ncausefailure = 3
+ faildelay = 0.1
+ sufficient_timeout = ncausefailure * (faildelay + 10)
+ insufficient_timeout = ncausefailure * faildelay * 0.5
+
+ @retry.retry_contextmanager()
+ def context(check, kwcheck=None):
+ assert check
+ assert kwcheck
+ nonlocal failcounter
+ if failcounter < ncausefailure:
+ time.sleep(faildelay)
+ failcounter += 1
+ raise retry.RetryError
+ yield True
+
+ failcounter = 0
+ kw = {"kwcheck": True, "retry_timeout": sufficient_timeout}
+ with context(True, **kw) as result:
+ self.assertTrue(result)
+
+ failcounter = 0
+ kw = {"kwcheck": True, "retry_timeout": insufficient_timeout}
+ with self.assertRaises(retry.RetryTimeoutError):
+ with context(True, **kw) as result:
+ pass
+
+ def test_retry_in_subprocess(self):
+ ncausefailure = 3
+ faildelay = 0.1
+ sufficient_timeout = ncausefailure * (faildelay + 10)
+ insufficient_timeout = ncausefailure * faildelay * 0.5
+
+ kw = {
+ "ncausefailure": ncausefailure,
+ "faildelay": faildelay,
+ "kwcheck": True,
+ "retry_timeout": sufficient_timeout,
+ }
+ with open(self.ctr_file, mode="w") as f:
+ f.write("0")
+ self.assertTrue(_wsubmain(self.ctr_file, **kw))
+
+ kw = {
+ "ncausefailure": ncausefailure,
+ "faildelay": faildelay,
+ "kwcheck": True,
+ "retry_timeout": insufficient_timeout,
+ }
+ with open(self.ctr_file, mode="w") as f:
+ f.write("0")
+ with self.assertRaises(retry.RetryTimeoutError):
+ _wsubmain(self.ctr_file, **kw)
diff --git a/src/silx/utils/test/test_testutils.py b/src/silx/utils/test/test_testutils.py
new file mode 100755
index 0000000..4f07c4e
--- /dev/null
+++ b/src/silx/utils/test/test_testutils.py
@@ -0,0 +1,94 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016-2021 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.
+#
+# ###########################################################################*/
+"""Tests for testutils module"""
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "18/11/2019"
+
+
+import unittest
+import logging
+from .. import testutils
+
+
+class TestLoggingValidator(unittest.TestCase):
+ """Tests for LoggingValidator"""
+
+ def testRight(self):
+ logger = logging.getLogger(__name__ + "testRight")
+ listener = testutils.LoggingValidator(logger, error=1)
+ with listener:
+ logger.error("expected")
+ logger.info("ignored")
+
+ def testCustomLevel(self):
+ logger = logging.getLogger(__name__ + "testCustomLevel")
+ listener = testutils.LoggingValidator(logger, error=1)
+ with listener:
+ logger.error("expected")
+ logger.log(666, "custom level have to be ignored")
+
+ def testWrong(self):
+ logger = logging.getLogger(__name__ + "testWrong")
+ listener = testutils.LoggingValidator(logger, error=1)
+ with self.assertRaises(RuntimeError):
+ with listener:
+ logger.error("expected")
+ logger.error("not expected")
+
+ def testManyErrors(self):
+ logger = logging.getLogger(__name__ + "testManyErrors")
+ listener = testutils.LoggingValidator(logger, error=1, warning=2)
+ with self.assertRaises(RuntimeError):
+ with listener:
+ pass
+
+ def testCanBeChecked(self):
+ logger = logging.getLogger(__name__ + "testCanBreak")
+ listener = testutils.LoggingValidator(logger, error=1, warning=2)
+ with self.assertRaises(RuntimeError):
+ with listener:
+ logger.error("aaa")
+ logger.warning("aaa")
+ self.assertFalse(listener.can_be_checked())
+ logger.error("aaa")
+ # Here we know that it's already wrong without a big cost
+ self.assertTrue(listener.can_be_checked())
+
+ def testWithAs(self):
+ logger = logging.getLogger(__name__ + "testCanBreak")
+ with testutils.LoggingValidator(logger) as listener:
+ logger.error("aaa")
+ self.assertIsNotNone(listener)
+
+ def testErrorMessage(self):
+ logger = logging.getLogger(__name__ + "testCanBreak")
+ listener = testutils.LoggingValidator(logger, error=1, warning=2)
+ with self.assertRaisesRegex(RuntimeError, "aaabbb"):
+ with listener:
+ logger.error("aaa")
+ logger.warning("aaabbb")
+ logger.error("aaa")
diff --git a/src/silx/utils/test/test_weakref.py b/src/silx/utils/test/test_weakref.py
new file mode 100644
index 0000000..06f3adf
--- /dev/null
+++ b/src/silx/utils/test/test_weakref.py
@@ -0,0 +1,315 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2016-2019 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.
+#
+# ###########################################################################*/
+"""Tests for weakref module"""
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "15/09/2016"
+
+
+import unittest
+from .. import weakref
+
+
+class Dummy(object):
+ """Dummy class to use it as geanie pig"""
+ def inc(self, a):
+ return a + 1
+
+ def __lt__(self, other):
+ return True
+
+
+def dummy_inc(a):
+ """Dummy function to use it as geanie pig"""
+ return a + 1
+
+
+class TestWeakMethod(unittest.TestCase):
+ """Tests for weakref.WeakMethod"""
+
+ def testMethod(self):
+ dummy = Dummy()
+ callable_ = weakref.WeakMethod(dummy.inc)
+ self.assertEqual(callable_()(10), 11)
+
+ def testMethodWithDeadObject(self):
+ dummy = Dummy()
+ callable_ = weakref.WeakMethod(dummy.inc)
+ dummy = None
+ self.assertIsNone(callable_())
+
+ def testMethodWithDeadFunction(self):
+ dummy = Dummy()
+ dummy.inc2 = lambda self, a: a + 1
+ callable_ = weakref.WeakMethod(dummy.inc2)
+ dummy.inc2 = None
+ self.assertIsNone(callable_())
+
+ def testFunction(self):
+ callable_ = weakref.WeakMethod(dummy_inc)
+ self.assertEqual(callable_()(10), 11)
+
+ def testDeadFunction(self):
+ def inc(a):
+ return a + 1
+ callable_ = weakref.WeakMethod(inc)
+ inc = None
+ self.assertIsNone(callable_())
+
+ def testLambda(self):
+ store = lambda a: a + 1 # noqa: E731
+ callable_ = weakref.WeakMethod(store)
+ self.assertEqual(callable_()(10), 11)
+
+ def testDeadLambda(self):
+ callable_ = weakref.WeakMethod(lambda a: a + 1)
+ self.assertIsNone(callable_())
+
+ def testCallbackOnDeadObject(self):
+ self.__count = 0
+
+ def callback(ref):
+ self.__count += 1
+ self.assertIs(callable_, ref)
+ dummy = Dummy()
+ callable_ = weakref.WeakMethod(dummy.inc, callback)
+ dummy = None
+ self.assertEqual(self.__count, 1)
+
+ def testCallbackOnDeadMethod(self):
+ self.__count = 0
+
+ def callback(ref):
+ self.__count += 1
+ self.assertIs(callable_, ref)
+ dummy = Dummy()
+ dummy.inc2 = lambda self, a: a + 1
+ callable_ = weakref.WeakMethod(dummy.inc2, callback)
+ dummy.inc2 = None
+ self.assertEqual(self.__count, 1)
+
+ def testCallbackOnDeadFunction(self):
+ self.__count = 0
+
+ def callback(ref):
+ self.__count += 1
+ self.assertIs(callable_, ref)
+ store = lambda a: a + 1 # noqa: E731
+ callable_ = weakref.WeakMethod(store, callback)
+ store = None
+ self.assertEqual(self.__count, 1)
+
+ def testEquals(self):
+ dummy = Dummy()
+ callable1 = weakref.WeakMethod(dummy.inc)
+ callable2 = weakref.WeakMethod(dummy.inc)
+ self.assertEqual(callable1, callable2)
+
+ def testInSet(self):
+ callable_set = set([])
+ dummy = Dummy()
+ callable_set.add(weakref.WeakMethod(dummy.inc))
+ callable_ = weakref.WeakMethod(dummy.inc)
+ self.assertIn(callable_, callable_set)
+
+ def testInDict(self):
+ callable_dict = {}
+ dummy = Dummy()
+ callable_dict[weakref.WeakMethod(dummy.inc)] = 10
+ callable_ = weakref.WeakMethod(dummy.inc)
+ self.assertEqual(callable_dict.get(callable_), 10)
+
+
+class TestWeakMethodProxy(unittest.TestCase):
+
+ def testMethod(self):
+ dummy = Dummy()
+ callable_ = weakref.WeakMethodProxy(dummy.inc)
+ self.assertEqual(callable_(10), 11)
+
+ def testMethodWithDeadObject(self):
+ dummy = Dummy()
+ method = weakref.WeakMethodProxy(dummy.inc)
+ dummy = None
+ self.assertRaises(ReferenceError, method, 9)
+
+
+class TestWeakList(unittest.TestCase):
+ """Tests for weakref.WeakList"""
+
+ def setUp(self):
+ self.list = weakref.WeakList()
+ self.object1 = Dummy()
+ self.object2 = Dummy()
+ self.list.append(self.object1)
+ self.list.append(self.object2)
+
+ def testAppend(self):
+ obj = Dummy()
+ self.list.append(obj)
+ self.assertEqual(len(self.list), 3)
+ obj = None
+ self.assertEqual(len(self.list), 2)
+
+ def testRemove(self):
+ self.list.remove(self.object1)
+ self.assertEqual(len(self.list), 1)
+
+ def testPop(self):
+ obj = self.list.pop(0)
+ self.assertIs(obj, self.object1)
+ self.assertEqual(len(self.list), 1)
+
+ def testGetItem(self):
+ self.assertIs(self.object1, self.list[0])
+
+ def testGetItemSlice(self):
+ objects = self.list[:]
+ self.assertEqual(len(objects), 2)
+ self.assertIs(self.object1, objects[0])
+ self.assertIs(self.object2, objects[1])
+
+ def testIter(self):
+ obj_list = list(self.list)
+ self.assertEqual(len(obj_list), 2)
+ self.assertIs(self.object1, obj_list[0])
+
+ def testLen(self):
+ self.assertEqual(len(self.list), 2)
+
+ def testSetItem(self):
+ obj = Dummy()
+ self.list[0] = obj
+ self.assertIsNot(self.object1, self.list[0])
+ obj = None
+ self.assertEqual(len(self.list), 1)
+
+ def testSetItemSlice(self):
+ obj = Dummy()
+ self.list[:] = [obj, obj]
+ self.assertEqual(len(self.list), 2)
+ self.assertIs(obj, self.list[0])
+ self.assertIs(obj, self.list[1])
+ obj = None
+ self.assertEqual(len(self.list), 0)
+
+ def testDelItem(self):
+ del self.list[0]
+ self.assertEqual(len(self.list), 1)
+ self.assertIs(self.object2, self.list[0])
+
+ def testDelItemSlice(self):
+ del self.list[:]
+ self.assertEqual(len(self.list), 0)
+
+ def testContains(self):
+ self.assertIn(self.object1, self.list)
+
+ def testAdd(self):
+ others = [Dummy()]
+ l = self.list + others
+ self.assertIs(l[0], self.object1)
+ self.assertEqual(len(l), 3)
+ others = None
+ self.assertEqual(len(l), 2)
+
+ def testExtend(self):
+ others = [Dummy()]
+ self.list.extend(others)
+ self.assertIs(self.list[0], self.object1)
+ self.assertEqual(len(self.list), 3)
+ others = None
+ self.assertEqual(len(self.list), 2)
+
+ def testIadd(self):
+ others = [Dummy()]
+ self.list += others
+ self.assertIs(self.list[0], self.object1)
+ self.assertEqual(len(self.list), 3)
+ others = None
+ self.assertEqual(len(self.list), 2)
+
+ def testMul(self):
+ l = self.list * 2
+ self.assertIs(l[0], self.object1)
+ self.assertEqual(len(l), 4)
+ self.object1 = None
+ self.assertEqual(len(l), 2)
+ self.assertIs(l[0], self.object2)
+ self.assertIs(l[1], self.object2)
+
+ def testImul(self):
+ self.list *= 2
+ self.assertIs(self.list[0], self.object1)
+ self.assertEqual(len(self.list), 4)
+ self.object1 = None
+ self.assertEqual(len(self.list), 2)
+ self.assertIs(self.list[0], self.object2)
+ self.assertIs(self.list[1], self.object2)
+
+ def testCount(self):
+ self.list.append(self.object2)
+ self.assertEqual(self.list.count(self.object1), 1)
+ self.assertEqual(self.list.count(self.object2), 2)
+
+ def testIndex(self):
+ self.assertEqual(self.list.index(self.object1), 0)
+ self.assertEqual(self.list.index(self.object2), 1)
+
+ def testInsert(self):
+ obj = Dummy()
+ self.list.insert(1, obj)
+ self.assertEqual(len(self.list), 3)
+ self.assertIs(self.list[1], obj)
+ obj = None
+ self.assertEqual(len(self.list), 2)
+
+ def testReverse(self):
+ self.list.reverse()
+ self.assertEqual(len(self.list), 2)
+ self.assertIs(self.list[0], self.object2)
+ self.assertIs(self.list[1], self.object1)
+
+ def testReverted(self):
+ new_list = reversed(self.list)
+ self.assertEqual(len(new_list), 2)
+ self.assertIs(self.list[1], self.object2)
+ self.assertIs(self.list[0], self.object1)
+ self.assertIs(new_list[0], self.object2)
+ self.assertIs(new_list[1], self.object1)
+ self.object1 = None
+ self.assertEqual(len(new_list), 1)
+
+ def testStr(self):
+ self.assertNotEqual(self.list.__str__(), "[]")
+
+ def testRepr(self):
+ self.assertNotEqual(self.list.__repr__(), "[]")
+
+ def testSort(self):
+ # only a coverage
+ self.list.sort()
+ self.assertEqual(len(self.list), 2)