Skip to content

Commit

Permalink
Clarify intent of roundtrip tests
Browse files Browse the repository at this point in the history
  • Loading branch information
moradology committed Feb 19, 2025
1 parent 251253e commit 935ac71
Showing 1 changed file with 44 additions and 22 deletions.
66 changes: 44 additions & 22 deletions tests/test_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,16 +153,59 @@ def test_vindex(data: st.DataObject) -> None:


@given(store=stores, meta=array_metadata()) # type: ignore[misc]
async def test_roundtrip_array_metadata(
async def test_roundtrip_array_metadata_from_store(
store: Store, meta: ArrayV2Metadata | ArrayV3Metadata
) -> None:
"""
Verify that the I/O for metadata in a store are lossless.
This test serializes an ArrayV2Metadata or ArrayV3Metadata object to a dict
of buffers via `to_buffer_dict`, writes each buffer to a store under keys
prefixed with "0/", and then reads them back. The test asserts that each
retrieved buffer exactly matches the original buffer.
"""
asdict = meta.to_buffer_dict(prototype=default_buffer_prototype())
for key, expected in asdict.items():
await store.set(f"0/{key}", expected)
actual = await store.get(f"0/{key}", prototype=default_buffer_prototype())
assert actual == expected


@given(data=st.data(), zarr_format=zarr_formats)
def test_roundtrip_array_metadata_from_json(data: st.DataObject, zarr_format: int) -> None:
"""
Verify that JSON serialization and deserialization of metadata is lossless.
For Zarr v2:
- The metadata is split into two JSON documents (one for array data and one
for attributes). The test merges the attributes back before deserialization.
For Zarr v3:
- All metadata is stored in a single JSON document. No manual merger is necessary.
The test then converts both the original and round-tripped metadata objects
into dictionaries using `dataclasses.asdict` and uses a deep equality check
to verify that the roundtrip has preserved all fields (including special
cases like NaN, Infinity, complex numbers, and datetime values).
"""
metadata = data.draw(array_metadata(zarr_formats=st.just(zarr_format)))
buffer_dict = metadata.to_buffer_dict(prototype=default_buffer_prototype())

if zarr_format == 2:
zarray_dict = json.loads(buffer_dict[ZARRAY_JSON].to_bytes().decode())
zattrs_dict = json.loads(buffer_dict[ZATTRS_JSON].to_bytes().decode())
# zattrs and zarray are separate in v2, we have to add attributes back prior to `from_dict`
zarray_dict["attributes"] = zattrs_dict
metadata_roundtripped = ArrayV2Metadata.from_dict(zarray_dict)
else:
zarray_dict = json.loads(buffer_dict[ZARR_JSON].to_bytes().decode())
metadata_roundtripped = ArrayV3Metadata.from_dict(zarray_dict)

orig = dataclasses.asdict(metadata)
rt = dataclasses.asdict(metadata_roundtripped)

assert deep_equal(orig, rt), f"Roundtrip mismatch:\nOriginal: {orig}\nRoundtripped: {rt}"


# @st.composite
# def advanced_indices(draw, *, shape):
# basic_idxr = draw(
Expand All @@ -187,27 +230,6 @@ async def test_roundtrip_array_metadata(
# assert_array_equal(nparray, zarray[:])


@given(data=st.data(), zarr_format=zarr_formats)
def test_meta_roundtrip(data: st.DataObject, zarr_format: int) -> None:
metadata = data.draw(array_metadata(zarr_formats=st.just(zarr_format)))
buffer_dict = metadata.to_buffer_dict(prototype=default_buffer_prototype())

if zarr_format == 2:
zarray_dict = json.loads(buffer_dict[ZARRAY_JSON].to_bytes().decode())
zattrs_dict = json.loads(buffer_dict[ZATTRS_JSON].to_bytes().decode())
# zattrs and zarray are separate in v2, we have to add attributes back prior to `from_dict`
zarray_dict["attributes"] = zattrs_dict
metadata_roundtripped = ArrayV2Metadata.from_dict(zarray_dict)
else:
zarray_dict = json.loads(buffer_dict[ZARR_JSON].to_bytes().decode())
metadata_roundtripped = ArrayV3Metadata.from_dict(zarray_dict)

orig = dataclasses.asdict(metadata)
rt = dataclasses.asdict(metadata_roundtripped)

assert deep_equal(orig, rt), f"Roundtrip mismatch:\nOriginal: {orig}\nRoundtripped: {rt}"


@given(npst.from_dtype(dtype=np.dtype("float64"), allow_nan=True, allow_infinity=True))
def test_v2meta_nan_and_infinity(fill_value):
metadata = ArrayV2Metadata(
Expand Down

0 comments on commit 935ac71

Please sign in to comment.