-
-
Notifications
You must be signed in to change notification settings - Fork 273
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add example hypothesis roundtrip test
- Loading branch information
Showing
1 changed file
with
109 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import pytest | ||
|
||
pytest.importorskip("hypothesis") | ||
|
||
import zarr | ||
import numpy as np | ||
|
||
from hypothesis import settings | ||
import hypothesis.strategies as st | ||
import hypothesis.extra.numpy as npst | ||
from hypothesis import given, settings | ||
from zarr.core import Array | ||
|
||
from zarr._storage.v3 import KVStoreV3 | ||
from zarr.storage import init_array, normalize_store_arg | ||
|
||
#### TODO: Provide this in zarr.strategies | ||
# Copied from Xarray | ||
# only use characters within the "Latin Extended-A" subset of unicode | ||
_readable_characters = st.characters(categories=["L", "N"], max_codepoint=0x017F) | ||
_readable_strings = st.text(_readable_characters, max_size=5) | ||
_attr_keys = st.text(_readable_characters, min_size=1) | ||
_attr_values = st.recursive( | ||
st.none() | st.booleans() | _readable_strings, | ||
lambda children: st.lists(children) | st.dictionaries(_attr_keys, children), | ||
max_leaves=3, | ||
) | ||
|
||
# No '/' in array names? | ||
# No '.' in paths? | ||
zarr_key_chars = st.sampled_from("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz") | ||
|
||
# The following should be public strategies | ||
attrs = st.none() | st.dictionaries(_attr_keys, _attr_values) | ||
paths = st.none() | st.text(zarr_key_chars, min_size=1) | st.just("/") | ||
array_names = st.text(zarr_key_chars | st.just("."), min_size=1).filter( | ||
lambda t: not t.startswith((".", "..")) | ||
) | ||
|
||
|
||
@st.composite | ||
def array_strategy(draw): | ||
"""A hypothesis strategy to generate small sized random arrays. | ||
Returns: a tuple of the array and a suitable random chunking for it. | ||
""" | ||
array = draw(npst.arrays(dtype=npst.scalar_dtypes(), shape=npst.array_shapes(max_dims=4))) | ||
# We want this strategy to shrink towards arrays with smaller number of chunks | ||
# 1. st.integers() shrinks towards smaller values. So we use that to generate number of chunks | ||
numchunks = draw(st.tuples(*[st.integers(min_value=1, max_value=size) for size in array.shape])) | ||
# 2. and now generate the chunks tuple | ||
chunks = tuple(size // nchunks for size, nchunks in zip(array.shape, numchunks)) | ||
return (array, chunks) | ||
|
||
|
||
##### | ||
|
||
|
||
# @pytest.mark.slow | ||
@settings(max_examples=300) | ||
@given(path=paths, attrs=attrs, name=array_names, array_and_chunks=array_strategy()) | ||
def test_roundtrip(path, array_and_chunks, name, attrs): | ||
store = KVStoreV3(dict()) | ||
|
||
nparray, chunks = array_and_chunks | ||
|
||
# TODO: clean this up | ||
if path is None and name is None: | ||
array_path = None | ||
array_name = None | ||
elif path is None and name is not None: | ||
array_path = f"{name}" | ||
array_name = f"/{name}" | ||
elif path is not None and name is None: | ||
array_path = path | ||
array_name = None | ||
elif path == "/": | ||
assert name is not None | ||
array_path = name | ||
array_name = "/" + name | ||
else: | ||
assert name is not None | ||
array_path = f"{path}/{name}" | ||
array_name = "/" + array_path | ||
|
||
expected_attrs = {} if attrs is None else attrs | ||
|
||
init_array(store, shape=nparray.shape, chunks=chunks, path=array_path, dtype=nparray.dtype.str) | ||
a = Array(store, path=array_path, zarr_version=3) | ||
if attrs is not None: | ||
a.attrs.put(attrs) | ||
|
||
assert isinstance(a, Array) | ||
assert nparray.shape == a.shape | ||
assert chunks == a.chunks | ||
assert array_path == a.path | ||
assert array_name == a.name | ||
# assert a.basename is None # TODO | ||
assert a.store == normalize_store_arg(store) | ||
assert a.attrs.asdict() == expected_attrs | ||
|
||
a[:] = nparray | ||
|
||
store.close() | ||
|
||
group = zarr.open_group(store) | ||
actual = group[array_path] | ||
assert actual.attrs.asdict() == expected_attrs | ||
np.testing.assert_equal(nparray, np.array(actual)) |