|
1 | 1 | import dataclasses
|
2 | 2 | import json
|
| 3 | +from numbers import Real |
3 | 4 |
|
4 | 5 | import numpy as np
|
5 | 6 | import pytest
|
@@ -230,24 +231,63 @@ def test_roundtrip_array_metadata_from_json(data: st.DataObject, zarr_format: in
|
230 | 231 | # assert_array_equal(nparray, zarray[:])
|
231 | 232 |
|
232 | 233 |
|
233 |
| -@given(npst.from_dtype(dtype=np.dtype("float64"), allow_nan=True, allow_infinity=True)) |
234 |
| -def test_v2meta_nan_and_infinity(fill_value): |
235 |
| - metadata = ArrayV2Metadata( |
236 |
| - shape=[10], |
237 |
| - dtype=np.dtype("float64"), |
238 |
| - chunks=[5], |
239 |
| - fill_value=fill_value, |
240 |
| - order="C", |
241 |
| - ) |
| 234 | +def serialized_float_is_valid(serialized): |
| 235 | + """ |
| 236 | + Validate that the serialized representation of a float conforms to the spec. |
242 | 237 |
|
243 |
| - buffer_dict = metadata.to_buffer_dict(prototype=default_buffer_prototype()) |
244 |
| - zarray_dict = json.loads(buffer_dict[ZARRAY_JSON].to_bytes().decode()) |
245 |
| - |
246 |
| - if np.isnan(fill_value): |
247 |
| - assert zarray_dict["fill_value"] == "NaN" |
248 |
| - elif np.isinf(fill_value) and fill_value > 0: |
249 |
| - assert zarray_dict["fill_value"] == "Infinity" |
250 |
| - elif np.isinf(fill_value): |
251 |
| - assert zarray_dict["fill_value"] == "-Infinity" |
| 238 | + The specification requires that a serialized float must be either: |
| 239 | + - A JSON number, or |
| 240 | + - One of the strings "NaN", "Infinity", or "-Infinity". |
| 241 | +
|
| 242 | + Args: |
| 243 | + serialized: The value produced by JSON serialization for a floating point number. |
| 244 | +
|
| 245 | + Returns: |
| 246 | + bool: True if the serialized value is valid according to the spec, False otherwise. |
| 247 | + """ |
| 248 | + if isinstance(serialized, Real): |
| 249 | + return True |
| 250 | + return serialized in ("NaN", "Infinity", "-Infinity") |
| 251 | + |
| 252 | + |
| 253 | +@given(meta=array_metadata()) # type: ignore[misc] |
| 254 | +def test_array_metadata_meets_spec(meta: ArrayV2Metadata | ArrayV3Metadata) -> None: |
| 255 | + """ |
| 256 | + Validate that the array metadata produced by the library conforms to the relevant spec (V2 vs V3). |
| 257 | +
|
| 258 | + For ArrayV2Metadata: |
| 259 | + - Ensures that 'zarr_format' is 2. |
| 260 | + - Verifies that 'filters' is either None or a tuple (and not an empty tuple). |
| 261 | + For ArrayV3Metadata: |
| 262 | + - Ensures that 'zarr_format' is 3. |
| 263 | +
|
| 264 | + For both versions: |
| 265 | + - If the dtype is a floating point of some kind, verifies of fill values: |
| 266 | + * NaN is serialized as the string "NaN" |
| 267 | + * Positive Infinity is serialized as the string "Infinity" |
| 268 | + * Negative Infinity is serialized as the string "-Infinity" |
| 269 | + * Other fill values are preserved as-is. |
| 270 | + - If the dtype is a complex number of some kind, verifies that each component of the fill |
| 271 | + value (real and imaginary) satisfies the serialization rules for floating point numbers. |
| 272 | +
|
| 273 | + Note: |
| 274 | + This test validates spec-compliance for array metadata serialization. |
| 275 | + It is a work-in-progress and should be expanded as further edge cases are identified. |
| 276 | + """ |
| 277 | + asdict_dict = meta.to_dict() |
| 278 | + |
| 279 | + # version-specific validations |
| 280 | + if isinstance(meta, ArrayV2Metadata): |
| 281 | + assert asdict_dict["filters"] != () |
| 282 | + assert asdict_dict["filters"] is None or isinstance(asdict_dict["filters"], tuple) |
| 283 | + assert asdict_dict["zarr_format"] == 2 |
252 | 284 | else:
|
253 |
| - assert zarray_dict["fill_value"] == fill_value |
| 285 | + assert asdict_dict["zarr_format"] == 3 |
| 286 | + |
| 287 | + # version-agnostic validations |
| 288 | + if meta.dtype.kind == "f": |
| 289 | + assert serialized_float_is_valid(asdict_dict["fill_value"]) |
| 290 | + if meta.dtype.kind == "c": |
| 291 | + # fill_value should be a two-element array [real, imag]. |
| 292 | + assert serialized_float_is_valid(asdict_dict["fill_value"].real) |
| 293 | + assert serialized_float_is_valid(asdict_dict["fill_value"].imag) |
0 commit comments