Coverage for tests/test_nightReport.py: 25%
120 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-01 03:42 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-01 03:42 -0800
1# This file is part of summit_extras.
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/>.
22import unittest
23import tempfile
24import itertools
25import os
26from unittest import mock
27from numpy.random import shuffle
28from astro_metadata_translator import ObservationInfo
30import lsst.utils.tests
32import matplotlib as mpl
33mpl.use('Agg')
35from lsst.summit.utils.nightReport import NightReport, ColorAndMarker # noqa: E402
36import lsst.summit.utils.butlerUtils as butlerUtils # noqa: E402
39class NightReportTestCase(lsst.utils.tests.TestCase):
41 @classmethod
42 def setUpClass(cls):
43 try:
44 cls.butler = butlerUtils.makeDefaultLatissButler()
45 except FileNotFoundError:
46 raise unittest.SkipTest("Skipping tests that require the LATISS butler repo.")
48 cls.dayObs = 20200314 # has 377 images and data also exists on the TTS & summit
50 # Do the init in setUpClass because this takes about 35s for 20200314
51 cls.report = NightReport(cls.butler, cls.dayObs)
52 # number of images isn't necessarily the same as the number for the
53 # the dayObs in the registry becacuse of the test stands/summit
54 # having partial data, so get the number of images from the length
55 # of the scraped data. Not ideal, but best that can be done due to
56 # only having partial days in the test datasets.
57 cls.nImages = len(cls.report.data.keys())
58 cls.seqNums = list(cls.report.data.keys())
60 def test_saveAndLoad(self):
61 """Test that a NightReport can save itself, and be loaded back.
62 """
63 writeDir = tempfile.mkdtemp()
64 saveFile = os.path.join(writeDir, f'testNightReport_{self.dayObs}.pickle')
65 self.report.save(saveFile)
66 self.assertTrue(os.path.exists(saveFile))
68 loaded = NightReport(self.butler, self.dayObs, saveFile)
69 self.assertIsInstance(loaded, lsst.summit.utils.nightReport.NightReport)
70 self.assertGreaterEqual(len(loaded.data), 1)
71 self.assertEqual(loaded.dayObs, self.dayObs)
73 # TODO: add a self.assertRaises on a mismatched dayObs
75 def test_getSortedData(self):
76 """Test the _getSortedData returns the seqNums in order.
77 """
78 shuffledKeys = list(self.report.data.keys())
79 shuffle(shuffledKeys)
80 shuffledData = {k: self.report.data[k] for k in shuffledKeys}
82 sortedData = self.report._getSortedData(shuffledData)
83 sortedKeys = sorted(list(sortedData.keys()))
84 self.assertEqual(sortedKeys, list(self.report.data.keys()))
85 return
87 def test_getExpRecordDictForDayObs(self):
88 """Test getExpRecordDictForDayObs.
90 Test it returns a dict of dicts, keyed by integer seqNums.
91 """
92 expRecDict = self.report.getExpRecordDictForDayObs(self.dayObs)
93 self.assertIsInstance(expRecDict, dict)
94 self.assertGreaterEqual(len(expRecDict), 1)
96 # check all the keys are ints
97 seqNums = list(expRecDict.keys())
98 self.assertTrue(all(isinstance(s, int) for s in seqNums))
100 # check all the values are dicts
101 self.assertTrue(all(isinstance(expRecDict[s], dict) for s in seqNums))
102 return
104 def test_getObsInfoAndMetadataForSeqNum(self):
105 """Test that getObsInfoAndMetadataForSeqNum returns the correct types.
106 """
107 seqNum = self.seqNums[0]
108 obsInfo, md = self.report.getObsInfoAndMetadataForSeqNum(seqNum)
109 self.assertIsInstance(obsInfo, ObservationInfo)
110 self.assertIsInstance(md, dict)
111 return
113 def test_rebuild(self):
114 """Test that rebuild does nothing, as no data will be being added.
116 NB Do not call full=True on this, as it will double the length of the
117 tests and they're already extremely slow.
118 """
119 lenBefore = len(self.report.data)
120 self.report.rebuild()
121 self.assertEqual(len(self.report.data), lenBefore)
122 return
124 def test_getExposureMidpoint(self):
125 """Test the exposure midpoint calculation
126 """
127 midPoint = self.report.getExposureMidpoint(self.seqNums[0])
128 record = self.report.data[self.seqNums[0]]
129 self.assertGreater(midPoint, record['datetime_begin'].mjd)
130 self.assertLess(midPoint, record['datetime_end'].mjd)
131 return
133 def test_getTimeDeltas(self):
134 """Test the time delta calculation returns a dict.
135 """
136 dts = self.report.getTimeDeltas()
137 self.assertIsInstance(dts, dict)
138 return
140 def test_makeStarColorAndMarkerMap(self):
141 """Test the color map maker returns a dict of ColorAndMarker objects.
142 """
143 cMap = self.report.makeStarColorAndMarkerMap(self.report.stars)
144 self.assertEqual(len(cMap), len(self.report.stars))
145 self.assertIsInstance(cMap, dict)
146 values = list(cMap.values())
147 self.assertTrue(all(isinstance(value, ColorAndMarker) for value in values))
148 return
150 def test_printObsTable(self):
151 """Test that a the printObsTable() method prints out the correct
152 number of lines.
153 """
154 with mock.patch('sys.stdout') as fake_stdout:
155 self.report.printObsTable()
157 # newline for each row plus header line, plus the line with dashes
158 self.assertEqual(len(fake_stdout.mock_calls), 2*(self.nImages + 2))
160 def test_plotPerObjectAirMass(self):
161 """Test that a the per-object airmass plots runs.
162 """
163 # We assume matplotlib is making plots, so just check that these
164 # don't crash.
166 # Default plotting:
167 self.report.plotPerObjectAirMass()
168 # plot with only one object as a str not a list of str
169 self.report.plotPerObjectAirMass(objects=self.report.stars[0])
170 # plot with first two objects as a list
171 self.report.plotPerObjectAirMass(objects=self.report.stars[0:2])
172 # flip y axis option
173 self.report.plotPerObjectAirMass(airmassOneAtTop=True)
174 # flip and select stars
175 self.report.plotPerObjectAirMass(objects=self.report.stars[0], airmassOneAtTop=True) # both
177 def test_makeAltAzCoveragePlot(self):
178 """Test that a the polar coverage plotting code runs.
179 """
180 # We assume matplotlib is making plots, so just check that these
181 # don't crash.
183 # test the default case
184 self.report.makeAltAzCoveragePlot()
185 # plot with only one object as a str not a list of str
186 self.report.makeAltAzCoveragePlot(objects=self.report.stars[0])
187 # plot with first two objects as a list
188 self.report.makeAltAzCoveragePlot(objects=self.report.stars[0:2])
189 # test turning lines off
190 self.report.makeAltAzCoveragePlot(objects=self.report.stars[0:2], withLines=False)
192 def test_calcShutterTimes(self):
193 timings = self.report.calcShutterTimes()
194 efficiency = 100*(timings['scienceTimeTotal']/timings['nightLength'])
195 self.assertGreater(efficiency, 0)
196 self.assertLessEqual(efficiency, 100)
198 def test_doesNotRaise(self):
199 """Tests for things which are hard to test, so just make sure they run.
200 """
201 self.report.printShutterTimes()
202 for sample, includeRaw in itertools.product((True, False), (True, False)):
203 self.report.printAvailableKeys(sample=sample, includeRaw=includeRaw)
204 self.report.printObsTable()
205 for threshold, includeCalibs in itertools.product((0, 1, 10), (True, False)):
206 self.report.printObsGaps(threshold=threshold, includeCalibs=includeCalibs)
208 def test_internals(self):
209 startNum = self.report.getObservingStartSeqNum()
210 self.assertIsInstance(startNum, int)
211 self.assertGreater(startNum, 0) # the day starts at 1, so zero would be an error of some sort
213 starsFromGetter = self.report.getObservedObjects()
214 self.assertIsInstance(starsFromGetter, list)
215 self.assertSetEqual(set(starsFromGetter), set(self.report.stars))
217 starsFromGetter = self.report.getObservedObjects(ignoreTileNum=True)
218 self.assertLessEqual(len(starsFromGetter), len(self.report.stars))
220 # check the internal color map has the right number of items
221 self.assertEqual(len(self.report.cMap), len(starsFromGetter))
224class TestMemory(lsst.utils.tests.MemoryTestCase):
225 pass
228def setup_module(module):
229 lsst.utils.tests.init()
232if __name__ == "__main__": 232 ↛ 233line 232 didn't jump to line 233, because the condition on line 232 was never true
233 lsst.utils.tests.init()
234 unittest.main()