4545y_dim_st = st .integers (1 , 32 )
4646x_dim_st = st .integers (1 , 32 )
4747channel_names_st = c_dim_st .flatmap (lambda c_dim : st .lists (short_text_st , min_size = c_dim , max_size = c_dim , unique = True ))
48- ngff_versions_st = st .just ( "0.5" )
48+ ngff_versions_st = st .sampled_from ([ "0.4" , "0.5" ] )
4949short_alpha_numeric = st .text (
5050 alphabet = list (string .ascii_lowercase + string .ascii_uppercase + string .digits ),
5151 min_size = 1 ,
@@ -238,28 +238,33 @@ def test_init_ome_zarr(channel_names, version):
238238
239239
240240@pytest .mark .parametrize ("mode" , ["w" , "w-" ])
241- def test_open_ome_zarr_v04_write_raises (tmp_path , mode ):
242- """Creating new v0.4 stores must raise ValueError for all write modes."""
243- with pytest .raises (ValueError , match = r"v0.4" ):
244- open_ome_zarr (
245- tmp_path / "out.zarr" ,
246- layout = "fov" ,
247- mode = mode ,
248- channel_names = ["DAPI" ],
249- version = "0.4" ,
250- )
251-
252-
253- def test_open_ome_zarr_v04_append_new_path_raises (tmp_path ):
254- """mode='a' on a nonexistent path is a new store and must also raise."""
255- with pytest .raises (ValueError , match = r"v0.4" ):
256- open_ome_zarr (
257- tmp_path / "nonexistent.zarr" ,
258- layout = "fov" ,
259- mode = "a" ,
260- channel_names = ["DAPI" ],
261- version = "0.4" ,
262- )
241+ def test_open_ome_zarr_v04_write_succeeds (tmp_path , mode ):
242+ """Creating new v0.4 stores must succeed."""
243+ store_path = tmp_path / "out.zarr"
244+ with open_ome_zarr (
245+ store_path ,
246+ layout = "fov" ,
247+ mode = mode ,
248+ channel_names = ["DAPI" ],
249+ version = "0.4" ,
250+ ) as ds :
251+ assert ds .version == "0.4"
252+ assert (store_path / ".zgroup" ).exists ()
253+ assert not (store_path / "zarr.json" ).exists ()
254+
255+
256+ def test_open_ome_zarr_v04_append_new_path_succeeds (tmp_path ):
257+ """mode='a' on a nonexistent path should create a v0.4 store."""
258+ store_path = tmp_path / "nonexistent.zarr"
259+ with open_ome_zarr (
260+ store_path ,
261+ layout = "fov" ,
262+ mode = "a" ,
263+ channel_names = ["DAPI" ],
264+ version = "0.4" ,
265+ ) as ds :
266+ assert ds .version == "0.4"
267+ assert (store_path / ".zgroup" ).exists ()
263268
264269
265270@pytest .mark .parametrize ("version" , ["0.5" ])
@@ -391,12 +396,14 @@ def test_write_ome_zarr(channels_and_random_5d, arr_name, version):
391396 channel_names , random_5d = channels_and_random_5d
392397 with _temp_ome_zarr (random_5d , channel_names , arr_name , version = version ) as dataset :
393398 assert_allclose (dataset [arr_name ][:], random_5d )
394- # round-trip test with the offical reader implementation
395- ext_reader = ome_zarr .reader .Reader (ome_zarr .io .parse_url (dataset .zgroup .store .root ))
396- node = next (iter (ext_reader ()))
397- assert node .metadata ["channel_names" ] == channel_names
398- assert node .specs [0 ].datasets == [arr_name ]
399- assert_allclose (node .data [0 ], random_5d )
399+ if version == "0.5" :
400+ # round-trip test with the official reader implementation
401+ # ome-zarr-py reader requires zarr-python Group with .store.root
402+ ext_reader = ome_zarr .reader .Reader (ome_zarr .io .parse_url (dataset .zgroup .store .root ))
403+ node = next (iter (ext_reader ()))
404+ assert node .metadata ["channel_names" ] == channel_names
405+ assert node .specs [0 ].datasets == [arr_name ]
406+ assert_allclose (node .data [0 ], random_5d )
400407
401408
402409@given (
@@ -421,9 +428,10 @@ def test_create_zeros(ch_shape_dtype, arr_name, version):
421428 version = version ,
422429 )
423430 dataset .create_zeros (name = arr_name , shape = shape , dtype = dtype )
424- assert {e .name for e in (Path (store_path ) / arr_name ).iterdir ()} == {
425- "zarr.json" ,
426- }
431+ if version == "0.5" :
432+ assert (Path (store_path ) / arr_name / "zarr.json" ).exists ()
433+ else :
434+ assert (Path (store_path ) / arr_name / ".zarray" ).exists ()
427435 if version == "0.5" :
428436 assert dataset [arr_name ].metadata .dimension_names == (
429437 "T" ,
@@ -1198,7 +1206,7 @@ def test_create_position(row, col, pos, version):
11981206 version = version ,
11991207 )
12001208 _ = dataset .create_position (row_name = row , col_name = col , pos_name = pos )
1201- ome = dataset .zgroup .attrs ["ome" ]
1209+ ome = dict ( dataset . zgroup . attrs ) if version == "0.4" else dataset .zgroup .attrs ["ome" ]
12021210 assert [c ["name" ] for c in ome ["plate" ]["columns" ]] == [col ]
12031211 assert [r ["name" ] for r in ome ["plate" ]["rows" ]] == [row ]
12041212 assert (store_path / row / col / pos ).is_dir ()
@@ -1892,3 +1900,69 @@ def test_initialize_pyramid_invalid_dims(implementation, tmp_path):
18921900 pos .create_zeros ("0" , shape = (1 , 1 , 2 , 8 , 8 ), dtype = np .float32 )
18931901 with pytest .raises (ValueError , match = "not in dataset axes" ):
18941902 pos .initialize_pyramid (levels = 2 , dims = {"w" })
1903+
1904+
1905+ # ---------- v0.4 dedicated tests ----------
1906+
1907+
1908+ def test_write_ome_zarr_v04_fov_roundtrip (tmp_path ):
1909+ """Full round-trip: create v0.4 FOV store, write image, read back."""
1910+ store_path = tmp_path / "v04.ome.zarr"
1911+ data = np .random .default_rng (42 ).random ((1 , 2 , 3 , 64 , 64 )).astype (np .float32 )
1912+ with open_ome_zarr (
1913+ store_path ,
1914+ layout = "fov" ,
1915+ mode = "w-" ,
1916+ channel_names = ["A" , "B" ],
1917+ version = "0.4" ,
1918+ ) as ds :
1919+ ds .create_image ("0" , data )
1920+ assert ds .version == "0.4"
1921+ # Verify v2 file structure
1922+ assert (store_path / ".zgroup" ).exists ()
1923+ assert (store_path / ".zattrs" ).exists ()
1924+ assert not (store_path / "zarr.json" ).exists ()
1925+ # Re-open read-only
1926+ with open_ome_zarr (store_path , layout = "fov" , mode = "r" ) as ds :
1927+ assert ds .version == "0.4"
1928+ assert_array_equal (ds ["0" ][:], data )
1929+ assert ds .channel_names == ["A" , "B" ]
1930+
1931+
1932+ def test_write_ome_zarr_v04_hcs_roundtrip (tmp_path ):
1933+ """HCS plate creation with v0.4."""
1934+ store_path = tmp_path / "v04_hcs.ome.zarr"
1935+ data = np .zeros ((1 , 2 , 3 , 32 , 32 ), dtype = np .uint16 )
1936+ with open_ome_zarr (
1937+ store_path ,
1938+ layout = "hcs" ,
1939+ mode = "w-" ,
1940+ channel_names = ["A" , "B" ],
1941+ version = "0.4" ,
1942+ ) as plate :
1943+ pos = plate .create_position ("A" , "1" , "0" )
1944+ pos .create_image ("0" , data )
1945+ # Flat metadata, no "ome" wrapper
1946+ assert "plate" in plate .zattrs
1947+ assert "ome" not in plate .zattrs
1948+ with open_ome_zarr (store_path , layout = "hcs" , mode = "r" ) as plate :
1949+ assert plate .version == "0.4"
1950+ assert_array_equal (plate ["A/1/0" ]["0" ][:], data )
1951+
1952+
1953+ def test_sharding_raises_on_v04 (tmp_path ):
1954+ """Sharding must raise ValueError for v0.4."""
1955+ store_path = tmp_path / "v04_shard.zarr"
1956+ with open_ome_zarr (
1957+ store_path ,
1958+ layout = "fov" ,
1959+ mode = "w-" ,
1960+ channel_names = ["A" ],
1961+ version = "0.4" ,
1962+ ) as ds :
1963+ with pytest .raises (ValueError , match = "Sharding is not supported" ):
1964+ ds .create_image (
1965+ "0" ,
1966+ np .zeros ((1 , 1 , 1 , 64 , 64 )),
1967+ shards_ratio = (1 , 1 , 1 , 2 , 2 ),
1968+ )
0 commit comments