Coverage for tests/test_simpleGenericMap.py: 17%
230 statements
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-03 02:47 -0700
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-03 02:47 -0700
1# This file is part of afw.
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 collections.abc import MutableMapping
23import unittest
25import lsst.utils.tests
26import lsst.pex.exceptions as pexExcept
28from lsst.afw.typehandling import SimpleGenericMap, Storable
29from lsst.afw.typehandling.testUtils import MutableGenericMapTestBaseClass
30import testGenericMapLib as cppLib
33class SimpleGenericMapTestSuite(MutableGenericMapTestBaseClass):
35 @staticmethod
36 def makeMap(mapType, values):
37 """Initialize a map type using __setattr__ instead of a bulk constructor.
38 """
39 result = mapType()
40 for key, value in values.items():
41 result[key] = value
42 return result
44 @classmethod
45 def setUpClass(cls):
46 super().setUpClass()
47 cls.genericConstructor = SimpleGenericMap # Must be mapping-constructible
48 cls.targets = {SimpleGenericMap[str]} # Must be default-constructible
49 cls.examples = {
50 "SimpleGenericMap(testData(str))": (SimpleGenericMap[str], cls.getTestData(str)),
51 "SimpleGenericMap(testKeys(str) : 0)":
52 (SimpleGenericMap[str], {key: 0 for key in cls.getTestData(str).keys()}),
53 "SimpleGenericMap(dtype=str)": (SimpleGenericMap[str], {}),
54 }
56 def tearDown(self):
57 pass
59 def testClass(self):
60 for target in self.targets:
61 self.assertTrue(issubclass(target, MutableMapping))
62 self.assertIsInstance(target(), MutableMapping)
64 def testInitKeywords(self):
65 for target in self.targets:
66 for keyType in self.getValidKeys(target):
67 self.checkInitKwargs(target, self.getTestData(keyType), msg=str(target))
69 def testInitPairs(self):
70 for target in self.targets | {self.genericConstructor}:
71 for keyType in self.getValidKeys(target):
72 self.checkInitPairs(target, self.getTestData(keyType), msg=str(target))
74 def testInitMapping(self):
75 for target in self.targets | {self.genericConstructor}:
76 for keyType in self.getValidKeys(target):
77 # Init from dict
78 self.checkInitMapping(target, self.getTestData(keyType), msg=str(target))
79 # Init from GenericMap
80 self.checkInitMapping(target, target(self.getTestData(keyType)),
81 msg=str(target))
83 def testUnknownKeys(self):
84 with self.assertRaises(TypeError):
85 self.genericConstructor()
86 # Should not raise
87 self.genericConstructor(dtype=str)
89 def testMixedKeys(self):
90 badData = {"What do you get if you multiply six by nine?": "Ultimate Question",
91 42: "Ultimate Answer",
92 }
93 for target in self.targets | {self.genericConstructor}:
94 with self.assertRaises(TypeError):
95 target(badData)
96 with self.assertRaises(TypeError):
97 target(badData.items())
98 for target in self.targets:
99 with self.assertRaises(TypeError):
100 target(**badData)
102 def testFromKeys(self):
103 for target in self.targets:
104 for keyType in self.getValidKeys(target):
105 keys = self.getTestData(keyType).keys()
106 for value in self.getTestData(keyType).values():
107 self.checkFromKeys(target, keys, value,
108 msg=f" class={target}, value={value}")
109 self.checkFromKeysDefault(target, keys, msg=f" class={target}, no value")
111 def testCopy(self):
112 for label, (mappingType, contents) in self.examples.items():
113 for keyType in self.getValidKeys(mappingType):
114 mapping1 = self.makeMap(mappingType, contents)
115 mapping2 = mapping1.copy()
116 self.assertEqual(mapping1, mapping2, msg=f"{label}")
117 mapping1[keyType(42)] = "A random value!"
118 self.assertNotEqual(mapping1, mapping2, msg=f"{label}")
120 def testEquality(self):
121 for label1, (mappingType1, contents1) in self.examples.items():
122 mapping1 = self.makeMap(mappingType1, contents1)
123 for label2, (mappingType2, contents2) in self.examples.items():
124 mapping2 = self.makeMap(mappingType2, contents2)
125 if contents1 == contents2:
126 self.assertIsNot(mapping1, mapping2, msg=f"{label1} vs {label2}")
127 self.assertEqual(mapping1, mapping2, msg=f"{label1} vs {label2}")
128 self.assertEqual(mapping1, contents2, msg=f"{label1} vs dict({label2})")
129 self.assertEqual(contents1, mapping2, msg=f"dict({label1}) vs {label2}")
130 else:
131 self.assertNotEqual(mapping1, mapping2, msg=f"{label1} vs {label2}")
132 self.assertNotEqual(mapping1, contents2, msg=f"{label1} vs dict({label2})")
133 self.assertNotEqual(contents1, mapping2, msg=f"dict({label1}) vs {label2}")
135 def testBool(self):
136 for label, (mappingType, contents) in self.examples.items():
137 mapping = self.makeMap(mappingType, contents)
138 if contents:
139 self.assertTrue(mapping, msg=label)
140 else:
141 self.assertFalse(mapping, msg=label)
143 def testContains(self):
144 for label, (mappingType, contents) in self.examples.items():
145 mapping = self.makeMap(mappingType, contents)
146 self.checkContains(mapping, contents, msg=label)
148 def testContents(self):
149 for label, (mappingType, contents) in self.examples.items():
150 mapping = self.makeMap(mappingType, contents)
151 self.checkContents(mapping, contents, msg=label)
153 def testGet(self):
154 for label, (mappingType, contents) in self.examples.items():
155 mapping = self.makeMap(mappingType, contents)
156 self.checkGet(mapping, contents, msg=label)
158 def testIteration(self):
159 for label, (mappingType, contents) in self.examples.items():
160 mapping = self.makeMap(mappingType, contents)
161 self.checkIteration(mapping, contents, msg=label)
163 def testViews(self):
164 for label, (mappingType, contents) in self.examples.items():
165 self.checkMutableViews(mappingType, contents, msg=label)
167 def testInsertItem(self):
168 for target in self.targets:
169 for keyType in self.getValidKeys(target):
170 self.checkInsertItem(target, self.getTestData(keyType), msg=str(target))
172 def testSetdefault(self):
173 for target in self.targets:
174 for keyType in self.getValidKeys(target):
175 self.checkSetdefault(target, self.getTestData(keyType), msg=str(target))
177 def testUpdateMapping(self):
178 for target in self.targets:
179 for keyType in self.getValidKeys(target):
180 # Update from dict
181 self.checkUpdateMapping(target, self.getTestData(keyType), msg=str(target))
182 # Update from GenericMap
183 self.checkUpdateMapping(target, self.makeMap(target, self.getTestData(keyType)),
184 msg=str(target))
186 def testUpdatePairs(self):
187 for target in self.targets:
188 for keyType in self.getValidKeys(target):
189 self.checkUpdatePairs(target, self.getTestData(keyType), msg=str(target))
191 def testUpdateKwargs(self):
192 for target in self.targets:
193 for keyType in self.getValidKeys(target):
194 self.checkUpdateKwargs(target, self.getTestData(keyType), msg=str(target))
196 def testReplaceItem(self):
197 for target in self.targets:
198 self.checkReplaceItem(target(), msg=str(target))
200 def testRemoveItem(self):
201 for target in self.targets:
202 for keyType in self.getValidKeys(target):
203 self.checkRemoveItem(target, self.getTestData(keyType), msg=str(target))
205 def testPop(self):
206 for target in self.targets:
207 for keyType in self.getValidKeys(target):
208 self.checkPop(target, self.getTestData(keyType), msg=str(target))
210 def testPopitem(self):
211 for target in self.targets:
212 for keyType in self.getValidKeys(target):
213 self.checkPopitem(target, self.getTestData(keyType), msg=str(target))
215 def testClear(self):
216 for target in self.targets:
217 for keyType in self.getValidKeys(target):
218 self.checkClear(target, self.getTestData(keyType), msg=str(target))
221class PyStorable(Storable):
222 """A Storable with simple, mutable state.
224 Parameters
225 ----------
226 value
227 A value to be stored inside the object. Affects the object's string
228 representation. Two PyStorables are equal if and only if their
229 internal values are the same.
230 """
232 def __init__(self, value):
233 Storable.__init__(self) # pybind11 discourages using super()
234 self.value = value
236 def __repr__(self):
237 return repr(self.value)
239 def __eq__(self, other):
240 return self.value == other.value
243class SimpleGenericMapCppTestSuite(lsst.utils.tests.TestCase):
244 def setUp(self):
245 self.data = {'one': 1,
246 'pi': 3.1415927,
247 'string': 'neither a number nor NaN',
248 }
249 self.pymap = SimpleGenericMap(self.data)
250 self.cppmap = cppLib.makeInitialMap()
252 def testPythonValues(self):
253 """Check that built-in types added in Python are visible in C++.
254 """
255 for key, value in self.data.items():
256 cppLib.assertKeyValue(self.pymap, key, value)
257 # Ensure the test isn't giving false negatives
258 with self.assertRaises(pexExcept.NotFoundError):
259 cppLib.assertKeyValue(self.pymap, "NotAKey", 42)
261 def testCppValues(self):
262 """Check that built-in types added in C++ are visible in Python.
263 """
264 for key, value in self.data.items():
265 self.assertIn(key, self.cppmap)
266 self.assertEqual(value, self.cppmap[key], msg="key=" + key)
267 # Ensure the test isn't giving false negatives
268 self.assertNotIn("NotAKey", self.cppmap)
270 def _checkPythonUpdates(self, testmap, msg=''):
271 for key, value in self.data.items():
272 self.assertIn(key, testmap, msg=msg)
273 self.assertEqual(value, testmap[key], msg='key=' + key + ', ' + msg)
274 cppLib.assertKeyValue(testmap, key, value)
275 testmap['answer'] = 42 # New key-value pair
276 testmap['pi'] = 3.0 # Replace `float` with `float`
277 testmap['string'] = False # Replace `str` with `bool`
279 for key, value in {'answer': 42, 'pi': 3.0, 'string': False}.items():
280 # Test both Python and C++ state
281 self.assertIn(key, testmap, msg=msg)
282 self.assertEqual(value, testmap[key], msg='key=' + key + ', ' + msg)
283 cppLib.assertKeyValue(testmap, key, value)
285 def testPythonUpdates(self):
286 """Check that changes to built-in types made in Python are visible in
287 both languages.
288 """
289 self._checkPythonUpdates(self.pymap, msg='map=pymap')
290 self._checkPythonUpdates(self.cppmap, msg='map=cppmap')
292 def _checkCppUpdates(self, testmap, msg=''):
293 for key, value in self.data.items():
294 self.assertIn(key, testmap, msg=msg)
295 self.assertEqual(value, testmap[key], msg='key=' + key + ', ' + msg)
296 cppLib.assertKeyValue(testmap, key, value)
297 cppLib.makeCppUpdates(testmap)
299 for key, value in {'answer': 42, 'pi': 3.0, 'string': False}.items():
300 # Test both Python and C++ state
301 self.assertIn(key, testmap, msg=msg)
302 self.assertEqual(value, testmap[key], msg='key=' + key + ', ' + msg)
303 cppLib.assertKeyValue(testmap, key, value)
305 def testCppUpdates(self):
306 """Check that changes to built-in types made in C++ are visible in
307 both languages.
308 """
309 self._checkCppUpdates(self.pymap, msg='map=pymap')
310 self._checkCppUpdates(self.cppmap, msg='map=cppmap')
312 def _checkPythonStorableUpdates(self, testmap, msg=''):
313 cppLib.addCppStorable(testmap)
314 self.assertIn('cppValue', testmap, msg=msg)
315 self.assertEqual(testmap['cppValue'], cppLib.CppStorable('value'), msg=msg)
316 self.assertIn('cppPointer', testmap, msg=msg)
317 self.assertEqual(testmap['cppPointer'], cppLib.CppStorable('pointer'), msg=msg)
319 # should have no effect because pybind11 copies Storable values for safety
320 testmap['cppValue'].value = 'new_value'
321 testmap['cppPointer'].value = 'extra_pointy'
323 for key, value in {'cppValue': cppLib.CppStorable('value'),
324 'cppPointer': cppLib.CppStorable('extra_pointy'),
325 }.items():
326 # Test both Python and C++ state
327 self.assertIn(key, testmap, msg=msg)
328 self.assertEqual(value, testmap[key], msg='key=' + key + ', ' + msg)
329 cppLib.assertKeyValue(testmap, key, value)
331 def testPythonStorableUpdates(self):
332 """Check that changes to Storables made in Python are visible in
333 both languages.
334 """
335 self._checkPythonStorableUpdates(self.pymap, msg='map=pymap')
336 self._checkPythonStorableUpdates(self.cppmap, msg='map=cppmap')
338 def _checkCppStorableRead(self, testmap, msg=''):
339 # WARNING: the Python variables holding PyStorable must survive to the end of the test
340 # This is a known bug in pybind11; see DM-21314
341 storableData = {'answer': PyStorable(42),
342 'question': PyStorable('Unknown'),
343 }
344 testmap.update(storableData)
346 for key, value in storableData.items():
347 self.assertIn(key, testmap, msg=msg)
348 self.assertEqual(value, testmap[key], msg='key=' + key + ', ' + msg)
349 # Exercise C++ equality operator
350 cppLib.assertKeyValue(testmap, key, PyStorable(value.value))
351 # Exercise C++ string representation
352 cppLib.assertPythonStorable(testmap, key, repr(value))
354 def testCppStorableRead(self):
355 """Check that Storables made in Python are visible in both languages.
356 """
357 self._checkCppStorableRead(self.pymap, msg='map=pymap')
358 self._checkCppStorableRead(self.cppmap, msg='map=cppmap')
361class MemoryTester(lsst.utils.tests.MemoryTestCase):
362 pass
365def setup_module(module):
366 lsst.utils.tests.init()
369if __name__ == "__main__": 369 ↛ 370line 369 didn't jump to line 370, because the condition on line 369 was never true
370 lsst.utils.tests.init()
371 unittest.main()