From 42cf1c0ed33fc8082a888aee7a5b0578dd62495b Mon Sep 17 00:00:00 2001 From: ernestoarbitrio Date: Tue, 30 Jun 2020 13:54:10 +0200 Subject: [PATCH 1/4] data: add tests --- tests/unit/data/test_data.py | 65 +++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/tests/unit/data/test_data.py b/tests/unit/data/test_data.py index e5bdb8380..973b8b34b 100644 --- a/tests/unit/data/test_data.py +++ b/tests/unit/data/test_data.py @@ -1,17 +1,72 @@ # coding: utf-8 + import os +from unittest.mock import MagicMock, patch + +import pytest + +import openslide +from histolab.data import _has_hash, _load_svs, cmu_small_region, data_dir, file_hash + +from ...fixtures import SVS +from ...unitutil import function_mock + + +def it_can_calculate_file_hash(): + with patch.dict("sys.modules", unknown=MagicMock()): + file = SVS.CMU_1_SMALL_REGION + + fh = file_hash(file) + + assert fh == "ed92d5a9f2e86df67640d6f92ce3e231419ce127131697fbbce42ad5e002c8a7" + + +def but_it_raises_valueerror_when_algorithm_is_not_available(): + with patch( + "pooch.utils.file_hash", side_effect=ModuleNotFoundError("mocked error") + ): + file = SVS.CMU_1_SMALL_REGION -import histolab.data as data + with pytest.raises(ValueError) as err: + file_hash(file, alg="fake_alg") + + assert isinstance(err.value, ValueError) + assert str(err.value) == "Algorithm 'fake_alg' not available in hashlib" def test_data_dir(): # data_dir should be a directory that can be used as a standard directory - data_dir = data.data_dir - assert "cmu_small_region.svs" in os.listdir(data_dir) + data_directory = data_dir + assert "cmu_small_region.svs" in os.listdir(data_directory) def test_cmu_small_region(): """ Test that "cmu_small_region" svs can be loaded. """ - cmu_small_region, path = data.cmu_small_region() - assert cmu_small_region.dimensions == (2220, 2967) + cmu_small_region_image, path = cmu_small_region() + assert cmu_small_region_image.dimensions == (2220, 2967) + + +def test_load_svs(request): + file = SVS.CMU_1_SMALL_REGION + _fetch = function_mock(request, "histolab.data._fetch") + _fetch.return_value = file + + svs, path = _load_svs(file) + + assert type(svs) == openslide.OpenSlide + assert path == file + + +@pytest.mark.parametrize( + "file, hash, expected_value", + ((SVS.CMU_1_SMALL_REGION, "1234abcd", True), ("/fake/file", "1234abcd", False)), +) +def it_knows_its_hash(request, file, hash, expected_value): + file = file + file_hash_ = function_mock(request, "histolab.data.file_hash") + file_hash_.return_value = hash + + has_hash = _has_hash(file, hash) + + assert has_hash is expected_value From a0543f9919322445eb0b38d4a7e54ab8a9cc0282 Mon Sep 17 00:00:00 2001 From: ernestoarbitrio Date: Tue, 30 Jun 2020 14:39:33 +0200 Subject: [PATCH 2/4] filters: more coverage for unit/image_filters --- tests/unit/data/test_data.py | 25 +----------------------- tests/unit/filters/test_image_filters.py | 24 +++++++++++++++++------ 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/tests/unit/data/test_data.py b/tests/unit/data/test_data.py index 973b8b34b..e92f31ddf 100644 --- a/tests/unit/data/test_data.py +++ b/tests/unit/data/test_data.py @@ -2,39 +2,16 @@ import os -from unittest.mock import MagicMock, patch import pytest import openslide -from histolab.data import _has_hash, _load_svs, cmu_small_region, data_dir, file_hash +from histolab.data import _has_hash, _load_svs, cmu_small_region, data_dir from ...fixtures import SVS from ...unitutil import function_mock -def it_can_calculate_file_hash(): - with patch.dict("sys.modules", unknown=MagicMock()): - file = SVS.CMU_1_SMALL_REGION - - fh = file_hash(file) - - assert fh == "ed92d5a9f2e86df67640d6f92ce3e231419ce127131697fbbce42ad5e002c8a7" - - -def but_it_raises_valueerror_when_algorithm_is_not_available(): - with patch( - "pooch.utils.file_hash", side_effect=ModuleNotFoundError("mocked error") - ): - file = SVS.CMU_1_SMALL_REGION - - with pytest.raises(ValueError) as err: - file_hash(file, alg="fake_alg") - - assert isinstance(err.value, ValueError) - assert str(err.value) == "Algorithm 'fake_alg' not available in hashlib" - - def test_data_dir(): # data_dir should be a directory that can be used as a standard directory data_directory = data_dir diff --git a/tests/unit/filters/test_image_filters.py b/tests/unit/filters/test_image_filters.py index 7d37720c5..2cf349333 100644 --- a/tests/unit/filters/test_image_filters.py +++ b/tests/unit/filters/test_image_filters.py @@ -2,7 +2,6 @@ import numpy as np import PIL - from histolab.filters import image_filters as imf from ...unitutil import NpArrayMock, PILImageMock, function_mock @@ -101,7 +100,7 @@ def it_calls_stretch_contrast_functional(self, request): def it_calls_histogram_equalization_functional(self, request): image = PILImageMock.DIMS_500X500_RGBA_COLOR_155_249_240 F_histogram_equalization = function_mock( - request, "histolab.filters.image_filters_functional.histogram_equalization", + request, "histolab.filters.image_filters_functional.histogram_equalization" ) F_histogram_equalization.return_value = image histogram_equalization = imf.HistogramEqualization(200) @@ -114,7 +113,7 @@ def it_calls_histogram_equalization_functional(self, request): def it_calls_adaptive_equalization_functional(self, request): image = PILImageMock.DIMS_500X500_RGBA_COLOR_155_249_240 F_adaptive_equalization = function_mock( - request, "histolab.filters.image_filters_functional.adaptive_equalization", + request, "histolab.filters.image_filters_functional.adaptive_equalization" ) F_adaptive_equalization.return_value = image adaptive_equalization = imf.AdaptiveEqualization(250, 0.2) @@ -166,7 +165,7 @@ def it_calls_rag_threshold_functional(self, request): def it_applies_hysteresis_threshold(self, request): image = PILImageMock.DIMS_500X500_RGBA_COLOR_155_249_240 F_hysteresis_threshold = function_mock( - request, "histolab.filters.image_filters_functional.hysteresis_threshold", + request, "histolab.filters.image_filters_functional.hysteresis_threshold" ) F_hysteresis_threshold.return_value = image hysteresis_threshold = imf.HysteresisThreshold(20, 150) @@ -206,7 +205,7 @@ def it_calls_otsu_threshold_functional(self, request): def it_calls_local_otsu_threshold_functional(self, request): image = PILImageMock.DIMS_500X500_RGBA_COLOR_155_249_240 F_local_otsu_threshold = function_mock( - request, "histolab.filters.image_filters_functional.local_otsu_threshold", + request, "histolab.filters.image_filters_functional.local_otsu_threshold" ) F_local_otsu_threshold.return_value = np.array(image) local_otsu_threshold = imf.LocalOtsuThreshold(5) @@ -258,7 +257,7 @@ def it_calls_grays_functional(self, request): def it_calls_green_channel_filter_functional(self, request): image = PILImageMock.DIMS_500X500_RGBA_COLOR_155_249_240 F_green_channel_filter = function_mock( - request, "histolab.filters.image_filters_functional.green_channel_filter", + request, "histolab.filters.image_filters_functional.green_channel_filter" ) F_green_channel_filter.return_value = np.array(image) green_channel_filter = imf.GreenChannelFilter(250, False, 85.0) @@ -346,6 +345,19 @@ def it_calls_blue_pen_filter_functional(self, request): F_blue_pen_filter.assert_called_once_with(image) assert type(blue_pen_filter(image)) == np.ndarray + def it_calls_pen_marks_filter_functional(self, request): + image = PILImageMock.DIMS_500X500_RGBA_COLOR_155_249_240 + F_pen_marks = function_mock( + request, "histolab.filters.image_filters_functional.pen_marks" + ) + F_pen_marks.return_value = np.array(image) + pen_marks = imf.PenMarks() + + pen_marks(image) + + F_pen_marks.assert_called_once_with(image) + assert type(pen_marks(image)) == np.ndarray + def it_calls_np_to_pil(self, request): array = NpArrayMock.ONES_30X30_UINT8 util_np_to_pil = function_mock(request, "histolab.util.np_to_pil") From 563ee1bfffca570db159c0b4e6f2a9a9cb35b5b9 Mon Sep 17 00:00:00 2001 From: ernestoarbitrio Date: Tue, 30 Jun 2020 22:32:49 +0200 Subject: [PATCH 3/4] rfctr: remove print statement in test --- tests/unit/filters/test_image_filters.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/filters/test_image_filters.py b/tests/unit/filters/test_image_filters.py index 2cf349333..f0e1ddb7c 100644 --- a/tests/unit/filters/test_image_filters.py +++ b/tests/unit/filters/test_image_filters.py @@ -365,7 +365,6 @@ def it_calls_np_to_pil(self, request): to_pil_image = imf.ToPILImage() to_pil_image(array) - print(util_np_to_pil.call_count) util_np_to_pil.assert_called_once_with(array) assert type(to_pil_image(array)) == PIL.Image.Image From e9dc3fdcb024a82d82668838c1e7a2e8cff48196 Mon Sep 17 00:00:00 2001 From: ernestoarbitrio Date: Tue, 30 Jun 2020 22:48:15 +0200 Subject: [PATCH 4/4] tests: increase coverage for morph filters --- .../filters/test_morphological_fillters.py | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/tests/unit/filters/test_morphological_fillters.py b/tests/unit/filters/test_morphological_fillters.py index 1bcb5d6e9..079bb4b58 100644 --- a/tests/unit/filters/test_morphological_fillters.py +++ b/tests/unit/filters/test_morphological_fillters.py @@ -1,12 +1,13 @@ # encoding: utf-8 +import pytest + import numpy as np import skimage.morphology - from histolab.filters import morphological_filters as mof -from ...unitutil import NpArrayMock, function_mock from ...base import IMAGE1_RGB, IMAGE2_RGBA +from ...unitutil import NpArrayMock, function_mock class DescribeMorphologicalFilters(object): @@ -56,6 +57,19 @@ def it_calls_binary_erosion_filter_functional(self, request): assert F_binary_erosion.call_args_list[0][0][2] == 1 assert type(binary_erosion(mask_arr)) == np.ndarray + def but_it_raises_exception_when_call_binary_erosion_non_mask_array(self, request): + array = np.array([(1, 2, 3), (4, 5, 6)]) + F_binary_erosion = function_mock( + request, "scipy.ndimage.morphology.binary_erosion" + ) + F_binary_erosion.return_value = array + + with pytest.raises(ValueError) as err: + binary_erosion = mof.BinaryErosion() + binary_erosion(array) + + assert str(err.value) == "Mask must be binary" + def it_calls_binary_dilation_filter_functional(self, request): mask_arr = NpArrayMock.ONES_500X500X4_BOOL disk = skimage.morphology.disk(5) @@ -75,6 +89,19 @@ def it_calls_binary_dilation_filter_functional(self, request): assert F_binary_dilation.call_args_list[0][0][2] == 1 assert type(binary_dilation(mask_arr)) == np.ndarray + def but_it_raises_exception_when_call_binary_dilation_non_mask_array(self, request): + array = np.array([(1, 2, 3), (4, 5, 6)]) + F_binary_dilation = function_mock( + request, "scipy.ndimage.morphology.binary_dilation" + ) + F_binary_dilation.return_value = array + + with pytest.raises(ValueError) as err: + binary_dilation = mof.BinaryDilation() + binary_dilation(array) + + assert str(err.value) == "Mask must be binary" + def it_calls_binary_opening_filter_functional(self, request): mask_arr = NpArrayMock.ONES_500X500X4_BOOL disk = skimage.morphology.disk(3) @@ -94,6 +121,19 @@ def it_calls_binary_opening_filter_functional(self, request): assert F_binary_opening.call_args_list[0][0][2] == 1 assert type(binary_opening(mask_arr)) == np.ndarray + def but_it_raises_exception_when_call_binary_opening_non_mask_array(self, request): + array = np.array([(1, 2, 3), (4, 5, 6)]) + F_binary_opening = function_mock( + request, "scipy.ndimage.morphology.binary_opening" + ) + F_binary_opening.return_value = array + + with pytest.raises(ValueError) as err: + binary_opening = mof.BinaryOpening() + binary_opening(array) + + assert str(err.value) == "Mask must be binary" + def it_calls_binary_closing_filter_functional(self, request): mask_arr = NpArrayMock.ONES_500X500X4_BOOL disk = skimage.morphology.disk(3) @@ -112,3 +152,16 @@ def it_calls_binary_closing_filter_functional(self, request): np.testing.assert_array_equal(F_binary_closing.call_args_list[0][0][1], disk) assert F_binary_closing.call_args_list[0][0][2] == 1 assert type(binary_closing(mask_arr)) == np.ndarray + + def but_it_raises_exception_when_call_binary_closing_non_mask_array(self, request): + array = np.array([(1, 2, 3), (4, 5, 6)]) + F_binary_closing = function_mock( + request, "scipy.ndimage.morphology.binary_closing" + ) + F_binary_closing.return_value = array + + with pytest.raises(ValueError) as err: + binary_closing = mof.BinaryClosing() + binary_closing(array) + + assert str(err.value) == "Mask must be binary"