Coverage for python/lsst/obs/lsst/testHelper.py: 34%

78 statements  

« prev     ^ index     » next       coverage.py v6.4, created at 2022-05-24 03:42 -0700

1# This file is part of obs_lsst. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://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# 

22 

23"""Test support classes for obs_lsst""" 

24__all__ = ("ObsLsstButlerTests", "ObsLsstObsBaseOverrides") 

25 

26import os.path 

27import unittest 

28import lsst.utils.tests 

29import lsst.obs.base.tests 

30from lsst.utils import getPackageDir 

31 

32# Define the data location relative to this package 

33DATAROOT = os.path.join(getPackageDir("obs_lsst"), "data", "input") 

34 

35 

36class ObsLsstButlerTests(lsst.utils.tests.TestCase): 

37 """Base class shared by all tests of the butler and mapper. 

38 

39 This class can not inherit from `~lsst.obs.base.tests.ObsTests` since 

40 that will trigger tests in this class directly that will fail. 

41 

42 This class defines a butler and a mapper for each test subclass. 

43 They are stored in the ``_mapper`` and ``_butler`` class attributes 

44 to distinguish them from the ``mapper`` and ``butler`` instance 

45 attributes used by `~lsst.obs.base.tests.ObsTests`. 

46 """ 

47 

48 instrumentDir = "TBD" # Override in subclass 

49 """Name of instrument directory within data/input.""" 

50 

51 _mapper = None 

52 _butler = None 

53 

54 @classmethod 

55 def tearDownClass(cls): 

56 del cls._mapper 

57 del cls._butler 

58 

59 @classmethod 

60 def setUpClass(cls): 

61 cls.data_dir = os.path.normpath(os.path.join(DATAROOT, cls.instrumentDir)) 

62 # Protection against the base class values being used 

63 if not os.path.exists(cls.data_dir): 

64 raise unittest.SkipTest(f"Data directory {cls.data_dir} does not exist.") 

65 

66 cls._butler = lsst.daf.persistence.Butler(root=cls.data_dir) 

67 mapper_class = cls._butler.getMapperClass(root=cls.data_dir) 

68 cls._mapper = mapper_class(root=cls.data_dir) 

69 

70 

71class ObsLsstObsBaseOverrides(lsst.obs.base.tests.ObsTests): 

72 """Specialist butler tests for obs_lsst.""" 

73 

74 def testRawVisitInfo(self): 

75 visitInfo = self.butler.get("raw_visitInfo", self.dataIds["raw"]) 

76 self.assertIsInstance(visitInfo, lsst.afw.image.VisitInfo) 

77 # We should always get a valid date and exposure time 

78 self.assertIsInstance(visitInfo.getDate(), lsst.daf.base.DateTime) 

79 self.assertTrue(visitInfo.getDate().isValid()) 

80 self.assertEqual(visitInfo.getExposureTime(), self.butler_get_data.exptimes["raw"]) 

81 

82 def testRawFilename(self): 

83 filepath = self.butler.get("raw_filename", self.dataIds["raw"])[0] 

84 if "[" in filepath: # Remove trailing HDU specifier 

85 filepath = filepath[:filepath.find("[")] 

86 filename = os.path.split(filepath)[1] 

87 self.assertEqual(filename, self.mapper_data.raw_filename) 

88 

89 def testQueryRawAmp(self): 

90 # Base the tests on the first reference metadata query 

91 formats = self.mapper_data.query_format.copy() 

92 query, expect = self.mapper_data.queryMetadata[0] 

93 result = self.mapper.queryMetadata("raw_amp", formats, query) 

94 self.assertEqual(sorted(result), sorted(expect)) 

95 

96 # Listing all channels -- we expect the result to be the expected 

97 # result copied for each channel 

98 formats = formats.copy() 

99 formats.insert(0, "channel") 

100 expectall = [(i+1, *expect[0]) for i in range(16)] 

101 result = self.mapper.queryMetadata("raw_amp", formats, query) 

102 self.assertEqual(sorted(result), sorted(expectall)) 

103 

104 # Now fix a channel 

105 query = query.copy() 

106 query["channel"] = 3 

107 result = self.mapper.queryMetadata("raw_amp", formats, query) 

108 expect = [(query["channel"], *expect[0])] 

109 self.assertEqual(sorted(result), sorted(expect)) 

110 

111 # Fix a channel out of range 

112 query["channel"] = 20 

113 with self.assertRaises(ValueError): 

114 self.mapper.queryMetadata("raw_amp", formats, query) 

115 

116 def _testCoaddId(self, idName): 

117 coaddId = self.butler.get(idName, dataId={"tract": 9813, "patch": "3,4", 

118 "filter": self.butler_get_data.filters["raw"]}) 

119 self.assertIsInstance(coaddId, int) 

120 

121 # Check failure modes 

122 with self.assertRaises(RuntimeError): 

123 self.butler.get(idName, dataId={"tract": 9813, "patch": "-3,4"}) 

124 with self.assertRaises(RuntimeError): 

125 self.butler.get(idName, dataId={"tract": -9813, "patch": "3,4"}) 

126 with self.assertRaises(RuntimeError): 

127 self.butler.get(idName, dataId={"tract": 2**self.mapper._nbit_tract+1, "patch": "3,4"}) 

128 with self.assertRaises(RuntimeError): 

129 self.butler.get(idName, dataId={"tract": 2, "patch": f"3,{2**self.mapper._nbit_patch+1}"}) 

130 

131 def testDeepCoaddId(self): 

132 self._testCoaddId("deepCoaddId") 

133 

134 def testDcrCoaddId(self): 

135 self._testCoaddId("dcrCoaddId") 

136 

137 def testDeepMergedCoaddId(self): 

138 self._testCoaddId("deepMergedCoaddId") 

139 

140 def testDcrMergedCoaddId(self): 

141 self._testCoaddId("dcrMergedCoaddId") 

142 

143 def testCcdExposureIdBits(self): 

144 """Check that we have enough bits for the exposure ID""" 

145 bits = self.butler.get('ccdExposureId_bits') 

146 ccdExposureId = self.butler_get_data.exposureIds["raw"] 

147 self.assertLessEqual(ccdExposureId.bit_length(), bits, 

148 f"Can detector_exposure_id {ccdExposureId} fit in {bits} bits")