Coverage for python/lsst/afw/typehandling/testUtils.py: 16%

279 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-07-19 04:54 -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 

22__all__ = [] 

23 

24from collections import Counter 

25 

26import numpy as np 

27 

28import lsst.utils.tests 

29 

30from lsst.afw.typehandling import Storable 

31 

32 

33class GenericMapTestBaseClass(lsst.utils.tests.TestCase): 

34 """Base class for unit tests of GenericMap. 

35 

36 Subclasses must call `GenericMapTestBaseClass.setUp(self)` 

37 if they provide their own version. 

38 

39 This class is not *quite* a generic Mapping testbed, because it assumes 

40 that the map being tested only accepts keys of a particular type, stored as 

41 the `dtype` member. 

42 """ 

43 

44 class SimpleStorable(Storable): 

45 """A subclass of Storable for testing purposes. 

46 """ 

47 def __repr__(self): 

48 return "Simplest possible representation" 

49 

50 def __hash__(self): 

51 return 100 

52 

53 def __eq__(self, other): 

54 """Warning: violates both substitution and equality symmetry! 

55 """ 

56 return self.__class__ == other.__class__ 

57 

58 class ComplexStorable(SimpleStorable): 

59 """A subclass of Storable for testing purposes. 

60 """ 

61 def __init__(self, storage): 

62 super().__init__() 

63 self._storage = storage 

64 

65 def __repr__(self): 

66 return f"ComplexStorable({self._storage!r})" 

67 

68 def __hash__(self): 

69 return hash(self._storage) 

70 

71 def __eq__(self, other): 

72 """Warning: violates both substitution and equality symmetry! 

73 """ 

74 if self.__class__ == other.__class__: 

75 return self._storage == other._storage 

76 else: 

77 return False 

78 

79 class NotAStorable: 

80 """A class that should not be a legal value in a GenericMap. 

81 """ 

82 def __str__(self): 

83 return "Non-Storable" 

84 

85 _testData = { 

86 0: True, 

87 1: 42, 

88 2: 42.0, 

89 3: "How many roads must a man walk down?", 

90 4: SimpleStorable(), 

91 5: ComplexStorable(-100.0), 

92 6: None, 

93 } 

94 

95 @classmethod 

96 def getTestData(cls, keyClass): 

97 """Generic dataset for testing GenericMap classes that can handle it. 

98 

99 Parameters 

100 ---------- 

101 keyClass : `type` 

102 The type of key expected by the GenericMap. 

103 """ 

104 return {keyClass(key): value for key, value in cls._testData.items()} 

105 

106 @staticmethod 

107 def getValidKeys(mapClass): 

108 """Return all keys suitable for a GenericMap. 

109 

110 Parameters 

111 ---------- 

112 mapClass : `type` 

113 A type object for a subclass of either `GenericMap` or a 

114 key-specific specialization. 

115 

116 Returns 

117 ------- 

118 keyTypes: `set` [`type`] 

119 The types that can be used as keys. If ``mapClass`` is a 

120 key-specific specialization, this set will contain exactly 

121 one type. 

122 """ 

123 try: 

124 return {mapClass.dtype} 

125 except AttributeError: 

126 return {cls.dtype for cls in mapClass.__subclasses__()} 

127 

128 def setUp(self): 

129 """Set up a test 

130 

131 Subclasses must call this method if they override setUp. 

132 """ 

133 super().setUp() 

134 # tell unittest to use the msg argument of asserts as a supplement 

135 # to the error message, rather than as the whole error message 

136 self.longMessage = True 

137 

138 # Mapping must have: 

139 # __str__ 

140 # __repr__ 

141 # __eq__ 

142 # __ne__ 

143 # __contains__ 

144 # __getitem__ 

145 # get 

146 # __iter__ 

147 # __len__ 

148 # __bool__ 

149 # keys 

150 # items 

151 # values 

152 

153 def checkInitMapping(self, mapClass, contents, msg=""): 

154 """Check initialization from a mapping. 

155 

156 Parameters 

157 ---------- 

158 mapClass : `lsst.afw.typehandling.GenericMap`-type 

159 The class whose ``__init__`` method will be tested. 

160 contents : `Mapping` 

161 The key-value pairs to insert into the map 

162 msg : `str` 

163 Error message suffix describing test parameters 

164 """ 

165 for keyType in self.getValidKeys(mapClass): 

166 genericMap = mapClass(contents) 

167 self.checkContents(genericMap, contents, msg=msg) 

168 

169 extraContents = {key: value for key, value in contents.items()} # contents need not define copy() 

170 extraKey = keyType(101) 

171 extraValue = 'Extra value' 

172 extraContents[extraKey] = extraValue 

173 genericMap = mapClass(contents, **{keyType(101): extraValue}) 

174 self.checkContents(genericMap, extraContents, msg=msg) 

175 

176 with self.assertRaises(TypeError, msg=msg): 

177 mapClass({keyType(0): GenericMapTestBaseClass.NotAStorable()}) 

178 

179 def checkInitPairs(self, mapClass, contents, msg=""): 

180 """Check initialization from an iterable of pairs. 

181 

182 Parameters 

183 ---------- 

184 mapClass: `lsst.afw.typehandling.GenericMap`-type 

185 The class whose ``__init__`` method will be tested. 

186 contents : `Mapping` 

187 The key-value pairs to insert into the map 

188 msg : `str` 

189 Error message suffix describing test parameters 

190 """ 

191 for keyType in self.getValidKeys(mapClass): 

192 genericMap = mapClass(contents.items()) 

193 self.checkContents(genericMap, contents, msg=msg) 

194 

195 extraContents = {key: value for key, value in contents.items()} # contents need not define copy() 

196 extraKey = keyType(101) 

197 extraValue = 'Extra value' 

198 extraContents[extraKey] = extraValue 

199 genericMap = mapClass(contents.items(), **{keyType(101): extraValue}) 

200 self.checkContents(genericMap, extraContents, msg=msg) 

201 

202 with self.assertRaises(TypeError, msg=msg): 

203 mapClass([(keyType(0), GenericMapTestBaseClass.NotAStorable())]) 

204 

205 def checkInitKwargs(self, mapClass, contents, msg=""): 

206 """Check bulk insertion from keywords into a GenericMap. 

207 

208 Parameters 

209 ---------- 

210 mapClass: `lsst.afw.typehandling.GenericMapS`-type 

211 The class whose ``__init__`` method will be tested. 

212 Must allow string keys. 

213 contents : `Mapping` 

214 The key-value pairs to insert into the map 

215 msg : `str` 

216 Error message suffix describing test parameters 

217 """ 

218 genericMap = mapClass(**contents) 

219 self.checkContents(genericMap, contents, msg=msg) 

220 

221 with self.assertRaises(TypeError, msg=msg): 

222 mapClass(notAKey=GenericMapTestBaseClass.NotAStorable()) 

223 

224 def checkFromKeysDefault(self, mapClass, keys, msg=""): 

225 """Check initialization using the ``fromkeys`` factory. 

226 

227 Unlike `checkFromKeys`, this method lets ``fromkeys`` use its default 

228 value (which may give different behavior, in nonconforming 

229 implementations, from explicitly passing `None`). 

230 

231 Parameters 

232 ---------- 

233 mapClass: `lsst.afw.typehandling.GenericMap`-type 

234 The class whose ``fromkeys`` method will be tested. 

235 keys : `iterable` 

236 The keys to insert into the map. 

237 msg : `str` 

238 Error message suffix describing test parameters 

239 """ 

240 genericMap = mapClass.fromkeys(keys) 

241 self.assertIsInstance(genericMap, mapClass, msg=msg) 

242 self.checkContents(genericMap, dict.fromkeys(keys), msg=msg) 

243 

244 def checkFromKeys(self, mapClass, keys, value, msg=""): 

245 """Check initialization using the ``fromkeys`` factory. 

246 

247 Parameters 

248 ---------- 

249 mapClass: `lsst.afw.typehandling.GenericMap`-type 

250 The class whose ``fromkeys`` method will be tested. 

251 keys : `iterable` 

252 The keys to insert into the map. 

253 value 

254 A legal value for a GenericMap. 

255 msg : `str` 

256 Error message suffix describing test parameters 

257 """ 

258 genericMap = mapClass.fromkeys(keys, value) 

259 self.assertIsInstance(genericMap, mapClass, msg=msg) 

260 self.checkContents(genericMap, dict.fromkeys(keys, value), msg=msg) 

261 

262 def checkContains(self, genericMap, contents, msg=""): 

263 """Check the contents of a GenericMap. 

264 

265 Parameters 

266 ---------- 

267 genericMap : `lsst.afw.typehandling.GenericMap` 

268 The map to test. 

269 contents : `Mapping` 

270 The key-value pairs that should be present in ``genericMap`` 

271 msg : `str` 

272 Error message suffix describing test parameters 

273 """ 

274 for key in contents: 

275 self.assertIn(key, genericMap, msg=msg) 

276 

277 keyType = genericMap.dtype 

278 for key in range(30): 

279 if keyType(key) not in contents: 

280 self.assertNotIn(keyType(key), genericMap, msg=msg) 

281 

282 wrongType = float if keyType is not float else int 

283 with self.assertRaises(TypeError): 

284 wrongType(0) in genericMap 

285 

286 def checkContents(self, genericMap, contents, msg=""): 

287 """Check the contents of a GenericMap. 

288 

289 Parameters 

290 ---------- 

291 genericMap : `lsst.afw.typehandling.GenericMap` 

292 The map to test. 

293 contents : `Mapping` 

294 The key-value pairs that should be present in ``genericMap`` 

295 msg : `str` 

296 Error message suffix describing test parameters 

297 """ 

298 for key, value in contents.items(): 

299 self.assertEqual(genericMap[key], value, msg=msg) 

300 

301 keyType = genericMap.dtype 

302 for key in (keyType(key) for key in range(30) if keyType(key) not in contents): 

303 with self.assertRaises(KeyError, msg=msg): 

304 genericMap[key] 

305 

306 wrongType = float if keyType is not float else int 

307 with self.assertRaises(TypeError): 

308 genericMap[wrongType(0)] 

309 

310 def checkGet(self, genericMap, contents, msg=""): 

311 """Check that GenericMap.get works correctly. 

312 

313 Parameters 

314 ---------- 

315 genericMap : `lsst.afw.typehandling.GenericMap` 

316 The map to test. 

317 contents : `Mapping` 

318 The key-value pairs that should be present in ``genericMap`` 

319 msg : `str` 

320 Error message suffix describing test parameters 

321 """ 

322 default = "Not a default value" 

323 for key, value in contents.items(): 

324 self.assertEqual(genericMap.get(key), value, msg=msg) 

325 self.assertEqual(genericMap.get(key, default), value, msg=msg) 

326 

327 keyType = genericMap.dtype 

328 for key in (keyType(key) for key in range(30) if keyType(key) not in contents): 

329 self.assertEqual(genericMap.get(key), None, msg=msg) 

330 self.assertEqual(genericMap.get(key, default), default, msg=msg) 

331 

332 wrongType = float if keyType is not float else int 

333 with self.assertRaises(TypeError): 

334 genericMap.get(wrongType(0)) 

335 

336 def checkIteration(self, genericMap, contents, msg=""): 

337 """Check the result of iterating over a GenericMap. 

338 

339 Parameters 

340 ---------- 

341 genericMap : `lsst.afw.typehandling.GenericMap` 

342 The map to test. 

343 contents : `Mapping` 

344 The key-value pairs that should be present in ``genericMap`` 

345 msg : `str` 

346 Error message suffix describing test parameters 

347 """ 

348 self.assertEqual({key: genericMap[key] for key in genericMap}, dict(contents), msg=msg) 

349 

350 def checkViews(self, genericMap, contents, msg=""): 

351 """Check the views provided by a GenericMap. 

352 

353 Parameters 

354 ---------- 

355 genericMap : `lsst.afw.typehandling.GenericMap` 

356 The map to test. 

357 contents : `Mapping` 

358 The key-value pairs that should be present in ``genericMap`` 

359 msg : `str` 

360 Error message suffix describing test parameters 

361 """ 

362 self.assertEqual(set(genericMap.keys()), set(contents.keys()), msg=msg) 

363 self.assertEqual(Counter(genericMap.values()), Counter(contents.values()), msg=msg) 

364 self.assertEqual(Counter(genericMap.items()), Counter(contents.items()), msg=msg) 

365 

366 

367class MutableGenericMapTestBaseClass(GenericMapTestBaseClass): 

368 """Base class for unit tests of GenericMap that allow insertion/deletion. 

369 

370 Subclasses must call `MutableGenericMapTestBaseClass.setUp(self)` 

371 if they provide their own version. 

372 """ 

373 

374 @classmethod 

375 def _fillMap(cls, mapFactory, contents): 

376 """Create a new GenericMap with particular contents. 

377 

378 Parameters 

379 ---------- 

380 mapFactory : callable 

381 A zero-argument callable that creates an empty 

382 `lsst.afw.typehandling.GenericMap` object 

383 contents : `Mapping` 

384 The key-value pairs that should be present in the new map. 

385 

386 Returns 

387 ------- 

388 map : `lsst.afw.typehandling.GenericMap` 

389 a GenericMap equivalent to ``contents`` 

390 """ 

391 return cls._fillPartialMap(mapFactory, contents, len(contents)) 

392 

393 @classmethod 

394 def _fillPartialMap(cls, mapFactory, contents, numElements): 

395 """Create a new GenericMap with particular contents. 

396 

397 Parameters 

398 ---------- 

399 mapFactory : callable 

400 A zero-argument callable that creates an empty 

401 `lsst.afw.typehandling.GenericMap` object 

402 contents : `Mapping` 

403 The key-value pairs that should be present in the new map. 

404 numElements : `int` 

405 The number of elements from ``contents`` to be inserted. 

406 

407 Returns 

408 ------- 

409 map : `lsst.afw.typehandling.GenericMap` 

410 a GenericMap containing ``numElements`` of ``contents`` or all of 

411 ``contents``, whichever is smaller 

412 """ 

413 newMap = mapFactory() 

414 for i, (key, value) in enumerate(contents.items()): 

415 if i < numElements: 

416 newMap[key] = value 

417 else: 

418 break 

419 return newMap 

420 

421 # MutableMapping must have: 

422 # __setitem__ 

423 # setdefault 

424 # __delitem__ 

425 # pop 

426 # popitem 

427 # clear 

428 # update 

429 

430 def checkInsertItem(self, mapFactory, contents, msg=""): 

431 """Check element insertion in a GenericMap. 

432 

433 Parameters 

434 ---------- 

435 mapFactory : callable 

436 A zero-argument callable that creates an empty 

437 `lsst.afw.typehandling.GenericMap` object of the type to be tested 

438 contents : `Mapping` 

439 The key-value pairs to insert into the map 

440 msg : `str` 

441 Error message suffix describing test parameters 

442 """ 

443 genericMap = mapFactory() 

444 

445 for length, (key, value) in enumerate(contents.items()): 

446 loopMsg = f"{msg} Inserting {key!r}={value!r}" 

447 genericMap[key] = value 

448 self.assertEqual(len(genericMap), length+1, msg=loopMsg) 

449 self.assertEqual(genericMap[key], value, msg=loopMsg) 

450 

451 self.assertEqual(dict(genericMap), dict(contents), msg=msg) 

452 

453 keyType = genericMap.dtype 

454 with self.assertRaises(TypeError, msg=msg): 

455 genericMap[keyType(0)] = GenericMapTestBaseClass.NotAStorable() 

456 

457 wrongType = float if keyType is not float else int 

458 with self.assertRaises(TypeError): 

459 genericMap[wrongType(0)] = 0 

460 

461 def checkSetdefault(self, mapFactory, contents, msg=""): 

462 """Check that GenericMap.setdefault works correctly. 

463 

464 Parameters 

465 ---------- 

466 mapFactory : callable 

467 A zero-argument callable that creates an empty 

468 `lsst.afw.typehandling.GenericMap` object of the type to be tested 

469 contents : `Mapping` 

470 The key-value pairs to insert into the map 

471 msg : `str` 

472 Error message suffix describing test parameters 

473 """ 

474 genericMap = mapFactory() 

475 

476 keyType = genericMap.dtype 

477 result = genericMap.setdefault(keyType(0)) 

478 self.assertEqual(len(genericMap), 1, msg=msg) 

479 self.assertIsNone(result, msg=msg) 

480 self.assertIsNone(genericMap[keyType(0)], msg=msg) 

481 del genericMap[keyType(0)] 

482 

483 default = "This is a default" 

484 for length, (key, _) in enumerate(contents.items()): 

485 loopMsg = f"{msg} Defaulting {key!r}" 

486 result = genericMap.setdefault(key, default) 

487 self.assertEqual(len(genericMap), length+1, msg=loopMsg) 

488 self.assertEqual(result, default, msg=loopMsg) 

489 self.assertEqual(genericMap[key], default, msg=loopMsg) 

490 

491 self.assertEqual(genericMap.keys(), contents.keys(), msg=msg) 

492 

493 with self.assertRaises(TypeError, msg=msg): 

494 genericMap.setdefault(keyType(100), GenericMapTestBaseClass.NotAStorable()) 

495 

496 wrongType = float if keyType is not float else int 

497 with self.assertRaises(TypeError): 

498 genericMap.setdefault(wrongType(0), default) 

499 

500 genericMap = self._fillMap(mapFactory, contents) 

501 for length, (key, value) in enumerate(contents.items()): 

502 loopMsg = f"{msg} Defaulting existing {key!r}={value!r}" 

503 result = genericMap.setdefault(key, default) 

504 self.assertEqual(len(genericMap), len(contents), msg=loopMsg) 

505 self.assertEqual(result, contents[key], msg=loopMsg) 

506 self.assertEqual(genericMap[key], contents[key], msg=loopMsg) 

507 

508 def checkUpdateMapping(self, mapFactory, contents, msg=""): 

509 """Check bulk insertion from a mapping into a GenericMap. 

510 

511 Parameters 

512 ---------- 

513 mapFactory : callable 

514 A zero-argument callable that creates an empty 

515 `lsst.afw.typehandling.GenericMap` object of the type to be tested 

516 contents : `Mapping` 

517 The key-value pairs to insert into the map 

518 msg : `str` 

519 Error message suffix describing test parameters 

520 """ 

521 genericMap = self._fillPartialMap(mapFactory, dict.fromkeys(contents, 0), len(contents)/2) 

522 self.assertLess(len(genericMap), len(contents), msg=msg) 

523 

524 genericMap.update(contents) 

525 self.assertEqual(dict(genericMap), dict(contents), msg=msg) 

526 

527 keyType = genericMap.dtype 

528 with self.assertRaises(TypeError, msg=msg): 

529 genericMap.update({keyType(0): GenericMapTestBaseClass.NotAStorable()}) 

530 

531 wrongType = float if keyType is not float else int 

532 with self.assertRaises(TypeError, msg=msg): 

533 genericMap.update({wrongType(0): 0}) 

534 

535 def checkUpdatePairs(self, mapFactory, contents, msg=""): 

536 """Check bulk insertion from an iterable of pairs into a GenericMap. 

537 

538 Parameters 

539 ---------- 

540 mapFactory : callable 

541 A zero-argument callable that creates an empty 

542 `lsst.afw.typehandling.GenericMap` object of the type to be tested 

543 contents : `Mapping` 

544 The key-value pairs to insert into the map 

545 msg : `str` 

546 Error message suffix describing test parameters 

547 """ 

548 genericMap = self._fillPartialMap(mapFactory, dict.fromkeys(contents, 0), len(contents)/2) 

549 self.assertLess(len(genericMap), len(contents), msg=msg) 

550 

551 genericMap.update(contents.items()) 

552 self.assertEqual(dict(genericMap), dict(contents), msg=msg) 

553 

554 keyType = genericMap.dtype 

555 with self.assertRaises(TypeError, msg=msg): 

556 genericMap.update([(keyType(0), GenericMapTestBaseClass.NotAStorable())]) 

557 

558 wrongType = float if keyType is not float else int 

559 with self.assertRaises(TypeError, msg=msg): 

560 genericMap.update([(wrongType(0), 0)]) 

561 

562 def checkUpdateKwargs(self, mapFactory, contents, msg=""): 

563 """Check bulk insertion from keywords into a GenericMap. 

564 

565 Parameters 

566 ---------- 

567 mapFactory : callable 

568 A zero-argument callable that creates an empty 

569 `lsst.afw.typehandling.GenericMap` object of the type to be tested 

570 Must allow string keys. 

571 contents : `Mapping` 

572 The key-value pairs to insert into the map 

573 msg : `str` 

574 Error message suffix describing test parameters 

575 """ 

576 genericMap = self._fillPartialMap(mapFactory, dict.fromkeys(contents, 0), len(contents)/2) 

577 self.assertLess(len(genericMap), len(contents), msg=msg) 

578 

579 genericMap.update(**contents) 

580 self.assertEqual(dict(genericMap), dict(contents), msg=msg) 

581 

582 with self.assertRaises(TypeError, msg=msg): 

583 genericMap.update(notAKey=GenericMapTestBaseClass.NotAStorable()) 

584 

585 def checkReplaceItem(self, genericMap, msg=""): 

586 """Check element replacement in a GenericMap. 

587 

588 Parameters 

589 ---------- 

590 genericMap : `lsst.afw.typehandling.GenericMap` 

591 The map to test. Must be empty. 

592 msg : `str` 

593 Error message suffix describing test parameters 

594 """ 

595 self.assertFalse(genericMap, msg=msg) 

596 keyType = genericMap.dtype 

597 key = keyType(42) 

598 

599 for value in self.getTestData(keyType).values(): 

600 loopMsg = f"{msg} Inserting {key!r}={value!r}" 

601 genericMap[key] = value # value may be of a different type 

602 self.assertEqual(len(genericMap), 1, msg=loopMsg) 

603 self.assertEqual(genericMap[key], value, msg=loopMsg) 

604 

605 self.assertEqual(dict(genericMap), {key: value}, msg=msg) 

606 

607 with self.assertRaises(TypeError, msg=msg): 

608 genericMap[key] = GenericMapTestBaseClass.NotAStorable() 

609 

610 def checkRemoveItem(self, mapFactory, contents, msg=""): 

611 """Check element removal from a GenericMap. 

612 

613 Parameters 

614 ---------- 

615 mapFactory : callable 

616 A zero-argument callable that creates an empty 

617 `lsst.afw.typehandling.GenericMap` object of the type to be tested 

618 contents : `Mapping` 

619 The key-value pairs initially occupying the map 

620 msg : `str` 

621 Error message suffix describing test parameters 

622 """ 

623 genericMap = self._fillMap(mapFactory, contents) 

624 

625 keyType = genericMap.dtype 

626 with self.assertRaises(KeyError, msg=msg): 

627 del genericMap[keyType(2019)] 

628 

629 keysToRemove = list(contents.keys()) 

630 np.random.shuffle(keysToRemove) 

631 for numPrevious, rawKey in enumerate(keysToRemove): 

632 key = keyType(rawKey) 

633 loopMsg = f"{msg} Deleting {key!r}" 

634 del genericMap[key] 

635 self.assertEqual(len(genericMap), len(contents)-numPrevious-1, msg=loopMsg) 

636 self.assertNotIn(key, genericMap, msg=loopMsg) 

637 

638 wrongType = float if keyType is not float else int 

639 with self.assertRaises(TypeError): 

640 del genericMap[wrongType(0)] 

641 

642 def checkPop(self, mapFactory, contents, msg=""): 

643 """Check that GenericMap.pop works correctly. 

644 

645 Parameters 

646 ---------- 

647 mapFactory : callable 

648 A zero-argument callable that creates an empty 

649 `lsst.afw.typehandling.GenericMap` object of the type to be tested 

650 contents : `Mapping` 

651 The key-value pairs initially occupying the map 

652 msg : `str` 

653 Error message suffix describing test parameters 

654 """ 

655 genericMap = self._fillMap(mapFactory, contents) 

656 

657 keyType = genericMap.dtype 

658 with self.assertRaises(KeyError, msg=msg): 

659 genericMap.pop(keyType(2019)) 

660 

661 default = "This is a default" 

662 result = genericMap.pop(keyType(2019), default) 

663 self.assertEqual(dict(genericMap), dict(contents), msg=msg) 

664 self.assertEqual(result, default) 

665 

666 wrongType = float if keyType is not float else int 

667 with self.assertRaises(TypeError): 

668 genericMap.pop(wrongType(0)) 

669 

670 itemsToRemove = list(contents.items()) 

671 np.random.shuffle(itemsToRemove) 

672 for numPrevious, (rawKey, value) in enumerate(itemsToRemove): 

673 key = keyType(rawKey) 

674 loopMsg = f"{msg} Popping {key}={value}" 

675 result = genericMap.pop(key) 

676 self.assertEqual(len(genericMap), len(contents)-numPrevious-1, msg=loopMsg) 

677 self.assertNotIn(key, genericMap, msg=loopMsg) 

678 self.assertEqual(result, value, msg=loopMsg) 

679 

680 def checkPopitem(self, mapFactory, contents, msg=""): 

681 """Check that GenericMap.popitem works correctly. 

682 

683 Parameters 

684 ---------- 

685 mapFactory : callable 

686 A zero-argument callable that creates an empty 

687 `lsst.afw.typehandling.GenericMap` object of the type to be tested 

688 contents : `Mapping` 

689 The key-value pairs initially occupying the map 

690 msg : `str` 

691 Error message suffix describing test parameters 

692 """ 

693 genericMap = self._fillMap(mapFactory, contents) 

694 

695 for numPrevious in range(len(genericMap)): 

696 key, value = genericMap.popitem() 

697 loopMsg = f"{msg} Popping {key}={value}" 

698 self.assertIn((key, value), contents.items(), msg=loopMsg) 

699 self.assertEqual(len(genericMap), len(contents)-numPrevious-1, msg=loopMsg) 

700 self.assertNotIn(key, genericMap, msg=loopMsg) 

701 

702 with self.assertRaises(KeyError, msg=msg): 

703 genericMap.popitem() 

704 

705 def checkClear(self, mapFactory, contents, msg=""): 

706 """Check erasing a GenericMap. 

707 

708 Parameters 

709 ---------- 

710 mapFactory : callable 

711 A zero-argument callable that creates an empty 

712 `lsst.afw.typehandling.GenericMap` object of the type to be tested 

713 contents : `Mapping` 

714 The key-value pairs initially occupying the map 

715 msg : `str` 

716 Error message suffix describing test parameters 

717 """ 

718 genericMap = self._fillMap(mapFactory, contents) 

719 self.assertTrue(genericMap, msg=msg) 

720 

721 genericMap.clear() 

722 self.assertFalse(genericMap, msg=msg) 

723 self.assertEqual(len(genericMap), 0, msg=msg) 

724 for key in genericMap: 

725 self.fail(f"Unexpected key: {key}", msg=msg) 

726 

727 def checkMutableViews(self, mapFactory, contents, msg=""): 

728 """Check that the views of a GenericMap update automatically. 

729 

730 This test supersedes `GenericMapTestBaseClass.checkViews`. 

731 

732 Parameters 

733 ---------- 

734 genericMap : `lsst.afw.typehandling.GenericMap` 

735 The map to test. 

736 contents : `Mapping` 

737 The key-value pairs that should be present in ``genericMap`` 

738 msg : `str` 

739 Error message suffix describing test parameters 

740 """ 

741 genericMap = mapFactory() 

742 keyView = genericMap.keys() 

743 valueView = genericMap.values() 

744 itemView = genericMap.items() 

745 

746 for view in (keyView, valueView, itemView): 

747 self.assertEqual(len(view), len(genericMap), msg=msg) 

748 # is initial state correct? 

749 self.checkViews(genericMap, {}, msg) 

750 

751 for key, value in contents.items(): 

752 genericMap[key] = value 

753 

754 for view in (keyView, valueView, itemView): 

755 self.assertEqual(len(view), len(genericMap), msg=msg) 

756 # is final state correct? 

757 self.checkViews(genericMap, contents, msg)