Coverage for tests/test_testUtils.py: 22%

311 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-04-24 10:01 +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 software is dual licensed under the GNU General Public License and also 

10# under a 3-clause BSD license. Recipients may choose which of these licenses 

11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt, 

12# respectively. If you choose the GPL option then the following text applies 

13# (but note that there is still no warranty even if you opt for BSD instead): 

14# 

15# This program is free software: you can redistribute it and/or modify 

16# it under the terms of the GNU General Public License as published by 

17# the Free Software Foundation, either version 3 of the License, or 

18# (at your option) any later version. 

19# 

20# This program is distributed in the hope that it will be useful, 

21# but WITHOUT ANY WARRANTY; without even the implied warranty of 

22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

23# GNU General Public License for more details. 

24# 

25# You should have received a copy of the GNU General Public License 

26# along with this program. If not, see <https://www.gnu.org/licenses/>. 

27 

28"""Unit tests for `lsst.pipe.base.tests`, a library for testing 

29PipelineTask subclasses. 

30""" 

31 

32import shutil 

33import tempfile 

34import unittest 

35 

36import lsst.daf.butler 

37import lsst.daf.butler.tests as butlerTests 

38import lsst.pex.config 

39import lsst.utils.tests 

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

41from lsst.pipe.base.testUtils import ( 

42 assertValidInitOutput, 

43 assertValidOutput, 

44 getInitInputs, 

45 lintConnections, 

46 makeQuantum, 

47 runTestQuantum, 

48) 

49 

50 

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

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

53 

54 initIn = connectionTypes.InitInput( 

55 name="VisitInitIn", 

56 storageClass="StructuredData", 

57 multiple=False, 

58 ) 

59 a = connectionTypes.Input( 

60 name="VisitA", 

61 storageClass="StructuredData", 

62 multiple=False, 

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

64 ) 

65 b = connectionTypes.Input( 

66 name="VisitB", 

67 storageClass="StructuredData", 

68 multiple=False, 

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

70 ) 

71 initOut = connectionTypes.InitOutput( 

72 name="VisitInitOut", 

73 storageClass="StructuredData", 

74 multiple=True, 

75 ) 

76 outA = connectionTypes.Output( 

77 name="VisitOutA", 

78 storageClass="StructuredData", 

79 multiple=False, 

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

81 ) 

82 outB = connectionTypes.Output( 

83 name="VisitOutB", 

84 storageClass="StructuredData", 

85 multiple=False, 

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

87 ) 

88 

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

90 super().__init__(config=config) 

91 

92 if not config.doUseInitIn: 

93 self.initInputs.remove("initIn") 

94 

95 

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

97 """Test Connections class for patch.""" 

98 

99 a = connectionTypes.Input( 

100 name="PatchA", 

101 storageClass="StructuredData", 

102 multiple=True, 

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

104 ) 

105 b = connectionTypes.PrerequisiteInput( 

106 name="PatchB", 

107 storageClass="StructuredData", 

108 multiple=False, 

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

110 ) 

111 initOutA = connectionTypes.InitOutput( 

112 name="PatchInitOutA", 

113 storageClass="StructuredData", 

114 multiple=False, 

115 ) 

116 initOutB = connectionTypes.InitOutput( 

117 name="PatchInitOutB", 

118 storageClass="StructuredData", 

119 multiple=False, 

120 ) 

121 out = connectionTypes.Output( 

122 name="PatchOut", 

123 storageClass="StructuredData", 

124 multiple=True, 

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

126 ) 

127 

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

129 super().__init__(config=config) 

130 

131 if not config.doUseB: 

132 self.prerequisiteInputs.remove("b") 

133 

134 

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

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

137 

138 a = connectionTypes.Input( 

139 name="PixA", 

140 storageClass="StructuredData", 

141 dimensions={"skypix"}, 

142 ) 

143 out = connectionTypes.Output( 

144 name="PixOut", 

145 storageClass="StructuredData", 

146 dimensions={"skypix"}, 

147 ) 

148 

149 

150class VisitConfig(PipelineTaskConfig, pipelineConnections=VisitConnections): 

151 """Config for Visit.""" 

152 

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

154 

155 

156class PatchConfig(PipelineTaskConfig, pipelineConnections=PatchConnections): 

157 """Config for Patch.""" 

158 

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

160 

161 

162class SkyPixConfig(PipelineTaskConfig, pipelineConnections=SkyPixConnections): 

163 """Config for SkyPix.""" 

164 

165 

166class VisitTask(PipelineTask): 

167 """Visit-based Task.""" 

168 

169 ConfigClass = VisitConfig 

170 _DefaultName = "visit" 

171 

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

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

174 self.initOut = [ 

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

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

177 ] 

178 

179 def run(self, a, b): 

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

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

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

183 

184 

185class PatchTask(PipelineTask): 

186 """Patch-based Task.""" 

187 

188 ConfigClass = PatchConfig 

189 _DefaultName = "patch" 

190 

191 def __init__(self, **kwargs): 

192 super().__init__(**kwargs) 

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

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

195 

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

197 if self.config.doUseB: 

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

199 else: 

200 out = a 

201 return Struct(out=out) 

202 

203 

204class SkyPixTask(PipelineTask): 

205 """Skypix-based Task.""" 

206 

207 ConfigClass = SkyPixConfig 

208 _DefaultName = "skypix" 

209 

210 def run(self, a): 

211 return Struct(out=a) 

212 

213 

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

215 """Test pipeline task.""" 

216 

217 @classmethod 

218 def setUpClass(cls): 

219 super().setUpClass() 

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

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

222 cls.root = tempfile.mkdtemp() 

223 

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

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

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

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

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

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

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

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

232 butlerTests.registerMetricsExample(cls.repo) 

233 

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

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

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

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

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

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

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

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

242 

243 @classmethod 

244 def tearDownClass(cls): 

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

246 super().tearDownClass() 

247 

248 def setUp(self): 

249 super().setUp() 

250 self.butler = butlerTests.makeTestCollection(self.repo, uniqueId=self.id()) 

251 

252 def _makeVisitTestData(self, dataId): 

253 """Create dummy datasets suitable for VisitTask. 

254 

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

256 

257 Parameters 

258 ---------- 

259 dataId : any data ID type 

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

261 

262 Returns 

263 ------- 

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

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

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

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

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

269 to manipulate in test code. 

270 """ 

271 inInit = [4, 2] 

272 inA = [1, 2, 3] 

273 inB = [4, 0, 1] 

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

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

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

277 return { 

278 "VisitA": inA, 

279 "VisitB": inB, 

280 "VisitInitIn": inInit, 

281 } 

282 

283 def _makePatchTestData(self, dataId): 

284 """Create dummy datasets suitable for PatchTask. 

285 

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

287 

288 Parameters 

289 ---------- 

290 dataId : any data ID type 

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

292 overridden to create multiple datasets. 

293 

294 Returns 

295 ------- 

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

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

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

299 datasets stored in the butler are 

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

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

302 in test code. 

303 """ 

304 inA = [1, 2, 3] 

305 inB = [4, 0, 1] 

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

307 for patch in {0, 1}: 

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

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

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

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

312 return datasets 

313 

314 def testMakeQuantumNoSuchDatatype(self): 

315 config = VisitConfig() 

316 config.connections.a = "Visit" 

317 task = VisitTask(config=config) 

318 

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

320 self._makeVisitTestData(dataId) 

321 

322 with self.assertRaises(ValueError): 

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

324 

325 def testMakeQuantumInvalidDimension(self): 

326 config = VisitConfig() 

327 config.connections.a = "PatchA" 

328 task = VisitTask(config=config) 

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

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

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

332 

333 inA = [1, 2, 3] 

334 inB = [4, 0, 1] 

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

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

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

338 

339 # dataIdV is correct everywhere, dataIdP should error 

340 with self.assertRaises(ValueError): 

341 makeQuantum( 

342 task, 

343 self.butler, 

344 dataIdV, 

345 { 

346 "a": dataIdP, 

347 "b": dataIdV, 

348 "outA": dataIdV, 

349 "outB": dataIdV, 

350 }, 

351 ) 

352 with self.assertRaises(ValueError): 

353 makeQuantum( 

354 task, 

355 self.butler, 

356 dataIdP, 

357 { 

358 "a": dataIdV, 

359 "b": dataIdV, 

360 "outA": dataIdV, 

361 "outB": dataIdV, 

362 }, 

363 ) 

364 # should not accept small changes, either 

365 with self.assertRaises(ValueError): 

366 makeQuantum( 

367 task, 

368 self.butler, 

369 dataIdV, 

370 { 

371 "a": dataIdV, 

372 "b": dataIdV, 

373 "outA": dataIdVExtra, 

374 "outB": dataIdV, 

375 }, 

376 ) 

377 with self.assertRaises(ValueError): 

378 makeQuantum( 

379 task, 

380 self.butler, 

381 dataIdVExtra, 

382 { 

383 "a": dataIdV, 

384 "b": dataIdV, 

385 "outA": dataIdV, 

386 "outB": dataIdV, 

387 }, 

388 ) 

389 

390 def testMakeQuantumMissingMultiple(self): 

391 task = PatchTask() 

392 

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

394 self._makePatchTestData(dataId) 

395 

396 with self.assertRaises(ValueError): 

397 makeQuantum( 

398 task, 

399 self.butler, 

400 dataId, 

401 { 

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

403 "b": dataId, 

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

405 }, 

406 ) 

407 

408 def testMakeQuantumExtraMultiple(self): 

409 task = PatchTask() 

410 

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

412 self._makePatchTestData(dataId) 

413 

414 with self.assertRaises(ValueError): 

415 makeQuantum( 

416 task, 

417 self.butler, 

418 dataId, 

419 { 

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

421 "b": [dataId], 

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

423 }, 

424 ) 

425 

426 def testMakeQuantumMissingDataId(self): 

427 task = VisitTask() 

428 

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

430 self._makeVisitTestData(dataId) 

431 

432 with self.assertRaises(ValueError): 

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

434 with self.assertRaises(ValueError): 

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

436 

437 def testMakeQuantumCorruptedDataId(self): 

438 task = VisitTask() 

439 

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

441 self._makeVisitTestData(dataId) 

442 

443 with self.assertRaises(ValueError): 

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

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

446 

447 def testRunTestQuantumVisitWithRun(self): 

448 task = VisitTask() 

449 

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

451 data = self._makeVisitTestData(dataId) 

452 

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

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

455 

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

457 # correct inputs/outputs? 

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

459 self.assertEqual( 

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

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

462 ) 

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

464 self.assertEqual( 

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

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

467 ) 

468 

469 def testRunTestQuantumPatchWithRun(self): 

470 task = PatchTask() 

471 

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

473 data = self._makePatchTestData(dataId) 

474 

475 quantum = makeQuantum( 

476 task, 

477 self.butler, 

478 dataId, 

479 { 

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

481 "b": dataId, 

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

483 }, 

484 ) 

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

486 

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

488 # correct inputs/outputs? 

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

490 for dataset in data["PatchA"]: 

491 patchId = dataset[0] 

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

493 self.assertEqual( 

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

495 ) 

496 

497 def testRunTestQuantumVisitMockRun(self): 

498 task = VisitTask() 

499 

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

501 data = self._makeVisitTestData(dataId) 

502 

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

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

505 

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

507 # correct inputs? 

508 run.assert_called_once_with( 

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

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

511 ) 

512 

513 def testRunTestQuantumPatchMockRun(self): 

514 task = PatchTask() 

515 

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

517 data = self._makePatchTestData(dataId) 

518 

519 quantum = makeQuantum( 

520 task, 

521 self.butler, 

522 dataId, 

523 { 

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

525 # assertion. 

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

527 "b": dataId, 

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

529 }, 

530 ) 

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

532 

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

534 # correct inputs? 

535 run.assert_called_once_with( 

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

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

538 ) 

539 

540 def testRunTestQuantumPatchOptionalInput(self): 

541 config = PatchConfig() 

542 config.doUseB = False 

543 task = PatchTask(config=config) 

544 

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

546 data = self._makePatchTestData(dataId) 

547 

548 quantum = makeQuantum( 

549 task, 

550 self.butler, 

551 dataId, 

552 { 

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

554 # assertion. 

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

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

557 }, 

558 ) 

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

560 

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

562 # correct inputs? 

563 run.assert_called_once_with( 

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

565 ) 

566 

567 def testAssertValidOutputPass(self): 

568 task = VisitTask() 

569 

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

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

572 result = task.run(inA, inB) 

573 

574 # should not throw 

575 assertValidOutput(task, result) 

576 

577 def testAssertValidOutputMissing(self): 

578 task = VisitTask() 

579 

580 def run(a, b): 

581 return Struct(outA=a) 

582 

583 task.run = run 

584 

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

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

587 result = task.run(inA, inB) 

588 

589 with self.assertRaises(AssertionError): 

590 assertValidOutput(task, result) 

591 

592 def testAssertValidOutputSingle(self): 

593 task = PatchTask() 

594 

595 def run(a, b): 

596 return Struct(out=b) 

597 

598 task.run = run 

599 

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

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

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

603 

604 with self.assertRaises(AssertionError): 

605 assertValidOutput(task, result) 

606 

607 def testAssertValidOutputMultiple(self): 

608 task = VisitTask() 

609 

610 def run(a, b): 

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

612 

613 task.run = run 

614 

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

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

617 result = task.run(inA, inB) 

618 

619 with self.assertRaises(AssertionError): 

620 assertValidOutput(task, result) 

621 

622 def testAssertValidInitOutputPass(self): 

623 task = VisitTask() 

624 # should not throw 

625 assertValidInitOutput(task) 

626 

627 task = PatchTask() 

628 # should not throw 

629 assertValidInitOutput(task) 

630 

631 def testAssertValidInitOutputMissing(self): 

632 class BadVisitTask(VisitTask): 

633 def __init__(self, **kwargs): 

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

635 pass # do not set fields 

636 

637 task = BadVisitTask() 

638 

639 with self.assertRaises(AssertionError): 

640 assertValidInitOutput(task) 

641 

642 def testAssertValidInitOutputSingle(self): 

643 class BadVisitTask(VisitTask): 

644 def __init__(self, **kwargs): 

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

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

647 

648 task = BadVisitTask() 

649 

650 with self.assertRaises(AssertionError): 

651 assertValidInitOutput(task) 

652 

653 def testAssertValidInitOutputMultiple(self): 

654 class BadPatchTask(PatchTask): 

655 def __init__(self, **kwargs): 

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

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

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

659 

660 task = BadPatchTask() 

661 

662 with self.assertRaises(AssertionError): 

663 assertValidInitOutput(task) 

664 

665 def testGetInitInputs(self): 

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

667 data = self._makeVisitTestData(dataId) 

668 

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

670 

671 config = VisitConfig() 

672 config.doUseInitIn = True 

673 self.assertEqual( 

674 getInitInputs(self.butler, config), 

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

676 ) 

677 

678 def testSkypixHandling(self): 

679 task = SkyPixTask() 

680 

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

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

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

684 

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

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

687 

688 # PixA dataset should have been retrieved by runTestQuantum 

689 run.assert_called_once_with(a=data) 

690 

691 def testLintConnectionsOk(self): 

692 lintConnections(VisitConnections) 

693 lintConnections(PatchConnections) 

694 lintConnections(SkyPixConnections) 

695 

696 def testLintConnectionsMissingMultiple(self): 

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

698 coadds = connectionTypes.Input( 

699 name="coadd_calexp", 

700 storageClass="ExposureF", 

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

702 # can handle it. 

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

704 ) 

705 

706 with self.assertRaises(AssertionError): 

707 lintConnections(BadConnections) 

708 lintConnections(BadConnections, checkMissingMultiple=False) 

709 

710 def testLintConnectionsExtraMultiple(self): 

711 class BadConnections( 

712 PipelineTaskConnections, 

713 # Some authors use list rather than set. 

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

715 ): 

716 coadds = connectionTypes.Input( 

717 name="coadd_calexp", 

718 storageClass="ExposureF", 

719 multiple=True, 

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

721 ) 

722 

723 with self.assertRaises(AssertionError): 

724 lintConnections(BadConnections) 

725 lintConnections(BadConnections, checkUnnecessaryMultiple=False) 

726 

727 

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

729 """Run file leak tests.""" 

730 

731 

732def setup_module(module): 

733 """Configure pytest.""" 

734 lsst.utils.tests.init() 

735 

736 

737if __name__ == "__main__": 

738 lsst.utils.tests.init() 

739 unittest.main()