Coverage for tests/test_defineVisits.py: 28%

78 statements  

« prev     ^ index     » next       coverage.py v7.4.2, created at 2024-02-22 11:09 +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/>. 

21 

22import os 

23import pickle 

24import shutil 

25import tempfile 

26import unittest 

27from collections import defaultdict 

28 

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 

34 

35TESTDIR = os.path.dirname(__file__) 

36DATADIR = os.path.join(TESTDIR, "data", "visits") 

37 

38 

39class DefineVisitsTestCase(unittest.TestCase): 

40 """Test visit definition.""" 

41 

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()) 

49 

50 self.config = DefineVisitsTask.ConfigClass() 

51 self.task = DefineVisitsTask(config=self.config, butler=self.butler) 

52 

53 # Need to register the instrument. 

54 DummyCam().register(self.butler.registry) 

55 

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) 

62 

63 def tearDown(self): 

64 if self.root is not None: 

65 shutil.rmtree(self.root, ignore_errors=True) 

66 

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 ) 

74 

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) 

80 

81 self.assertEqual( 

82 dict(defmap), 

83 { 

84 92022040500348: {2022040500348}, 

85 2022040500347: {2022040500347}, 

86 2022040500348: {2022040500348, 2022040500349}, 

87 2022040500349: {2022040500349}, 

88 }, 

89 ) 

90 

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) 

99 

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() 

104 

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() 

109 

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() 

117 

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) 

126 

127 def test_incremental(self): 

128 for record in self.records.values(): 

129 self.define_visits_incrementally(record) 

130 self.assertVisits() 

131 

132 def test_incremental_reverse(self): 

133 for record in reversed(self.records.values()): 

134 self.define_visits_incrementally(record) 

135 self.assertVisits() 

136 

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) 

147 

148 

149if __name__ == "__main__": 149 ↛ 150line 149 didn't jump to line 150, because the condition on line 149 was never true

150 unittest.main()