Coverage for tests/test_ndf_model.py: 94%
63 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-16 15:24 -0700
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-16 15:24 -0700
1# This file is part of lsst-images.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
12from __future__ import annotations
14from pathlib import Path
16import numpy as np
17import pytest
19try:
20 import h5py
22 from lsst.images.ndf import _hds
23 from lsst.images.ndf._model import HdsPrimitive, Ndf, NdfArray, NdfDocument, NdfQuality, NdfWcs
25 HAVE_H5PY = True
26except ImportError:
27 HAVE_H5PY = False
29skip_no_h5py = pytest.mark.skipif(not HAVE_H5PY, reason="h5py is not installed")
32def _attr_str(value: object) -> str:
33 """Return value decoded to str, handling bytes from HDF5 attributes."""
34 if isinstance(value, bytes): 34 ↛ 36line 34 didn't jump to line 36 because the condition on line 34 was always true
35 return value.decode("ascii")
36 return str(value)
39@skip_no_h5py
40def test_ndf_document_writes_standard_components(tmp_path: Path) -> None:
41 """Verify NdfDocument writes all standard NDF components to HDF5
42 correctly.
43 """
44 image = np.arange(6, dtype=np.float32).reshape(2, 3)
45 quality = np.array([[0, 1, 0], [1, 0, 0]], dtype=np.uint8)
46 wcs_lines = [" Begin FrameSet", " End FrameSet"]
47 document = NdfDocument(root=Ndf(), root_name="TEST")
48 document.root.set_array_component("DATA_ARRAY", image, origin=(20, 10))
49 document.root.set_quality(
50 NdfQuality(
51 NdfArray(
52 quality,
53 origin=np.array([20, 10], dtype=np.int32),
54 bad_pixel=False,
55 )
56 )
57 )
58 document.root.set_wcs(NdfWcs(wcs_lines))
59 document.root.set_units("adu")
60 lsst = document.root.ensure_lsst_extension()
61 lsst.children["JSON"] = HdsPrimitive.char_array(['{"kind":"image"}'], width=80)
63 path = str(tmp_path / "test.sdf")
64 with h5py.File(path, "w") as f:
65 document.write_to_hdf5(f)
66 with h5py.File(path, "r") as f:
67 assert _attr_str(f["/"].attrs[_hds.ATTR_CLASS]) == "NDF"
68 assert _attr_str(f["/"].attrs[_hds.ATTR_ROOT_NAME]) == "TEST"
69 assert _attr_str(f["/DATA_ARRAY"].attrs[_hds.ATTR_CLASS]) == "ARRAY"
70 np.testing.assert_array_equal(f["/DATA_ARRAY/DATA"][()], image)
71 np.testing.assert_array_equal(f["/DATA_ARRAY/ORIGIN"][()], np.array([20, 10]))
72 assert _attr_str(f["/QUALITY"].attrs[_hds.ATTR_CLASS]) == "QUALITY"
73 assert f["/QUALITY/BADBITS"][()] == 255
74 np.testing.assert_array_equal(f["/QUALITY/QUALITY/DATA"][()], quality)
75 assert f["/QUALITY/QUALITY/BAD_PIXEL"].id.get_type().get_class() == h5py.h5t.BITFIELD
76 assert _attr_str(f["/WCS"].attrs[_hds.ATTR_CLASS]) == "WCS"
77 assert _hds.read_char_array(f["/WCS/DATA"]) == wcs_lines
78 assert f["/UNITS"].shape == ()
79 assert f["/UNITS"][()].decode("ascii").rstrip(" ") == "adu"
80 assert _hds.read_char_array(f["/MORE/LSST/JSON"]) == ['{"kind":"image"}']
83@skip_no_h5py
84def test_document_read_preserves_typed_ndf_components(tmp_path: Path) -> None:
85 """Verify NdfDocument.from_hdf5 recovers typed NDF components after
86 a round-trip.
87 """
88 image = np.arange(4, dtype=np.int16).reshape(2, 2)
89 path = str(tmp_path / "test.sdf")
90 with h5py.File(path, "w") as f:
91 document = NdfDocument(root=Ndf(), root_name="READ")
92 document.root.set_array_component("DATA_ARRAY", image, origin=(5, 6))
93 document.root.set_units("count")
94 document.write_to_hdf5(f)
95 with h5py.File(path, "r") as f:
96 recovered = NdfDocument.from_hdf5(f)
97 assert isinstance(recovered.root, Ndf)
98 assert recovered.root_name == "READ"
99 assert recovered.root.get_units() == "count"
100 data = recovered.get("/DATA_ARRAY/DATA")
101 assert isinstance(data, HdsPrimitive)
102 np.testing.assert_array_equal(data.read_array(), image)