Coverage for tests/test_defineVisits.py: 28%
78 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-10 12:00 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-10 12:00 +0000
1# This file is part of obs_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 <http://www.gnu.org/licenses/>.
22import os
23import pickle
24import shutil
25import tempfile
26import unittest
27from collections import defaultdict
29import lsst.daf.butler.tests as butlerTests
30from lsst.daf.butler import DataCoordinate, DimensionRecord, SerializedDimensionRecord
31from lsst.obs.base import DefineVisitsTask
32from lsst.obs.base.instrument_tests import DummyCam
33from lsst.utils.iteration import ensure_iterable
35TESTDIR = os.path.dirname(__file__)
36DATADIR = os.path.join(TESTDIR, "data", "visits")
39class DefineVisitsTestCase(unittest.TestCase):
40 """Test visit definition."""
42 def setUp(self):
43 """Create a new butler for each test since we are changing dimension
44 records.
45 """
46 self.root = tempfile.mkdtemp(dir=TESTDIR)
47 self.creatorButler = butlerTests.makeTestRepo(self.root, [])
48 self.butler = butlerTests.makeTestCollection(self.creatorButler, uniqueId=self.id())
50 self.config = DefineVisitsTask.ConfigClass()
51 self.task = DefineVisitsTask(config=self.config, butler=self.butler)
53 # Need to register the instrument.
54 DummyCam().register(self.butler.registry)
56 # Read the exposure records.
57 self.records: dict[int, DimensionRecord] = {}
58 for i in (347, 348, 349):
59 with open(os.path.join(DATADIR, f"exp_{i}.json")) as fh:
60 simple = SerializedDimensionRecord.model_validate_json(fh.read())
61 self.records[i] = DimensionRecord.from_simple(simple, registry=self.butler.registry)
63 def tearDown(self):
64 if self.root is not None:
65 shutil.rmtree(self.root, ignore_errors=True)
67 def assertVisits(self):
68 """Check that the visits were registered as expected."""
69 visits = list(self.butler.registry.queryDimensionRecords("visit"))
70 self.assertEqual(len(visits), 4)
71 self.assertEqual(
72 {visit.id for visit in visits}, {2022040500347, 2022040500348, 2022040500349, 92022040500348}
73 )
75 # Ensure that the definitions are correct (ignoring order).
76 defmap = defaultdict(set)
77 definitions = list(self.butler.registry.queryDimensionRecords("visit_definition"))
78 for defn in definitions:
79 defmap[defn.visit].add(defn.exposure)
81 self.assertEqual(
82 dict(defmap),
83 {
84 92022040500348: {2022040500348},
85 2022040500347: {2022040500347},
86 2022040500348: {2022040500348, 2022040500349},
87 2022040500349: {2022040500349},
88 },
89 )
91 def define_visits(
92 self, exposures: list[DimensionRecord | list[DimensionRecord]], incremental: bool
93 ) -> None:
94 for records in exposures:
95 self.butler.registry.insertDimensionData("exposure", *ensure_iterable(records))
96 # Include all records so far in definition.
97 dataIds = list(self.butler.registry.queryDataIds("exposure", instrument="DummyCam"))
98 self.task.run(dataIds, incremental=incremental)
100 def test_defineVisits(self):
101 # Test visit definition with all the records.
102 self.define_visits([list(self.records.values())], incremental=False) # list inside a list
103 self.assertVisits()
105 def test_incremental_cumulative(self):
106 # Define the visits after each exposure.
107 self.define_visits(list(self.records.values()), incremental=True)
108 self.assertVisits()
110 def test_incremental_cumulative_reverse(self):
111 # In reverse order we should still eventually end up with the right
112 # answer.
113 with self.assertLogs("lsst.defineVisits.groupExposures", level="WARNING") as cm:
114 self.define_visits(list(reversed(self.records.values())), incremental=True)
115 self.assertIn("Skipping the multi-snap definition", "\n".join(cm.output))
116 self.assertVisits()
118 def define_visits_incrementally(self, exposure: DimensionRecord) -> None:
119 self.butler.registry.insertDimensionData("exposure", exposure)
120 dataIds = [
121 DataCoordinate.standardize(
122 instrument="DummyCam", exposure=exposure.id, universe=self.butler.dimensions
123 )
124 ]
125 self.task.run(dataIds, incremental=True)
127 def test_incremental(self):
128 for record in self.records.values():
129 self.define_visits_incrementally(record)
130 self.assertVisits()
132 def test_incremental_reverse(self):
133 for record in reversed(self.records.values()):
134 self.define_visits_incrementally(record)
135 self.assertVisits()
137 def testPickleTask(self):
138 stream = pickle.dumps(self.task)
139 copy = pickle.loads(stream)
140 self.assertEqual(self.task.getFullName(), copy.getFullName())
141 self.assertEqual(self.task.log.name, copy.log.name)
142 self.assertEqual(self.task.config, copy.config)
143 self.assertEqual(self.task.butler._config, copy.butler._config)
144 self.assertEqual(self.task.butler.collections, copy.butler.collections)
145 self.assertEqual(self.task.butler.run, copy.butler.run)
146 self.assertEqual(self.task.universe, copy.universe)
149if __name__ == "__main__": 149 ↛ 150line 149 didn't jump to line 150, because the condition on line 149 was never true
150 unittest.main()