Repository: Project-MONAI/MONAI

Issue:
Deprecating Old Pathology Components
Following our discussion in #4216 about wether we should deprecate the old pathology components (including the components in `monai/app/pathology` and elsewhere), or we should simply remove them and mention it in the release note.

My suggestion would be to deprecate since we have users that are continuously building pipelines based on our pathology components but I see Nic's point (https://github.com/Project-MONAI/MONAI/pull/4216#issuecomment-1116222932) about making a breaking change too. Any Thought?

@Nic-Ma @wyli @ericspod 


Discussion:
If we have no real need to delete things I would apply our deprecation decorators and set a version to remove things in, if people complain about things disappearing too soon it's easier to bump that version rather than reintroduce deleted code.

Resolving patch:
diff --git a/monai/apps/pathology/data/datasets.py b/monai/apps/pathology/data/datasets.py
--- a/monai/apps/pathology/data/datasets.py
+++ b/monai/apps/pathology/data/datasets.py
@@ -17,11 +17,12 @@
 
 from monai.data import Dataset, SmartCacheDataset
 from monai.data.image_reader import WSIReader
-from monai.utils import ensure_tuple_rep
+from monai.utils import deprecated, ensure_tuple_rep
 
 __all__ = ["PatchWSIDataset", "SmartCachePatchWSIDataset", "MaskedInferenceWSIDataset"]
 
 
+@deprecated(since="0.8", msg_suffix="use `monai.data.PatchWSIDataset` instead.")
 class PatchWSIDataset(Dataset):
     """
     This dataset reads whole slide images, extracts regions, and creates patches.
@@ -103,6 +104,7 @@ def __getitem__(self, index):
         return patches
 
 
+@deprecated(since="0.8", msg_suffix="use `monai.data.SmartCacheDataset` with `monai.data.PatchWSIDataset` instead.")
 class SmartCachePatchWSIDataset(SmartCacheDataset):
     """Add SmartCache functionality to `PatchWSIDataset`.
 
@@ -177,6 +179,7 @@ def __init__(
         )
 
 
+@deprecated(since="0.8", msg_suffix="use `monai.data.MaskedPatchWSIDataset` instead.")
 class MaskedInferenceWSIDataset(Dataset):
     """
     This dataset load the provided foreground masks at an arbitrary resolution level,
diff --git a/monai/apps/pathology/handlers/prob_map_producer.py b/monai/apps/pathology/handlers/prob_map_producer.py
--- a/monai/apps/pathology/handlers/prob_map_producer.py
+++ b/monai/apps/pathology/handlers/prob_map_producer.py
@@ -27,7 +27,10 @@
 
 @deprecated(
     since="0.8",
-    msg_suffix="use `monai.handler.ProbMapProducer` (with `monai.data.wsi_dataset.SlidingPatchWSIDataset`) instead.",
+    msg_suffix=(
+        "use `monai.handler.ProbMapProducer` (with `monai.data.wsi_dataset.MaskedPatchWSIDataset` or "
+        "`monai.data.wsi_dataset.SlidingPatchWSIDataset`) instead."
+    ),
 )
 class ProbMapProducer:
     """
diff --git a/monai/data/image_reader.py b/monai/data/image_reader.py
--- a/monai/data/image_reader.py
+++ b/monai/data/image_reader.py
@@ -28,7 +28,7 @@
     orientation_ras_lps,
 )
 from monai.transforms.utility.array import EnsureChannelFirst
-from monai.utils import MetaKeys, SpaceKeys, ensure_tuple, ensure_tuple_rep, optional_import, require_pkg
+from monai.utils import MetaKeys, SpaceKeys, deprecated, ensure_tuple, ensure_tuple_rep, optional_import, require_pkg
 
 if TYPE_CHECKING:
     import itk
@@ -1218,6 +1218,7 @@ def _get_spatial_shape(self, img):
         return np.asarray((img.width, img.height))
 
 
+@deprecated(since="0.8", msg_suffix="use `monai.wsi_reader.WSIReader` instead.")
 class WSIReader(ImageReader):
     """
     Read whole slide images and extract patches.


Test patch:
diff --git a/tests/min_tests.py b/tests/min_tests.py
--- a/tests/min_tests.py
+++ b/tests/min_tests.py
@@ -162,7 +162,6 @@ def run_testsuit():
         "test_vitautoenc",
         "test_write_metrics_reports",
         "test_wsireader",
-        "test_wsireader_new",
         "test_zoom",
         "test_zoom_affine",
         "test_zoomd",
diff --git a/tests/test_patch_wsi_dataset.py b/tests/test_patch_wsi_dataset.py
--- a/tests/test_patch_wsi_dataset.py
+++ b/tests/test_patch_wsi_dataset.py
@@ -17,20 +17,25 @@
 from numpy.testing import assert_array_equal
 from parameterized import parameterized
 
-from monai.apps.pathology.data import PatchWSIDataset
-from monai.utils import optional_import
+from monai.apps.pathology.data import PatchWSIDataset as PatchWSIDatasetDeprecated
+from monai.data import PatchWSIDataset
+from monai.data.wsi_reader import CuCIMWSIReader, OpenSlideWSIReader
+from monai.utils import deprecated, optional_import
 from tests.utils import download_url_or_skip_test, testing_data_config
 
-_cucim, has_cim = optional_import("cucim")
-has_cim = has_cim and hasattr(_cucim, "CuImage")
-_, has_osl = optional_import("openslide")
+cucim, has_cim = optional_import("cucim")
+has_cim = has_cim and hasattr(cucim, "CuImage")
+openslide, has_osl = optional_import("openslide")
+imwrite, has_tiff = optional_import("tifffile", name="imwrite")
+_, has_codec = optional_import("imagecodecs")
+has_tiff = has_tiff and has_codec
 
 FILE_KEY = "wsi_img"
 FILE_URL = testing_data_config("images", FILE_KEY, "url")
 base_name, extension = os.path.basename(f"{FILE_URL}"), ".tiff"
 FILE_PATH = os.path.join(os.path.dirname(__file__), "testing_data", "temp_" + base_name + extension)
 
-TEST_CASE_0 = [
+TEST_CASE_DEP_0 = [
     {
         "data": [{"image": FILE_PATH, "location": [0, 0], "label": [1]}],
         "region_size": (1, 1),
@@ -41,7 +46,7 @@
     [{"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[1]]])}],
 ]
 
-TEST_CASE_0_L1 = [
+TEST_CASE_DEP_0_L1 = [
     {
         "data": [{"image": FILE_PATH, "location": [0, 0], "label": [1]}],
         "region_size": (1, 1),
@@ -53,7 +58,7 @@
     [{"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[1]]])}],
 ]
 
-TEST_CASE_0_L2 = [
+TEST_CASE_DEP_0_L2 = [
     {
         "data": [{"image": FILE_PATH, "location": [0, 0], "label": [1]}],
         "region_size": (1, 1),
@@ -66,7 +71,7 @@
 ]
 
 
-TEST_CASE_1 = [
+TEST_CASE_DEP_1 = [
     {
         "data": [{"image": FILE_PATH, "location": [10004, 20004], "label": [0, 0, 0, 1]}],
         "region_size": (8, 8),
@@ -83,7 +88,7 @@
 ]
 
 
-TEST_CASE_1_L0 = [
+TEST_CASE_DEP_1_L0 = [
     {
         "data": [{"image": FILE_PATH, "location": [10004, 20004], "label": [0, 0, 0, 1]}],
         "region_size": (8, 8),
@@ -101,7 +106,7 @@
 ]
 
 
-TEST_CASE_1_L1 = [
+TEST_CASE_DEP_1_L1 = [
     {
         "data": [{"image": FILE_PATH, "location": [10004, 20004], "label": [0, 0, 0, 1]}],
         "region_size": (8, 8),
@@ -117,7 +122,7 @@
         {"image": np.array([[[246]], [[242]], [[243]]], dtype=np.uint8), "label": np.array([[[1]]])},
     ],
 ]
-TEST_CASE_2 = [
+TEST_CASE_DEP_2 = [
     {
         "data": [{"image": FILE_PATH, "location": [0, 0], "label": [1]}],
         "region_size": 1,
@@ -128,7 +133,7 @@
     [{"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[1]]])}],
 ]
 
-TEST_CASE_3 = [
+TEST_CASE_DEP_3 = [
     {
         "data": [{"image": FILE_PATH, "location": [0, 0], "label": [[[0, 1], [1, 0]]]}],
         "region_size": 1,
@@ -139,7 +144,7 @@
     [{"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[0, 1], [1, 0]]])}],
 ]
 
-TEST_CASE_OPENSLIDE_0 = [
+TEST_CASE_DEP_OPENSLIDE_0 = [
     {
         "data": [{"image": FILE_PATH, "location": [0, 0], "label": [1]}],
         "region_size": (1, 1),
@@ -150,7 +155,7 @@
     [{"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[1]]])}],
 ]
 
-TEST_CASE_OPENSLIDE_0_L0 = [
+TEST_CASE_DEP_OPENSLIDE_0_L0 = [
     {
         "data": [{"image": FILE_PATH, "location": [0, 0], "label": [1]}],
         "region_size": (1, 1),
@@ -162,7 +167,7 @@
     [{"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[1]]])}],
 ]
 
-TEST_CASE_OPENSLIDE_0_L1 = [
+TEST_CASE_DEP_OPENSLIDE_0_L1 = [
     {
         "data": [{"image": FILE_PATH, "location": [0, 0], "label": [1]}],
         "region_size": (1, 1),
@@ -175,7 +180,7 @@
 ]
 
 
-TEST_CASE_OPENSLIDE_0_L2 = [
+TEST_CASE_DEP_OPENSLIDE_0_L2 = [
     {
         "data": [{"image": FILE_PATH, "location": [0, 0], "label": [1]}],
         "region_size": (1, 1),
@@ -187,7 +192,7 @@
     [{"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[1]]])}],
 ]
 
-TEST_CASE_OPENSLIDE_1 = [
+TEST_CASE_DEP_OPENSLIDE_1 = [
     {
         "data": [{"image": FILE_PATH, "location": [10004, 20004], "label": [0, 0, 0, 1]}],
         "region_size": (8, 8),
@@ -204,27 +209,99 @@
 ]
 
 
-class TestPatchWSIDataset(unittest.TestCase):
-    def setUp(self):
-        hash_type = testing_data_config("images", FILE_KEY, "hash_type")
-        hash_val = testing_data_config("images", FILE_KEY, "hash_val")
-        download_url_or_skip_test(FILE_URL, FILE_PATH, hash_type=hash_type, hash_val=hash_val)
+TEST_CASE_0 = [
+    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "label": [1], "patch_level": 0}], "patch_size": (1, 1)},
+    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([1])},
+]
+
+TEST_CASE_0_L1 = [
+    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "label": [1]}], "patch_size": (1, 1), "patch_level": 1},
+    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([1])},
+]
 
+TEST_CASE_0_L2 = [
+    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "label": [1]}], "patch_size": (1, 1), "patch_level": 1},
+    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([1])},
+]
+TEST_CASE_1 = [
+    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "patch_size": 1, "label": [1]}]},
+    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([1])},
+]
+
+TEST_CASE_2 = [
+    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "label": [1]}], "patch_size": 1, "patch_level": 0},
+    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([1])},
+]
+
+TEST_CASE_3 = [
+    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "label": [[[0, 1], [1, 0]]]}], "patch_size": 1},
+    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[0, 1], [1, 0]]])},
+]
+
+TEST_CASE_4 = [
+    {
+        "data": [
+            {"image": FILE_PATH, "patch_location": [0, 0], "label": [[[0, 1], [1, 0]]]},
+            {"image": FILE_PATH, "patch_location": [0, 0], "label": [[[1, 0], [0, 0]]]},
+        ],
+        "patch_size": 1,
+    },
+    [
+        {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[0, 1], [1, 0]]])},
+        {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[1, 0], [0, 0]]])},
+    ],
+]
+
+TEST_CASE_5 = [
+    {
+        "data": [
+            {
+                "image": FILE_PATH,
+                "patch_location": [0, 0],
+                "label": [[[0, 1], [1, 0]]],
+                "patch_size": 1,
+                "patch_level": 1,
+            },
+            {
+                "image": FILE_PATH,
+                "patch_location": [100, 100],
+                "label": [[[1, 0], [0, 0]]],
+                "patch_size": 1,
+                "patch_level": 1,
+            },
+        ]
+    },
+    [
+        {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[0, 1], [1, 0]]])},
+        {"image": np.array([[[243]], [[243]], [[243]]], dtype=np.uint8), "label": np.array([[[1, 0], [0, 0]]])},
+    ],
+]
+
+
+@skipUnless(has_cim or has_osl or has_tiff, "Requires cucim, openslide, or tifffile!")
+def setUpModule():
+    hash_type = testing_data_config("images", FILE_KEY, "hash_type")
+    hash_val = testing_data_config("images", FILE_KEY, "hash_val")
+    download_url_or_skip_test(FILE_URL, FILE_PATH, hash_type=hash_type, hash_val=hash_val)
+
+
+@deprecated(since="0.8", msg_suffix="use tests for `monai.data.PatchWSIDataset` instead, `PatchWSIDatasetTests`.")
+class TestPatchWSIDatasetDeprecated(unittest.TestCase):
     @parameterized.expand(
         [
-            TEST_CASE_0,
-            TEST_CASE_0_L1,
-            TEST_CASE_0_L2,
-            TEST_CASE_1,
-            TEST_CASE_1_L0,
-            TEST_CASE_1_L1,
-            TEST_CASE_2,
-            TEST_CASE_3,
+            TEST_CASE_DEP_0,
+            TEST_CASE_DEP_0_L1,
+            TEST_CASE_DEP_0_L2,
+            TEST_CASE_DEP_1,
+            TEST_CASE_DEP_1_L0,
+            TEST_CASE_DEP_1_L1,
+            TEST_CASE_DEP_2,
+            TEST_CASE_DEP_3,
         ]
     )
     @skipUnless(has_cim, "Requires CuCIM")
     def test_read_patches_cucim(self, input_parameters, expected):
-        dataset = PatchWSIDataset(**input_parameters)
+        dataset = PatchWSIDatasetDeprecated(**input_parameters)
         samples = dataset[0]
         for i in range(len(samples)):
             self.assertTupleEqual(samples[i]["label"].shape, expected[i]["label"].shape)
@@ -234,16 +311,16 @@ def test_read_patches_cucim(self, input_parameters, expected):
 
     @parameterized.expand(
         [
-            TEST_CASE_OPENSLIDE_0,
-            TEST_CASE_OPENSLIDE_0_L0,
-            TEST_CASE_OPENSLIDE_0_L1,
-            TEST_CASE_OPENSLIDE_0_L2,
-            TEST_CASE_OPENSLIDE_1,
+            TEST_CASE_DEP_OPENSLIDE_0,
+            TEST_CASE_DEP_OPENSLIDE_0_L0,
+            TEST_CASE_DEP_OPENSLIDE_0_L1,
+            TEST_CASE_DEP_OPENSLIDE_0_L2,
+            TEST_CASE_DEP_OPENSLIDE_1,
         ]
     )
     @skipUnless(has_osl, "Requires OpenSlide")
     def test_read_patches_openslide(self, input_parameters, expected):
-        dataset = PatchWSIDataset(**input_parameters)
+        dataset = PatchWSIDatasetDeprecated(**input_parameters)
         samples = dataset[0]
         for i in range(len(samples)):
             self.assertTupleEqual(samples[i]["label"].shape, expected[i]["label"].shape)
@@ -252,5 +329,72 @@ def test_read_patches_openslide(self, input_parameters, expected):
             self.assertIsNone(assert_array_equal(samples[i]["image"], expected[i]["image"]))
 
 
+class PatchWSIDatasetTests:
+    class Tests(unittest.TestCase):
+        backend = None
+
+        @parameterized.expand([TEST_CASE_0, TEST_CASE_0_L1, TEST_CASE_0_L2, TEST_CASE_1, TEST_CASE_2, TEST_CASE_3])
+        def test_read_patches_str(self, input_parameters, expected):
+            dataset = PatchWSIDataset(reader=self.backend, **input_parameters)
+            sample = dataset[0]
+            self.assertTupleEqual(sample["label"].shape, expected["label"].shape)
+            self.assertTupleEqual(sample["image"].shape, expected["image"].shape)
+            self.assertIsNone(assert_array_equal(sample["label"], expected["label"]))
+            self.assertIsNone(assert_array_equal(sample["image"], expected["image"]))
+
+        @parameterized.expand([TEST_CASE_0, TEST_CASE_0_L1, TEST_CASE_0_L2, TEST_CASE_1, TEST_CASE_2, TEST_CASE_3])
+        def test_read_patches_class(self, input_parameters, expected):
+            if self.backend == "openslide":
+                reader = OpenSlideWSIReader
+            elif self.backend == "cucim":
+                reader = CuCIMWSIReader
+            else:
+                raise ValueError("Unsupported backend: {self.backend}")
+            dataset = PatchWSIDataset(reader=reader, **input_parameters)
+            sample = dataset[0]
+            self.assertTupleEqual(sample["label"].shape, expected["label"].shape)
+            self.assertTupleEqual(sample["image"].shape, expected["image"].shape)
+            self.assertIsNone(assert_array_equal(sample["label"], expected["label"]))
+            self.assertIsNone(assert_array_equal(sample["image"], expected["image"]))
+
+        @parameterized.expand([TEST_CASE_0, TEST_CASE_0_L1, TEST_CASE_0_L2, TEST_CASE_1, TEST_CASE_2, TEST_CASE_3])
+        def test_read_patches_object(self, input_parameters, expected):
+            if self.backend == "openslide":
+                reader = OpenSlideWSIReader(level=input_parameters.get("patch_level", 0))
+            elif self.backend == "cucim":
+                reader = CuCIMWSIReader(level=input_parameters.get("patch_level", 0))
+            else:
+                raise ValueError("Unsupported backend: {self.backend}")
+            dataset = PatchWSIDataset(reader=reader, **input_parameters)
+            sample = dataset[0]
+            self.assertTupleEqual(sample["label"].shape, expected["label"].shape)
+            self.assertTupleEqual(sample["image"].shape, expected["image"].shape)
+            self.assertIsNone(assert_array_equal(sample["label"], expected["label"]))
+            self.assertIsNone(assert_array_equal(sample["image"], expected["image"]))
+
+        @parameterized.expand([TEST_CASE_4, TEST_CASE_5])
+        def test_read_patches_str_multi(self, input_parameters, expected):
+            dataset = PatchWSIDataset(reader=self.backend, **input_parameters)
+            for i in range(len(dataset)):
+                self.assertTupleEqual(dataset[i]["label"].shape, expected[i]["label"].shape)
+                self.assertTupleEqual(dataset[i]["image"].shape, expected[i]["image"].shape)
+                self.assertIsNone(assert_array_equal(dataset[i]["label"], expected[i]["label"]))
+                self.assertIsNone(assert_array_equal(dataset[i]["image"], expected[i]["image"]))
+
+
+@skipUnless(has_cim, "Requires cucim")
+class TestPatchWSIDatasetCuCIM(PatchWSIDatasetTests.Tests):
+    @classmethod
+    def setUpClass(cls):
+        cls.backend = "cucim"
+
+
+@skipUnless(has_osl, "Requires openslide")
+class TestPatchWSIDatasetOpenSlide(PatchWSIDatasetTests.Tests):
+    @classmethod
+    def setUpClass(cls):
+        cls.backend = "openslide"
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/test_patch_wsi_dataset_new.py b/tests/test_patch_wsi_dataset_new.py
deleted file mode 100644
--- a/tests/test_patch_wsi_dataset_new.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# Copyright (c) MONAI Consortium
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#     http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import os
-import unittest
-from unittest import skipUnless
-
-import numpy as np
-from numpy.testing import assert_array_equal
-from parameterized import parameterized
-
-from monai.data import PatchWSIDataset
-from monai.data.wsi_reader import CuCIMWSIReader, OpenSlideWSIReader
-from monai.utils import optional_import
-from tests.utils import download_url_or_skip_test, testing_data_config
-
-cucim, has_cucim = optional_import("cucim")
-has_cucim = has_cucim and hasattr(cucim, "CuImage")
-openslide, has_osl = optional_import("openslide")
-imwrite, has_tiff = optional_import("tifffile", name="imwrite")
-_, has_codec = optional_import("imagecodecs")
-has_tiff = has_tiff and has_codec
-
-FILE_KEY = "wsi_img"
-FILE_URL = testing_data_config("images", FILE_KEY, "url")
-base_name, extension = os.path.basename(f"{FILE_URL}"), ".tiff"
-FILE_PATH = os.path.join(os.path.dirname(__file__), "testing_data", "temp_" + base_name + extension)
-
-TEST_CASE_0 = [
-    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "label": [1], "patch_level": 0}], "patch_size": (1, 1)},
-    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([1])},
-]
-
-TEST_CASE_0_L1 = [
-    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "label": [1]}], "patch_size": (1, 1), "patch_level": 1},
-    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([1])},
-]
-
-TEST_CASE_0_L2 = [
-    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "label": [1]}], "patch_size": (1, 1), "patch_level": 1},
-    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([1])},
-]
-TEST_CASE_1 = [
-    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "patch_size": 1, "label": [1]}]},
-    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([1])},
-]
-
-TEST_CASE_2 = [
-    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "label": [1]}], "patch_size": 1, "patch_level": 0},
-    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([1])},
-]
-
-TEST_CASE_3 = [
-    {"data": [{"image": FILE_PATH, "patch_location": [0, 0], "label": [[[0, 1], [1, 0]]]}], "patch_size": 1},
-    {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[0, 1], [1, 0]]])},
-]
-
-TEST_CASE_4 = [
-    {
-        "data": [
-            {"image": FILE_PATH, "patch_location": [0, 0], "label": [[[0, 1], [1, 0]]]},
-            {"image": FILE_PATH, "patch_location": [0, 0], "label": [[[1, 0], [0, 0]]]},
-        ],
-        "patch_size": 1,
-    },
-    [
-        {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[0, 1], [1, 0]]])},
-        {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[1, 0], [0, 0]]])},
-    ],
-]
-
-TEST_CASE_5 = [
-    {
-        "data": [
-            {
-                "image": FILE_PATH,
-                "patch_location": [0, 0],
-                "label": [[[0, 1], [1, 0]]],
-                "patch_size": 1,
-                "patch_level": 1,
-            },
-            {
-                "image": FILE_PATH,
-                "patch_location": [100, 100],
-                "label": [[[1, 0], [0, 0]]],
-                "patch_size": 1,
-                "patch_level": 1,
-            },
-        ]
-    },
-    [
-        {"image": np.array([[[239]], [[239]], [[239]]], dtype=np.uint8), "label": np.array([[[0, 1], [1, 0]]])},
-        {"image": np.array([[[243]], [[243]], [[243]]], dtype=np.uint8), "label": np.array([[[1, 0], [0, 0]]])},
-    ],
-]
-
-
-@skipUnless(has_cucim or has_osl or has_tiff, "Requires cucim, openslide, or tifffile!")
-def setUpModule():
-    hash_type = testing_data_config("images", FILE_KEY, "hash_type")
-    hash_val = testing_data_config("images", FILE_KEY, "hash_val")
-    download_url_or_skip_test(FILE_URL, FILE_PATH, hash_type=hash_type, hash_val=hash_val)
-
-
-class PatchWSIDatasetTests:
-    class Tests(unittest.TestCase):
-        backend = None
-
-        @parameterized.expand([TEST_CASE_0, TEST_CASE_0_L1, TEST_CASE_0_L2, TEST_CASE_1, TEST_CASE_2, TEST_CASE_3])
-        def test_read_patches_str(self, input_parameters, expected):
-            dataset = PatchWSIDataset(reader=self.backend, **input_parameters)
-            sample = dataset[0]
-            self.assertTupleEqual(sample["label"].shape, expected["label"].shape)
-            self.assertTupleEqual(sample["image"].shape, expected["image"].shape)
-            self.assertIsNone(assert_array_equal(sample["label"], expected["label"]))
-            self.assertIsNone(assert_array_equal(sample["image"], expected["image"]))
-
-        @parameterized.expand([TEST_CASE_0, TEST_CASE_0_L1, TEST_CASE_0_L2, TEST_CASE_1, TEST_CASE_2, TEST_CASE_3])
-        def test_read_patches_class(self, input_parameters, expected):
-            if self.backend == "openslide":
-                reader = OpenSlideWSIReader
-            elif self.backend == "cucim":
-                reader = CuCIMWSIReader
-            else:
-                raise ValueError("Unsupported backend: {self.backend}")
-            dataset = PatchWSIDataset(reader=reader, **input_parameters)
-            sample = dataset[0]
-            self.assertTupleEqual(sample["label"].shape, expected["label"].shape)
-            self.assertTupleEqual(sample["image"].shape, expected["image"].shape)
-            self.assertIsNone(assert_array_equal(sample["label"], expected["label"]))
-            self.assertIsNone(assert_array_equal(sample["image"], expected["image"]))
-
-        @parameterized.expand([TEST_CASE_0, TEST_CASE_0_L1, TEST_CASE_0_L2, TEST_CASE_1, TEST_CASE_2, TEST_CASE_3])
-        def test_read_patches_object(self, input_parameters, expected):
-            if self.backend == "openslide":
-                reader = OpenSlideWSIReader(level=input_parameters.get("patch_level", 0))
-            elif self.backend == "cucim":
-                reader = CuCIMWSIReader(level=input_parameters.get("patch_level", 0))
-            else:
-                raise ValueError("Unsupported backend: {self.backend}")
-            dataset = PatchWSIDataset(reader=reader, **input_parameters)
-            sample = dataset[0]
-            self.assertTupleEqual(sample["label"].shape, expected["label"].shape)
-            self.assertTupleEqual(sample["image"].shape, expected["image"].shape)
-            self.assertIsNone(assert_array_equal(sample["label"], expected["label"]))
-            self.assertIsNone(assert_array_equal(sample["image"], expected["image"]))
-
-        @parameterized.expand([TEST_CASE_4, TEST_CASE_5])
-        def test_read_patches_str_multi(self, input_parameters, expected):
-            dataset = PatchWSIDataset(reader=self.backend, **input_parameters)
-            for i in range(len(dataset)):
-                self.assertTupleEqual(dataset[i]["label"].shape, expected[i]["label"].shape)
-                self.assertTupleEqual(dataset[i]["image"].shape, expected[i]["image"].shape)
-                self.assertIsNone(assert_array_equal(dataset[i]["label"], expected[i]["label"]))
-                self.assertIsNone(assert_array_equal(dataset[i]["image"], expected[i]["image"]))
-
-
-@skipUnless(has_cucim, "Requires cucim")
-class TestPatchWSIDatasetCuCIM(PatchWSIDatasetTests.Tests):
-    @classmethod
-    def setUpClass(cls):
-        cls.backend = "cucim"
-
-
-@skipUnless(has_osl, "Requires openslide")
-class TestPatchWSIDatasetOpenSlide(PatchWSIDatasetTests.Tests):
-    @classmethod
-    def setUpClass(cls):
-        cls.backend = "openslide"
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/test_wsireader.py b/tests/test_wsireader.py
--- a/tests/test_wsireader.py
+++ b/tests/test_wsireader.py
@@ -14,16 +14,16 @@
 from unittest import skipUnless
 
 import numpy as np
-import torch
 from numpy.testing import assert_array_equal
 from parameterized import parameterized
 
 from monai.data import DataLoader, Dataset
-from monai.data.image_reader import WSIReader
+from monai.data.image_reader import WSIReader as WSIReaderDeprecated
+from monai.data.wsi_reader import WSIReader
 from monai.transforms import Compose, FromMetaTensord, LoadImaged, ToTensord
-from monai.utils import first, optional_import
+from monai.utils import deprecated, first, optional_import
 from monai.utils.enums import PostFix
-from tests.utils import download_url_or_skip_test, testing_data_config
+from tests.utils import assert_allclose, download_url_or_skip_test, testing_data_config
 
 cucim, has_cucim = optional_import("cucim")
 has_cucim = has_cucim and hasattr(cucim, "CuImage")
@@ -44,19 +44,19 @@
 
 TEST_CASE_TRANSFORM_0 = [FILE_PATH, 4, (HEIGHT // 16, WIDTH // 16), (1, 3, HEIGHT // 16, WIDTH // 16)]
 
-TEST_CASE_1 = [
+TEST_CASE_DEP_1 = [
     FILE_PATH,
     {"location": (HEIGHT // 2, WIDTH // 2), "size": (2, 1), "level": 0},
     np.array([[[246], [246]], [[246], [246]], [[246], [246]]]),
 ]
 
-TEST_CASE_2 = [
+TEST_CASE_DEP_2 = [
     FILE_PATH,
     {"location": (0, 0), "size": (2, 1), "level": 2},
     np.array([[[239], [239]], [[239], [239]], [[239], [239]]]),
 ]
 
-TEST_CASE_3 = [
+TEST_CASE_DEP_3 = [
     FILE_PATH,
     {"location": (0, 0), "size": (8, 8), "level": 2, "grid_shape": (2, 1), "patch_size": 2},
     np.array(
@@ -67,18 +67,58 @@
     ),
 ]
 
-TEST_CASE_4 = [
+TEST_CASE_DEP_4 = [
     FILE_PATH,
     {"location": (0, 0), "size": (8, 8), "level": 2, "grid_shape": (2, 1), "patch_size": 1},
     np.array([[[[239]], [[239]], [[239]]], [[[243]], [[243]], [[243]]]]),
 ]
 
-TEST_CASE_5 = [
+TEST_CASE_DEP_5 = [
     FILE_PATH,
     {"location": (HEIGHT - 2, WIDTH - 2), "level": 0, "grid_shape": (1, 1)},
     np.array([[[239, 239], [239, 239]], [[239, 239], [239, 239]], [[237, 237], [237, 237]]]),
 ]
 
+TEST_CASE_1 = [
+    FILE_PATH,
+    {},
+    {"location": (HEIGHT // 2, WIDTH // 2), "size": (2, 1), "level": 0},
+    np.array([[[246], [246]], [[246], [246]], [[246], [246]]]),
+]
+
+TEST_CASE_2 = [
+    FILE_PATH,
+    {},
+    {"location": (0, 0), "size": (2, 1), "level": 2},
+    np.array([[[239], [239]], [[239], [239]], [[239], [239]]]),
+]
+
+TEST_CASE_3 = [
+    FILE_PATH,
+    {"channel_dim": -1},
+    {"location": (HEIGHT // 2, WIDTH // 2), "size": (2, 1), "level": 0},
+    np.moveaxis(np.array([[[246], [246]], [[246], [246]], [[246], [246]]]), 0, -1),
+]
+
+TEST_CASE_4 = [
+    FILE_PATH,
+    {"channel_dim": 2},
+    {"location": (0, 0), "size": (2, 1), "level": 2},
+    np.moveaxis(np.array([[[239], [239]], [[239], [239]], [[239], [239]]]), 0, -1),
+]
+
+TEST_CASE_MULTI_WSI = [
+    [FILE_PATH, FILE_PATH],
+    {"location": (0, 0), "size": (2, 1), "level": 2},
+    np.concatenate(
+        [
+            np.array([[[239], [239]], [[239], [239]], [[239], [239]]]),
+            np.array([[[239], [239]], [[239], [239]], [[239], [239]]]),
+        ],
+        axis=0,
+    ),
+]
+
 
 TEST_CASE_RGB_0 = [np.ones((3, 2, 2), dtype=np.uint8)]  # CHW
 
@@ -108,6 +148,20 @@ def save_rgba_tiff(array: np.ndarray, filename: str, mode: str):
     return filename
 
 
+def save_gray_tiff(array: np.ndarray, filename: str):
+    """
+    Save numpy array into a TIFF file
+
+    Args:
+        array: numpy ndarray with any shape
+        filename: the filename to be used for the tiff file.
+    """
+    img_gray = array
+    imwrite(filename, img_gray, shape=img_gray.shape)
+
+    return filename
+
+
 @skipUnless(has_cucim or has_osl or has_tiff, "Requires cucim, openslide, or tifffile!")
 def setUpModule():
     hash_type = testing_data_config("images", FILE_KEY, "hash_type")
@@ -115,21 +169,22 @@ def setUpModule():
     download_url_or_skip_test(FILE_URL, FILE_PATH, hash_type=hash_type, hash_val=hash_val)
 
 
-class WSIReaderTests:
+@deprecated(since="0.8", msg_suffix="use tests for `monai.wsi_reader.WSIReader` instead, `WSIReaderTests`.")
+class WSIReaderDeprecatedTests:
     class Tests(unittest.TestCase):
         backend = None
 
         @parameterized.expand([TEST_CASE_0])
         def test_read_whole_image(self, file_path, level, expected_shape):
-            reader = WSIReader(self.backend, level=level)
+            reader = WSIReaderDeprecated(self.backend, level=level)
             with reader.read(file_path) as img_obj:
                 img = reader.get_data(img_obj)[0]
             self.assertTupleEqual(img.shape, expected_shape)
 
-        @parameterized.expand([TEST_CASE_1, TEST_CASE_2, TEST_CASE_5])
+        @parameterized.expand([TEST_CASE_DEP_1, TEST_CASE_DEP_2, TEST_CASE_DEP_5])
         def test_read_region(self, file_path, patch_info, expected_img):
             kwargs = {"name": None, "offset": None} if self.backend == "tifffile" else {}
-            reader = WSIReader(self.backend, **kwargs)
+            reader = WSIReaderDeprecated(self.backend, **kwargs)
             with reader.read(file_path, **kwargs) as img_obj:
                 if self.backend == "tifffile":
                     with self.assertRaises(ValueError):
@@ -143,9 +198,9 @@ def test_read_region(self, file_path, patch_info, expected_img):
                     self.assertTupleEqual(img.shape, expected_img.shape)
                     self.assertIsNone(assert_array_equal(img, expected_img))
 
-        @parameterized.expand([TEST_CASE_3, TEST_CASE_4])
+        @parameterized.expand([TEST_CASE_DEP_3, TEST_CASE_DEP_4])
         def test_read_patches(self, file_path, patch_info, expected_img):
-            reader = WSIReader(self.backend)
+            reader = WSIReaderDeprecated(self.backend)
             with reader.read(file_path) as img_obj:
                 if self.backend == "tifffile":
                     with self.assertRaises(ValueError):
@@ -155,6 +210,115 @@ def test_read_patches(self, file_path, patch_info, expected_img):
                     self.assertTupleEqual(img.shape, expected_img.shape)
                     self.assertIsNone(assert_array_equal(img, expected_img))
 
+        @parameterized.expand([TEST_CASE_RGB_0, TEST_CASE_RGB_1])
+        @skipUnless(has_tiff, "Requires tifffile.")
+        def test_read_rgba(self, img_expected):
+            # skip for OpenSlide since not working with images without tiles
+            if self.backend == "openslide":
+                return
+            image = {}
+            reader = WSIReaderDeprecated(self.backend)
+            for mode in ["RGB", "RGBA"]:
+                file_path = save_rgba_tiff(
+                    img_expected,
+                    os.path.join(os.path.dirname(__file__), "testing_data", f"temp_tiff_image_{mode}.tiff"),
+                    mode=mode,
+                )
+                with reader.read(file_path) as img_obj:
+                    image[mode], _ = reader.get_data(img_obj)
+
+            self.assertIsNone(assert_array_equal(image["RGB"], img_expected))
+            self.assertIsNone(assert_array_equal(image["RGBA"], img_expected))
+
+        @parameterized.expand([TEST_CASE_ERROR_0C, TEST_CASE_ERROR_1C, TEST_CASE_ERROR_2C, TEST_CASE_ERROR_3D])
+        @skipUnless(has_tiff, "Requires tifffile.")
+        def test_read_malformats(self, img_expected):
+            if self.backend == "cucim" and (len(img_expected.shape) < 3 or img_expected.shape[2] == 1):
+                # Until cuCIM addresses https://github.com/rapidsai/cucim/issues/230
+                return
+            reader = WSIReaderDeprecated(self.backend)
+            file_path = os.path.join(os.path.dirname(__file__), "testing_data", "temp_tiff_image_gray.tiff")
+            imwrite(file_path, img_expected, shape=img_expected.shape)
+            with self.assertRaises((RuntimeError, ValueError, openslide.OpenSlideError if has_osl else ValueError)):
+                with reader.read(file_path) as img_obj:
+                    reader.get_data(img_obj)
+
+        @parameterized.expand([TEST_CASE_TRANSFORM_0])
+        def test_with_dataloader(self, file_path, level, expected_spatial_shape, expected_shape):
+            train_transform = Compose(
+                [
+                    LoadImaged(keys=["image"], reader=WSIReaderDeprecated, backend=self.backend, level=level),
+                    FromMetaTensord(keys=["image"]),
+                    ToTensord(keys=["image"]),
+                ]
+            )
+            dataset = Dataset([{"image": file_path}], transform=train_transform)
+            data_loader = DataLoader(dataset)
+            data: dict = first(data_loader)
+            for s in data[PostFix.meta("image")]["spatial_shape"]:
+                assert_allclose(s, expected_spatial_shape, type_test=False)
+            self.assertTupleEqual(data["image"].shape, expected_shape)
+
+
+class WSIReaderTests:
+    class Tests(unittest.TestCase):
+        backend = None
+
+        @parameterized.expand([TEST_CASE_0])
+        def test_read_whole_image(self, file_path, level, expected_shape):
+            reader = WSIReader(self.backend, level=level)
+            with reader.read(file_path) as img_obj:
+                img, meta = reader.get_data(img_obj)
+            self.assertTupleEqual(img.shape, expected_shape)
+            self.assertEqual(meta["backend"], self.backend)
+            self.assertEqual(meta["path"], str(os.path.abspath(file_path)))
+            self.assertEqual(meta["patch_level"], level)
+            assert_array_equal(meta["patch_size"], expected_shape[1:])
+            assert_array_equal(meta["patch_location"], (0, 0))
+
+        @parameterized.expand([TEST_CASE_1, TEST_CASE_2, TEST_CASE_3, TEST_CASE_4])
+        def test_read_region(self, file_path, kwargs, patch_info, expected_img):
+            reader = WSIReader(self.backend, **kwargs)
+            with reader.read(file_path) as img_obj:
+                if self.backend == "tifffile":
+                    with self.assertRaises(ValueError):
+                        reader.get_data(img_obj, **patch_info)[0]
+                else:
+                    # Read twice to check multiple calls
+                    img, meta = reader.get_data(img_obj, **patch_info)
+                    img2 = reader.get_data(img_obj, **patch_info)[0]
+                    self.assertTupleEqual(img.shape, img2.shape)
+                    self.assertIsNone(assert_array_equal(img, img2))
+                    self.assertTupleEqual(img.shape, expected_img.shape)
+                    self.assertIsNone(assert_array_equal(img, expected_img))
+                    self.assertEqual(meta["backend"], self.backend)
+                    self.assertEqual(meta["path"], str(os.path.abspath(file_path)))
+                    self.assertEqual(meta["patch_level"], patch_info["level"])
+                    assert_array_equal(meta["patch_size"], patch_info["size"])
+                    assert_array_equal(meta["patch_location"], patch_info["location"])
+
+        @parameterized.expand([TEST_CASE_MULTI_WSI])
+        def test_read_region_multi_wsi(self, file_path_list, patch_info, expected_img):
+            kwargs = {"name": None, "offset": None} if self.backend == "tifffile" else {}
+            reader = WSIReader(self.backend, **kwargs)
+            img_obj_list = reader.read(file_path_list, **kwargs)
+            if self.backend == "tifffile":
+                with self.assertRaises(ValueError):
+                    reader.get_data(img_obj_list, **patch_info)[0]
+            else:
+                # Read twice to check multiple calls
+                img, meta = reader.get_data(img_obj_list, **patch_info)
+                img2 = reader.get_data(img_obj_list, **patch_info)[0]
+                self.assertTupleEqual(img.shape, img2.shape)
+                self.assertIsNone(assert_array_equal(img, img2))
+                self.assertTupleEqual(img.shape, expected_img.shape)
+                self.assertIsNone(assert_array_equal(img, expected_img))
+                self.assertEqual(meta["backend"], self.backend)
+                self.assertEqual(meta["path"][0], str(os.path.abspath(file_path_list[0])))
+                self.assertEqual(meta["patch_level"][0], patch_info["level"])
+                assert_array_equal(meta["patch_size"][0], expected_img.shape[1:])
+                assert_array_equal(meta["patch_location"][0], patch_info["location"])
+
         @parameterized.expand([TEST_CASE_RGB_0, TEST_CASE_RGB_1])
         @skipUnless(has_tiff, "Requires tifffile.")
         def test_read_rgba(self, img_expected):
@@ -201,30 +365,61 @@ def test_with_dataloader(self, file_path, level, expected_spatial_shape, expecte
             data_loader = DataLoader(dataset)
             data: dict = first(data_loader)
             for s in data[PostFix.meta("image")]["spatial_shape"]:
-                torch.testing.assert_allclose(s, expected_spatial_shape)
+                assert_allclose(s, expected_spatial_shape, type_test=False)
             self.assertTupleEqual(data["image"].shape, expected_shape)
 
+        @parameterized.expand([TEST_CASE_TRANSFORM_0])
+        def test_with_dataloader_batch(self, file_path, level, expected_spatial_shape, expected_shape):
+            train_transform = Compose(
+                [
+                    LoadImaged(keys=["image"], reader=WSIReader, backend=self.backend, level=level),
+                    FromMetaTensord(keys=["image"]),
+                    ToTensord(keys=["image"]),
+                ]
+            )
+            dataset = Dataset([{"image": file_path}, {"image": file_path}], transform=train_transform)
+            batch_size = 2
+            data_loader = DataLoader(dataset, batch_size=batch_size)
+            data: dict = first(data_loader)
+            for s in data[PostFix.meta("image")]["spatial_shape"]:
+                assert_allclose(s, expected_spatial_shape, type_test=False)
+            self.assertTupleEqual(data["image"].shape, (batch_size, *expected_shape[1:]))
+
 
 @skipUnless(has_cucim, "Requires cucim")
-class TestCuCIM(WSIReaderTests.Tests):
+class TestCuCIMDeprecated(WSIReaderDeprecatedTests.Tests):
     @classmethod
     def setUpClass(cls):
         cls.backend = "cucim"
 
 
 @skipUnless(has_osl, "Requires OpenSlide")
-class TestOpenSlide(WSIReaderTests.Tests):
+class TestOpenSlideDeprecated(WSIReaderDeprecatedTests.Tests):
     @classmethod
     def setUpClass(cls):
         cls.backend = "openslide"
 
 
 @skipUnless(has_tiff, "Requires TiffFile")
-class TestTiffFile(WSIReaderTests.Tests):
+class TestTiffFileDeprecated(WSIReaderDeprecatedTests.Tests):
     @classmethod
     def setUpClass(cls):
         cls.backend = "tifffile"
 
 
+@skipUnless(has_cucim, "Requires cucim")
+class TestCuCIM(WSIReaderTests.Tests):
+    @classmethod
+    def setUpClass(cls):
+        cls.backend = "cucim"
+
+
+@skipUnless(has_osl, "Requires openslide")
+class TestOpenSlide(WSIReaderTests.Tests):
+    @classmethod
+    def setUpClass(cls):
+        cls.backend = "openslide"
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/test_wsireader_new.py b/tests/test_wsireader_new.py
deleted file mode 100644
--- a/tests/test_wsireader_new.py
+++ /dev/null
@@ -1,277 +0,0 @@
-# Copyright (c) MONAI Consortium
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#     http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import os
-import unittest
-from unittest import skipUnless
-
-import numpy as np
-from numpy.testing import assert_array_equal
-from parameterized import parameterized
-
-from monai.data import DataLoader, Dataset
-from monai.data.wsi_reader import WSIReader
-from monai.transforms import Compose, FromMetaTensord, LoadImaged, ToTensord
-from monai.utils import first, optional_import
-from monai.utils.enums import PostFix
-from tests.utils import assert_allclose, download_url_or_skip_test, testing_data_config
-
-cucim, has_cucim = optional_import("cucim")
-has_cucim = has_cucim and hasattr(cucim, "CuImage")
-openslide, has_osl = optional_import("openslide")
-imwrite, has_tiff = optional_import("tifffile", name="imwrite")
-_, has_codec = optional_import("imagecodecs")
-has_tiff = has_tiff and has_codec
-
-FILE_KEY = "wsi_img"
-FILE_URL = testing_data_config("images", FILE_KEY, "url")
-base_name, extension = os.path.basename(f"{FILE_URL}"), ".tiff"
-FILE_PATH = os.path.join(os.path.dirname(__file__), "testing_data", "temp_" + base_name + extension)
-
-HEIGHT = 32914
-WIDTH = 46000
-
-TEST_CASE_0 = [FILE_PATH, 2, (3, HEIGHT // 4, WIDTH // 4)]
-
-TEST_CASE_TRANSFORM_0 = [FILE_PATH, 4, (HEIGHT // 16, WIDTH // 16), (1, 3, HEIGHT // 16, WIDTH // 16)]
-
-TEST_CASE_1 = [
-    FILE_PATH,
-    {},
-    {"location": (HEIGHT // 2, WIDTH // 2), "size": (2, 1), "level": 0},
-    np.array([[[246], [246]], [[246], [246]], [[246], [246]]]),
-]
-
-TEST_CASE_2 = [
-    FILE_PATH,
-    {},
-    {"location": (0, 0), "size": (2, 1), "level": 2},
-    np.array([[[239], [239]], [[239], [239]], [[239], [239]]]),
-]
-
-TEST_CASE_3 = [
-    FILE_PATH,
-    {"channel_dim": -1},
-    {"location": (HEIGHT // 2, WIDTH // 2), "size": (2, 1), "level": 0},
-    np.moveaxis(np.array([[[246], [246]], [[246], [246]], [[246], [246]]]), 0, -1),
-]
-
-TEST_CASE_4 = [
-    FILE_PATH,
-    {"channel_dim": 2},
-    {"location": (0, 0), "size": (2, 1), "level": 2},
-    np.moveaxis(np.array([[[239], [239]], [[239], [239]], [[239], [239]]]), 0, -1),
-]
-
-TEST_CASE_MULTI_WSI = [
-    [FILE_PATH, FILE_PATH],
-    {"location": (0, 0), "size": (2, 1), "level": 2},
-    np.concatenate(
-        [
-            np.array([[[239], [239]], [[239], [239]], [[239], [239]]]),
-            np.array([[[239], [239]], [[239], [239]], [[239], [239]]]),
-        ],
-        axis=0,
-    ),
-]
-
-
-TEST_CASE_RGB_0 = [np.ones((3, 2, 2), dtype=np.uint8)]  # CHW
-
-TEST_CASE_RGB_1 = [np.ones((3, 100, 100), dtype=np.uint8)]  # CHW
-
-TEST_CASE_ERROR_0C = [np.ones((16, 16), dtype=np.uint8)]  # no color channel
-TEST_CASE_ERROR_1C = [np.ones((16, 16, 1), dtype=np.uint8)]  # one color channel
-TEST_CASE_ERROR_2C = [np.ones((16, 16, 2), dtype=np.uint8)]  # two color channels
-TEST_CASE_ERROR_3D = [np.ones((16, 16, 16, 3), dtype=np.uint8)]  # 3D + color
-
-
-def save_rgba_tiff(array: np.ndarray, filename: str, mode: str):
-    """
-    Save numpy array into a TIFF RGB/RGBA file
-
-    Args:
-        array: numpy ndarray with the shape of CxHxW and C==3 representing a RGB image
-        filename: the filename to be used for the tiff file. '_RGB.tiff' or '_RGBA.tiff' will be appended to this filename.
-        mode: RGB or RGBA
-    """
-    if mode == "RGBA":
-        array = np.concatenate([array, 255 * np.ones_like(array[0])[np.newaxis]]).astype(np.uint8)
-
-    img_rgb = array.transpose(1, 2, 0)
-    imwrite(filename, img_rgb, shape=img_rgb.shape, tile=(16, 16))
-
-    return filename
-
-
-def save_gray_tiff(array: np.ndarray, filename: str):
-    """
-    Save numpy array into a TIFF file
-
-    Args:
-        array: numpy ndarray with any shape
-        filename: the filename to be used for the tiff file.
-    """
-    img_gray = array
-    imwrite(filename, img_gray, shape=img_gray.shape)
-
-    return filename
-
-
-@skipUnless(has_cucim or has_osl or has_tiff, "Requires cucim, openslide, or tifffile!")
-def setUpModule():
-    hash_type = testing_data_config("images", FILE_KEY, "hash_type")
-    hash_val = testing_data_config("images", FILE_KEY, "hash_val")
-    download_url_or_skip_test(FILE_URL, FILE_PATH, hash_type=hash_type, hash_val=hash_val)
-
-
-class WSIReaderTests:
-    class Tests(unittest.TestCase):
-        backend = None
-
-        @parameterized.expand([TEST_CASE_0])
-        def test_read_whole_image(self, file_path, level, expected_shape):
-            reader = WSIReader(self.backend, level=level)
-            with reader.read(file_path) as img_obj:
-                img, meta = reader.get_data(img_obj)
-            self.assertTupleEqual(img.shape, expected_shape)
-            self.assertEqual(meta["backend"], self.backend)
-            self.assertEqual(meta["path"], str(os.path.abspath(file_path)))
-            self.assertEqual(meta["patch_level"], level)
-            assert_array_equal(meta["patch_size"], expected_shape[1:])
-            assert_array_equal(meta["patch_location"], (0, 0))
-
-        @parameterized.expand([TEST_CASE_1, TEST_CASE_2, TEST_CASE_3, TEST_CASE_4])
-        def test_read_region(self, file_path, kwargs, patch_info, expected_img):
-            reader = WSIReader(self.backend, **kwargs)
-            with reader.read(file_path) as img_obj:
-                if self.backend == "tifffile":
-                    with self.assertRaises(ValueError):
-                        reader.get_data(img_obj, **patch_info)[0]
-                else:
-                    # Read twice to check multiple calls
-                    img, meta = reader.get_data(img_obj, **patch_info)
-                    img2 = reader.get_data(img_obj, **patch_info)[0]
-                    self.assertTupleEqual(img.shape, img2.shape)
-                    self.assertIsNone(assert_array_equal(img, img2))
-                    self.assertTupleEqual(img.shape, expected_img.shape)
-                    self.assertIsNone(assert_array_equal(img, expected_img))
-                    self.assertEqual(meta["backend"], self.backend)
-                    self.assertEqual(meta["path"], str(os.path.abspath(file_path)))
-                    self.assertEqual(meta["patch_level"], patch_info["level"])
-                    assert_array_equal(meta["patch_size"], patch_info["size"])
-                    assert_array_equal(meta["patch_location"], patch_info["location"])
-
-        @parameterized.expand([TEST_CASE_MULTI_WSI])
-        def test_read_region_multi_wsi(self, file_path_list, patch_info, expected_img):
-            kwargs = {"name": None, "offset": None} if self.backend == "tifffile" else {}
-            reader = WSIReader(self.backend, **kwargs)
-            img_obj_list = reader.read(file_path_list, **kwargs)
-            if self.backend == "tifffile":
-                with self.assertRaises(ValueError):
-                    reader.get_data(img_obj_list, **patch_info)[0]
-            else:
-                # Read twice to check multiple calls
-                img, meta = reader.get_data(img_obj_list, **patch_info)
-                img2 = reader.get_data(img_obj_list, **patch_info)[0]
-                self.assertTupleEqual(img.shape, img2.shape)
-                self.assertIsNone(assert_array_equal(img, img2))
-                self.assertTupleEqual(img.shape, expected_img.shape)
-                self.assertIsNone(assert_array_equal(img, expected_img))
-                self.assertEqual(meta["backend"], self.backend)
-                self.assertEqual(meta["path"][0], str(os.path.abspath(file_path_list[0])))
-                self.assertEqual(meta["patch_level"][0], patch_info["level"])
-                assert_array_equal(meta["patch_size"][0], expected_img.shape[1:])
-                assert_array_equal(meta["patch_location"][0], patch_info["location"])
-
-        @parameterized.expand([TEST_CASE_RGB_0, TEST_CASE_RGB_1])
-        @skipUnless(has_tiff, "Requires tifffile.")
-        def test_read_rgba(self, img_expected):
-            # skip for OpenSlide since not working with images without tiles
-            if self.backend == "openslide":
-                return
-            image = {}
-            reader = WSIReader(self.backend)
-            for mode in ["RGB", "RGBA"]:
-                file_path = save_rgba_tiff(
-                    img_expected,
-                    os.path.join(os.path.dirname(__file__), "testing_data", f"temp_tiff_image_{mode}.tiff"),
-                    mode=mode,
-                )
-                with reader.read(file_path) as img_obj:
-                    image[mode], _ = reader.get_data(img_obj)
-
-            self.assertIsNone(assert_array_equal(image["RGB"], img_expected))
-            self.assertIsNone(assert_array_equal(image["RGBA"], img_expected))
-
-        @parameterized.expand([TEST_CASE_ERROR_0C, TEST_CASE_ERROR_1C, TEST_CASE_ERROR_2C, TEST_CASE_ERROR_3D])
-        @skipUnless(has_tiff, "Requires tifffile.")
-        def test_read_malformats(self, img_expected):
-            if self.backend == "cucim" and (len(img_expected.shape) < 3 or img_expected.shape[2] == 1):
-                # Until cuCIM addresses https://github.com/rapidsai/cucim/issues/230
-                return
-            reader = WSIReader(self.backend)
-            file_path = os.path.join(os.path.dirname(__file__), "testing_data", "temp_tiff_image_gray.tiff")
-            imwrite(file_path, img_expected, shape=img_expected.shape)
-            with self.assertRaises((RuntimeError, ValueError, openslide.OpenSlideError if has_osl else ValueError)):
-                with reader.read(file_path) as img_obj:
-                    reader.get_data(img_obj)
-
-        @parameterized.expand([TEST_CASE_TRANSFORM_0])
-        def test_with_dataloader(self, file_path, level, expected_spatial_shape, expected_shape):
-            train_transform = Compose(
-                [
-                    LoadImaged(keys=["image"], reader=WSIReader, backend=self.backend, level=level),
-                    FromMetaTensord(keys=["image"]),
-                    ToTensord(keys=["image"]),
-                ]
-            )
-            dataset = Dataset([{"image": file_path}], transform=train_transform)
-            data_loader = DataLoader(dataset)
-            data: dict = first(data_loader)
-            for s in data[PostFix.meta("image")]["spatial_shape"]:
-                assert_allclose(s, expected_spatial_shape, type_test=False)
-            self.assertTupleEqual(data["image"].shape, expected_shape)
-
-        @parameterized.expand([TEST_CASE_TRANSFORM_0])
-        def test_with_dataloader_batch(self, file_path, level, expected_spatial_shape, expected_shape):
-            train_transform = Compose(
-                [
-                    LoadImaged(keys=["image"], reader=WSIReader, backend=self.backend, level=level),
-                    FromMetaTensord(keys=["image"]),
-                    ToTensord(keys=["image"]),
-                ]
-            )
-            dataset = Dataset([{"image": file_path}, {"image": file_path}], transform=train_transform)
-            batch_size = 2
-            data_loader = DataLoader(dataset, batch_size=batch_size)
-            data: dict = first(data_loader)
-            for s in data[PostFix.meta("image")]["spatial_shape"]:
-                assert_allclose(s, expected_spatial_shape, type_test=False)
-            self.assertTupleEqual(data["image"].shape, (batch_size, *expected_shape[1:]))
-
-
-@skipUnless(has_cucim, "Requires cucim")
-class TestCuCIM(WSIReaderTests.Tests):
-    @classmethod
-    def setUpClass(cls):
-        cls.backend = "cucim"
-
-
-@skipUnless(has_osl, "Requires openslide")
-class TestOpenSlide(WSIReaderTests.Tests):
-    @classmethod
-    def setUpClass(cls):
-        cls.backend = "openslide"
-
-
-if __name__ == "__main__":
-    unittest.main()