Coverage for tests/test_simpleGenericMap.py: 19%

230 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-02 02:18 -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/>. 

21 

22from collections.abc import MutableMapping 

23import unittest 

24 

25import lsst.utils.tests 

26import lsst.pex.exceptions as pexExcept 

27 

28from lsst.afw.typehandling import SimpleGenericMap, Storable 

29from lsst.afw.typehandling.testUtils import MutableGenericMapTestBaseClass 

30import testGenericMapLib as cppLib 

31 

32 

33class SimpleGenericMapTestSuite(MutableGenericMapTestBaseClass): 

34 

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 

43 

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 } 

55 

56 def tearDown(self): 

57 pass 

58 

59 def testClass(self): 

60 for target in self.targets: 

61 self.assertTrue(issubclass(target, MutableMapping)) 

62 self.assertIsInstance(target(), MutableMapping) 

63 

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

68 

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

73 

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

82 

83 def testUnknownKeys(self): 

84 with self.assertRaises(TypeError): 

85 self.genericConstructor() 

86 # Should not raise 

87 self.genericConstructor(dtype=str) 

88 

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) 

101 

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

110 

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}") 

119 

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}") 

134 

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) 

142 

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) 

147 

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) 

152 

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) 

157 

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) 

162 

163 def testViews(self): 

164 for label, (mappingType, contents) in self.examples.items(): 

165 self.checkMutableViews(mappingType, contents, msg=label) 

166 

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

171 

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

176 

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

185 

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

190 

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

195 

196 def testReplaceItem(self): 

197 for target in self.targets: 

198 self.checkReplaceItem(target(), msg=str(target)) 

199 

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

204 

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

209 

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

214 

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

219 

220 

221class PyStorable(Storable): 

222 """A Storable with simple, mutable state. 

223 

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 """ 

231 

232 def __init__(self, value): 

233 Storable.__init__(self) # pybind11 discourages using super() 

234 self.value = value 

235 

236 def __repr__(self): 

237 return repr(self.value) 

238 

239 def __eq__(self, other): 

240 return self.value == other.value 

241 

242 

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

251 

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) 

260 

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) 

269 

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` 

278 

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) 

284 

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

291 

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) 

298 

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) 

304 

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

311 

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) 

318 

319 # should have no effect because pybind11 copies Storable values for safety 

320 testmap['cppValue'].value = 'new_value' 

321 testmap['cppPointer'].value = 'extra_pointy' 

322 

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) 

330 

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

337 

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) 

345 

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

353 

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

359 

360 

361class MemoryTester(lsst.utils.tests.MemoryTestCase): 

362 pass 

363 

364 

365def setup_module(module): 

366 lsst.utils.tests.init() 

367 

368 

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