Coverage for tests/test_grid_container.py: 14%

127 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-03-16 02:22 -0700

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/>. 

21 

22from __future__ import annotations 

23 

24import copy 

25import pickle 

26import unittest 

27 

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 

32 

33 

34class GridContainerTestCase(unittest.TestCase): 

35 """Tests for GridContainer and GridIndex/Index2D's C++/Python 

36 translation. 

37 """ 

38 

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} 

46 

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) 

53 

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 ) 

94 

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) 

110 

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) 

127 

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) 

137 

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())) 

146 

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) 

159 

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) 

173 

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) 

178 

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) 

191 

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) 

209 

210 

211if __name__ == "__main__": 211 ↛ 212line 211 didn't jump to line 212, because the condition on line 211 was never true

212 unittest.main()