Coverage for tests/test_testUtils.py: 22%

311 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-23 08:14 +0000

1# This file is part of pipe_base. 

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"""Unit tests for `lsst.pipe.base.tests`, a library for testing 

23PipelineTask subclasses. 

24""" 

25 

26import shutil 

27import tempfile 

28import unittest 

29 

30import lsst.daf.butler 

31import lsst.daf.butler.tests as butlerTests 

32import lsst.pex.config 

33import lsst.utils.tests 

34from lsst.pipe.base import PipelineTask, PipelineTaskConfig, PipelineTaskConnections, Struct, connectionTypes 

35from lsst.pipe.base.testUtils import ( 

36 assertValidInitOutput, 

37 assertValidOutput, 

38 getInitInputs, 

39 lintConnections, 

40 makeQuantum, 

41 runTestQuantum, 

42) 

43 

44 

45class VisitConnections(PipelineTaskConnections, dimensions={"instrument", "visit"}): 

46 """Test connections class involving a visit.""" 

47 

48 initIn = connectionTypes.InitInput( 

49 name="VisitInitIn", 

50 storageClass="StructuredData", 

51 multiple=False, 

52 ) 

53 a = connectionTypes.Input( 

54 name="VisitA", 

55 storageClass="StructuredData", 

56 multiple=False, 

57 dimensions={"instrument", "visit"}, 

58 ) 

59 b = connectionTypes.Input( 

60 name="VisitB", 

61 storageClass="StructuredData", 

62 multiple=False, 

63 dimensions={"instrument", "visit"}, 

64 ) 

65 initOut = connectionTypes.InitOutput( 

66 name="VisitInitOut", 

67 storageClass="StructuredData", 

68 multiple=True, 

69 ) 

70 outA = connectionTypes.Output( 

71 name="VisitOutA", 

72 storageClass="StructuredData", 

73 multiple=False, 

74 dimensions={"instrument", "visit"}, 

75 ) 

76 outB = connectionTypes.Output( 

77 name="VisitOutB", 

78 storageClass="StructuredData", 

79 multiple=False, 

80 dimensions={"instrument", "visit"}, 

81 ) 

82 

83 def __init__(self, *, config=None): 

84 super().__init__(config=config) 

85 

86 if not config.doUseInitIn: 

87 self.initInputs.remove("initIn") 

88 

89 

90class PatchConnections(PipelineTaskConnections, dimensions={"skymap", "tract"}): 

91 """Test Connections class for patch.""" 

92 

93 a = connectionTypes.Input( 

94 name="PatchA", 

95 storageClass="StructuredData", 

96 multiple=True, 

97 dimensions={"skymap", "tract", "patch"}, 

98 ) 

99 b = connectionTypes.PrerequisiteInput( 

100 name="PatchB", 

101 storageClass="StructuredData", 

102 multiple=False, 

103 dimensions={"skymap", "tract"}, 

104 ) 

105 initOutA = connectionTypes.InitOutput( 

106 name="PatchInitOutA", 

107 storageClass="StructuredData", 

108 multiple=False, 

109 ) 

110 initOutB = connectionTypes.InitOutput( 

111 name="PatchInitOutB", 

112 storageClass="StructuredData", 

113 multiple=False, 

114 ) 

115 out = connectionTypes.Output( 

116 name="PatchOut", 

117 storageClass="StructuredData", 

118 multiple=True, 

119 dimensions={"skymap", "tract", "patch"}, 

120 ) 

121 

122 def __init__(self, *, config=None): 

123 super().__init__(config=config) 

124 

125 if not config.doUseB: 

126 self.prerequisiteInputs.remove("b") 

127 

128 

129class SkyPixConnections(PipelineTaskConnections, dimensions={"skypix"}): 

130 """Test connections class for a SkyPix.""" 

131 

132 a = connectionTypes.Input( 

133 name="PixA", 

134 storageClass="StructuredData", 

135 dimensions={"skypix"}, 

136 ) 

137 out = connectionTypes.Output( 

138 name="PixOut", 

139 storageClass="StructuredData", 

140 dimensions={"skypix"}, 

141 ) 

142 

143 

144class VisitConfig(PipelineTaskConfig, pipelineConnections=VisitConnections): 

145 """Config for Visit.""" 

146 

147 doUseInitIn = lsst.pex.config.Field(default=False, dtype=bool, doc="test") 

148 

149 

150class PatchConfig(PipelineTaskConfig, pipelineConnections=PatchConnections): 

151 """Config for Patch.""" 

152 

153 doUseB = lsst.pex.config.Field(default=True, dtype=bool, doc="test") 

154 

155 

156class SkyPixConfig(PipelineTaskConfig, pipelineConnections=SkyPixConnections): 

157 """Config for SkyPix.""" 

158 

159 

160class VisitTask(PipelineTask): 

161 """Visit-based Task.""" 

162 

163 ConfigClass = VisitConfig 

164 _DefaultName = "visit" 

165 

166 def __init__(self, initInputs=None, **kwargs): 

167 super().__init__(initInputs=initInputs, **kwargs) 

168 self.initOut = [ 

169 butlerTests.MetricsExample(data=[1, 2]), 

170 butlerTests.MetricsExample(data=[3, 4]), 

171 ] 

172 

173 def run(self, a, b): 

174 outA = butlerTests.MetricsExample(data=(a.data + b.data)) 

175 outB = butlerTests.MetricsExample(data=(a.data * max(b.data))) 

176 return Struct(outA=outA, outB=outB) 

177 

178 

179class PatchTask(PipelineTask): 

180 """Patch-based Task.""" 

181 

182 ConfigClass = PatchConfig 

183 _DefaultName = "patch" 

184 

185 def __init__(self, **kwargs): 

186 super().__init__(**kwargs) 

187 self.initOutA = butlerTests.MetricsExample(data=[1, 2, 4]) 

188 self.initOutB = butlerTests.MetricsExample(data=[1, 2, 3]) 

189 

190 def run(self, a, b=None): 

191 if self.config.doUseB: 

192 out = [butlerTests.MetricsExample(data=(oneA.data + b.data)) for oneA in a] 

193 else: 

194 out = a 

195 return Struct(out=out) 

196 

197 

198class SkyPixTask(PipelineTask): 

199 """Skypix-based Task.""" 

200 

201 ConfigClass = SkyPixConfig 

202 _DefaultName = "skypix" 

203 

204 def run(self, a): 

205 return Struct(out=a) 

206 

207 

208class PipelineTaskTestSuite(lsst.utils.tests.TestCase): 

209 """Test pipeline task.""" 

210 

211 @classmethod 

212 def setUpClass(cls): 

213 super().setUpClass() 

214 # Repository should be re-created for each test case, but 

215 # this has a prohibitive run-time cost at present 

216 cls.root = tempfile.mkdtemp() 

217 

218 cls.repo = butlerTests.makeTestRepo(cls.root) 

219 butlerTests.addDataIdValue(cls.repo, "instrument", "notACam") 

220 butlerTests.addDataIdValue(cls.repo, "visit", 101) 

221 butlerTests.addDataIdValue(cls.repo, "visit", 102) 

222 butlerTests.addDataIdValue(cls.repo, "skymap", "sky") 

223 butlerTests.addDataIdValue(cls.repo, "tract", 42) 

224 butlerTests.addDataIdValue(cls.repo, "patch", 0) 

225 butlerTests.addDataIdValue(cls.repo, "patch", 1) 

226 butlerTests.registerMetricsExample(cls.repo) 

227 

228 for typeName in {"VisitA", "VisitB", "VisitOutA", "VisitOutB"}: 

229 butlerTests.addDatasetType(cls.repo, typeName, {"instrument", "visit"}, "StructuredData") 

230 for typeName in {"PatchA", "PatchOut"}: 

231 butlerTests.addDatasetType(cls.repo, typeName, {"skymap", "tract", "patch"}, "StructuredData") 

232 butlerTests.addDatasetType(cls.repo, "PatchB", {"skymap", "tract"}, "StructuredData") 

233 for typeName in {"PixA", "PixOut"}: 

234 butlerTests.addDatasetType(cls.repo, typeName, {"htm7"}, "StructuredData") 

235 butlerTests.addDatasetType(cls.repo, "VisitInitIn", set(), "StructuredData") 

236 

237 @classmethod 

238 def tearDownClass(cls): 

239 shutil.rmtree(cls.root, ignore_errors=True) 

240 super().tearDownClass() 

241 

242 def setUp(self): 

243 super().setUp() 

244 self.butler = butlerTests.makeTestCollection(self.repo) 

245 

246 def _makeVisitTestData(self, dataId): 

247 """Create dummy datasets suitable for VisitTask. 

248 

249 This method updates ``self.butler`` directly. 

250 

251 Parameters 

252 ---------- 

253 dataId : any data ID type 

254 The (shared) ID for the datasets to create. 

255 

256 Returns 

257 ------- 

258 datasets : `dict` [`str`, `list`] 

259 A dictionary keyed by dataset type. Its values are the list of 

260 integers used to create each dataset. The datasets stored in the 

261 butler are `lsst.daf.butler.tests.MetricsExample` objects with 

262 these lists as their ``data`` argument, but the lists are easier 

263 to manipulate in test code. 

264 """ 

265 inInit = [4, 2] 

266 inA = [1, 2, 3] 

267 inB = [4, 0, 1] 

268 self.butler.put(butlerTests.MetricsExample(data=inA), "VisitA", dataId) 

269 self.butler.put(butlerTests.MetricsExample(data=inB), "VisitB", dataId) 

270 self.butler.put(butlerTests.MetricsExample(data=inInit), "VisitInitIn", set()) 

271 return { 

272 "VisitA": inA, 

273 "VisitB": inB, 

274 "VisitInitIn": inInit, 

275 } 

276 

277 def _makePatchTestData(self, dataId): 

278 """Create dummy datasets suitable for PatchTask. 

279 

280 This method updates ``self.butler`` directly. 

281 

282 Parameters 

283 ---------- 

284 dataId : any data ID type 

285 The (shared) ID for the datasets to create. Any patch ID is 

286 overridden to create multiple datasets. 

287 

288 Returns 

289 ------- 

290 datasets : `dict` [`str`, `list` [`tuple` [data ID, `list`]]] 

291 A dictionary keyed by dataset type. Its values are the data ID 

292 of each dataset and the list of integers used to create each. The 

293 datasets stored in the butler are 

294 `lsst.daf.butler.tests.MetricsExample` objects with these lists as 

295 their ``data`` argument, but the lists are easier to manipulate 

296 in test code. 

297 """ 

298 inA = [1, 2, 3] 

299 inB = [4, 0, 1] 

300 datasets = {"PatchA": [], "PatchB": []} 

301 for patch in {0, 1}: 

302 self.butler.put(butlerTests.MetricsExample(data=(inA + [patch])), "PatchA", dataId, patch=patch) 

303 datasets["PatchA"].append((dict(dataId, patch=patch), inA + [patch])) 

304 self.butler.put(butlerTests.MetricsExample(data=inB), "PatchB", dataId) 

305 datasets["PatchB"].append((dataId, inB)) 

306 return datasets 

307 

308 def testMakeQuantumNoSuchDatatype(self): 

309 config = VisitConfig() 

310 config.connections.a = "Visit" 

311 task = VisitTask(config=config) 

312 

313 dataId = {"instrument": "notACam", "visit": 102} 

314 self._makeVisitTestData(dataId) 

315 

316 with self.assertRaises(ValueError): 

317 makeQuantum(task, self.butler, dataId, {key: dataId for key in {"a", "b", "outA", "outB"}}) 

318 

319 def testMakeQuantumInvalidDimension(self): 

320 config = VisitConfig() 

321 config.connections.a = "PatchA" 

322 task = VisitTask(config=config) 

323 dataIdV = {"instrument": "notACam", "visit": 102} 

324 dataIdVExtra = {"instrument": "notACam", "visit": 102, "detector": 42} 

325 dataIdP = {"skymap": "sky", "tract": 42, "patch": 0} 

326 

327 inA = [1, 2, 3] 

328 inB = [4, 0, 1] 

329 self.butler.put(butlerTests.MetricsExample(data=inA), "VisitA", dataIdV) 

330 self.butler.put(butlerTests.MetricsExample(data=inA), "PatchA", dataIdP) 

331 self.butler.put(butlerTests.MetricsExample(data=inB), "VisitB", dataIdV) 

332 

333 # dataIdV is correct everywhere, dataIdP should error 

334 with self.assertRaises(ValueError): 

335 makeQuantum( 

336 task, 

337 self.butler, 

338 dataIdV, 

339 { 

340 "a": dataIdP, 

341 "b": dataIdV, 

342 "outA": dataIdV, 

343 "outB": dataIdV, 

344 }, 

345 ) 

346 with self.assertRaises(ValueError): 

347 makeQuantum( 

348 task, 

349 self.butler, 

350 dataIdP, 

351 { 

352 "a": dataIdV, 

353 "b": dataIdV, 

354 "outA": dataIdV, 

355 "outB": dataIdV, 

356 }, 

357 ) 

358 # should not accept small changes, either 

359 with self.assertRaises(ValueError): 

360 makeQuantum( 

361 task, 

362 self.butler, 

363 dataIdV, 

364 { 

365 "a": dataIdV, 

366 "b": dataIdV, 

367 "outA": dataIdVExtra, 

368 "outB": dataIdV, 

369 }, 

370 ) 

371 with self.assertRaises(ValueError): 

372 makeQuantum( 

373 task, 

374 self.butler, 

375 dataIdVExtra, 

376 { 

377 "a": dataIdV, 

378 "b": dataIdV, 

379 "outA": dataIdV, 

380 "outB": dataIdV, 

381 }, 

382 ) 

383 

384 def testMakeQuantumMissingMultiple(self): 

385 task = PatchTask() 

386 

387 dataId = {"skymap": "sky", "tract": 42} 

388 self._makePatchTestData(dataId) 

389 

390 with self.assertRaises(ValueError): 

391 makeQuantum( 

392 task, 

393 self.butler, 

394 dataId, 

395 { 

396 "a": dict(dataId, patch=0), 

397 "b": dataId, 

398 "out": [dict(dataId, patch=patch) for patch in {0, 1}], 

399 }, 

400 ) 

401 

402 def testMakeQuantumExtraMultiple(self): 

403 task = PatchTask() 

404 

405 dataId = {"skymap": "sky", "tract": 42} 

406 self._makePatchTestData(dataId) 

407 

408 with self.assertRaises(ValueError): 

409 makeQuantum( 

410 task, 

411 self.butler, 

412 dataId, 

413 { 

414 "a": [dict(dataId, patch=patch) for patch in {0, 1}], 

415 "b": [dataId], 

416 "out": [dict(dataId, patch=patch) for patch in {0, 1}], 

417 }, 

418 ) 

419 

420 def testMakeQuantumMissingDataId(self): 

421 task = VisitTask() 

422 

423 dataId = {"instrument": "notACam", "visit": 102} 

424 self._makeVisitTestData(dataId) 

425 

426 with self.assertRaises(ValueError): 

427 makeQuantum(task, self.butler, dataId, {key: dataId for key in {"a", "outA", "outB"}}) 

428 with self.assertRaises(ValueError): 

429 makeQuantum(task, self.butler, dataId, {key: dataId for key in {"a", "b", "outB"}}) 

430 

431 def testMakeQuantumCorruptedDataId(self): 

432 task = VisitTask() 

433 

434 dataId = {"instrument": "notACam", "visit": 102} 

435 self._makeVisitTestData(dataId) 

436 

437 with self.assertRaises(ValueError): 

438 # fourth argument should be a mapping keyed by component name 

439 makeQuantum(task, self.butler, dataId, dataId) 

440 

441 def testRunTestQuantumVisitWithRun(self): 

442 task = VisitTask() 

443 

444 dataId = {"instrument": "notACam", "visit": 102} 

445 data = self._makeVisitTestData(dataId) 

446 

447 quantum = makeQuantum(task, self.butler, dataId, {key: dataId for key in {"a", "b", "outA", "outB"}}) 

448 runTestQuantum(task, self.butler, quantum, mockRun=False) 

449 

450 # Can we use runTestQuantum to verify that task.run got called with 

451 # correct inputs/outputs? 

452 self.assertTrue(self.butler.exists("VisitOutA", dataId)) 

453 self.assertEqual( 

454 self.butler.get("VisitOutA", dataId), 

455 butlerTests.MetricsExample(data=(data["VisitA"] + data["VisitB"])), 

456 ) 

457 self.assertTrue(self.butler.exists("VisitOutB", dataId)) 

458 self.assertEqual( 

459 self.butler.get("VisitOutB", dataId), 

460 butlerTests.MetricsExample(data=(data["VisitA"] * max(data["VisitB"]))), 

461 ) 

462 

463 def testRunTestQuantumPatchWithRun(self): 

464 task = PatchTask() 

465 

466 dataId = {"skymap": "sky", "tract": 42} 

467 data = self._makePatchTestData(dataId) 

468 

469 quantum = makeQuantum( 

470 task, 

471 self.butler, 

472 dataId, 

473 { 

474 "a": [dataset[0] for dataset in data["PatchA"]], 

475 "b": dataId, 

476 "out": [dataset[0] for dataset in data["PatchA"]], 

477 }, 

478 ) 

479 runTestQuantum(task, self.butler, quantum, mockRun=False) 

480 

481 # Can we use runTestQuantum to verify that task.run got called with 

482 # correct inputs/outputs? 

483 inB = data["PatchB"][0][1] 

484 for dataset in data["PatchA"]: 

485 patchId = dataset[0] 

486 self.assertTrue(self.butler.exists("PatchOut", patchId)) 

487 self.assertEqual( 

488 self.butler.get("PatchOut", patchId), butlerTests.MetricsExample(data=(dataset[1] + inB)) 

489 ) 

490 

491 def testRunTestQuantumVisitMockRun(self): 

492 task = VisitTask() 

493 

494 dataId = {"instrument": "notACam", "visit": 102} 

495 data = self._makeVisitTestData(dataId) 

496 

497 quantum = makeQuantum(task, self.butler, dataId, {key: dataId for key in {"a", "b", "outA", "outB"}}) 

498 run = runTestQuantum(task, self.butler, quantum, mockRun=True) 

499 

500 # Can we use the mock to verify that task.run got called with the 

501 # correct inputs? 

502 run.assert_called_once_with( 

503 a=butlerTests.MetricsExample(data=data["VisitA"]), 

504 b=butlerTests.MetricsExample(data=data["VisitB"]), 

505 ) 

506 

507 def testRunTestQuantumPatchMockRun(self): 

508 task = PatchTask() 

509 

510 dataId = {"skymap": "sky", "tract": 42} 

511 data = self._makePatchTestData(dataId) 

512 

513 quantum = makeQuantum( 

514 task, 

515 self.butler, 

516 dataId, 

517 { 

518 # Use lists, not sets, to ensure order agrees with test 

519 # assertion. 

520 "a": [dataset[0] for dataset in data["PatchA"]], 

521 "b": dataId, 

522 "out": [dataset[0] for dataset in data["PatchA"]], 

523 }, 

524 ) 

525 run = runTestQuantum(task, self.butler, quantum, mockRun=True) 

526 

527 # Can we use the mock to verify that task.run got called with the 

528 # correct inputs? 

529 run.assert_called_once_with( 

530 a=[butlerTests.MetricsExample(data=dataset[1]) for dataset in data["PatchA"]], 

531 b=butlerTests.MetricsExample(data=data["PatchB"][0][1]), 

532 ) 

533 

534 def testRunTestQuantumPatchOptionalInput(self): 

535 config = PatchConfig() 

536 config.doUseB = False 

537 task = PatchTask(config=config) 

538 

539 dataId = {"skymap": "sky", "tract": 42} 

540 data = self._makePatchTestData(dataId) 

541 

542 quantum = makeQuantum( 

543 task, 

544 self.butler, 

545 dataId, 

546 { 

547 # Use lists, not sets, to ensure order agrees with test 

548 # assertion. 

549 "a": [dataset[0] for dataset in data["PatchA"]], 

550 "out": [dataset[0] for dataset in data["PatchA"]], 

551 }, 

552 ) 

553 run = runTestQuantum(task, self.butler, quantum, mockRun=True) 

554 

555 # Can we use the mock to verify that task.run got called with the 

556 # correct inputs? 

557 run.assert_called_once_with( 

558 a=[butlerTests.MetricsExample(data=dataset[1]) for dataset in data["PatchA"]] 

559 ) 

560 

561 def testAssertValidOutputPass(self): 

562 task = VisitTask() 

563 

564 inA = butlerTests.MetricsExample(data=[1, 2, 3]) 

565 inB = butlerTests.MetricsExample(data=[4, 0, 1]) 

566 result = task.run(inA, inB) 

567 

568 # should not throw 

569 assertValidOutput(task, result) 

570 

571 def testAssertValidOutputMissing(self): 

572 task = VisitTask() 

573 

574 def run(a, b): 

575 return Struct(outA=a) 

576 

577 task.run = run 

578 

579 inA = butlerTests.MetricsExample(data=[1, 2, 3]) 

580 inB = butlerTests.MetricsExample(data=[4, 0, 1]) 

581 result = task.run(inA, inB) 

582 

583 with self.assertRaises(AssertionError): 

584 assertValidOutput(task, result) 

585 

586 def testAssertValidOutputSingle(self): 

587 task = PatchTask() 

588 

589 def run(a, b): 

590 return Struct(out=b) 

591 

592 task.run = run 

593 

594 inA = butlerTests.MetricsExample(data=[1, 2, 3]) 

595 inB = butlerTests.MetricsExample(data=[4, 0, 1]) 

596 result = task.run([inA], inB) 

597 

598 with self.assertRaises(AssertionError): 

599 assertValidOutput(task, result) 

600 

601 def testAssertValidOutputMultiple(self): 

602 task = VisitTask() 

603 

604 def run(a, b): 

605 return Struct(outA=[a], outB=b) 

606 

607 task.run = run 

608 

609 inA = butlerTests.MetricsExample(data=[1, 2, 3]) 

610 inB = butlerTests.MetricsExample(data=[4, 0, 1]) 

611 result = task.run(inA, inB) 

612 

613 with self.assertRaises(AssertionError): 

614 assertValidOutput(task, result) 

615 

616 def testAssertValidInitOutputPass(self): 

617 task = VisitTask() 

618 # should not throw 

619 assertValidInitOutput(task) 

620 

621 task = PatchTask() 

622 # should not throw 

623 assertValidInitOutput(task) 

624 

625 def testAssertValidInitOutputMissing(self): 

626 class BadVisitTask(VisitTask): 

627 def __init__(self, **kwargs): 

628 PipelineTask.__init__(self, **kwargs) # Bypass VisitTask constructor 

629 pass # do not set fields 

630 

631 task = BadVisitTask() 

632 

633 with self.assertRaises(AssertionError): 

634 assertValidInitOutput(task) 

635 

636 def testAssertValidInitOutputSingle(self): 

637 class BadVisitTask(VisitTask): 

638 def __init__(self, **kwargs): 

639 PipelineTask.__init__(self, **kwargs) # Bypass VisitTask constructor 

640 self.initOut = butlerTests.MetricsExample(data=[1, 2]) 

641 

642 task = BadVisitTask() 

643 

644 with self.assertRaises(AssertionError): 

645 assertValidInitOutput(task) 

646 

647 def testAssertValidInitOutputMultiple(self): 

648 class BadPatchTask(PatchTask): 

649 def __init__(self, **kwargs): 

650 PipelineTask.__init__(self, **kwargs) # Bypass PatchTask constructor 

651 self.initOutA = [butlerTests.MetricsExample(data=[1, 2, 4])] 

652 self.initOutB = butlerTests.MetricsExample(data=[1, 2, 3]) 

653 

654 task = BadPatchTask() 

655 

656 with self.assertRaises(AssertionError): 

657 assertValidInitOutput(task) 

658 

659 def testGetInitInputs(self): 

660 dataId = {"instrument": "notACam", "visit": 102} 

661 data = self._makeVisitTestData(dataId) 

662 

663 self.assertEqual(getInitInputs(self.butler, VisitConfig()), {}) 

664 

665 config = VisitConfig() 

666 config.doUseInitIn = True 

667 self.assertEqual( 

668 getInitInputs(self.butler, config), 

669 {"initIn": butlerTests.MetricsExample(data=data["VisitInitIn"])}, 

670 ) 

671 

672 def testSkypixHandling(self): 

673 task = SkyPixTask() 

674 

675 dataId = {"htm7": 157227} # connection declares skypix, but Butler uses htm7 

676 data = butlerTests.MetricsExample(data=[1, 2, 3]) 

677 self.butler.put(data, "PixA", dataId) 

678 

679 quantum = makeQuantum(task, self.butler, dataId, {key: dataId for key in {"a", "out"}}) 

680 run = runTestQuantum(task, self.butler, quantum, mockRun=True) 

681 

682 # PixA dataset should have been retrieved by runTestQuantum 

683 run.assert_called_once_with(a=data) 

684 

685 def testLintConnectionsOk(self): 

686 lintConnections(VisitConnections) 

687 lintConnections(PatchConnections) 

688 lintConnections(SkyPixConnections) 

689 

690 def testLintConnectionsMissingMultiple(self): 

691 class BadConnections(PipelineTaskConnections, dimensions={"tract", "patch", "skymap"}): 

692 coadds = connectionTypes.Input( 

693 name="coadd_calexp", 

694 storageClass="ExposureF", 

695 # Some authors use list rather than set; check that linter 

696 # can handle it. 

697 dimensions=["tract", "patch", "band", "skymap"], 

698 ) 

699 

700 with self.assertRaises(AssertionError): 

701 lintConnections(BadConnections) 

702 lintConnections(BadConnections, checkMissingMultiple=False) 

703 

704 def testLintConnectionsExtraMultiple(self): 

705 class BadConnections( 

706 PipelineTaskConnections, 

707 # Some authors use list rather than set. 

708 dimensions=["tract", "patch", "band", "skymap"], 

709 ): 

710 coadds = connectionTypes.Input( 

711 name="coadd_calexp", 

712 storageClass="ExposureF", 

713 multiple=True, 

714 dimensions={"tract", "patch", "band", "skymap"}, 

715 ) 

716 

717 with self.assertRaises(AssertionError): 

718 lintConnections(BadConnections) 

719 lintConnections(BadConnections, checkUnnecessaryMultiple=False) 

720 

721 

722class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

723 """Run file leak tests.""" 

724 

725 

726def setup_module(module): 

727 """Configure pytest.""" 

728 lsst.utils.tests.init() 

729 

730 

731if __name__ == "__main__": 

732 lsst.utils.tests.init() 

733 unittest.main()