Coverage for tests/test_testUtils.py: 22%
311 statements
« prev ^ index » next coverage.py v7.3.0, created at 2023-08-31 09:39 +0000
« prev ^ index » next coverage.py v7.3.0, created at 2023-08-31 09:39 +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/>.
22"""Unit tests for `lsst.pipe.base.tests`, a library for testing
23PipelineTask subclasses.
24"""
26import shutil
27import tempfile
28import unittest
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)
45class VisitConnections(PipelineTaskConnections, dimensions={"instrument", "visit"}):
46 """Test connections class involving a visit."""
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 )
83 def __init__(self, *, config=None):
84 super().__init__(config=config)
86 if not config.doUseInitIn:
87 self.initInputs.remove("initIn")
90class PatchConnections(PipelineTaskConnections, dimensions={"skymap", "tract"}):
91 """Test Connections class for patch."""
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 )
122 def __init__(self, *, config=None):
123 super().__init__(config=config)
125 if not config.doUseB:
126 self.prerequisiteInputs.remove("b")
129class SkyPixConnections(PipelineTaskConnections, dimensions={"skypix"}):
130 """Test connections class for a SkyPix."""
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 )
144class VisitConfig(PipelineTaskConfig, pipelineConnections=VisitConnections):
145 """Config for Visit."""
147 doUseInitIn = lsst.pex.config.Field(default=False, dtype=bool, doc="test")
150class PatchConfig(PipelineTaskConfig, pipelineConnections=PatchConnections):
151 """Config for Patch."""
153 doUseB = lsst.pex.config.Field(default=True, dtype=bool, doc="test")
156class SkyPixConfig(PipelineTaskConfig, pipelineConnections=SkyPixConnections):
157 """Config for SkyPix."""
160class VisitTask(PipelineTask):
161 """Visit-based Task."""
163 ConfigClass = VisitConfig
164 _DefaultName = "visit"
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 ]
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)
179class PatchTask(PipelineTask):
180 """Patch-based Task."""
182 ConfigClass = PatchConfig
183 _DefaultName = "patch"
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])
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)
198class SkyPixTask(PipelineTask):
199 """Skypix-based Task."""
201 ConfigClass = SkyPixConfig
202 _DefaultName = "skypix"
204 def run(self, a):
205 return Struct(out=a)
208class PipelineTaskTestSuite(lsst.utils.tests.TestCase):
209 """Test pipeline task."""
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()
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)
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")
237 @classmethod
238 def tearDownClass(cls):
239 shutil.rmtree(cls.root, ignore_errors=True)
240 super().tearDownClass()
242 def setUp(self):
243 super().setUp()
244 self.butler = butlerTests.makeTestCollection(self.repo)
246 def _makeVisitTestData(self, dataId):
247 """Create dummy datasets suitable for VisitTask.
249 This method updates ``self.butler`` directly.
251 Parameters
252 ----------
253 dataId : any data ID type
254 The (shared) ID for the datasets to create.
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 }
277 def _makePatchTestData(self, dataId):
278 """Create dummy datasets suitable for PatchTask.
280 This method updates ``self.butler`` directly.
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.
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
308 def testMakeQuantumNoSuchDatatype(self):
309 config = VisitConfig()
310 config.connections.a = "Visit"
311 task = VisitTask(config=config)
313 dataId = {"instrument": "notACam", "visit": 102}
314 self._makeVisitTestData(dataId)
316 with self.assertRaises(ValueError):
317 makeQuantum(task, self.butler, dataId, {key: dataId for key in {"a", "b", "outA", "outB"}})
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}
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)
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 )
384 def testMakeQuantumMissingMultiple(self):
385 task = PatchTask()
387 dataId = {"skymap": "sky", "tract": 42}
388 self._makePatchTestData(dataId)
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 )
402 def testMakeQuantumExtraMultiple(self):
403 task = PatchTask()
405 dataId = {"skymap": "sky", "tract": 42}
406 self._makePatchTestData(dataId)
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 )
420 def testMakeQuantumMissingDataId(self):
421 task = VisitTask()
423 dataId = {"instrument": "notACam", "visit": 102}
424 self._makeVisitTestData(dataId)
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"}})
431 def testMakeQuantumCorruptedDataId(self):
432 task = VisitTask()
434 dataId = {"instrument": "notACam", "visit": 102}
435 self._makeVisitTestData(dataId)
437 with self.assertRaises(ValueError):
438 # fourth argument should be a mapping keyed by component name
439 makeQuantum(task, self.butler, dataId, dataId)
441 def testRunTestQuantumVisitWithRun(self):
442 task = VisitTask()
444 dataId = {"instrument": "notACam", "visit": 102}
445 data = self._makeVisitTestData(dataId)
447 quantum = makeQuantum(task, self.butler, dataId, {key: dataId for key in {"a", "b", "outA", "outB"}})
448 runTestQuantum(task, self.butler, quantum, mockRun=False)
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 )
463 def testRunTestQuantumPatchWithRun(self):
464 task = PatchTask()
466 dataId = {"skymap": "sky", "tract": 42}
467 data = self._makePatchTestData(dataId)
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)
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 )
491 def testRunTestQuantumVisitMockRun(self):
492 task = VisitTask()
494 dataId = {"instrument": "notACam", "visit": 102}
495 data = self._makeVisitTestData(dataId)
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)
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 )
507 def testRunTestQuantumPatchMockRun(self):
508 task = PatchTask()
510 dataId = {"skymap": "sky", "tract": 42}
511 data = self._makePatchTestData(dataId)
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)
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 )
534 def testRunTestQuantumPatchOptionalInput(self):
535 config = PatchConfig()
536 config.doUseB = False
537 task = PatchTask(config=config)
539 dataId = {"skymap": "sky", "tract": 42}
540 data = self._makePatchTestData(dataId)
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)
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 )
561 def testAssertValidOutputPass(self):
562 task = VisitTask()
564 inA = butlerTests.MetricsExample(data=[1, 2, 3])
565 inB = butlerTests.MetricsExample(data=[4, 0, 1])
566 result = task.run(inA, inB)
568 # should not throw
569 assertValidOutput(task, result)
571 def testAssertValidOutputMissing(self):
572 task = VisitTask()
574 def run(a, b):
575 return Struct(outA=a)
577 task.run = run
579 inA = butlerTests.MetricsExample(data=[1, 2, 3])
580 inB = butlerTests.MetricsExample(data=[4, 0, 1])
581 result = task.run(inA, inB)
583 with self.assertRaises(AssertionError):
584 assertValidOutput(task, result)
586 def testAssertValidOutputSingle(self):
587 task = PatchTask()
589 def run(a, b):
590 return Struct(out=b)
592 task.run = run
594 inA = butlerTests.MetricsExample(data=[1, 2, 3])
595 inB = butlerTests.MetricsExample(data=[4, 0, 1])
596 result = task.run([inA], inB)
598 with self.assertRaises(AssertionError):
599 assertValidOutput(task, result)
601 def testAssertValidOutputMultiple(self):
602 task = VisitTask()
604 def run(a, b):
605 return Struct(outA=[a], outB=b)
607 task.run = run
609 inA = butlerTests.MetricsExample(data=[1, 2, 3])
610 inB = butlerTests.MetricsExample(data=[4, 0, 1])
611 result = task.run(inA, inB)
613 with self.assertRaises(AssertionError):
614 assertValidOutput(task, result)
616 def testAssertValidInitOutputPass(self):
617 task = VisitTask()
618 # should not throw
619 assertValidInitOutput(task)
621 task = PatchTask()
622 # should not throw
623 assertValidInitOutput(task)
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
631 task = BadVisitTask()
633 with self.assertRaises(AssertionError):
634 assertValidInitOutput(task)
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])
642 task = BadVisitTask()
644 with self.assertRaises(AssertionError):
645 assertValidInitOutput(task)
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])
654 task = BadPatchTask()
656 with self.assertRaises(AssertionError):
657 assertValidInitOutput(task)
659 def testGetInitInputs(self):
660 dataId = {"instrument": "notACam", "visit": 102}
661 data = self._makeVisitTestData(dataId)
663 self.assertEqual(getInitInputs(self.butler, VisitConfig()), {})
665 config = VisitConfig()
666 config.doUseInitIn = True
667 self.assertEqual(
668 getInitInputs(self.butler, config),
669 {"initIn": butlerTests.MetricsExample(data=data["VisitInitIn"])},
670 )
672 def testSkypixHandling(self):
673 task = SkyPixTask()
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)
679 quantum = makeQuantum(task, self.butler, dataId, {key: dataId for key in {"a", "out"}})
680 run = runTestQuantum(task, self.butler, quantum, mockRun=True)
682 # PixA dataset should have been retrieved by runTestQuantum
683 run.assert_called_once_with(a=data)
685 def testLintConnectionsOk(self):
686 lintConnections(VisitConnections)
687 lintConnections(PatchConnections)
688 lintConnections(SkyPixConnections)
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 )
700 with self.assertRaises(AssertionError):
701 lintConnections(BadConnections)
702 lintConnections(BadConnections, checkMissingMultiple=False)
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 )
717 with self.assertRaises(AssertionError):
718 lintConnections(BadConnections)
719 lintConnections(BadConnections, checkUnnecessaryMultiple=False)
722class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
723 """Run file leak tests."""
726def setup_module(module):
727 """Configure pytest."""
728 lsst.utils.tests.init()
731if __name__ == "__main__":
732 lsst.utils.tests.init()
733 unittest.main()