Skip to content

Commit 88afc02

Browse files
author
Vasu Jaganath
committed
convert legacy unittest to pytest
1 parent 9d4a74c commit 88afc02

File tree

4 files changed

+158
-112
lines changed

4 files changed

+158
-112
lines changed

.github/workflows/publish_pypi.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ jobs:
8484
CIBW_ARCHS: ${{ matrix.cibw_archs }}
8585
CIBW_BEFORE_TEST_LINUX: dnf -y install maven java
8686
CIBW_TEST_REQUIRES: bfio>=2.4.0 tensorstore numpy pytest
87-
CIBW_TEST_COMMAND: python -W default -m unittest discover -s {project}/tests -v
87+
CIBW_TEST_COMMAND: python -W default -m pytest -vv -s {project}/tests/python
8888

8989
- name: Install Dependencies
9090
run: python -m pip install --upgrade twine requests
@@ -144,7 +144,7 @@ jobs:
144144
MACOSX_DEPLOYMENT_TARGET=11.0 DYLD_LIBRARY_PATH=$REPAIR_LIBRARY_PATH delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel} -e libc++ -e libunwind
145145
CIBW_ARCHS: ${{ matrix.cibw_archs }}
146146
CIBW_TEST_REQUIRES: bfio>=2.4.0 tensorstore numpy pytest
147-
CIBW_TEST_COMMAND: python -W default -m unittest discover -s {project}/tests -v
147+
CIBW_TEST_COMMAND: python -W default -m pytest -vv -s {project}/tests/python
148148

149149
- name: Install Dependencies
150150
run: python -m pip install --upgrade twine requests

.github/workflows/wheel_build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ jobs:
8383
CIBW_ARCHS: ${{ matrix.cibw_archs }}
8484
CIBW_BEFORE_TEST_LINUX: dnf -y install maven java
8585
CIBW_TEST_REQUIRES: bfio>=2.4.0 tensorstore numpy pytest
86-
CIBW_TEST_COMMAND: python -W default -m unittest discover -s {project}/tests -v
86+
CIBW_TEST_COMMAND: python -W default -m pytest -vv -s {project}/tests/python
8787

8888
- name: Upload Artifact
8989
uses: actions/upload-artifact@v4
@@ -140,7 +140,7 @@ jobs:
140140
MACOSX_DEPLOYMENT_TARGET=11.0 DYLD_LIBRARY_PATH=$REPAIR_LIBRARY_PATH delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel} -e libc++ -e libunwind
141141
CIBW_ARCHS: ${{ matrix.cibw_archs }}
142142
CIBW_TEST_REQUIRES: bfio>=2.4.0 tensorstore numpy pytest
143-
CIBW_TEST_COMMAND: python -W default -m unittest discover -s {project}/tests -v
143+
CIBW_TEST_COMMAND: python -W default -m pytest -vv -s {project}/tests/python
144144

145145
- name: Upload Artifact
146146
uses: actions/upload-artifact@v4

pytest.ini

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ testpaths =
44
python_files =
55
test_*.py
66
addopts =
7-
--ignore=tests/python/test_read.py
87
--ignore=tests/python/test_pytest_bridge.py
98
markers =
109
integration: integration tests that touch disk/network

tests/python/test_read.py

Lines changed: 154 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,170 @@
1-
import unittest
2-
import io, pathlib, shutil, logging, sys
1+
import logging
2+
from pathlib import Path
3+
34
import bfio
45
import numpy as np
6+
import pytest
57
import tensorstore as ts
6-
import argolid
78
import ome_types
9+
import argolid
810

9-
TEST_DIR = pathlib.Path(__file__).with_name("data")
10-
11-
logging.basicConfig(
12-
format="%(asctime)s - %(name)-8s - %(levelname)-8s - %(message)s",
13-
datefmt="%d-%b-%y %H:%M:%S",
14-
)
1511
logger = logging.getLogger("bfio.test")
1612

17-
if "-v" in sys.argv:
18-
logger.setLevel(logging.INFO)
1913

20-
def setUpModule():
21-
"""Create images for testing"""
22-
TEST_DIR.mkdir(exist_ok=True)
14+
15+
@pytest.fixture(scope="session")
16+
def test_dir(tmp_path_factory: pytest.TempPathFactory) -> Path:
17+
"""A per-session temp dir for generating all images and outputs."""
18+
return tmp_path_factory.mktemp("argolid_test_data")
19+
20+
21+
@pytest.fixture(scope="session")
22+
def input_tiles_dir(test_dir: Path) -> Path:
23+
"""Create the input OME-TIFF tiles once per test session."""
24+
tiles_dir = test_dir / "data"
25+
tiles_dir.mkdir(parents=True, exist_ok=True)
26+
2327
for ch in range(4):
2428
for r in range(3):
2529
for c in range(2):
26-
image_name = f"{TEST_DIR}/test_image_r{r}_c{c}_ch{ch}.ome.tiff"
27-
with bfio.BioWriter(image_name, backend="python", X=1024, Y=1024, C=1, Z=1, T=1) as bw:
28-
test_val = np.ones((1024,1024), dtype=np.uint16)
30+
image_path = tiles_dir / f"test_image_r{r}_c{c}_ch{ch}.ome.tiff"
31+
32+
# BioWriter expects X,Y ordering as named args, data written as [X, Y, Z, C, T]
33+
with bfio.BioWriter(
34+
str(image_path),
35+
backend="python",
36+
X=1024,
37+
Y=1024,
38+
C=1,
39+
Z=1,
40+
T=1,
41+
) as bw:
42+
test_val = np.ones((1024, 1024), dtype=np.uint16)
43+
2944
# setting up test data for mean sampling
30-
if r==0 and c==0:
31-
test_val[0,0] = 8
32-
test_val[0,1] = 9
33-
test_val[1,0] = 7
34-
test_val[1,1] = 14
45+
if r == 0 and c == 0:
46+
test_val[0, 0] = 8
47+
test_val[0, 1] = 9
48+
test_val[1, 0] = 7
49+
test_val[1, 1] = 14
50+
3551
# setting up test data for mode sampling
36-
if r==0 and c==1 and ch==1:
37-
test_val[0,0] = 8
38-
test_val[0,1] = 8
39-
test_val[1,0] = 9
40-
test_val[1,1] = 9
41-
42-
bw[0:1024, 0:1024, 0, 0, 0] = test_val
43-
44-
45-
46-
class TestSingleChannelVivPyramidFromImageCollection(unittest.TestCase):
47-
@classmethod
48-
def setUpClass(self):
49-
input_dir = f"{TEST_DIR}"
50-
file_pattern = "test_image_r{x:d+}_c{y:d+}_ch0.ome.tiff"
51-
output_dir = f"{TEST_DIR}"
52-
image_name = "single_channel_image_viv"
53-
self._image_name = image_name + ".zarr"
54-
pyr_gen = argolid.PyramidGenerartor()
55-
pyr_gen.set_log_level(4)
56-
pyr_gen.generate_from_image_collection(input_dir, file_pattern, image_name, output_dir, 512, "Viv")
57-
58-
def test_omexml_metadata_exists(self):
59-
ome_xml_path = f"{TEST_DIR}/{self._image_name}/METADATA.ome.xml"
60-
assert pathlib.Path(ome_xml_path).is_file() == True
61-
62-
63-
def test_valid_omexml_metadata(self):
64-
ome_xml_path = f"{TEST_DIR}/{self._image_name}/METADATA.ome.xml"
65-
ome_metadata = ome_types.from_xml(ome_xml_path)
66-
assert len(ome_metadata.images) == 1
67-
assert len(ome_metadata.images[0].pixels.channels) == 1
68-
assert ome_metadata.images[0].pixels.type == ome_types.model.PixelType.UINT8
69-
assert ome_metadata.images[0].pixels.size_x == 3072
70-
assert ome_metadata.images[0].pixels.size_y == 2048
71-
assert ome_metadata.images[0].pixels.size_z == 1
72-
assert ome_metadata.images[0].pixels.size_c == 1
73-
assert ome_metadata.images[0].pixels.size_t == 1
74-
assert ome_metadata.images[0].pixels.dimension_order == ome_types.model.Pixels_DimensionOrder.XYZCT
75-
76-
77-
def test_num_pyramid_levels(self):
78-
for i in range(5):
79-
assert pathlib.Path(TEST_DIR.joinpath(f"{self._image_name}/data.zarr/0/{i}")).is_dir() == True
80-
81-
82-
class TestMultipleChannelVivPyramidFromImageCollection(unittest.TestCase):
83-
@classmethod
84-
def setUpClass(self):
85-
input_dir = f"{TEST_DIR}"
86-
file_pattern = "test_image_r{x:d+}_c{y:d+}_ch{c:d}.ome.tiff"
87-
output_dir = f"{TEST_DIR}"
88-
image_name = "multiple_channel_image_viv"
89-
self._image_name = image_name + ".zarr"
90-
pyr_gen = argolid.PyramidGenerartor()
91-
pyr_gen.set_log_level(4)
92-
pyr_gen.generate_from_image_collection(input_dir, file_pattern, image_name, output_dir, 1024, "Viv")
93-
94-
def test_valid_omexml_metadata(self):
95-
ome_xml_path = f"{TEST_DIR}/{self._image_name}/METADATA.ome.xml"
96-
ome_metadata = ome_types.from_xml(ome_xml_path)
97-
assert len(ome_metadata.images) == 1
98-
assert len(ome_metadata.images[0].pixels.channels) == 4
99-
assert ome_metadata.images[0].pixels.type == ome_types.model.PixelType.UINT8
100-
assert ome_metadata.images[0].pixels.size_x == 3072
101-
assert ome_metadata.images[0].pixels.size_y == 2048
102-
assert ome_metadata.images[0].pixels.size_z == 1
103-
assert ome_metadata.images[0].pixels.size_c == 4
104-
assert ome_metadata.images[0].pixels.size_t == 1
105-
assert ome_metadata.images[0].pixels.dimension_order == ome_types.model.Pixels_DimensionOrder.XYZCT
106-
107-
def test_num_pyramid_levels(self):
108-
for i in range(4):
109-
assert pathlib.Path(TEST_DIR.joinpath(f"{self._image_name}/data.zarr/0/{i}")).is_dir() == True
110-
111-
112-
def test_base_layer_data(self):
113-
dataset_future = ts.open({ 'driver':'zarr',
114-
'kvstore':
115-
{'driver':'file',
116-
'path':f'{TEST_DIR}/{self._image_name}/data.zarr/0/0'
117-
}
118-
})
119-
dataset = dataset_future.result()
120-
assert dataset.shape == (1, 4, 1, 2048, 3072)
52+
if r == 0 and c == 1 and ch == 1:
53+
test_val[0, 0] = 8
54+
test_val[0, 1] = 8
55+
test_val[1, 0] = 9
56+
test_val[1, 1] = 9
57+
58+
bw[0:1024, 0:1024, 0, 0, 0] = test_val
59+
60+
return tiles_dir
61+
62+
63+
@pytest.fixture(scope="session")
64+
def single_channel_viv_zarr(input_tiles_dir: Path) -> Path:
65+
"""
66+
Generate single-channel Viv pyramid once, return output *.zarr path.
67+
"""
68+
input_dir = str(input_tiles_dir)
69+
file_pattern = "test_image_r{x:d+}_c{y:d+}_ch0.ome.tiff"
70+
image_name = "single_channel_image_viv"
71+
output_dir = str(input_tiles_dir) # legacy wrote alongside input
72+
73+
pyr_gen = argolid.PyramidGenerartor()
74+
pyr_gen.set_log_level(4)
75+
pyr_gen.generate_from_image_collection(
76+
input_dir, file_pattern, image_name, output_dir, 512, "Viv")
77+
78+
return input_tiles_dir / f"{image_name}.zarr"
79+
80+
81+
@pytest.fixture(scope="session")
82+
def multiple_channel_viv_zarr(input_tiles_dir: Path) -> Path:
83+
"""
84+
Generate multi-channel Viv pyramid once, return output *.zarr path.
85+
"""
86+
input_dir = str(input_tiles_dir)
87+
file_pattern = "test_image_r{x:d+}_c{y:d+}_ch{c:d}.ome.tiff"
88+
image_name = "multiple_channel_image_viv"
89+
output_dir = str(input_tiles_dir)
90+
91+
pyr_gen = argolid.PyramidGenerartor()
92+
pyr_gen.set_log_level(4)
93+
pyr_gen.generate_from_image_collection(
94+
input_dir, file_pattern, image_name, output_dir, 1024, "Viv")
95+
96+
return input_tiles_dir / f"{image_name}.zarr"
97+
98+
99+
# -----------------------
100+
# Tests: single-channel
101+
# -----------------------
102+
103+
def test_single_channel_omexml_metadata_exists(single_channel_viv_zarr: Path) -> None:
104+
ome_xml_path = single_channel_viv_zarr / "METADATA.ome.xml"
105+
assert ome_xml_path.is_file()
106+
107+
108+
def test_single_channel_valid_omexml_metadata(single_channel_viv_zarr: Path) -> None:
109+
ome_xml_path = single_channel_viv_zarr / "METADATA.ome.xml"
110+
ome_metadata = ome_types.from_xml(str(ome_xml_path))
111+
112+
assert len(ome_metadata.images) == 1
113+
assert len(ome_metadata.images[0].pixels.channels) == 1
114+
assert ome_metadata.images[0].pixels.type == ome_types.model.PixelType.UINT8
115+
assert ome_metadata.images[0].pixels.size_x == 3072
116+
assert ome_metadata.images[0].pixels.size_y == 2048
117+
assert ome_metadata.images[0].pixels.size_z == 1
118+
assert ome_metadata.images[0].pixels.size_c == 1
119+
assert ome_metadata.images[0].pixels.size_t == 1
120+
assert ome_metadata.images[0].pixels.dimension_order == ome_types.model.Pixels_DimensionOrder.XYZCT
121+
122+
123+
def test_single_channel_num_pyramid_levels(single_channel_viv_zarr: Path) -> None:
124+
# expected 5 levels: 0..4 under data.zarr/0/<level>
125+
for i in range(5):
126+
assert (single_channel_viv_zarr / "data.zarr" / "0" / str(i)).is_dir()
127+
128+
129+
130+
def test_multiple_channel_valid_omexml_metadata(multiple_channel_viv_zarr: Path) -> None:
131+
ome_xml_path = multiple_channel_viv_zarr / "METADATA.ome.xml"
132+
ome_metadata = ome_types.from_xml(str(ome_xml_path))
133+
134+
assert len(ome_metadata.images) == 1
135+
assert len(ome_metadata.images[0].pixels.channels) == 4
136+
assert ome_metadata.images[0].pixels.type == ome_types.model.PixelType.UINT8
137+
assert ome_metadata.images[0].pixels.size_x == 3072
138+
assert ome_metadata.images[0].pixels.size_y == 2048
139+
assert ome_metadata.images[0].pixels.size_z == 1
140+
assert ome_metadata.images[0].pixels.size_c == 4
141+
assert ome_metadata.images[0].pixels.size_t == 1
142+
assert ome_metadata.images[0].pixels.dimension_order == ome_types.model.Pixels_DimensionOrder.XYZCT
143+
144+
145+
def test_multiple_channel_num_pyramid_levels(multiple_channel_viv_zarr: Path) -> None:
146+
# expected 4 levels: 0..3
147+
for i in range(4):
148+
assert (multiple_channel_viv_zarr / "data.zarr" / "0" / str(i)).is_dir()
149+
150+
151+
def test_multiple_channel_base_layer_data(multiple_channel_viv_zarr: Path) -> None:
152+
# Base layer store: .../data.zarr/0/0
153+
base_path = multiple_channel_viv_zarr / "data.zarr" / "0" / "0"
154+
155+
dataset_future = ts.open(
156+
{
157+
"driver": "zarr",
158+
"kvstore": {
159+
"driver": "file",
160+
"path": str(base_path),
161+
},
162+
}
163+
)
164+
dataset = dataset_future.result()
165+
166+
assert dataset.shape == (1, 4, 1, 2048, 3072)
167+
121168
# test Viv compatible Zarr is produced
122169
# test OmeXml metadata
123170
# num channels

0 commit comments

Comments
 (0)