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 

29import lsst.ctrl.mpexec.cmdLineParser as parser_mod 

30 

31 

32class _Error(Exception): 

33 pass 

34 

35 

36class _NoExitParser(ArgumentParser): 

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

38 """ 

39 

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

41 pass 

42 

43 def error(self, message): 

44 raise _Error(message) 

45 

46 

47class CmdLineParserTestCase(unittest.TestCase): 

48 """A test case for parser module 

49 """ 

50 

51 def setUp(self): 

52 pass 

53 

54 def tearDown(self): 

55 pass 

56 

57 def testPipelineAction(self): 

58 """Test for a _PipelineAction and _pipe_action 

59 """ 

60 

61 parser = _NoExitParser() 

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

63 type=parser_mod._ACTION_ADD_TASK) 

64 

65 PipelineAction = parser_mod._PipelineAction 

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

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

68 

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

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

71 

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

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

74 

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

76 with self.assertRaises(_Error): 

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

78 

79 # Literal but of the wrong type 

80 with self.assertRaises(_Error): 

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

82 

83 # dictionary but not all strings 

84 with self.assertRaises(_Error): 

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

86 

87 def testInputCollectionAction(self): 

88 """Test for a _InputCollectionAction 

89 """ 

90 

91 parser = _NoExitParser() 

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

93 default={}) 

94 

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

96 self.assertEqual(args.input, {}) 

97 

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

99 self.assertEqual(args.input, {"": ["coll"]}) 

100 

101 # collection can appear twice 

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

103 self.assertEqual(args.input, {"": ["coll", "coll"]}) 

104 

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

106 self.assertEqual(args.input, {"": ["coll1", "coll2", "coll3"]}) 

107 

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

109 self.assertEqual(args.input, {"": ["coll1", "coll2", "coll3"]}) 

110 

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

112 self.assertEqual(args.input, {"": ["coll1", "coll2", "coll3"]}) 

113 

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

115 self.assertEqual(args.input, {"": ["coll1", "coll2", "coll3"]}) 

116 

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

118 self.assertEqual(args.input, {"ds": ["coll"]}) 

119 

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

121 self.assertEqual(args.input, {"ds1": ["coll1"], 

122 "ds2": ["coll2", "coll3"]}) 

123 

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

125 self.assertEqual(args.input, {"": ["coll1", "coll2", "coll3"], 

126 "ds1": ["coll1"], 

127 "ds2": ["coll2", "coll3"]}) 

128 

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

130 self.assertEqual(args.input, {"": ["coll1", "coll2", "coll3"], 

131 "ds1": ["coll1"], 

132 "ds2": ["coll2", "coll3"]}) 

133 

134 # use non-empty default 

135 parser = _NoExitParser() 

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

137 default={"": ["coll"]}) 

138 

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

140 self.assertEqual(args.input, {"": ["coll"]}) 

141 

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

143 self.assertEqual(args.input, {"": ["coll", "coll"]}) 

144 

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

146 self.assertEqual(args.input, {"": ["coll", "coll1", "coll2", "coll3"]}) 

147 

148 def testOutputCollectionType(self): 

149 """Test for a _outputCollectionType 

150 """ 

151 

152 parser = _NoExitParser() 

153 parser.add_argument("-o", dest="output", type=parser_mod._outputCollectionType, 

154 default={}) 

155 

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

157 self.assertEqual(args.output, {}) 

158 

159 args = parser.parse_args("-o coll".split()) 

160 self.assertEqual(args.output, {"": "coll"}) 

161 

162 with self.assertRaises(_Error): 

163 args = parser.parse_args("-o coll1,coll2,coll3".split()) 

164 

165 args = parser.parse_args("-o ds:coll".split()) 

166 self.assertEqual(args.output, {"ds": "coll"}) 

167 

168 args = parser.parse_args("-o ds1:coll1,ds2:coll2,ds3:coll3".split()) 

169 self.assertEqual(args.output, {"ds1": "coll1", 

170 "ds2": "coll2", 

171 "ds3": "coll3"}) 

172 

173 args = parser.parse_args("-o coll1,ds2:coll2".split()) 

174 self.assertEqual(args.output, {"": "coll1", 

175 "ds2": "coll2"}) 

176 

177 with self.assertRaises(_Error): 

178 args = parser.parse_args("-o coll1,ds2:coll2,coll3".split()) 

179 

180 def testCmdLineParser(self): 

181 """Test for parser_mod.CmdLineParser 

182 """ 

183 parser = parser_mod.makeParser(parser_class=_NoExitParser) 

184 

185 # this should result in error 

186 self.assertRaises(_Error, parser.parse_args) 

187 

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

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

190 

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

192 args = parser.parse_args( 

193 """ 

194 build -t cmd 

195 """.split()) 

196 build_options = """pipeline pipeline_actions order_pipeline 

197 save_pipeline pipeline_dot show""".split() 

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

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

200 

201 args = parser.parse_args( 

202 """ 

203 qgraph -t cmd 

204 """.split()) 

205 qgraph_options = build_options + """qgraph data_query butler_config 

206 input output skip_existing clobber_output 

207 save_qgraph qgraph_dot save_single_quanta""".split() 

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

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

210 

211 args = parser.parse_args( 

212 """ 

213 run -t taskname 

214 """.split()) 

215 run_options = qgraph_options + """register_dataset_types skip_init_writes 

216 init_only clobberConfig clobberVersions noBackupConfig noVersions 

217 processes profile timeout doraise""".split() 

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

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

220 

221 def testCmdLineTasks(self): 

222 

223 parser = parser_mod.makeParser(parser_class=_NoExitParser) 

224 

225 PipelineAction = parser_mod._PipelineAction 

226 

227 # default options 

228 args = parser.parse_args( 

229 """ 

230 run -t taskname 

231 """.split()) 

232 self.assertFalse(args.clobberConfig) 

233 self.assertFalse(args.clobberVersions) 

234 self.assertFalse(args.enableLsstDebug) 

235 self.assertFalse(args.doraise) 

236 self.assertEqual(args.input, {}) 

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

238 self.assertFalse(args.longlog) 

239 self.assertFalse(args.noBackupConfig) 

240 self.assertFalse(args.noVersions) 

241 self.assertEqual(args.output, {}) 

242 self.assertEqual(args.processes, 1) 

243 self.assertIsNone(args.profile) 

244 self.assertIsNone(args.timeout) 

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

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

247 self.assertIsNotNone(args.subparser) 

248 self.assertIsNone(args.pipeline) 

249 

250 # bunch of random options 

251 args = parser.parse_args( 

252 """ 

253 run 

254 --clobber-config 

255 --clobber-versions 

256 --debug 

257 --doraise 

258 --input inputColl 

259 --loglevel DEBUG -L component=trace 

260 --longlog 

261 --no-backup-config 

262 --no-versions 

263 --output outputColl 

264 -j 66 

265 --profile profile.out 

266 --timeout 10.10 

267 -t taskname:label 

268 --show config 

269 --show config=Task.* 

270 -c label:a=b 

271 -C label:filename1 

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

273 -C label:filename2 -C label:filename3 

274 --skip-existing 

275 """.split()) 

276 self.assertTrue(args.clobberConfig) 

277 self.assertTrue(args.clobberVersions) 

278 self.assertTrue(args.enableLsstDebug) 

279 self.assertTrue(args.doraise) 

280 self.assertEqual(args.input, {"": ["inputColl"]}) 

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

282 self.assertTrue(args.longlog) 

283 self.assertTrue(args.noBackupConfig) 

284 self.assertTrue(args.noVersions) 

285 self.assertEqual(args.output, {"": "outputColl"}) 

286 self.assertEqual(args.processes, 66) 

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

288 self.assertEqual(args.timeout, 10.10) 

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

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

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

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

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

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

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

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

297 self.assertIsNone(args.pipeline) 

298 self.assertIsNone(args.qgraph) 

299 self.assertFalse(args.order_pipeline) 

300 self.assertIsNone(args.save_pipeline) 

301 self.assertIsNone(args.save_qgraph) 

302 self.assertIsNone(args.pipeline_dot) 

303 self.assertIsNone(args.qgraph_dot) 

304 self.assertTrue(args.skip_existing) 

305 

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

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

308 args = parser.parse_args( 

309 """ 

310 run 

311 -p pipeline.yaml 

312 -g qgraph.pickle 

313 -t task1 

314 -t task2:label2 

315 -t task3 

316 -t task4 

317 --show config 

318 -c task1:a=b 

319 -C task1:filename1 

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

321 -C task3:filename2 -C task3:filename3 

322 --show config=Task.* 

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

324 --order-pipeline 

325 --save-pipeline=newpipe.yaml 

326 --save-qgraph=newqgraph.pickle 

327 --pipeline-dot pipe.dot 

328 --qgraph-dot qgraph.dot 

329 """.split()) 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

345 self.assertTrue(args.order_pipeline) 

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

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

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

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

350 

351 # check that exclusive options generate error 

352 with self.assertRaises(_Error): 

353 parser.parse_args("qgraph -p pipeline.yaml --skip-existing --clobber-output".split()) 

354 

355 def testCmdLinePipeline(self): 

356 

357 parser = parser_mod.makeParser(parser_class=_NoExitParser) 

358 

359 PipelineAction = parser_mod._PipelineAction 

360 

361 args = parser.parse_args( 

362 """ 

363 run -p pipeline 

364 --show config 

365 --show config=Task.* 

366 """.split()) 

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

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

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

370 

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

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

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

374 

375 

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

377 pass 

378 

379 

380def setup_module(module): 

381 lsst.utils.tests.init() 

382 

383 

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

385 lsst.utils.tests.init() 

386 unittest.main()