Skip to content

Port the OME-Zarr symmetry test (readOmeZarr) to Python #36

Closed

Description

@stevevanhooser

NDR-matlab has a cross-language symmetry test for ndr.format.omezarr.readArray (tools/tests/+ndr/+symmetry/{+makeArtifacts,+readArtifacts}/+format/readOmeZarr.m). NDR-python has no half of it, so in test-symmetry.yml:

  • Stage 2: Python never reads the OME-Zarr artifacts MATLAB writes. Nothing on the Python side checks that the two readers decode the same store to the same bytes.
  • Stage 4: MATLAB's pythonArtifacts case can't run, so symmetry: a comparison that could not run fails CI instead of passing #35 deselects it by name as a declared known gap (noPythonHalf = ["readOmeZarr"]).

Zarr is lossless, so if the two ports decode the same chunks to different values, one of them is wrong. That is exactly what this test exists to catch, and today it only runs MATLAB-against-MATLAB.

What the MATLAB half does

  • makeArtifacts: builds example.zarr with ndr.test.format.omezarr.makeExampleFixture(dir, 'WithChunks', true):

    • a dual pyramid: a shared level-0 array plus mean and max chains, zarr v2;
    • a seeded uint16 [1 8 8 10] volume in [1 4 4 4] chunks, so there are edge chunks;
    • Blosc+Zstd compression.

    It then writes metadata.json (fixtureName, level0Shape, level0ChunkShape, dtype, seed) and checksums.json. The checksums file holds 8 regions (whole arrays at levels 1 and 2 of both pyramids, inside one chunk, straddling chunks, reaching the x edge, inside the x edge). Each region has the SHA-256 of typecast(got(:), 'uint8').

  • readArtifacts: re-reads the other port's fixture with its own reader, and checks each region's SHA-256.

What the Python half needs

  1. The same SHA, from the Python side. Both readArrays return the stored axis order (c,z,y,x) with a 1-based, inclusive Region = [start; stop]. MATLAB places chunks so got(i,j,k,l) == zarr[i-1,j-1,k-1,l-1]. But got(:) is column-major, so the Python digest is sha256(np.asarray(arr, "<u2").ravel(order="F").tobytes()).
  2. A Blosc+Zstd fixture written by zarr-python. tests/_omezarr_fixture.py already mirrors makeExampleFixture's layout and seed, but writes uncompressed chunks by hand. That suits the unit tests, but MATLAB's own docstring warns that an uncompressed fixture "would prove nothing about the Blosc+Zstd path the lab actually writes". The make side should write the arrays through zarr-python (zarr v2 format, numcodecs.Blosc(cname="zstd", clevel=5, shuffle=SHUFFLE)), the same library that writes the lab's real data. Then MATLAB's hand-rolled reader gets checked against genuine zarr-python output. zarr 3.x's v2 output already matches what MATLAB's readZArrayMeta accepts: "filters": null, "dimension_separator": ".", C order, blosc/zstd.
  3. tests/symmetry/make_artifacts/format/test_read_omezarr.py writes the same three things (example.zarr, metadata.json, checksums.json with the same 8 regions) to pythonArtifacts/format/readOmeZarr/testReadOmeZarrArtifacts/.
  4. tests/symmetry/read_artifacts/format/test_read_omezarr.py, parametrized over source_type, checks every region's SHA-256 against the other port's checksums.
  5. test-symmetry.yml:

Done when

The symmetry job runs readOmeZarr both ways across the languages, with nothing deselected for it:

  • Stage 2: Python reads MATLAB's fixture and matches every checksum.
  • Stage 4: MATLAB reads Python's fixture and matches every checksum, alongside its own.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions