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

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"""Simple unit test for parser module. 

23""" 

24 

25from argparse import ArgumentParser 

26import unittest 

27 

28import lsst.utils.tests 

29from lsst.daf.butler import CollectionSearch 

30import lsst.ctrl.mpexec.cmdLineParser as parser_mod 

31 

32 

33class _Error(Exception): 

34 pass 

35 

36 

37class _NoExitParser(ArgumentParser): 

38 """Special parser subclass which does not exit on errors or help. 

39 """ 

40 

41 def exit(self, status=0, message=None): 

42 pass 

43 

44 def error(self, message): 

45 raise _Error(message) 

46 

47 

48class CmdLineParserTestCase(unittest.TestCase): 

49 """A test case for parser module 

50 """ 

51 

52 def setUp(self): 

53 pass 

54 

55 def tearDown(self): 

56 pass 

57 

58 def testPipelineAction(self): 

59 """Test for a _PipelineAction and _pipe_action 

60 """ 

61 

62 parser = _NoExitParser() 

63 parser.add_argument("-t", dest="pipeline_actions", action='append', 

64 type=parser_mod._ACTION_ADD_TASK) 

65 

66 PipelineAction = parser_mod._PipelineAction 

67 args = parser.parse_args("-t task".split()) 

68 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "task")]) 

69 

70 args = parser.parse_args("-t task:label".split()) 

71 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", "label", "task")]) 

72 

73 args = parser.parse_args("-t task".split()) 

74 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "task")]) 

75 

76 # something that is not syntaxically correct (not a literal) 

77 with self.assertRaises(_Error): 

78 parser.parse_args("-t task -s task:CannotEval".split()) 

79 

80 # Literal but of the wrong type 

81 with self.assertRaises(_Error): 

82 parser.parse_args("-t task -s task:[1,2]".split()) 

83 

84 # dictionary but not all strings 

85 with self.assertRaises(_Error): 

86 parser.parse_args("-t task -s task:{'x':1}".split()) 

87 

88 def testInputCollectionAction(self): 

89 """Test for a _InputCollectionAction 

90 """ 

91 

92 parser = _NoExitParser() 

93 parser.add_argument("-i", dest="input", action=parser_mod._InputCollectionAction, default=[]) 

94 

95 args = parser.parse_args("".split()) 

96 

97 def assertExpressionsEquivalent(a, b): 

98 """Test that input collection expressions are equivalent after 

99 being standardized by CollectionSearch.fromExpression. 

100 """ 

101 self.assertEqual(CollectionSearch.fromExpression(a), CollectionSearch.fromExpression(b)) 

102 

103 assertExpressionsEquivalent(args.input, []) 

104 

105 args = parser.parse_args("-i coll".split()) 

106 assertExpressionsEquivalent(args.input, ["coll"]) 

107 

108 # collection can appear twice 

109 args = parser.parse_args("-i coll,coll".split()) 

110 assertExpressionsEquivalent(args.input, ["coll"]) 

111 

112 args = parser.parse_args("-i coll1,coll2,coll3".split()) 

113 assertExpressionsEquivalent(args.input, ["coll1", "coll2", "coll3"]) 

114 

115 args = parser.parse_args("-i coll1 -i coll2 -i coll3".split()) 

116 assertExpressionsEquivalent(args.input, ["coll1", "coll2", "coll3"]) 

117 

118 args = parser.parse_args("-i coll1 -i coll2,coll3".split()) 

119 assertExpressionsEquivalent(args.input, ["coll1", "coll2", "coll3"]) 

120 

121 args = parser.parse_args("-i coll1 -i coll2 -i coll3".split()) 

122 assertExpressionsEquivalent(args.input, ["coll1", "coll2", "coll3"]) 

123 

124 args = parser.parse_args("-i ds:coll".split()) 

125 assertExpressionsEquivalent(args.input, [("coll", "ds")]) 

126 

127 args = parser.parse_args("-i ds1:coll1,ds2:coll2,ds2:coll3".split()) 

128 assertExpressionsEquivalent(args.input, [("coll1", "ds1"), ("coll2", "ds2"), ("coll3", "ds2")]) 

129 

130 args = parser.parse_args("-i coll1,coll2,ds1:coll1,ds2:coll2,coll3".split()) 

131 assertExpressionsEquivalent(args.input, ["coll1", "coll2", 

132 ("coll1", "ds1"), ("coll2", "ds2"), 

133 "coll3"]) 

134 

135 args = parser.parse_args("-i coll1 -i coll2 -i ds1:coll1 -i ds2:coll2 -i coll3".split()) 

136 assertExpressionsEquivalent(args.input, ["coll1", "coll2", 

137 ("coll1", "ds1"), ("coll2", "ds2"), 

138 "coll3"]) 

139 

140 # use non-empty default 

141 parser = _NoExitParser() 

142 parser.add_argument("-i", dest="input", action=parser_mod._InputCollectionAction, 

143 default=[("coll", ...)]) 

144 

145 args = parser.parse_args("".split()) 

146 assertExpressionsEquivalent(args.input, ["coll"]) 

147 

148 args = parser.parse_args("-i coll".split()) 

149 assertExpressionsEquivalent(args.input, ["coll"]) 

150 

151 args = parser.parse_args("-i coll1 -i coll2 -i coll3".split()) 

152 assertExpressionsEquivalent(args.input, ["coll", "coll1", "coll2", "coll3"]) 

153 

154 def testCmdLineParser(self): 

155 """Test for parser_mod.CmdLineParser 

156 """ 

157 parser = parser_mod.makeParser(parser_class=_NoExitParser) 

158 

159 # this should result in error 

160 self.assertRaises(_Error, parser.parse_args) 

161 

162 # know attributes to appear in parser output for all subcommands 

163 common_options = "loglevel longlog enableLsstDebug subcommand subparser".split() 

164 

165 # test for the set of options defined in each command 

166 args = parser.parse_args( 

167 """ 

168 build -t cmd 

169 """.split()) 

170 build_options = """pipeline pipeline_actions order_pipeline 

171 save_pipeline pipeline_dot show""".split() 

172 self.assertEqual(set(vars(args).keys()), set(common_options + build_options)) 

173 self.assertEqual(args.subcommand, 'build') 

174 

175 args = parser.parse_args( 

176 """ 

177 qgraph -t cmd 

178 """.split()) 

179 qgraph_options = build_options + """qgraph data_query butler_config 

180 input output skip_existing output_run extend_run 

181 replace_run prune_replaced 

182 save_qgraph qgraph_dot save_single_quanta""".split() 

183 self.assertEqual(set(vars(args).keys()), set(common_options + qgraph_options)) 

184 self.assertEqual(args.subcommand, 'qgraph') 

185 

186 args = parser.parse_args( 

187 """ 

188 run -t taskname 

189 """.split()) 

190 run_options = qgraph_options + """register_dataset_types skip_init_writes 

191 init_only processes profile timeout doraise graph_fixup 

192 no_versions""".split() 

193 self.assertEqual(set(vars(args).keys()), set(common_options + run_options)) 

194 self.assertEqual(args.subcommand, 'run') 

195 

196 def testCmdLineTasks(self): 

197 

198 parser = parser_mod.makeParser(parser_class=_NoExitParser) 

199 

200 PipelineAction = parser_mod._PipelineAction 

201 

202 # default options 

203 args = parser.parse_args( 

204 """ 

205 run -t taskname 

206 """.split()) 

207 self.assertFalse(args.enableLsstDebug) 

208 self.assertFalse(args.doraise) 

209 self.assertEqual(args.input, []) 

210 self.assertEqual(args.loglevel, []) 

211 self.assertFalse(args.longlog) 

212 self.assertEqual(args.output, None) 

213 self.assertEqual(args.output_run, None) 

214 self.assertEqual(args.extend_run, False) 

215 self.assertEqual(args.replace_run, False) 

216 self.assertEqual(args.processes, 1) 

217 self.assertIsNone(args.profile) 

218 self.assertIsNone(args.timeout) 

219 self.assertIsNone(args.graph_fixup) 

220 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "taskname")]) 

221 self.assertEqual(args.show, []) 

222 self.assertIsNotNone(args.subparser) 

223 self.assertIsNone(args.pipeline) 

224 

225 # bunch of random options 

226 args = parser.parse_args( 

227 """ 

228 run 

229 --debug 

230 --doraise 

231 --input inputColl 

232 --loglevel DEBUG -L component=trace 

233 --longlog 

234 --output outputColl 

235 -j 66 

236 --profile profile.out 

237 --timeout 10.10 

238 -t taskname:label 

239 --show config 

240 --show config=Task.* 

241 -c label:a=b 

242 -C label:filename1 

243 -c label:c=d -c label:e=f 

244 -C label:filename2 -C label:filename3 

245 --skip-existing 

246 """.split()) 

247 self.assertTrue(args.enableLsstDebug) 

248 self.assertTrue(args.doraise) 

249 self.assertEqual(args.input, [("inputColl", ...)]) 

250 self.assertEqual(args.loglevel, [(None, 'DEBUG'), ('component', 'TRACE')]) 

251 self.assertTrue(args.longlog) 

252 self.assertEqual(args.output, "outputColl") 

253 self.assertEqual(args.processes, 66) 

254 self.assertEqual(args.profile, 'profile.out') 

255 self.assertEqual(args.timeout, 10.10) 

256 self.assertIsNone(args.graph_fixup) 

257 self.assertEqual(args.show, ['config', 'config=Task.*']) 

258 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", "label", "taskname"), 

259 PipelineAction("config", "label", "a=b"), 

260 PipelineAction("configfile", "label", "filename1"), 

261 PipelineAction("config", "label", "c=d"), 

262 PipelineAction("config", "label", "e=f"), 

263 PipelineAction("configfile", "label", "filename2"), 

264 PipelineAction("configfile", "label", "filename3")]) 

265 self.assertIsNone(args.pipeline) 

266 self.assertIsNone(args.qgraph) 

267 self.assertFalse(args.order_pipeline) 

268 self.assertIsNone(args.save_pipeline) 

269 self.assertIsNone(args.save_qgraph) 

270 self.assertIsNone(args.pipeline_dot) 

271 self.assertIsNone(args.qgraph_dot) 

272 self.assertTrue(args.skip_existing) 

273 

274 # multiple tasks plus more options (-q should be exclusive with 

275 # some other options but we do not check it during parsing (yet)) 

276 args = parser.parse_args( 

277 """ 

278 run 

279 -p pipeline.yaml 

280 -g qgraph.pickle 

281 -t task1 

282 -t task2:label2 

283 -t task3 

284 -t task4 

285 --show config 

286 -c task1:a=b 

287 -C task1:filename1 

288 -c label2:c=d -c label2:e=f 

289 -C task3:filename2 -C task3:filename3 

290 --show config=Task.* 

291 -C task4:filename4 -c task4:x=y 

292 --order-pipeline 

293 --save-pipeline=newpipe.yaml 

294 --save-qgraph=newqgraph.pickle 

295 --pipeline-dot pipe.dot 

296 --qgraph-dot qgraph.dot 

297 --graph-fixup lsst.ctrl.mpexec.Fixup 

298 """.split()) 

299 self.assertEqual(args.show, ['config', 'config=Task.*']) 

300 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "task1"), 

301 PipelineAction("new_task", "label2", "task2"), 

302 PipelineAction("new_task", None, "task3"), 

303 PipelineAction("new_task", None, "task4"), 

304 PipelineAction("config", "task1", "a=b"), 

305 PipelineAction("configfile", "task1", "filename1"), 

306 PipelineAction("config", "label2", "c=d"), 

307 PipelineAction("config", "label2", "e=f"), 

308 PipelineAction("configfile", "task3", "filename2"), 

309 PipelineAction("configfile", "task3", "filename3"), 

310 PipelineAction("configfile", "task4", "filename4"), 

311 PipelineAction("config", "task4", "x=y")]) 

312 self.assertEqual(args.pipeline, "pipeline.yaml") 

313 self.assertEqual(args.qgraph, "qgraph.pickle") 

314 self.assertTrue(args.order_pipeline) 

315 self.assertEqual(args.save_pipeline, "newpipe.yaml") 

316 self.assertEqual(args.save_qgraph, "newqgraph.pickle") 

317 self.assertEqual(args.pipeline_dot, "pipe.dot") 

318 self.assertEqual(args.qgraph_dot, "qgraph.dot") 

319 self.assertEqual(args.graph_fixup, "lsst.ctrl.mpexec.Fixup") 

320 

321 def testCmdLinePipeline(self): 

322 

323 parser = parser_mod.makeParser(parser_class=_NoExitParser) 

324 

325 PipelineAction = parser_mod._PipelineAction 

326 

327 args = parser.parse_args( 

328 """ 

329 run -p pipeline 

330 --show config 

331 --show config=Task.* 

332 """.split()) 

333 self.assertEqual(args.show, ['config', 'config=Task.*']) 

334 self.assertEqual(args.pipeline, 'pipeline') 

335 self.assertEqual(args.pipeline_actions, []) 

336 

337 args = parser.parse_args("run -p pipeline -t task".split()) 

338 self.assertEqual(args.pipeline, 'pipeline') 

339 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "task")]) 

340 

341 

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

343 pass 

344 

345 

346def setup_module(module): 

347 lsst.utils.tests.init() 

348 

349 

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

351 lsst.utils.tests.init() 

352 unittest.main()