Coverage for tests/test_task.py: 31%

Shortcuts 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

152 statements  

1# 

2# LSST Data Management System 

3# Copyright 2008, 2009, 2010 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22import logging 

23import time 

24import unittest 

25import numbers 

26 

27import lsst.utils.tests 

28import lsst.daf.base as dafBase 

29import lsst.pex.config as pexConfig 

30import lsst.pipe.base as pipeBase 

31from lsst.utils.timer import timeMethod 

32 

33 

34class AddConfig(pexConfig.Config): 

35 addend = pexConfig.Field(doc="amount to add", dtype=float, default=3.1) 

36 

37 

38class AddTask(pipeBase.Task): 

39 ConfigClass = AddConfig 

40 

41 @timeMethod 

42 def run(self, val): 

43 self.metadata.add("add", self.config.addend) 

44 return pipeBase.Struct( 

45 val=val + self.config.addend, 

46 ) 

47 

48 

49class MultConfig(pexConfig.Config): 

50 multiplicand = pexConfig.Field(doc="amount by which to multiply", dtype=float, default=2.5) 

51 

52 

53class MultTask(pipeBase.Task): 

54 ConfigClass = MultConfig 

55 

56 @timeMethod 

57 def run(self, val): 

58 self.metadata.add("mult", self.config.multiplicand) 

59 return pipeBase.Struct( 

60 val=val * self.config.multiplicand, 

61 ) 

62 

63 

64# prove that registry fields can also be used to hold subtasks 

65# by using a registry to hold MultTask 

66multRegistry = pexConfig.makeRegistry("Registry for Mult-like tasks") 

67multRegistry.register("stdMult", MultTask) 

68 

69 

70class AddMultConfig(pexConfig.Config): 

71 add = AddTask.makeField("add task") 

72 mult = multRegistry.makeField("mult task", default="stdMult") 

73 

74 

75class AddMultTask(pipeBase.Task): 

76 ConfigClass = AddMultConfig 

77 _DefaultName = "addMult" 

78 

79 """First add, then multiply""" 

80 

81 def __init__(self, **keyArgs): 

82 pipeBase.Task.__init__(self, **keyArgs) 

83 self.makeSubtask("add") 

84 self.makeSubtask("mult") 

85 

86 @timeMethod 

87 def run(self, val): 

88 with self.timer("context"): 

89 addRet = self.add.run(val) 

90 multRet = self.mult.run(addRet.val) 

91 self.metadata.add("addmult", multRet.val) 

92 return pipeBase.Struct( 

93 val=multRet.val, 

94 ) 

95 

96 @timeMethod 

97 def failDec(self): 

98 """A method that fails with a decorator 

99 """ 

100 raise RuntimeError("failDec intentional error") 

101 

102 def failCtx(self): 

103 """A method that fails inside a context manager 

104 """ 

105 with self.timer("failCtx"): 

106 raise RuntimeError("failCtx intentional error") 

107 

108 

109class AddTwiceTask(AddTask): 

110 """Variant of AddTask that adds twice the addend""" 

111 

112 def run(self, val): 

113 addend = self.config.addend 

114 return pipeBase.Struct(val=val + (2 * addend)) 

115 

116 

117class TaskTestCase(unittest.TestCase): 

118 """A test case for Task 

119 """ 

120 

121 def setUp(self): 

122 self.valDict = dict() 

123 

124 def tearDown(self): 

125 self.valDict = None 

126 

127 def testBasics(self): 

128 """Test basic construction and use of a task 

129 """ 

130 for addend in (1.1, -3.5): 

131 for multiplicand in (0.9, -45.0): 

132 config = AddMultTask.ConfigClass() 

133 config.add.addend = addend 

134 config.mult["stdMult"].multiplicand = multiplicand 

135 # make sure both ways of accessing the registry work and give 

136 # the same result 

137 self.assertEqual(config.mult.active.multiplicand, multiplicand) 

138 addMultTask = AddMultTask(config=config) 

139 for val in (-1.0, 0.0, 17.5): 

140 ret = addMultTask.run(val=val) 

141 self.assertAlmostEqual(ret.val, (val + addend) * multiplicand) 

142 

143 def testNames(self): 

144 """Test getName() and getFullName() 

145 """ 

146 addMultTask = AddMultTask() 

147 self.assertEqual(addMultTask.getName(), "addMult") 

148 self.assertEqual(addMultTask.add.getName(), "add") 

149 self.assertEqual(addMultTask.mult.getName(), "mult") 

150 

151 self.assertEqual(addMultTask._name, "addMult") 

152 self.assertEqual(addMultTask.add._name, "add") 

153 self.assertEqual(addMultTask.mult._name, "mult") 

154 

155 self.assertEqual(addMultTask.getFullName(), "addMult") 

156 self.assertEqual(addMultTask.add.getFullName(), "addMult.add") 

157 self.assertEqual(addMultTask.mult.getFullName(), "addMult.mult") 

158 

159 self.assertEqual(addMultTask._fullName, "addMult") 

160 self.assertEqual(addMultTask.add._fullName, "addMult.add") 

161 self.assertEqual(addMultTask.mult._fullName, "addMult.mult") 

162 

163 def testLog(self): 

164 """Test the Task's logger 

165 """ 

166 addMultTask = AddMultTask() 

167 self.assertEqual(addMultTask.log.name, "addMult") 

168 self.assertEqual(addMultTask.add.log.name, "addMult.add") 

169 

170 log = logging.getLogger("tester") 

171 addMultTask = AddMultTask(log=log) 

172 self.assertEqual(addMultTask.log.name, "tester.addMult") 

173 self.assertEqual(addMultTask.add.log.name, "tester.addMult.add") 

174 

175 def testGetFullMetadata(self): 

176 """Test getFullMetadata() 

177 """ 

178 addMultTask = AddMultTask() 

179 fullMetadata = addMultTask.getFullMetadata() 

180 self.assertIsInstance(fullMetadata.getPropertySet("addMult"), dafBase.PropertySet) 

181 self.assertIsInstance(fullMetadata.getPropertySet("addMult:add"), dafBase.PropertySet) 

182 self.assertIsInstance(fullMetadata.getPropertySet("addMult:mult"), dafBase.PropertySet) 

183 

184 def testEmptyMetadata(self): 

185 task = AddMultTask() 

186 task.run(val=1.2345) 

187 task.emptyMetadata() 

188 fullMetadata = task.getFullMetadata() 

189 self.assertEqual(fullMetadata.getPropertySet("addMult").nameCount(), 0) 

190 self.assertEqual(fullMetadata.getPropertySet("addMult:add").nameCount(), 0) 

191 self.assertEqual(fullMetadata.getPropertySet("addMult:mult").nameCount(), 0) 

192 

193 def testReplace(self): 

194 """Test replacing one subtask with another 

195 """ 

196 for addend in (1.1, -3.5): 

197 for multiplicand in (0.9, -45.0): 

198 config = AddMultTask.ConfigClass() 

199 config.add.retarget(AddTwiceTask) 

200 config.add.addend = addend 

201 config.mult["stdMult"].multiplicand = multiplicand 

202 addMultTask = AddMultTask(config=config) 

203 for val in (-1.0, 0.0, 17.5): 

204 ret = addMultTask.run(val=val) 

205 self.assertAlmostEqual(ret.val, (val + (2 * addend)) * multiplicand) 

206 

207 def testFail(self): 

208 """Test timers when the code they are timing fails 

209 """ 

210 addMultTask = AddMultTask() 

211 try: 

212 addMultTask.failDec() 

213 self.fail("Expected RuntimeError") 

214 except RuntimeError: 

215 self.assertTrue(addMultTask.metadata.exists("failDecEndCpuTime")) 

216 try: 

217 addMultTask.failCtx() 

218 self.fail("Expected RuntimeError") 

219 except RuntimeError: 

220 self.assertTrue(addMultTask.metadata.exists("failCtxEndCpuTime")) 

221 

222 def testTimeMethod(self): 

223 """Test that the timer is adding the right metadata 

224 """ 

225 addMultTask = AddMultTask() 

226 addMultTask.run(val=1.1) 

227 # Check existence and type 

228 for key, keyType in (("Utc", str), 

229 ("CpuTime", float), 

230 ("UserTime", float), 

231 ("SystemTime", float), 

232 ("MaxResidentSetSize", numbers.Integral), 

233 ("MinorPageFaults", numbers.Integral), 

234 ("MajorPageFaults", numbers.Integral), 

235 ("BlockInputs", numbers.Integral), 

236 ("BlockOutputs", numbers.Integral), 

237 ("VoluntaryContextSwitches", numbers.Integral), 

238 ("InvoluntaryContextSwitches", numbers.Integral), 

239 ): 

240 for when in ("Start", "End"): 

241 for method in ("run", "context"): 

242 name = method + when + key 

243 self.assertIn(name, addMultTask.metadata.names(), 

244 name + " is missing from task metadata") 

245 self.assertIsInstance(addMultTask.metadata.getScalar(name), keyType, 

246 f"{name} is not of the right type " 

247 f"({keyType} vs {type(addMultTask.metadata.getScalar(name))})") 

248 # Some basic sanity checks 

249 currCpuTime = time.process_time() 

250 self.assertLessEqual( 

251 addMultTask.metadata.getScalar("runStartCpuTime"), 

252 addMultTask.metadata.getScalar("runEndCpuTime"), 

253 ) 

254 self.assertLessEqual(addMultTask.metadata.getScalar("runEndCpuTime"), currCpuTime) 

255 self.assertLessEqual( 

256 addMultTask.metadata.getScalar("contextStartCpuTime"), 

257 addMultTask.metadata.getScalar("contextEndCpuTime"), 

258 ) 

259 self.assertLessEqual(addMultTask.metadata.getScalar("contextEndCpuTime"), currCpuTime) 

260 self.assertLessEqual( 

261 addMultTask.add.metadata.getScalar("runStartCpuTime"), 

262 addMultTask.metadata.getScalar("runEndCpuTime"), 

263 ) 

264 self.assertLessEqual(addMultTask.add.metadata.getScalar("runEndCpuTime"), currCpuTime) 

265 

266 

267class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

268 pass 

269 

270 

271def setup_module(module): 

272 lsst.utils.tests.init() 

273 

274 

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

276 lsst.utils.tests.init() 

277 unittest.main()