From 5d62db93445adcdbf3802c11bf1ff2d421764b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Gr=C3=BCter?= Date: Tue, 23 May 2023 16:05:18 -0700 Subject: [PATCH 1/5] Enable testing against nightly upstream wheels See https://github.com/scientific-python/upload-nightly-action#using-nightly-builds-in-ci --- .github/workflows/tests.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1b6a819a151..74f510969c6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,11 +44,14 @@ jobs: BUILD_DOCS: 0 OPTIONS_NAME: "mini-req-optional-deps" - platform_id: manylinux_x86_64 - python-version: 3.9 - PIP_FLAGS: "--pre" + python-version: 3.11 + PIP_FLAGS: >- + -i https://pypi.org/simple + -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple + --pre SKIMAGE_TEST_STRICT_WARNINGS_GLOBAL: 1 - # test pre-releases - OPTIONS_NAME: "pre" + # test nightly wheels + OPTIONS_NAME: "nightly" - platform_id: manylinux_x86_64 python-version: 3.9 BUILD_DOCS: 1 From fc638f83b382cd7855f2e08fc9ffbcdd8991e3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Gr=C3=BCter?= Date: Tue, 23 May 2023 16:16:02 -0700 Subject: [PATCH 2/5] Apply new instructions from upload-nightly-action --- .github/workflows/tests.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 74f510969c6..533b7a33128 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -46,8 +46,9 @@ jobs: - platform_id: manylinux_x86_64 python-version: 3.11 PIP_FLAGS: >- - -i https://pypi.org/simple - -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple + --extra-index-url + https://pypi.anaconda.org/scientific-python-nightly-wheels/simple + --upgrade --pre SKIMAGE_TEST_STRICT_WARNINGS_GLOBAL: 1 # test nightly wheels From d3501ab76cb9a90093973b63cf5e940f19f82066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Gr=C3=BCter?= Date: Tue, 23 May 2023 17:02:31 -0700 Subject: [PATCH 3/5] Refactor failing unittest style to pytest This test raised a DeprecationWarning: It is deprecated to return a value that is not None from a test case. --- skimage/io/tests/test_simpleitk.py | 45 ++++++++++++------------------ 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/skimage/io/tests/test_simpleitk.py b/skimage/io/tests/test_simpleitk.py index 888df801441..6b93e14a6d4 100644 --- a/skimage/io/tests/test_simpleitk.py +++ b/skimage/io/tests/test_simpleitk.py @@ -1,14 +1,11 @@ import numpy as np -import unittest - -from tempfile import NamedTemporaryFile +import pytest from skimage.io import imread, imsave, use_plugin, reset_plugins from skimage._shared import testing -from pytest import importorskip, raises, fixture -importorskip('SimpleITK') +pytest.importorskip('SimpleITK') np.random.seed(0) @@ -17,7 +14,7 @@ def teardown(): reset_plugins() -@fixture(autouse=True) +@pytest.fixture(autouse=True) def setup_plugin(): """This ensures that `use_plugin` is directly called before all tests to ensure that SimpleITK is used. @@ -44,7 +41,7 @@ def test_bilevel(): def test_imread_truncated_jpg(): - with raises(RuntimeError): + with pytest.raises(RuntimeError): imread(testing.fetch('data/truncated.jpg')) @@ -61,23 +58,17 @@ def test_imread_uint16_big_endian(): np.testing.assert_array_almost_equal(img, expected) -class TestSave(unittest.TestCase): - def roundtrip(self, dtype, x): - with NamedTemporaryFile(suffix='.mha') as f: - fname = f.name - - imsave(fname, x) - y = imread(fname) - - np.testing.assert_array_almost_equal(x, y) - - def test_imsave_roundtrip(self): - for shape in [(10, 10), (10, 10, 3), (10, 10, 4)]: - for dtype in (np.uint8, np.uint16, np.float32, np.float64): - x = np.ones(shape, dtype=dtype) * np.random.rand(*shape) - - if np.issubdtype(dtype, np.floating): - yield self.roundtrip, dtype, x - else: - x = (x * 255).astype(dtype) - yield self.roundtrip, dtype, x +@pytest.mark.parametrize("shape", [(10, 10), (10, 10, 3), (10, 10, 4)]) +@pytest.mark.parametrize("dtype", [np.uint8, np.uint16, np.float32, np.float64]) +def test_imsave_roundtrip(shape, dtype, tmp_path): + expected = np.zeros(shape, dtype=dtype) + if np.issubdtype(dtype, np.floating): + info_func = np.finfo + else: + info_func = np.iinfo + expected.flat[0] = info_func(dtype).min + expected.flat[-1] = info_func(dtype).max + file_path = tmp_path / "roundtrip.mha" + imsave(file_path, expected) + actual = imread(file_path) + np.testing.assert_array_almost_equal(actual, expected) From a4cbf47ccaec65ee3e56009ea09471f18a580bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Gr=C3=BCter?= Date: Tue, 23 May 2023 17:34:59 -0700 Subject: [PATCH 4/5] Refactor failing unittest style to pytest (II) --- skimage/io/tests/test_histograms.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/skimage/io/tests/test_histograms.py b/skimage/io/tests/test_histograms.py index 1672ef5dd70..4fdb4dacc32 100644 --- a/skimage/io/tests/test_histograms.py +++ b/skimage/io/tests/test_histograms.py @@ -1,16 +1,14 @@ import numpy as np -from skimage.io._plugins._histograms import histograms -from skimage._shared.testing import assert_array_equal, assert_equal, TestCase +from skimage.io._plugins._histograms import histograms -class TestHistogram(TestCase): +class TestHistogram: def test_basic(self): img = np.ones((50, 50, 3), dtype=np.uint8) - r, g, b, v = histograms(img, 255) - - for band in (r, g, b, v): - yield assert_equal, band.sum(), 50 * 50 + bands = histograms(img, 255) + for band in bands: + np.testing.assert_equal(band.sum(), 50**2) def test_counts(self): channel = np.arange(255).reshape(51, 5) @@ -19,7 +17,7 @@ def test_counts(self): img[:, :, 1] = channel img[:, :, 2] = channel r, g, b, v = histograms(img, 255) - assert_array_equal(r, g) - assert_array_equal(r, b) - assert_array_equal(r, v) - assert_array_equal(r, np.ones(255)) + np.testing.assert_array_equal(r, g) + np.testing.assert_array_equal(r, b) + np.testing.assert_array_equal(r, v) + np.testing.assert_array_equal(r, np.ones(255)) From fdc1c0e407c2671aa5f5cd0d45a15039a90731f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Gr=C3=BCter?= Date: Wed, 24 May 2023 12:21:35 -0700 Subject: [PATCH 5/5] Refactor failing unittest style to pytest (III) --- skimage/io/tests/test_imageio.py | 68 +++++++++++++++++------------- skimage/io/tests/test_pil.py | 23 +++++----- skimage/io/tests/test_simpleitk.py | 32 ++++++++------ 3 files changed, 67 insertions(+), 56 deletions(-) diff --git a/skimage/io/tests/test_imageio.py b/skimage/io/tests/test_imageio.py index b1bf9d13a26..14253ceba0c 100644 --- a/skimage/io/tests/test_imageio.py +++ b/skimage/io/tests/test_imageio.py @@ -1,20 +1,21 @@ from tempfile import NamedTemporaryFile import numpy as np -from skimage.io import imread, imsave, use_plugin, reset_plugins +from skimage.io import imread, imsave,plugin_order from skimage._shared import testing -from skimage._shared.testing import assert_array_almost_equal, TestCase, fetch +from skimage._shared.testing import fetch import pytest -def setup(): - use_plugin('imageio') - - -def teardown(): - reset_plugins() +def test_prefered_plugin(): + # Don't call use_plugin("imageio") before, this way we test that imageio is used + # by default + order = plugin_order() + assert order["imread"][0] == "imageio" + assert order["imsave"][0] == "imageio" + assert order["imread_collection"][0] == "imageio" def test_imageio_as_gray(): @@ -41,28 +42,37 @@ def test_imageio_truncated_jpg(): imread(fetch('data/truncated.jpg')) -class TestSave(TestCase): - - def roundtrip(self, x, scaling=1): - with NamedTemporaryFile(suffix='.png') as f: - fname = f.name +class TestSave: - imsave(fname, x) - y = imread(fname) - - assert_array_almost_equal((x * scaling).astype(np.int32), y) - - def test_imsave_roundtrip(self): - dtype = np.uint8 - np.random.seed(0) - for shape in [(10, 10), (10, 10, 3), (10, 10, 4)]: - x = np.ones(shape, dtype=dtype) * np.random.rand(*shape) - - if np.issubdtype(dtype, np.floating): - yield self.roundtrip, x, 255 - else: - x = (x * 255).astype(dtype) - yield self.roundtrip, x + @pytest.mark.parametrize( + "shape,dtype", [ + # float32, float64 can't be saved as PNG and raise + # uint32 is not roundtripping properly + ((10, 10), np.uint8), + ((10, 10), np.uint16), + ((10, 10, 2), np.uint8), + ((10, 10, 3), np.uint8), + ((10, 10, 4), np.uint8), + ] + ) + def test_imsave_roundtrip(self, shape, dtype, tmp_path): + if np.issubdtype(dtype, np.floating): + min_ = 0 + max_ = 1 + else: + min_ = 0 + max_ = np.iinfo(dtype).max + expected = np.linspace( + min_, max_, + endpoint=True, + num=np.prod(shape), + dtype=dtype + ) + expected = expected.reshape(shape) + file_path = tmp_path / "roundtrip.png" + imsave(file_path, expected) + actual = imread(file_path) + np.testing.assert_array_almost_equal(actual, expected) def test_bool_array_save(self): with NamedTemporaryFile(suffix='.png') as f: diff --git a/skimage/io/tests/test_pil.py b/skimage/io/tests/test_pil.py index f857bb8e2e7..13f728b8a1e 100644 --- a/skimage/io/tests/test_pil.py +++ b/skimage/io/tests/test_pil.py @@ -16,27 +16,24 @@ from ... import img_as_float from ...color import rgb2lab -from .. import imread, imsave, reset_plugins, use_plugin +from .. import imread, imsave, reset_plugins, use_plugin, plugin_order from .._plugins.pil_plugin import (_palette_is_grayscale, ndarray_to_pil, pil_to_ndarray) -def setup(): +@pytest.fixture(autouse=True) +def use_pil_plugin(): + """Ensure that PIL plugin is used in tests here.""" use_plugin('pil') - - -def teardown(): + yield reset_plugins() -def setup_module(self): - """The effect of the `plugin.use` call may be overridden by later imports. - Call `use_plugin` directly before the tests to ensure that PIL is used. - """ - try: - use_plugin('pil') - except ImportError: - pass +def test_prefered_plugin(): + order = plugin_order() + assert order["imread"][0] == "pil" + assert order["imsave"][0] == "pil" + assert order["imread_collection"][0] == "pil" def test_png_round_trip(): diff --git a/skimage/io/tests/test_simpleitk.py b/skimage/io/tests/test_simpleitk.py index 6b93e14a6d4..c67f18552f4 100644 --- a/skimage/io/tests/test_simpleitk.py +++ b/skimage/io/tests/test_simpleitk.py @@ -1,26 +1,26 @@ import numpy as np import pytest -from skimage.io import imread, imsave, use_plugin, reset_plugins +from skimage.io import imread, imsave, use_plugin, reset_plugins, plugin_order from skimage._shared import testing pytest.importorskip('SimpleITK') -np.random.seed(0) - - -def teardown(): - reset_plugins() - @pytest.fixture(autouse=True) -def setup_plugin(): - """This ensures that `use_plugin` is directly called before all tests to - ensure that SimpleITK is used. - """ +def use_simpleitk_plugin(): + """Ensure that SimpleITK plugin is used.""" use_plugin('simpleitk') yield + reset_plugins() + + +def test_prefered_plugin(): + order = plugin_order() + assert order["imread"][0] == "simpleitk" + assert order["imsave"][0] == "simpleitk" + assert order["imread_collection"][0] == "simpleitk" def test_imread_as_gray(): @@ -61,13 +61,17 @@ def test_imread_uint16_big_endian(): @pytest.mark.parametrize("shape", [(10, 10), (10, 10, 3), (10, 10, 4)]) @pytest.mark.parametrize("dtype", [np.uint8, np.uint16, np.float32, np.float64]) def test_imsave_roundtrip(shape, dtype, tmp_path): - expected = np.zeros(shape, dtype=dtype) if np.issubdtype(dtype, np.floating): info_func = np.finfo else: info_func = np.iinfo - expected.flat[0] = info_func(dtype).min - expected.flat[-1] = info_func(dtype).max + expected = np.linspace( + info_func(dtype).min, info_func(dtype).max, + endpoint=True, + num=np.prod(shape), + dtype=dtype + ) + expected = expected.reshape(shape) file_path = tmp_path / "roundtrip.mha" imsave(file_path, expected) actual = imread(file_path)