diff --git a/docs/api/zarr/testing/conftest.md b/docs/api/zarr/testing/conftest.md deleted file mode 100644 index 67cecfd9b8..0000000000 --- a/docs/api/zarr/testing/conftest.md +++ /dev/null @@ -1,3 +0,0 @@ -## Conftest - -::: zarr.testing.conftest diff --git a/docs/api/zarr/testing/index.md b/docs/api/zarr/testing/index.md index 4ef56ec69c..ab5dd1daa0 100644 --- a/docs/api/zarr/testing/index.md +++ b/docs/api/zarr/testing/index.md @@ -5,7 +5,6 @@ title: testing See the following sub-modules: - [buffer](./buffer.md) -- [conftest](./conftest.md) - [stateful](./stateful.md) - [store](./store.md) - [strategies](./strategies.md) diff --git a/mkdocs.yml b/mkdocs.yml index 7a4bfa35ef..e4e757e630 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,7 +66,6 @@ nav: - Testing: - api/zarr/testing/index.md - api/zarr/testing/buffer.md - - api/zarr/testing/conftest.md - api/zarr/testing/stateful.md - api/zarr/testing/store.md - api/zarr/testing/strategies.md diff --git a/pyproject.toml b/pyproject.toml index 02e66c67e8..6a7238ff8f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -155,7 +155,6 @@ exclude_also = [ [tool.coverage.run] omit = [ "bench/compress_normal.py", - "src/zarr/testing/conftest.py", # only for downstream projects ] [tool.hatch] diff --git a/src/zarr/testing/__init__.py b/src/zarr/testing/__init__.py index 21a3572846..823c508052 100644 --- a/src/zarr/testing/__init__.py +++ b/src/zarr/testing/__init__.py @@ -1,16 +1,28 @@ import importlib.util import warnings +from typing import TYPE_CHECKING from zarr.errors import ZarrUserWarning if importlib.util.find_spec("pytest") is not None: from zarr.testing.store import StoreTests + from zarr.testing.utils import assert_bytes_equal else: warnings.warn( "pytest not installed, skipping test suite", category=ZarrUserWarning, stacklevel=2 ) -from zarr.testing.utils import assert_bytes_equal +if TYPE_CHECKING: + import pytest + + +def pytest_configure(config: "pytest.Config") -> None: + # The tests in zarr.testing are intended to be run by downstream projects. + # To allow those downstream projects to run with `--strict-markers`, we need + # to register an entry point with pytest11 and register our "plugin" with it, + # which just registers the markers used in zarr.testing + config.addinivalue_line("markers", "gpu: mark a test as requiring CuPy and GPU") + # TODO: import public buffer tests? diff --git a/src/zarr/testing/conftest.py b/src/zarr/testing/conftest.py deleted file mode 100644 index 59c148e0ec..0000000000 --- a/src/zarr/testing/conftest.py +++ /dev/null @@ -1,9 +0,0 @@ -import pytest - - -def pytest_configure(config: pytest.Config) -> None: - # The tests in zarr.testing are intended to be run by downstream projects. - # To allow those downstream projects to run with `--strict-markers`, we need - # to register an entry point with pytest11 and register our "plugin" with it, - # which just registers the markers used in zarr.testing - config.addinivalue_line("markers", "gpu: mark a test as requiring CuPy and GPU") diff --git a/tests/test_api.py b/tests/test_api.py index 121f02c7b2..503aeee405 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -75,11 +75,11 @@ def test_create(memory_store: Store) -> None: assert z.chunks == (40,) # create array with float shape - with pytest.raises(TypeError): + with pytest.raises(TypeError, match="Expected an iterable of integers"): z = create(shape=(400.5, 100), store=store, overwrite=True) # type: ignore[arg-type] # create array with float chunk shape - with pytest.raises(TypeError): + with pytest.raises(TypeError, match="'float' object is not iterable"): z = create(shape=(400, 100), chunks=(16, 16.5), store=store, overwrite=True) # type: ignore[arg-type] @@ -378,7 +378,7 @@ def test_save(store: Store, n_args: int, n_kwargs: int, path: None | str) -> Non kwargs = {f"arg_{i}": data for i in range(n_kwargs)} if n_kwargs == 0 and n_args == 0: - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="at least one array must be provided"): save(store, path=path) elif n_args == 1 and n_kwargs == 0: save(store, *args, path=path) @@ -397,17 +397,19 @@ def test_save(store: Store, n_args: int, n_kwargs: int, path: None | str) -> Non def test_save_errors() -> None: - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="at least one array must be provided"): # no arrays provided save_group("data/group.zarr") - with pytest.raises(TypeError): + with pytest.raises(TypeError, match="missing 1 required positional argument: 'arr'"): # no array provided save_array("data/group.zarr") # type: ignore[call-arg] - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="at least one array must be provided"): # no arrays provided save("data/group.zarr") a = np.arange(10) - with pytest.raises(TypeError): + with pytest.raises( + TypeError, match="Keyword argument 'mode' must be a numpy or other NDArrayLike array" + ): # mode is no valid argument and would get handled as an array zarr.save("data/example.zarr", a, mode="w") diff --git a/tests/test_experimental/test_cache_store.py b/tests/test_experimental/test_cache_store.py index fc17ccd5e1..5ad56a4335 100644 --- a/tests/test_experimental/test_cache_store.py +++ b/tests/test_experimental/test_cache_store.py @@ -131,21 +131,14 @@ async def test_cache_expiration(self) -> None: test_data = CPUBuffer.from_bytes(b"expiring data") await cached_store.set("expire_key", test_data) - # Should be fresh initially (if _is_key_fresh method exists) - if hasattr(cached_store, "_is_key_fresh"): - assert cached_store._is_key_fresh("expire_key") - - # Wait for expiration - await asyncio.sleep(1.1) - - # Should now be stale - assert not cached_store._is_key_fresh("expire_key") - else: - # Skip freshness check if method doesn't exist - await asyncio.sleep(1.1) - # Just verify the data is still accessible - result = await cached_store.get("expire_key", default_buffer_prototype()) - assert result is not None + # Should be fresh initially + assert cached_store._is_key_fresh("expire_key") + + # Wait for expiration + await asyncio.sleep(1.1) + + # Should now be stale + assert not cached_store._is_key_fresh("expire_key") async def test_cache_set_data_false(self, source_store: Store, cache_store: Store) -> None: """Test behavior when cache_set_data=False.""" @@ -225,10 +218,6 @@ async def test_stale_cache_refresh(self) -> None: async def test_infinity_max_age(self, cached_store: CacheStore) -> None: """Test that 'infinity' max_age means cache never expires.""" - # Skip test if _is_key_fresh method doesn't exist - if not hasattr(cached_store, "_is_key_fresh"): - pytest.skip("_is_key_fresh method not implemented") - test_data = CPUBuffer.from_bytes(b"eternal data") await cached_store.set("eternal_key", test_data) diff --git a/tests/test_group.py b/tests/test_group.py index 692b88c8af..bc80e19e86 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -395,7 +395,7 @@ def test_group_getitem(store: Store, zarr_format: ZarrFormat, consolidated: bool assert group["subgroup"]["subarray"] == subsubarray assert group["subgroup/subarray"] == subsubarray - with pytest.raises(KeyError): + with pytest.raises(KeyError, match="nope"): group["nope"] with pytest.raises(KeyError, match="subarray/subsubarray"): @@ -483,11 +483,11 @@ def test_group_delitem(store: Store, zarr_format: ZarrFormat, consolidated: bool assert group["subarray"] == subarray del group["subgroup"] - with pytest.raises(KeyError): + with pytest.raises(KeyError, match="subgroup"): group["subgroup"] del group["subarray"] - with pytest.raises(KeyError): + with pytest.raises(KeyError, match="subarray"): group["subarray"] @@ -1060,7 +1060,7 @@ async def test_asyncgroup_getitem(store: Store, zarr_format: ZarrFormat) -> None assert await agroup.getitem(sub_group_path) == sub_group # check that asking for a nonexistent key raises KeyError - with pytest.raises(KeyError): + with pytest.raises(KeyError, match="foo"): await agroup.getitem("foo") @@ -1316,7 +1316,7 @@ async def test_require_group(store: LocalStore | MemoryStore, zarr_format: ZarrF # await root.require_group("foo", overwrite=True) # test that requiring a group where an array is fails - with pytest.raises(TypeError): + with pytest.raises(TypeError, match="Incompatible object"): await foo_group.require_group("bar") @@ -1650,7 +1650,7 @@ def test_delitem_removes_children(store: Store, zarr_format: ZarrFormat) -> None arr = g1.create_array("0/0/0", shape=(1,), dtype="uint8") arr[:] = 1 del g1["0"] - with pytest.raises(KeyError): + with pytest.raises(KeyError, match="0/0"): g1["0/0"] diff --git a/tests/test_indexing.py b/tests/test_indexing.py index a9358e4fcf..3d80f6364c 100644 --- a/tests/test_indexing.py +++ b/tests/test_indexing.py @@ -158,7 +158,9 @@ def test_replace_ellipsis() -> None: [ (42, "uint8"), pytest.param( - (b"aaa", 1, 4.2), [("foo", "S3"), ("bar", "i4"), ("baz", "f8")], marks=pytest.mark.xfail + (b"aaa", 1, 4.2), + [("foo", "S3"), ("bar", "i4"), ("baz", "f8")], + marks=pytest.mark.filterwarnings("ignore::zarr.errors.UnstableSpecificationWarning"), ), ], ) @@ -171,8 +173,8 @@ def test_get_basic_selection_0d(store: StorePath, use_out: bool, value: Any, dty assert_array_equal(arr_np, arr_z.get_basic_selection(Ellipsis)) assert_array_equal(arr_np, arr_z[...]) - assert value == arr_z.get_basic_selection(()) - assert value == arr_z[()] + assert arr_np[()] == arr_z.get_basic_selection(()) + assert arr_np[()] == arr_z[()] if use_out: # test out param @@ -598,7 +600,9 @@ def test_fancy_indexing_doesnt_mix_with_implicit_slicing(store: StorePath) -> No [ (42, "uint8"), pytest.param( - (b"aaa", 1, 4.2), [("foo", "S3"), ("bar", "i4"), ("baz", "f8")], marks=pytest.mark.xfail + (b"aaa", 1, 4.2), + [("foo", "S3"), ("bar", "i4"), ("baz", "f8")], + marks=pytest.mark.filterwarnings("ignore::zarr.errors.UnstableSpecificationWarning"), ), ], ) @@ -612,11 +616,11 @@ def test_set_basic_selection_0d( assert_array_equal(arr_np_zeros, arr_z) arr_z.set_basic_selection(Ellipsis, value) - assert_array_equal(value, arr_z) - arr_z[...] = 0 + assert_array_equal(arr_np, arr_z) + arr_z[...] = arr_np_zeros[()] assert_array_equal(arr_np_zeros, arr_z) arr_z[...] = value - assert_array_equal(value, arr_z) + assert_array_equal(arr_np, arr_z) # todo: uncomment the structured array tests when we can make them pass, # or delete them if we formally decide not to support structured dtypes. diff --git a/tests/test_store/test_core.py b/tests/test_store/test_core.py index 0ba0330d08..ae64e17ee0 100644 --- a/tests/test_store/test_core.py +++ b/tests/test_store/test_core.py @@ -88,7 +88,7 @@ async def test_contains_invalid_format_raises(local_store: LocalStore, func: _Co Test contains_group and contains_array raise errors for invalid zarr_formats """ store_path = StorePath(local_store) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Invalid zarr_format provided. Got 3.0, expected 2 or 3"): assert await func(store_path, "3.0") # type: ignore[arg-type] @@ -214,7 +214,7 @@ async def test_make_store_path_invalid() -> None: """ Test that invalid types raise TypeError """ - with pytest.raises(TypeError): + with pytest.raises(TypeError, match="Unsupported type for store_like: 'int'"): await make_store_path(1) @@ -262,7 +262,7 @@ def test_normalize_path_none() -> None: @pytest.mark.parametrize("path", [".", ".."]) def test_normalize_path_invalid(path: str) -> None: - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="is invalid because its string representation contains"): normalize_path(path)