From 8cc2574399fdc25e3789c43b1c201c7eaa87adea Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Mon, 23 Sep 2024 13:28:38 -0700 Subject: [PATCH] add marks to all tests --- pyproject.toml | 8 +++- src/numpydantic/interface/dask.py | 2 +- tests/conftest.py | 2 + tests/fixtures.py | 5 +++ tests/test_interface/conftest.py | 56 +++++++++++++++++-------- tests/test_interface/test_dask.py | 7 +++- tests/test_interface/test_dunder.py | 10 ----- tests/test_interface/test_hdf5.py | 12 ++++++ tests/test_interface/test_interfaces.py | 7 ++++ tests/test_interface/test_numpy.py | 4 ++ tests/test_interface/test_video.py | 6 +++ tests/test_interface/test_zarr.py | 5 ++- tests/test_ndarray.py | 11 +++-- tests/test_shape.py | 2 + 14 files changed, 101 insertions(+), 36 deletions(-) delete mode 100644 tests/test_interface/test_dunder.py diff --git a/pyproject.toml b/pyproject.toml index 79d93ba..a53ef1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -116,7 +116,13 @@ markers = [ "dtype: mark test related to dtype validation", "shape: mark test related to shape validation", "json_schema: mark test related to json schema generation", - "serialization: mark test related to serialization" + "serialization: mark test related to serialization", + "proxy: test for proxy class in any interface", + "dask: dask interface", + "hdf5: hdf5 interface", + "numpy: numpy interface", + "video: video interface", + "zarr: zarr interface", ] [tool.ruff] diff --git a/src/numpydantic/interface/dask.py b/src/numpydantic/interface/dask.py index bc12a13..cd36a65 100644 --- a/src/numpydantic/interface/dask.py +++ b/src/numpydantic/interface/dask.py @@ -61,7 +61,7 @@ class DaskInterface(Interface): """ check if array is a dask array """ - if DaskArray is None: + if DaskArray is None: # pragma: no cover - no tests for interface deps atm return False elif isinstance(array, DaskArray): return True diff --git a/tests/conftest.py b/tests/conftest.py index 0467f25..3870be2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -83,6 +83,7 @@ STRING: TypeAlias = NDArray[Shape["*, *, *"], str] MODEL: TypeAlias = NDArray[Shape["*, *, *"], BasicModel] +@pytest.mark.shape @pytest.fixture( scope="module", params=[ @@ -120,6 +121,7 @@ def shape_cases(request) -> ValidationCase: return request.param +@pytest.mark.dtype @pytest.fixture( scope="module", params=[ diff --git a/tests/fixtures.py b/tests/fixtures.py index 89359ae..fb393b5 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -104,6 +104,7 @@ def model_blank() -> Type[BaseModel]: return BlankModel +@pytest.mark.hdf5 @pytest.fixture(scope="function") def hdf5_file(tmp_output_dir_func) -> h5py.File: h5f_file = tmp_output_dir_func / "h5f.h5" @@ -112,6 +113,7 @@ def hdf5_file(tmp_output_dir_func) -> h5py.File: h5f.close() +@pytest.mark.hdf5 @pytest.fixture(scope="function") def hdf5_array( hdf5_file, request @@ -154,6 +156,7 @@ def hdf5_array( return _hdf5_array +@pytest.mark.zarr @pytest.fixture(scope="function") def zarr_nested_array(tmp_output_dir_func) -> ZarrArrayPath: """Zarr array within a nested array""" @@ -164,6 +167,7 @@ def zarr_nested_array(tmp_output_dir_func) -> ZarrArrayPath: return ZarrArrayPath(file=file, path=path) +@pytest.mark.zarr @pytest.fixture(scope="function") def zarr_array(tmp_output_dir_func) -> Path: file = tmp_output_dir_func / "array.zarr" @@ -172,6 +176,7 @@ def zarr_array(tmp_output_dir_func) -> Path: return file +@pytest.mark.video @pytest.fixture(scope="function") def avi_video(tmp_path) -> Callable[[Tuple[int, int], int, bool], Path]: video_path = tmp_path / "test.avi" diff --git a/tests/test_interface/conftest.py b/tests/test_interface/conftest.py index 7e6f767..5d36fa5 100644 --- a/tests/test_interface/conftest.py +++ b/tests/test_interface/conftest.py @@ -12,24 +12,44 @@ from numpydantic import interface, NDArray @pytest.fixture( scope="function", params=[ - ([[1, 2], [3, 4]], interface.NumpyInterface), - (np.zeros((3, 4)), interface.NumpyInterface), - ("hdf5_array", interface.H5Interface), - (da.random.random((10, 10)), interface.DaskInterface), - (zarr.ones((10, 10)), interface.ZarrInterface), - ("zarr_nested_array", interface.ZarrInterface), - ("zarr_array", interface.ZarrInterface), - ("avi_video", interface.VideoInterface), - ], - ids=[ - "numpy_list", - "numpy", - "H5ArrayPath", - "dask", - "zarr_memory", - "zarr_nested", - "zarr_array", - "video", + pytest.param( + ([[1, 2], [3, 4]], interface.NumpyInterface), + marks=pytest.mark.numpy, + id="numpy-list", + ), + pytest.param( + (np.zeros((3, 4)), interface.NumpyInterface), + marks=pytest.mark.numpy, + id="numpy", + ), + pytest.param( + ("hdf5_array", interface.H5Interface), + marks=pytest.mark.hdf5, + id="h5-array-path", + ), + pytest.param( + (da.random.random((10, 10)), interface.DaskInterface), + marks=pytest.mark.dask, + id="dask", + ), + pytest.param( + (zarr.ones((10, 10)), interface.ZarrInterface), + marks=pytest.mark.zarr, + id="zarr-memory", + ), + pytest.param( + ("zarr_nested_array", interface.ZarrInterface), + marks=pytest.mark.zarr, + id="zarr-nested", + ), + pytest.param( + ("zarr_array", interface.ZarrInterface), + marks=pytest.mark.zarr, + id="zarr-array", + ), + pytest.param( + ("avi_video", interface.VideoInterface), marks=pytest.mark.video, id="video" + ), ], ) def interface_type(request) -> Tuple[NDArray, Type[interface.Interface]]: diff --git a/tests/test_interface/test_dask.py b/tests/test_interface/test_dask.py index fb1e4cb..c3b70e0 100644 --- a/tests/test_interface/test_dask.py +++ b/tests/test_interface/test_dask.py @@ -1,5 +1,3 @@ -import pdb - import pytest import json @@ -11,6 +9,8 @@ from numpydantic.exceptions import DtypeError, ShapeError from tests.conftest import ValidationCase +pytestmark = pytest.mark.dask + def dask_array(case: ValidationCase) -> da.Array: if issubclass(case.dtype, BaseModel): @@ -42,14 +42,17 @@ def test_dask_check(interface_type): assert not DaskInterface.check(interface_type[0]) +@pytest.mark.shape def test_dask_shape(shape_cases): _test_dask_case(shape_cases) +@pytest.mark.dtype def test_dask_dtype(dtype_cases): _test_dask_case(dtype_cases) +@pytest.mark.serialization def test_dask_to_json(array_model): array_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] array = da.array(array_list) diff --git a/tests/test_interface/test_dunder.py b/tests/test_interface/test_dunder.py deleted file mode 100644 index 60f42d8..0000000 --- a/tests/test_interface/test_dunder.py +++ /dev/null @@ -1,10 +0,0 @@ -""" -Tests for dunder methods on all interfaces -""" - - -def test_dunder_len(all_interfaces): - """ - Each interface or proxy type should support __len__ - """ - assert len(all_interfaces.array) == all_interfaces.array.shape[0] diff --git a/tests/test_interface/test_hdf5.py b/tests/test_interface/test_hdf5.py index b64d3fe..f88b411 100644 --- a/tests/test_interface/test_hdf5.py +++ b/tests/test_interface/test_hdf5.py @@ -14,6 +14,8 @@ from numpydantic.exceptions import DtypeError, ShapeError from tests.conftest import ValidationCase +pytestmark = pytest.mark.hdf5 + def hdf5_array_case( case: ValidationCase, array_func, compound: bool = False @@ -72,11 +74,13 @@ def test_hdf5_check_not_hdf5(tmp_path): assert not H5Interface.check(spec) +@pytest.mark.shape @pytest.mark.parametrize("compound", [True, False]) def test_hdf5_shape(shape_cases, hdf5_array, compound): _test_hdf5_case(shape_cases, hdf5_array, compound) +@pytest.mark.dtype @pytest.mark.parametrize("compound", [True, False]) def test_hdf5_dtype(dtype_cases, hdf5_array, compound): _test_hdf5_case(dtype_cases, hdf5_array, compound) @@ -90,6 +94,7 @@ def test_hdf5_dataset_not_exists(hdf5_array, model_blank): assert "no array found" in e +@pytest.mark.proxy def test_assignment(hdf5_array, model_blank): array = hdf5_array() @@ -101,6 +106,7 @@ def test_assignment(hdf5_array, model_blank): assert (model.array[1:3, 2:4] == 10).all() +@pytest.mark.serialization @pytest.mark.parametrize("round_trip", (True, False)) def test_to_json(hdf5_array, array_model, round_trip): """ @@ -125,6 +131,8 @@ def test_to_json(hdf5_array, array_model, round_trip): assert json_dumped == instance.array[:].tolist() +@pytest.mark.dtype +@pytest.mark.proxy def test_compound_dtype(tmp_path): """ hdf5 proxy indexes compound dtypes as single fields when field is given @@ -159,6 +167,8 @@ def test_compound_dtype(tmp_path): assert all(instance.array[1] == 2) +@pytest.mark.dtype +@pytest.mark.proxy @pytest.mark.parametrize("compound", [True, False]) def test_strings(hdf5_array, compound): """ @@ -178,6 +188,8 @@ def test_strings(hdf5_array, compound): assert all(instance.array[1] == "sup") +@pytest.mark.dtype +@pytest.mark.proxy @pytest.mark.parametrize("compound", [True, False]) def test_datetime(hdf5_array, compound): """ diff --git a/tests/test_interface/test_interfaces.py b/tests/test_interface/test_interfaces.py index 3d51ac0..01709d7 100644 --- a/tests/test_interface/test_interfaces.py +++ b/tests/test_interface/test_interfaces.py @@ -66,3 +66,10 @@ def test_interface_roundtrip_json(all_interfaces, round_trip): assert model.array.dtype == all_interfaces.array.dtype else: assert np.array_equal(model.array, np.array(all_interfaces.array)) + + +def test_dunder_len(all_interfaces): + """ + Each interface or proxy type should support __len__ + """ + assert len(all_interfaces.array) == all_interfaces.array.shape[0] diff --git a/tests/test_interface/test_numpy.py b/tests/test_interface/test_numpy.py index 6a34b98..bfb4c4d 100644 --- a/tests/test_interface/test_numpy.py +++ b/tests/test_interface/test_numpy.py @@ -5,6 +5,8 @@ from numpydantic.exceptions import DtypeError, ShapeError from tests.conftest import ValidationCase +pytestmark = pytest.mark.numpy + def numpy_array(case: ValidationCase) -> np.ndarray: if issubclass(case.dtype, BaseModel): @@ -22,10 +24,12 @@ def _test_np_case(case: ValidationCase): case.model(array=array) +@pytest.mark.shape def test_numpy_shape(shape_cases): _test_np_case(shape_cases) +@pytest.mark.dtype def test_numpy_dtype(dtype_cases): _test_np_case(dtype_cases) diff --git a/tests/test_interface/test_video.py b/tests/test_interface/test_video.py index d5ef1b3..44c8b9a 100644 --- a/tests/test_interface/test_video.py +++ b/tests/test_interface/test_video.py @@ -14,6 +14,8 @@ from numpydantic import NDArray, Shape from numpydantic import dtype as dt from numpydantic.interface.video import VideoProxy +pytestmark = pytest.mark.video + @pytest.mark.parametrize("input_type", [str, Path]) def test_video_validation(avi_video, input_type): @@ -49,6 +51,7 @@ def test_video_from_videocapture(avi_video): opened_vid.release() +@pytest.mark.shape def test_video_wrong_shape(avi_video): shape = (100, 50) @@ -65,6 +68,7 @@ def test_video_wrong_shape(avi_video): instance = MyModel(array=vid) +@pytest.mark.proxy def test_video_getitem(avi_video): """ Should be able to get individual frames and slices as if it were a normal array @@ -127,6 +131,7 @@ def test_video_getitem(avi_video): instance.array[5] = 10 +@pytest.mark.proxy def test_video_attrs(avi_video): """Should be able to access opencv properties""" shape = (100, 50) @@ -142,6 +147,7 @@ def test_video_attrs(avi_video): assert int(instance.array.get(cv2.CAP_PROP_POS_FRAMES)) == 5 +@pytest.mark.proxy def test_video_close(avi_video): """Should close and reopen video file if needed""" shape = (100, 50) diff --git a/tests/test_interface/test_zarr.py b/tests/test_interface/test_zarr.py index fca15ae..ed5c252 100644 --- a/tests/test_interface/test_zarr.py +++ b/tests/test_interface/test_zarr.py @@ -6,13 +6,14 @@ import zarr from pydantic import BaseModel, ValidationError from numcodecs import Pickle - from numpydantic.interface import ZarrInterface from numpydantic.interface.zarr import ZarrArrayPath from numpydantic.exceptions import DtypeError, ShapeError from tests.conftest import ValidationCase +pytestmark = pytest.mark.zarr + @pytest.fixture() def dir_array(tmp_output_dir_func) -> zarr.DirectoryStore: @@ -87,10 +88,12 @@ def test_zarr_check(interface_type): assert not ZarrInterface.check(interface_type[0]) +@pytest.mark.shape def test_zarr_shape(store, shape_cases): _test_zarr_case(shape_cases, store) +@pytest.mark.dtype def test_zarr_dtype(dtype_cases, store): _test_zarr_case(dtype_cases, store) diff --git a/tests/test_ndarray.py b/tests/test_ndarray.py index cef7dc3..73abedf 100644 --- a/tests/test_ndarray.py +++ b/tests/test_ndarray.py @@ -41,6 +41,7 @@ def test_ndarray_type(): instance = Model(array=np.zeros((2, 3)), array_any=np.ones((3, 4, 5))) +@pytest.mark.dtype @pytest.mark.json_schema def test_schema_unsupported_type(): """ @@ -57,10 +58,11 @@ def test_schema_unsupported_type(): } +@pytest.mark.dtype @pytest.mark.json_schema def test_schema_tuple(): """ - Types specified as tupled should have their schemas as a union + Types specified as tuples should have their schemas as a union """ class Model(BaseModel): @@ -75,6 +77,7 @@ def test_schema_tuple(): assert all([i["minimum"] == 0 for i in conditions]) +@pytest.mark.dtype @pytest.mark.json_schema def test_schema_number(): """ @@ -119,12 +122,12 @@ def test_ndarray_union(): instance = Model(array=np.random.random((5, 10, 4, 6))) +@pytest.mark.shape +@pytest.mark.dtype @pytest.mark.parametrize("dtype", dtype.Number) def test_ndarray_unparameterized(dtype): """ NDArray without any parameters is any shape, any type - Returns: - """ class Model(BaseModel): @@ -138,6 +141,7 @@ def test_ndarray_unparameterized(dtype): _ = Model(array=np.zeros(dim_sizes, dtype=dtype)) +@pytest.mark.shape def test_ndarray_any(): """ using :class:`typing.Any` in for the shape means any shape @@ -249,6 +253,7 @@ def test_json_schema_dtype_single(dtype, array_model): @pytest.mark.dtype +@pytest.mark.json_schema @pytest.mark.parametrize( "dtype,expected", [ diff --git a/tests/test_shape.py b/tests/test_shape.py index 3abff19..03de477 100644 --- a/tests/test_shape.py +++ b/tests/test_shape.py @@ -9,6 +9,8 @@ import numpy as np from numpydantic import NDArray, Shape +pytestmark = pytest.mark.shape + @pytest.mark.parametrize( "shape,valid",