Skip to content

Commit 404213a

Browse files
committed
Clarify intent of roundtrip tests
1 parent 251253e commit 404213a

File tree

1 file changed

+42
-22
lines changed

1 file changed

+42
-22
lines changed

tests/test_properties.py

+42-22
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,56 @@ def test_vindex(data: st.DataObject) -> None:
153153

154154

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

173+
@given(data=st.data(), zarr_format=zarr_formats)
174+
def test_roundtrip_array_metadata_from_json(data: st.DataObject, zarr_format: int) -> None:
175+
"""
176+
Verify that JSON serialization and deserialization of metadata is lossless.
177+
178+
For Zarr v2:
179+
- The metadata is split into two JSON documents (one for array data and one
180+
for attributes). The test merges the attributes back before deserialization.
181+
For Zarr v3:
182+
- All metadata is stored in a single JSON document. No manual merger is necessary.
183+
184+
The test then converts both the original and round-tripped metadata objects
185+
into dictionaries using `dataclasses.asdict` and uses a deep equality check
186+
to verify that the roundtrip has preserved all fields (including special
187+
cases like NaN, Infinity, complex numbers, and datetime values).
188+
"""
189+
metadata = data.draw(array_metadata(zarr_formats=st.just(zarr_format)))
190+
buffer_dict = metadata.to_buffer_dict(prototype=default_buffer_prototype())
191+
192+
if zarr_format == 2:
193+
zarray_dict = json.loads(buffer_dict[ZARRAY_JSON].to_bytes().decode())
194+
zattrs_dict = json.loads(buffer_dict[ZATTRS_JSON].to_bytes().decode())
195+
# zattrs and zarray are separate in v2, we have to add attributes back prior to `from_dict`
196+
zarray_dict["attributes"] = zattrs_dict
197+
metadata_roundtripped = ArrayV2Metadata.from_dict(zarray_dict)
198+
else:
199+
zarray_dict = json.loads(buffer_dict[ZARR_JSON].to_bytes().decode())
200+
metadata_roundtripped = ArrayV3Metadata.from_dict(zarray_dict)
201+
202+
orig = dataclasses.asdict(metadata)
203+
rt = dataclasses.asdict(metadata_roundtripped)
204+
205+
assert deep_equal(orig, rt), f"Roundtrip mismatch:\nOriginal: {orig}\nRoundtripped: {rt}"
165206

166207
# @st.composite
167208
# def advanced_indices(draw, *, shape):
@@ -187,27 +228,6 @@ async def test_roundtrip_array_metadata(
187228
# assert_array_equal(nparray, zarray[:])
188229

189230

190-
@given(data=st.data(), zarr_format=zarr_formats)
191-
def test_meta_roundtrip(data: st.DataObject, zarr_format: int) -> None:
192-
metadata = data.draw(array_metadata(zarr_formats=st.just(zarr_format)))
193-
buffer_dict = metadata.to_buffer_dict(prototype=default_buffer_prototype())
194-
195-
if zarr_format == 2:
196-
zarray_dict = json.loads(buffer_dict[ZARRAY_JSON].to_bytes().decode())
197-
zattrs_dict = json.loads(buffer_dict[ZATTRS_JSON].to_bytes().decode())
198-
# zattrs and zarray are separate in v2, we have to add attributes back prior to `from_dict`
199-
zarray_dict["attributes"] = zattrs_dict
200-
metadata_roundtripped = ArrayV2Metadata.from_dict(zarray_dict)
201-
else:
202-
zarray_dict = json.loads(buffer_dict[ZARR_JSON].to_bytes().decode())
203-
metadata_roundtripped = ArrayV3Metadata.from_dict(zarray_dict)
204-
205-
orig = dataclasses.asdict(metadata)
206-
rt = dataclasses.asdict(metadata_roundtripped)
207-
208-
assert deep_equal(orig, rt), f"Roundtrip mismatch:\nOriginal: {orig}\nRoundtripped: {rt}"
209-
210-
211231
@given(npst.from_dtype(dtype=np.dtype("float64"), allow_nan=True, allow_infinity=True))
212232
def test_v2meta_nan_and_infinity(fill_value):
213233
metadata = ArrayV2Metadata(

0 commit comments

Comments
 (0)