Coverage for tests/test_grid_container.py: 14%
127 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-08 11:19 +0000
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-08 11:19 +0000
1# This file is part of cell_coadds.
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# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22from __future__ import annotations
24import copy
25import pickle
26import unittest
28from lsst.cell_coadds import GridContainer, UniformGrid
29from lsst.geom import Box2I, Extent2I, Point2I
30from lsst.skymap import Index2D
31from lsst.utils.tests import methodParameters
34class GridContainerTestCase(unittest.TestCase):
35 """Tests for GridContainer and GridIndex/Index2D's C++/Python
36 translation.
37 """
39 def _fill(self, container: GridContainer[dict[str, int]]) -> None:
40 """Populate a GridContainer with dicts that map "x" or "y" to
41 the cell index in that dimension.
42 """
43 for y in range(container.offset.y, container.offset.y + container.shape.y):
44 for x in range(container.offset.x, container.offset.x + container.shape.x):
45 container[Index2D(x, y)] = {"x": x, "y": y}
47 def _check(self, container: GridContainer[dict[str, int]]) -> None:
48 """Perform a complete battery of tests on a GridContainer instance."""
49 for value in container.values():
50 self.assertEqual(container[Index2D(x=value["x"], y=value["y"])], value)
51 self.assertEqual(container.get(Index2D(x=value["x"], y=value["y"])), value)
52 self.assertEqual(container[Index2D(**value)], value)
54 self.assertEqual(container.first, {"x": container.offset.x, "y": container.offset.y})
55 self.assertEqual(
56 container.last,
57 {
58 "x": container.offset.x + container.shape.x - 1,
59 "y": container.offset.y + container.shape.y - 1,
60 },
61 )
62 transformed: GridContainer[list[int]] = container.rebuild_transformed(
63 lambda cell: list(cell.values()) # type: ignore
64 )
65 self.assertEqual(transformed.shape, container.shape)
66 self.assertEqual(transformed.offset, container.offset)
67 self.assertEqual(list(transformed.keys()), list(container.keys()))
68 self.assertEqual(list(transformed.values()), [list(v.values()) for v in container.values()])
69 emptied = GridContainer[dict[str, int]](container.shape, container.offset)
70 self._fill(emptied)
71 self.assertEqual(emptied.shape, container.shape)
72 self.assertEqual(emptied.offset, container.offset)
73 self.assertEqual(list(emptied), list(container))
74 copied = copy.copy(container)
75 self.assertEqual(copied.shape, container.shape)
76 self.assertEqual(copied.offset, container.offset)
77 self.assertEqual(list(copied), list(container))
78 self.assertTrue(
79 all(
80 copied_cell is original_cell
81 for copied_cell, original_cell in zip(copied, container, strict=True)
82 )
83 )
84 deep_copied = copy.deepcopy(container)
85 self.assertEqual(deep_copied.shape, container.shape)
86 self.assertEqual(deep_copied.offset, container.offset)
87 self.assertEqual(list(deep_copied), list(container))
88 self.assertTrue(
89 all(
90 deep_copied_cell is not original_cell
91 for deep_copied_cell, original_cell in zip(deep_copied, container, strict=True)
92 )
93 )
95 def test_simple_ctor(self) -> None:
96 """Test a GridContainer built with the shape-only GridContainer
97 constructor.
98 """
99 shape = Index2D(x=3, y=2)
100 gc = GridContainer(shape)
101 self.assertEqual(gc.shape, shape)
102 self.assertIsInstance(gc.shape, Index2D)
103 self.assertEqual(gc.offset, Index2D(x=0, y=0))
104 self.assertIsInstance(gc.offset, Index2D)
105 self.assertEqual(gc.size, shape[0] * shape[1])
106 self.assertEqual(len(gc), 0) # unfilled container has length 0.
107 self._fill(gc)
108 self.assertEqual(len(gc), shape[0] * shape[1])
109 self._check(gc)
111 def test_complex_ctor(self) -> None:
112 """Test a GridContainer built with the shape-and-offset GridContainer
113 constructor.
114 """
115 shape = Index2D(x=3, y=2)
116 offset = Index2D(x=1, y=2)
117 gc = GridContainer(shape, offset)
118 self.assertEqual(gc.shape, shape)
119 self.assertIsInstance(gc.shape, Index2D)
120 self.assertEqual(gc.offset, offset)
121 self.assertIsInstance(gc.offset, Index2D)
122 self.assertEqual(len(gc), 0)
123 self.assertEqual(gc.size, shape[0] * shape[1])
124 self._fill(gc)
125 self.assertEqual(len(gc), shape[0] * shape[1])
126 self._check(gc)
128 def test_subset_overlapping(self) -> None:
129 """Test various inputs to GridContainer.subset_overlapping."""
130 cell_size = Extent2I(x=3, y=2)
131 full_bbox = Box2I(Point2I(x=1, y=2), Extent2I(x=15, y=12))
132 full_shape = Index2D(x=5, y=6)
133 full_container = GridContainer(shape=full_shape)
134 self._fill(full_container)
135 grid = UniformGrid.from_bbox_cell_size(full_bbox, cell_size)
136 self.assertEqual(grid.shape, full_shape)
138 # Subset with the orignal bounding box; should behave like a deepcopy.
139 subset_container_full = full_container.subset_overlapping(grid, full_bbox)
140 self.assertEqual(subset_container_full.shape, full_container.shape)
141 self.assertEqual(subset_container_full.offset, full_container.offset)
142 self.assertEqual(list(subset_container_full), list(full_container))
143 subset_container_full[Index2D(x=2, y=2)] = {"x": -1, "y": -1}
144 self.assertEqual(list(subset_container_full.keys()), list(full_container.keys()))
145 self.assertNotEqual(list(subset_container_full.values()), list(full_container.values()))
147 # Subset the full container with a nontrivial bbox.
148 bbox_1 = Box2I(Point2I(x=6, y=4), Point2I(x=10, y=7))
149 subset_container_1 = full_container.subset_overlapping(grid, bbox_1)
150 self.assertEqual(subset_container_1.offset, Index2D(x=1, y=1))
151 self.assertEqual(subset_container_1.shape, Index2D(x=3, y=2))
152 union_bbox_1 = Box2I()
153 for v in subset_container_1.keys(): # noqa: SIM118
154 cell_bbox = grid.bbox_of(v)
155 self.assertTrue(cell_bbox.overlaps(bbox_1))
156 union_bbox_1.include(cell_bbox)
157 self.assertTrue(union_bbox_1.contains(bbox_1))
158 self._check(subset_container_1)
160 # Subset the subset container with an even smaller bbox, to check the
161 # case where the original offset is nonzero.
162 bbox_2 = Box2I(Point2I(x=6, y=5), Point2I(x=7, y=5))
163 subset_container_2 = subset_container_1.subset_overlapping(grid, bbox_2)
164 self.assertEqual(subset_container_2.offset, Index2D(x=1, y=1))
165 self.assertEqual(subset_container_2.shape, Index2D(x=2, y=1))
166 union_bbox_2 = Box2I()
167 for v in subset_container_2.keys(): # noqa: SIM118
168 cell_bbox = grid.bbox_of(v)
169 self.assertTrue(cell_bbox.overlaps(bbox_1))
170 union_bbox_2.include(cell_bbox)
171 self.assertTrue(union_bbox_2.contains(bbox_2))
172 self._check(subset_container_2)
174 # Subsetting the container by a bbox that isn't contained by the cells
175 # raise a KeyError.
176 with self.assertRaises(KeyError):
177 subset_container_1.subset_overlapping(grid, full_bbox)
179 def test_rebuild_transformed(self) -> None:
180 """Test that rebuild_transformed method works by squaring
181 transformation.
182 """
183 container = GridContainer(shape=Index2D(x=3, y=2))
184 self._fill(container)
185 container = container.rebuild_transformed(
186 transform=lambda cell: {key: value**2 for key, value in cell.items()}
187 )
188 for index in container.indices():
189 self.assertTrue(container[index]["x"] == index.x**2)
190 self.assertTrue(container[index]["y"] == index.y**2)
192 @methodParameters(offset=(Index2D(x=1, y=2), None))
193 def test_pickle(self, offset) -> None:
194 """Test that we can serialize GridContainer with pickle."""
195 shape = Index2D(x=3, y=2)
196 gc = GridContainer(shape, offset)
197 self._fill(gc)
198 grid_container = gc
199 pickled_container = pickle.loads(pickle.dumps(grid_container, pickle.HIGHEST_PROTOCOL))
200 self.assertIsInstance(pickled_container, GridContainer)
201 # This line below is failing, so compare internals.
202 # self.assertEqual(pickled_container, grid_container)
203 self.assertEqual(pickled_container.shape, grid_container.shape)
204 self.assertEqual(pickled_container.offset, grid_container.offset)
205 self.assertEqual(pickled_container.first, grid_container.first)
206 self.assertEqual(pickled_container.last, grid_container.last)
207 self.assertListEqual(list(pickled_container.__iter__()), list(grid_container.__iter__()))
208 self._check(pickled_container)
211if __name__ == "__main__": 211 ↛ 212line 211 didn't jump to line 212, because the condition on line 211 was never true
212 unittest.main()