Coverage for tests/test_cmdLineParser.py : 14%

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/>.
22"""Simple unit test for parser module.
23"""
25from argparse import ArgumentParser
26import unittest
28import lsst.utils.tests
29import lsst.ctrl.mpexec.cmdLineParser as parser_mod
32class _Error(Exception):
33 pass
36class _NoExitParser(ArgumentParser):
37 """Special parser subclass which does not exit on errors or help.
38 """
40 def exit(self, status=0, message=None):
41 pass
43 def error(self, message):
44 raise _Error(message)
47class CmdLineParserTestCase(unittest.TestCase):
48 """A test case for parser module
49 """
51 def setUp(self):
52 pass
54 def tearDown(self):
55 pass
57 def testPipelineAction(self):
58 """Test for a _PipelineAction and _pipe_action
59 """
61 parser = _NoExitParser()
62 parser.add_argument("-t", dest="pipeline_actions", action='append',
63 type=parser_mod._ACTION_ADD_TASK)
65 PipelineAction = parser_mod._PipelineAction
66 args = parser.parse_args("-t task".split())
67 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "task")])
69 args = parser.parse_args("-t task:label".split())
70 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", "label", "task")])
72 args = parser.parse_args("-t task".split())
73 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "task")])
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())
79 # Literal but of the wrong type
80 with self.assertRaises(_Error):
81 parser.parse_args("-t task -s task:[1,2]".split())
83 # dictionary but not all strings
84 with self.assertRaises(_Error):
85 parser.parse_args("-t task -s task:{'x':1}".split())
87 def testInputCollectionAction(self):
88 """Test for a _InputCollectionAction
89 """
91 parser = _NoExitParser()
92 parser.add_argument("-i", dest="input", action=parser_mod._InputCollectionAction,
93 default={})
95 args = parser.parse_args("".split())
96 self.assertEqual(args.input, {})
98 args = parser.parse_args("-i coll".split())
99 self.assertEqual(args.input, {"": ["coll"]})
101 # collection can appear twice
102 args = parser.parse_args("-i coll,coll".split())
103 self.assertEqual(args.input, {"": ["coll", "coll"]})
105 args = parser.parse_args("-i coll1,coll2,coll3".split())
106 self.assertEqual(args.input, {"": ["coll1", "coll2", "coll3"]})
108 args = parser.parse_args("-i coll1 -i coll2 -i coll3".split())
109 self.assertEqual(args.input, {"": ["coll1", "coll2", "coll3"]})
111 args = parser.parse_args("-i coll1 -i coll2,coll3".split())
112 self.assertEqual(args.input, {"": ["coll1", "coll2", "coll3"]})
114 args = parser.parse_args("-i coll1 -i coll2 -i coll3".split())
115 self.assertEqual(args.input, {"": ["coll1", "coll2", "coll3"]})
117 args = parser.parse_args("-i ds:coll".split())
118 self.assertEqual(args.input, {"ds": ["coll"]})
120 args = parser.parse_args("-i ds1:coll1,ds2:coll2,ds2:coll3".split())
121 self.assertEqual(args.input, {"ds1": ["coll1"],
122 "ds2": ["coll2", "coll3"]})
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"]})
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"]})
134 # use non-empty default
135 parser = _NoExitParser()
136 parser.add_argument("-i", dest="input", action=parser_mod._InputCollectionAction,
137 default={"": ["coll"]})
139 args = parser.parse_args("".split())
140 self.assertEqual(args.input, {"": ["coll"]})
142 args = parser.parse_args("-i coll".split())
143 self.assertEqual(args.input, {"": ["coll", "coll"]})
145 args = parser.parse_args("-i coll1 -i coll2 -i coll3".split())
146 self.assertEqual(args.input, {"": ["coll", "coll1", "coll2", "coll3"]})
148 def testOutputCollectionType(self):
149 """Test for a _outputCollectionType
150 """
152 parser = _NoExitParser()
153 parser.add_argument("-o", dest="output", type=parser_mod._outputCollectionType,
154 default={})
156 args = parser.parse_args("".split())
157 self.assertEqual(args.output, {})
159 args = parser.parse_args("-o coll".split())
160 self.assertEqual(args.output, {"": "coll"})
162 with self.assertRaises(_Error):
163 args = parser.parse_args("-o coll1,coll2,coll3".split())
165 args = parser.parse_args("-o ds:coll".split())
166 self.assertEqual(args.output, {"ds": "coll"})
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"})
173 args = parser.parse_args("-o coll1,ds2:coll2".split())
174 self.assertEqual(args.output, {"": "coll1",
175 "ds2": "coll2"})
177 with self.assertRaises(_Error):
178 args = parser.parse_args("-o coll1,ds2:coll2,coll3".split())
180 def testCmdLineParser(self):
181 """Test for parser_mod.CmdLineParser
182 """
183 parser = parser_mod.makeParser(parser_class=_NoExitParser)
185 # this should result in error
186 self.assertRaises(_Error, parser.parse_args)
188 # know attributes to appear in parser output for all subcommands
189 common_options = "loglevel longlog enableLsstDebug subcommand subparser".split()
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')
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')
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')
221 def testCmdLineTasks(self):
223 parser = parser_mod.makeParser(parser_class=_NoExitParser)
225 PipelineAction = parser_mod._PipelineAction
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)
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)
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")
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())
355 def testCmdLinePipeline(self):
357 parser = parser_mod.makeParser(parser_class=_NoExitParser)
359 PipelineAction = parser_mod._PipelineAction
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, [])
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")])
376class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
377 pass
380def setup_module(module):
381 lsst.utils.tests.init()
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()