From 6e93a8bcccae13bcb799cee99bf36a6b0fa5ee14 Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Mon, 6 Jul 2026 11:39:14 +0200 Subject: [PATCH] test: consolidate vlen F-contiguous regression tests Follow-up test-quality cleanup for the gh-3558 fix (#4116): - Merge test_vlen_string_f_contiguous and test_vlen_bytes_f_contiguous into one test parametrized over (dtype, fill_value, elements), matching the parametrized style of test_vlen_string in the same file. - Drop the chunks=(2,2) case: with (3,3) data it produces only partial chunks, which the codec pipeline recopies to C order before encoding, so it never exercised the F-contiguous path it claimed to cover. Use chunks == shape (a single complete chunk) so the F-contiguous buffer reaches the codec untouched. - Build the F-contiguous input directly via reshape(order="F") instead of wrapping a C-order reshape in np.asarray(..., order="F"), and assert data.flags.f_contiguous so a future simplification can't silently defeat the regression guard. - Add a docstring stating the verified behavior. Assisted-by: ClaudeCode:claude-opus-4.8 --- tests/test_codecs/test_vlen.py | 43 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/tests/test_codecs/test_vlen.py b/tests/test_codecs/test_vlen.py index 6729a68af9..c2c2c9b201 100644 --- a/tests/test_codecs/test_vlen.py +++ b/tests/test_codecs/test_vlen.py @@ -9,7 +9,7 @@ from zarr.abc.store import Store from zarr.codecs import ZstdCodec from zarr.codecs.vlen_utf8 import VLenBytesCodec, VLenUTF8Codec -from zarr.core.dtype import VariableLengthBytes, get_data_type_from_native_dtype +from zarr.core.dtype import get_data_type_from_native_dtype from zarr.core.metadata.v3 import ArrayV3Metadata from zarr.storage import StorePath @@ -67,30 +67,29 @@ def test_vlen_string( assert a.dtype == data.dtype -@pytest.mark.parametrize("store", ["memory"], indirect=["store"]) -@pytest.mark.parametrize("chunks", [(3, 3), (2, 2)]) -def test_vlen_string_f_contiguous(store: Store, chunks: tuple[int, int]) -> None: - # gh-3558: F-contiguous chunks were encoded in transposed element order - data = np.asarray( - np.array([f"S{i:05}" for i in range(9)], dtype=object).reshape(3, 3), order="F" - ) - sp = StorePath(store, path="string-f-contiguous") - a = zarr.create_array(sp, shape=data.shape, chunks=chunks, dtype=str, fill_value="") - a[:, :] = data - assert np.array_equal(data, np.asarray(a[:, :], dtype=object)) - - @pytest.mark.filterwarnings("ignore::zarr.core.dtype.common.UnstableSpecificationWarning") @pytest.mark.parametrize("store", ["memory"], indirect=["store"]) -@pytest.mark.parametrize("chunks", [(3, 3), (2, 2)]) -def test_vlen_bytes_f_contiguous(store: Store, chunks: tuple[int, int]) -> None: - # gh-3558: F-contiguous chunks were encoded in transposed element order - data = np.asarray( - np.array([b"%05d" % i for i in range(9)], dtype=object).reshape(3, 3), order="F" - ) - sp = StorePath(store, path="bytes-f-contiguous") +@pytest.mark.parametrize( + ("dtype", "fill_value", "elements"), + [ + pytest.param("string", "", [f"S{i:05}" for i in range(9)], id="string"), + pytest.param("variable_length_bytes", b"", [b"%05d" % i for i in range(9)], id="bytes"), + ], +) +def test_vlen_f_contiguous( + store: Store, dtype: str, fill_value: str | bytes, elements: list[str] | list[bytes] +) -> None: + """An F-contiguous chunk written through a vlen codec round-trips in the original + element order rather than the transposed memory order (gh-3558).""" + # reshape(order="F") gives an F-contiguous view directly, so the whole-array write + # below hands an F-contiguous chunk to the codec; assert it to guard the precondition. + data = np.array(elements, dtype=object).reshape((3, 3), order="F") + assert data.flags.f_contiguous + sp = StorePath(store, path="vlen-f-contiguous") + # chunks == shape so the write is a single complete chunk, which the codec pipeline + # forwards to the codec untouched; a partial-chunk layout would be recopied to C order. a = zarr.create_array( - sp, shape=data.shape, chunks=chunks, dtype=VariableLengthBytes(), fill_value=b"" + sp, shape=data.shape, chunks=data.shape, dtype=dtype, fill_value=fill_value ) a[:, :] = data assert np.array_equal(data, np.asarray(a[:, :], dtype=object))