Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of verify. 

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/>. 

21 

22__all__ = ["MetadataMetricTestCase", "ApdbMetricTestCase"] 

23 

24import abc 

25 

26import unittest.mock 

27from unittest.mock import patch 

28 

29import lsst.utils.tests 

30from lsst.daf.base import PropertySet 

31from lsst.dax.apdb import ApdbConfig 

32 

33import lsst.verify.gen2tasks.testUtils as gen2Utils 

34from lsst.verify.tasks import MetricComputationError 

35 

36 

37class MetricTaskTestCase(lsst.utils.tests.TestCase, metaclass=abc.ABCMeta): 

38 """Unit test base class for tests of `tasks.MetricTask`. 

39 

40 This class provides tests of the generic ``MetricTask`` API. Subclasses 

41 must override `taskFactory`, and may add extra tests for class-specific 

42 functionality. If subclasses override `setUp`, they must call 

43 `MetricTaskTestCase.setUp`. 

44 """ 

45 @classmethod 

46 @abc.abstractmethod 

47 def makeTask(cls): 

48 """Construct the task to be tested. 

49 

50 This overridable method will be called during test setup. 

51 

52 Returns 

53 ------- 

54 task : `lsst.verify.tasks.MetricTask` 

55 A new MetricTask object to test. 

56 """ 

57 

58 task = None 

59 """The ``MetricTask`` being tested by this object 

60 (`tasks.MetricTask`). 

61 

62 This attribute is initialized automatically. 

63 """ 

64 

65 taskClass = None 

66 """The type of `task` (`tasks.MetricTask`-type). 

67 

68 This attribute is initialized automatically. 

69 """ 

70 

71 def setUp(self): 

72 """Setup common to all MetricTask tests. 

73 

74 Notes 

75 ----- 

76 This implementation calls `taskFactory`, then initializes `task` 

77 and `taskClass`. 

78 """ 

79 self.task = self.makeTask() 

80 self.taskClass = type(self.task) 

81 

82 def testOutputDatasetName(self): 

83 config = self.task.config 

84 connections = config.connections.ConnectionsClass(config=config) 

85 dataset = connections.measurement.name 

86 

87 self.assertTrue(dataset.startswith("metricvalue_")) 

88 self.assertNotIn(".", dataset) 

89 

90 self.assertIn(config.connections.package, dataset) 

91 self.assertIn(config.connections.metric, dataset) 

92 

93 def testConfigValidation(self): 

94 config = self.task.config 

95 config.connections.metric = "verify.DummyMetric" 

96 with self.assertRaises(ValueError): 

97 config.validate() 

98 

99 

100class MetadataMetricTestCase(gen2Utils.MetricTaskTestCase, MetricTaskTestCase): 

101 """Unit test base class for tests of `MetadataMetricTask`. 

102 

103 Notes 

104 ----- 

105 Subclasses must override 

106 `~lsst.verify.gen2tasks.MetricTaskTestCase.makeTask` for the concrete task 

107 being tested. 

108 """ 

109 

110 @staticmethod 

111 def _takesScalarMetadata(task): 

112 return task.areInputDatasetsScalar(task.config)['metadata'] 

113 

114 def testValidRun(self): 

115 """Test how run delegates to the abstract methods. 

116 """ 

117 mockKey = "unitTestKey" 

118 with patch.object(self.task, "getInputMetadataKeys", 

119 return_value={"unused": mockKey}), \ 

120 patch.object(self.task, "makeMeasurement") as mockWorkhorse: 

121 if self._takesScalarMetadata(self.task): 

122 metadata1 = PropertySet() 

123 metadata1[mockKey] = 42 

124 

125 self.task.run(metadata1) 

126 mockWorkhorse.assert_called_once_with({"unused": 42}) 

127 mockWorkhorse.reset_mock() 

128 self.task.run(None) 

129 mockWorkhorse.assert_called_once_with({"unused": None}) 

130 else: 

131 metadata1 = PropertySet() 

132 metadata1[mockKey] = 42 

133 metadata2 = PropertySet() 

134 metadata2[mockKey] = "Sphere" 

135 self.task.run([metadata1, None, metadata2]) 

136 mockWorkhorse.assert_called_once_with( 

137 [{"unused": value} for value in [42, None, "Sphere"]]) 

138 

139 def testAmbiguousRun(self): 

140 mockKey = "unitTestKey" 

141 with patch.object(self.task, "getInputMetadataKeys", 

142 return_value={"unused": mockKey}): 

143 metadata = PropertySet() 

144 metadata[mockKey + "1"] = 42 

145 metadata[mockKey + "2"] = "Sphere" 

146 with self.assertRaises(MetricComputationError): 

147 if self._takesScalarMetadata(self.task): 

148 self.task.run(metadata) 

149 else: 

150 self.task.run([metadata]) 

151 

152 def testPassThroughRun(self): 

153 with patch.object(self.task, "makeMeasurement", 

154 side_effect=MetricComputationError): 

155 with self.assertRaises(MetricComputationError): 

156 if self._takesScalarMetadata(self.task): 

157 self.task.run(None) 

158 else: 

159 self.task.run([None]) 

160 

161 

162class ApdbMetricTestCase(gen2Utils.MetricTaskTestCase, MetricTaskTestCase): 

163 """Unit test base class for tests of `ApdbMetricTask`. 

164 

165 Notes 

166 ----- 

167 Subclasses must override 

168 `~lsst.verify.gen2tasks.MetricTaskTestCase.makeTask` for the concrete task 

169 being tested. Subclasses that use a custom DbLoader should also 

170 override `makeDbInfo`. 

171 """ 

172 

173 @classmethod 

174 def makeDbInfo(cls): 

175 """Return an object that can be passed as input to an `ApdbMetricTask`. 

176 

177 This method is intended for generic tests that simply need to call 

178 ``run`` on some valid input. If a test depends on specific input, it 

179 should create that input directly. 

180 

181 The default implementation creates a `~lsst.pex.config.Config` that 

182 will be accepted by `~lsst.verify.tasks.DirectApdbLoader`. Test suites 

183 that use a different loader should override this method. 

184 """ 

185 return ApdbConfig() 

186 

187 def testValidRun(self): 

188 info = self.makeDbInfo() 

189 with patch.object(self.task, "makeMeasurement") as mockWorkhorse: 

190 self.task.run([info]) 

191 mockWorkhorse.assert_called_once() 

192 

193 def testDataIdRun(self): 

194 info = self.makeDbInfo() 

195 with patch.object(self.task, "makeMeasurement") as mockWorkhorse: 

196 dataId = {'visit': 42} 

197 self.task.run([info], outputDataId=dataId) 

198 mockWorkhorse.assert_called_once_with( 

199 unittest.mock.ANY, {'visit': 42}) 

200 

201 def testPassThroughRun(self): 

202 with patch.object(self.task, "makeMeasurement", 

203 side_effect=MetricComputationError): 

204 info = self.makeDbInfo() 

205 with self.assertRaises(MetricComputationError): 

206 self.task.run([info])