Coverage for tests/test_cmdLineParser.py : 16%

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
29from lsst.daf.butler import CollectionSearch
30import lsst.ctrl.mpexec.cmdLineParser as parser_mod
33class _Error(Exception):
34 pass
37class _NoExitParser(ArgumentParser):
38 """Special parser subclass which does not exit on errors or help.
39 """
41 def exit(self, status=0, message=None):
42 pass
44 def error(self, message):
45 raise _Error(message)
48class CmdLineParserTestCase(unittest.TestCase):
49 """A test case for parser module
50 """
52 def setUp(self):
53 pass
55 def tearDown(self):
56 pass
58 def testPipelineAction(self):
59 """Test for a _PipelineAction and _pipe_action
60 """
62 parser = _NoExitParser()
63 parser.add_argument("-t", dest="pipeline_actions", action='append',
64 type=parser_mod._ACTION_ADD_TASK)
66 PipelineAction = parser_mod._PipelineAction
67 args = parser.parse_args("-t task".split())
68 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "task")])
70 args = parser.parse_args("-t task:label".split())
71 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", "label", "task")])
73 args = parser.parse_args("-t task".split())
74 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "task")])
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())
80 # Literal but of the wrong type
81 with self.assertRaises(_Error):
82 parser.parse_args("-t task -s task:[1,2]".split())
84 # dictionary but not all strings
85 with self.assertRaises(_Error):
86 parser.parse_args("-t task -s task:{'x':1}".split())
88 def testInputCollectionAction(self):
89 """Test for a _InputCollectionAction
90 """
92 parser = _NoExitParser()
93 parser.add_argument("-i", dest="input", action=parser_mod._InputCollectionAction, default=[])
95 args = parser.parse_args("".split())
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))
103 assertExpressionsEquivalent(args.input, [])
105 args = parser.parse_args("-i coll".split())
106 assertExpressionsEquivalent(args.input, ["coll"])
108 # collection can appear twice
109 args = parser.parse_args("-i coll,coll".split())
110 assertExpressionsEquivalent(args.input, ["coll"])
112 args = parser.parse_args("-i coll1,coll2,coll3".split())
113 assertExpressionsEquivalent(args.input, ["coll1", "coll2", "coll3"])
115 args = parser.parse_args("-i coll1 -i coll2 -i coll3".split())
116 assertExpressionsEquivalent(args.input, ["coll1", "coll2", "coll3"])
118 args = parser.parse_args("-i coll1 -i coll2,coll3".split())
119 assertExpressionsEquivalent(args.input, ["coll1", "coll2", "coll3"])
121 args = parser.parse_args("-i coll1 -i coll2 -i coll3".split())
122 assertExpressionsEquivalent(args.input, ["coll1", "coll2", "coll3"])
124 args = parser.parse_args("-i ds:coll".split())
125 assertExpressionsEquivalent(args.input, [("coll", "ds")])
127 args = parser.parse_args("-i ds1:coll1,ds2:coll2,ds2:coll3".split())
128 assertExpressionsEquivalent(args.input, [("coll1", "ds1"), ("coll2", "ds2"), ("coll3", "ds2")])
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"])
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"])
140 # use non-empty default
141 parser = _NoExitParser()
142 parser.add_argument("-i", dest="input", action=parser_mod._InputCollectionAction,
143 default=[("coll", ...)])
145 args = parser.parse_args("".split())
146 assertExpressionsEquivalent(args.input, ["coll"])
148 args = parser.parse_args("-i coll".split())
149 assertExpressionsEquivalent(args.input, ["coll"])
151 args = parser.parse_args("-i coll1 -i coll2 -i coll3".split())
152 assertExpressionsEquivalent(args.input, ["coll", "coll1", "coll2", "coll3"])
154 def testCmdLineParser(self):
155 """Test for parser_mod.CmdLineParser
156 """
157 parser = parser_mod.makeParser(parser_class=_NoExitParser)
159 # this should result in error
160 self.assertRaises(_Error, parser.parse_args)
162 # know attributes to appear in parser output for all subcommands
163 common_options = "loglevel longlog enableLsstDebug subcommand subparser".split()
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')
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')
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""".split()
192 self.assertEqual(set(vars(args).keys()), set(common_options + run_options))
193 self.assertEqual(args.subcommand, 'run')
195 def testCmdLineTasks(self):
197 parser = parser_mod.makeParser(parser_class=_NoExitParser)
199 PipelineAction = parser_mod._PipelineAction
201 # default options
202 args = parser.parse_args(
203 """
204 run -t taskname
205 """.split())
206 self.assertFalse(args.enableLsstDebug)
207 self.assertFalse(args.doraise)
208 self.assertEqual(args.input, [])
209 self.assertEqual(args.loglevel, [])
210 self.assertFalse(args.longlog)
211 self.assertEqual(args.output, None)
212 self.assertEqual(args.output_run, None)
213 self.assertEqual(args.extend_run, False)
214 self.assertEqual(args.replace_run, False)
215 self.assertEqual(args.processes, 1)
216 self.assertIsNone(args.profile)
217 self.assertIsNone(args.timeout)
218 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "taskname")])
219 self.assertEqual(args.show, [])
220 self.assertIsNotNone(args.subparser)
221 self.assertIsNone(args.pipeline)
223 # bunch of random options
224 args = parser.parse_args(
225 """
226 run
227 --debug
228 --doraise
229 --input inputColl
230 --loglevel DEBUG -L component=trace
231 --longlog
232 --output outputColl
233 -j 66
234 --profile profile.out
235 --timeout 10.10
236 -t taskname:label
237 --show config
238 --show config=Task.*
239 -c label:a=b
240 -C label:filename1
241 -c label:c=d -c label:e=f
242 -C label:filename2 -C label:filename3
243 --skip-existing
244 """.split())
245 self.assertTrue(args.enableLsstDebug)
246 self.assertTrue(args.doraise)
247 self.assertEqual(args.input, [("inputColl", ...)])
248 self.assertEqual(args.loglevel, [(None, 'DEBUG'), ('component', 'TRACE')])
249 self.assertTrue(args.longlog)
250 self.assertEqual(args.output, "outputColl")
251 self.assertEqual(args.processes, 66)
252 self.assertEqual(args.profile, 'profile.out')
253 self.assertEqual(args.timeout, 10.10)
254 self.assertEqual(args.show, ['config', 'config=Task.*'])
255 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", "label", "taskname"),
256 PipelineAction("config", "label", "a=b"),
257 PipelineAction("configfile", "label", "filename1"),
258 PipelineAction("config", "label", "c=d"),
259 PipelineAction("config", "label", "e=f"),
260 PipelineAction("configfile", "label", "filename2"),
261 PipelineAction("configfile", "label", "filename3")])
262 self.assertIsNone(args.pipeline)
263 self.assertIsNone(args.qgraph)
264 self.assertFalse(args.order_pipeline)
265 self.assertIsNone(args.save_pipeline)
266 self.assertIsNone(args.save_qgraph)
267 self.assertIsNone(args.pipeline_dot)
268 self.assertIsNone(args.qgraph_dot)
269 self.assertTrue(args.skip_existing)
271 # multiple tasks plus more options (-q should be exclusive with
272 # some other options but we do not check it during parsing (yet))
273 args = parser.parse_args(
274 """
275 run
276 -p pipeline.yaml
277 -g qgraph.pickle
278 -t task1
279 -t task2:label2
280 -t task3
281 -t task4
282 --show config
283 -c task1:a=b
284 -C task1:filename1
285 -c label2:c=d -c label2:e=f
286 -C task3:filename2 -C task3:filename3
287 --show config=Task.*
288 -C task4:filename4 -c task4:x=y
289 --order-pipeline
290 --save-pipeline=newpipe.yaml
291 --save-qgraph=newqgraph.pickle
292 --pipeline-dot pipe.dot
293 --qgraph-dot qgraph.dot
294 """.split())
295 self.assertEqual(args.show, ['config', 'config=Task.*'])
296 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "task1"),
297 PipelineAction("new_task", "label2", "task2"),
298 PipelineAction("new_task", None, "task3"),
299 PipelineAction("new_task", None, "task4"),
300 PipelineAction("config", "task1", "a=b"),
301 PipelineAction("configfile", "task1", "filename1"),
302 PipelineAction("config", "label2", "c=d"),
303 PipelineAction("config", "label2", "e=f"),
304 PipelineAction("configfile", "task3", "filename2"),
305 PipelineAction("configfile", "task3", "filename3"),
306 PipelineAction("configfile", "task4", "filename4"),
307 PipelineAction("config", "task4", "x=y")])
308 self.assertEqual(args.pipeline, "pipeline.yaml")
309 self.assertEqual(args.qgraph, "qgraph.pickle")
310 self.assertTrue(args.order_pipeline)
311 self.assertEqual(args.save_pipeline, "newpipe.yaml")
312 self.assertEqual(args.save_qgraph, "newqgraph.pickle")
313 self.assertEqual(args.pipeline_dot, "pipe.dot")
314 self.assertEqual(args.qgraph_dot, "qgraph.dot")
316 def testCmdLinePipeline(self):
318 parser = parser_mod.makeParser(parser_class=_NoExitParser)
320 PipelineAction = parser_mod._PipelineAction
322 args = parser.parse_args(
323 """
324 run -p pipeline
325 --show config
326 --show config=Task.*
327 """.split())
328 self.assertEqual(args.show, ['config', 'config=Task.*'])
329 self.assertEqual(args.pipeline, 'pipeline')
330 self.assertEqual(args.pipeline_actions, [])
332 args = parser.parse_args("run -p pipeline -t task".split())
333 self.assertEqual(args.pipeline, 'pipeline')
334 self.assertEqual(args.pipeline_actions, [PipelineAction("new_task", None, "task")])
337class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
338 pass
341def setup_module(module):
342 lsst.utils.tests.init()
345if __name__ == "__main__": 345 ↛ 346line 345 didn't jump to line 346, because the condition on line 345 was never true
346 lsst.utils.tests.init()
347 unittest.main()