OCaml Zarr jsont codecs for v2/v3 and common conventions
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

test: roundtrip and unknown field preservation tests

Add Task 13 roundtrip tests exercising real-world v2/v3 Zarr JSON through
encode-decode-encode cycles, and Task 14 tests verifying that unknown fields
survive roundtrips in V2.Array_meta, V2.Compressor.Blosc, and Attrs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

+144
+144
test/test_zarr_jsont.ml
··· 536 536 let () = test_dispatch_v2_group () 537 537 let () = test_dispatch_v3_array () 538 538 let () = test_dispatch_v3_group () 539 + 540 + (* Task 13: Roundtrip tests with real example JSON *) 541 + 542 + let roundtrip_v3 json_str = 543 + let v = decode Zarr_jsont.v3_jsont json_str in 544 + let json' = encode Zarr_jsont.v3_jsont v in 545 + let v' = decode Zarr_jsont.v3_jsont json' in 546 + (match Zarr_jsont.V3_node.kind v, Zarr_jsont.V3_node.kind v' with 547 + | `Array a, `Array a' -> 548 + assert (Zarr_jsont.V3.Array_meta.shape a = Zarr_jsont.V3.Array_meta.shape a'); 549 + assert (Zarr_jsont.V3.Array_meta.data_type a = Zarr_jsont.V3.Array_meta.data_type a') 550 + | `Group, `Group -> () 551 + | _ -> assert false) 552 + 553 + let test_roundtrip_v3_array () = 554 + roundtrip_v3 {|{ 555 + "zarr_format": 3, 556 + "node_type": "array", 557 + "shape": [10000, 1000], 558 + "dimension_names": ["rows", "columns"], 559 + "data_type": "float64", 560 + "chunk_grid": {"name": "regular", "configuration": {"chunk_shape": [1000, 100]}}, 561 + "chunk_key_encoding": {"name": "default", "configuration": {"separator": "/"}}, 562 + "codecs": [{"name": "bytes", "configuration": {"endian": "little"}}], 563 + "fill_value": "NaN", 564 + "attributes": {"foo": 42, "bar": "apples", "baz": [1, 2, 3, 4]} 565 + }|}; 566 + print_endline "test_roundtrip_v3_array: ok" 567 + 568 + let test_roundtrip_v3_group_with_convs () = 569 + roundtrip_v3 {|{ 570 + "zarr_format": 3, 571 + "node_type": "group", 572 + "attributes": { 573 + "zarr_conventions": [ 574 + {"uuid": "f17cb550-5864-4468-aeb7-f3180cfb622f", "name": "proj:", "description": "CRS info"}, 575 + {"uuid": "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4", "name": "spatial:", "description": "Spatial info"} 576 + ], 577 + "proj:code": "EPSG:32633", 578 + "spatial:dimensions": ["Y", "X"], 579 + "spatial:bbox": [500000.0, 4900000.0, 600000.0, 5000000.0] 580 + } 581 + }|}; 582 + print_endline "test_roundtrip_v3_group_with_convs: ok" 583 + 584 + let test_roundtrip_v2 () = 585 + let json = {|{ 586 + "zarr_format": 2, 587 + "shape": [10000, 10000], 588 + "chunks": [1000, 1000], 589 + "dtype": "<f8", 590 + "compressor": {"id": "blosc", "cname": "lz4", "clevel": 5, "shuffle": 1}, 591 + "fill_value": "NaN", 592 + "order": "C", 593 + "filters": [{"id": "delta", "dtype": "<f8", "astype": "<f4"}] 594 + }|} in 595 + let v = decode Zarr_jsont.v2_array_jsont json in 596 + let json' = encode Zarr_jsont.v2_array_jsont v in 597 + let v' = decode Zarr_jsont.v2_array_jsont json' in 598 + let get_shape n = match Zarr_jsont.V2_node.kind n with 599 + | `Array a -> Zarr_jsont.V2.Array_meta.shape a 600 + | `Group -> failwith "expected array" 601 + in 602 + assert (get_shape v = get_shape v'); 603 + print_endline "test_roundtrip_v2: ok" 604 + 605 + let test_roundtrip_multiscales () = 606 + roundtrip_v3 {|{ 607 + "zarr_format": 3, 608 + "node_type": "group", 609 + "attributes": { 610 + "multiscales": { 611 + "layout": [ 612 + {"asset": "r10m", "transform": {"scale": [1.0, 1.0]}, "spatial:shape": [10980, 10980], "spatial:transform": [10.0, 0.0, 500000.0, 0.0, -10.0, 5000000.0]}, 613 + {"asset": "r20m", "derived_from": "r10m", "transform": {"scale": [2.0, 2.0], "translation": [0.0, 0.0]}, "spatial:shape": [5490, 5490]} 614 + ] 615 + }, 616 + "proj:code": "EPSG:32633", 617 + "spatial:dimensions": ["Y", "X"], 618 + "spatial:bbox": [500000.0, 4900000.0, 600000.0, 5000000.0] 619 + } 620 + }|}; 621 + print_endline "test_roundtrip_multiscales: ok" 622 + 623 + let () = test_roundtrip_v3_array () 624 + let () = test_roundtrip_v3_group_with_convs () 625 + let () = test_roundtrip_v2 () 626 + let () = test_roundtrip_multiscales () 627 + 628 + (* Task 14: Unknown field preservation tests *) 629 + 630 + let test_unknown_preservation () = 631 + (* V2 array with extra unknown field *) 632 + let json = {|{ 633 + "zarr_format": 2, 634 + "shape": [10], 635 + "chunks": [5], 636 + "dtype": "<f4", 637 + "compressor": null, 638 + "fill_value": 0.0, 639 + "order": "C", 640 + "filters": null, 641 + "custom_extension": {"nested": true} 642 + }|} in 643 + let v = decode Zarr_jsont.v2_array_jsont json in 644 + let json' = encode Zarr_jsont.v2_array_jsont v in 645 + let v' = decode Zarr_jsont.v2_array_jsont json' in 646 + (match Zarr_jsont.V2_node.kind v' with 647 + | `Array a -> 648 + let unk = Zarr_jsont.V2.Array_meta.unknown a in 649 + (match unk with 650 + | Jsont.Object (mems, _) -> 651 + assert (List.exists (fun ((k, _), _) -> k = "custom_extension") mems) 652 + | _ -> assert false) 653 + | _ -> assert false); 654 + 655 + (* V2 compressor with extra unknown fields *) 656 + let json = {|{"id":"blosc","cname":"lz4","clevel":5,"shuffle":1,"extra_param":"test"}|} in 657 + let v = decode Zarr_jsont.V2.compressor_jsont json in 658 + let json' = encode Zarr_jsont.V2.compressor_jsont v in 659 + let v' = decode Zarr_jsont.V2.compressor_jsont json' in 660 + (match v' with 661 + | `Blosc b -> 662 + let unk = Zarr_jsont.V2.Compressor.Blosc.unknown b in 663 + (match unk with 664 + | Jsont.Object (mems, _) -> 665 + assert (List.exists (fun ((k, _), _) -> k = "extra_param") mems) 666 + | _ -> assert false) 667 + | _ -> assert false); 668 + 669 + (* Attrs with unknown custom key *) 670 + let json = {|{"custom_key": "custom_value", "another": 42}|} in 671 + let v = decode Zarr_jsont.attrs_jsont json in 672 + let json' = encode Zarr_jsont.attrs_jsont v in 673 + let v' = decode Zarr_jsont.attrs_jsont json' in 674 + let unk = Zarr_jsont.Attrs.unknown v' in 675 + (match unk with 676 + | Jsont.Object (mems, _) -> 677 + assert (List.exists (fun ((k, _), _) -> k = "custom_key") mems); 678 + assert (List.exists (fun ((k, _), _) -> k = "another") mems) 679 + | _ -> assert false); 680 + print_endline "test_unknown_preservation: ok" 681 + 682 + let () = test_unknown_preservation ()