1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
5 Tests for L{twisted.python.usage}, a command line option parsing library.
8 from twisted.trial import unittest
9 from twisted.python import usage
12 class WellBehaved(usage.Options):
13 optParameters = [['long', 'w', 'default', 'and a docstring'],
14 ['another', 'n', 'no docstring'],
15 ['longonly', None, 'noshort'],
16 ['shortless', None, 'except',
17 'this one got docstring'],
19 optFlags = [['aflag', 'f',
22 flagallicious docstringness for this here
29 self.opts['myflag'] = "PONY!"
32 def opt_myparam(self, value):
33 self.opts['myparam'] = "%s WITH A PONY!" % (value,)
37 class ParseCorrectnessTest(unittest.TestCase):
39 Test Options.parseArgs for correct values under good conditions.
43 Instantiate and parseOptions a well-behaved Options class.
46 self.niceArgV = ("--long Alpha -n Beta "
47 "--shortless Gamma -f --myflag "
48 "--myparam Tofu").split()
50 self.nice = WellBehaved()
52 self.nice.parseOptions(self.niceArgV)
54 def test_checkParameters(self):
56 Checking that parameters have correct values.
58 self.assertEqual(self.nice.opts['long'], "Alpha")
59 self.assertEqual(self.nice.opts['another'], "Beta")
60 self.assertEqual(self.nice.opts['longonly'], "noshort")
61 self.assertEqual(self.nice.opts['shortless'], "Gamma")
63 def test_checkFlags(self):
65 Checking that flags have correct values.
67 self.assertEqual(self.nice.opts['aflag'], 1)
68 self.assertEqual(self.nice.opts['flout'], 0)
70 def test_checkCustoms(self):
72 Checking that custom flags and parameters have correct values.
74 self.assertEqual(self.nice.opts['myflag'], "PONY!")
75 self.assertEqual(self.nice.opts['myparam'], "Tofu WITH A PONY!")
79 class TypedOptions(usage.Options):
81 ['fooint', None, 392, 'Foo int', int],
82 ['foofloat', None, 4.23, 'Foo float', float],
83 ['eggint', None, None, 'Egg int without default', int],
84 ['eggfloat', None, None, 'Egg float without default', float],
87 def opt_under_score(self, value):
89 This option has an underscore in its name to exercise the _ to -
92 self.underscoreValue = value
93 opt_u = opt_under_score
97 class TypedTestCase(unittest.TestCase):
99 Test Options.parseArgs for options with forced types.
102 self.usage = TypedOptions()
104 def test_defaultValues(self):
106 Test parsing of default values.
109 self.usage.parseOptions(argV)
110 self.assertEqual(self.usage.opts['fooint'], 392)
111 self.assert_(isinstance(self.usage.opts['fooint'], int))
112 self.assertEqual(self.usage.opts['foofloat'], 4.23)
113 self.assert_(isinstance(self.usage.opts['foofloat'], float))
114 self.assertEqual(self.usage.opts['eggint'], None)
115 self.assertEqual(self.usage.opts['eggfloat'], None)
118 def test_parsingValues(self):
120 Test basic parsing of int and float values.
122 argV = ("--fooint 912 --foofloat -823.1 "
123 "--eggint 32 --eggfloat 21").split()
124 self.usage.parseOptions(argV)
125 self.assertEqual(self.usage.opts['fooint'], 912)
126 self.assert_(isinstance(self.usage.opts['fooint'], int))
127 self.assertEqual(self.usage.opts['foofloat'], -823.1)
128 self.assert_(isinstance(self.usage.opts['foofloat'], float))
129 self.assertEqual(self.usage.opts['eggint'], 32)
130 self.assert_(isinstance(self.usage.opts['eggint'], int))
131 self.assertEqual(self.usage.opts['eggfloat'], 21.)
132 self.assert_(isinstance(self.usage.opts['eggfloat'], float))
135 def test_underscoreOption(self):
137 A dash in an option name is translated to an underscore before being
138 dispatched to a handler.
140 self.usage.parseOptions(['--under-score', 'foo'])
141 self.assertEqual(self.usage.underscoreValue, 'foo')
144 def test_underscoreOptionAlias(self):
146 An option name with a dash in it can have an alias.
148 self.usage.parseOptions(['-u', 'bar'])
149 self.assertEqual(self.usage.underscoreValue, 'bar')
152 def test_invalidValues(self):
154 Check that passing wrong values raises an error.
156 argV = "--fooint egg".split()
157 self.assertRaises(usage.UsageError, self.usage.parseOptions, argV)
161 class WrongTypedOptions(usage.Options):
163 ['barwrong', None, None, 'Bar with wrong coerce', 'he']
167 class WeirdCallableOptions(usage.Options):
169 raise RuntimeError("Ouch")
171 raise ValueError("Yay")
173 ['barwrong', None, None, 'Bar with strange callable', _bar],
174 ['foowrong', None, None, 'Foo with strange callable', _foo]
178 class WrongTypedTestCase(unittest.TestCase):
180 Test Options.parseArgs for wrong coerce options.
182 def test_nonCallable(self):
184 Check that using a non callable type fails.
186 us = WrongTypedOptions()
187 argV = "--barwrong egg".split()
188 self.assertRaises(TypeError, us.parseOptions, argV)
190 def test_notCalledInDefault(self):
192 Test that the coerce functions are not called if no values are
195 us = WeirdCallableOptions()
197 us.parseOptions(argV)
199 def test_weirdCallable(self):
201 Test what happens when coerce functions raise errors.
203 us = WeirdCallableOptions()
204 argV = "--foowrong blah".split()
205 # ValueError is swallowed as UsageError
206 e = self.assertRaises(usage.UsageError, us.parseOptions, argV)
207 self.assertEqual(str(e), "Parameter type enforcement failed: Yay")
209 us = WeirdCallableOptions()
210 argV = "--barwrong blah".split()
211 # RuntimeError is not swallowed
212 self.assertRaises(RuntimeError, us.parseOptions, argV)
215 class OutputTest(unittest.TestCase):
216 def test_uppercasing(self):
218 Error output case adjustment does not mangle options
221 e = self.assertRaises(usage.UsageError,
222 opt.parseOptions, ['-Z'])
223 self.assertEqual(str(e), 'option -Z not recognized')
226 class InquisitionOptions(usage.Options):
231 ('torture-device', 't',
233 'set preferred torture device'),
237 class HolyQuestOptions(usage.Options):
238 optFlags = [('horseback', 'h',
244 class SubCommandOptions(usage.Options):
245 optFlags = [('europian-swallow', None,
246 'set default swallow type to Europian'),
249 ('inquisition', 'inquest', InquisitionOptions,
250 'Perform an inquisition'),
251 ('holyquest', 'quest', HolyQuestOptions,
252 'Embark upon a holy quest'),
256 class SubCommandTest(unittest.TestCase):
258 def test_simpleSubcommand(self):
259 o = SubCommandOptions()
260 o.parseOptions(['--europian-swallow', 'inquisition'])
261 self.assertEqual(o['europian-swallow'], True)
262 self.assertEqual(o.subCommand, 'inquisition')
263 self.failUnless(isinstance(o.subOptions, InquisitionOptions))
264 self.assertEqual(o.subOptions['expect'], False)
265 self.assertEqual(o.subOptions['torture-device'], 'comfy-chair')
267 def test_subcommandWithFlagsAndOptions(self):
268 o = SubCommandOptions()
269 o.parseOptions(['inquisition', '--expect', '--torture-device=feather'])
270 self.assertEqual(o['europian-swallow'], False)
271 self.assertEqual(o.subCommand, 'inquisition')
272 self.failUnless(isinstance(o.subOptions, InquisitionOptions))
273 self.assertEqual(o.subOptions['expect'], True)
274 self.assertEqual(o.subOptions['torture-device'], 'feather')
276 def test_subcommandAliasWithFlagsAndOptions(self):
277 o = SubCommandOptions()
278 o.parseOptions(['inquest', '--expect', '--torture-device=feather'])
279 self.assertEqual(o['europian-swallow'], False)
280 self.assertEqual(o.subCommand, 'inquisition')
281 self.failUnless(isinstance(o.subOptions, InquisitionOptions))
282 self.assertEqual(o.subOptions['expect'], True)
283 self.assertEqual(o.subOptions['torture-device'], 'feather')
285 def test_anotherSubcommandWithFlagsAndOptions(self):
286 o = SubCommandOptions()
287 o.parseOptions(['holyquest', '--for-grail'])
288 self.assertEqual(o['europian-swallow'], False)
289 self.assertEqual(o.subCommand, 'holyquest')
290 self.failUnless(isinstance(o.subOptions, HolyQuestOptions))
291 self.assertEqual(o.subOptions['horseback'], False)
292 self.assertEqual(o.subOptions['for-grail'], True)
294 def test_noSubcommand(self):
295 o = SubCommandOptions()
296 o.parseOptions(['--europian-swallow'])
297 self.assertEqual(o['europian-swallow'], True)
298 self.assertEqual(o.subCommand, None)
299 self.failIf(hasattr(o, 'subOptions'))
301 def test_defaultSubcommand(self):
302 o = SubCommandOptions()
303 o.defaultSubCommand = 'inquest'
304 o.parseOptions(['--europian-swallow'])
305 self.assertEqual(o['europian-swallow'], True)
306 self.assertEqual(o.subCommand, 'inquisition')
307 self.failUnless(isinstance(o.subOptions, InquisitionOptions))
308 self.assertEqual(o.subOptions['expect'], False)
309 self.assertEqual(o.subOptions['torture-device'], 'comfy-chair')
311 def test_subCommandParseOptionsHasParent(self):
312 class SubOpt(usage.Options):
313 def parseOptions(self, *a, **kw):
314 self.sawParent = self.parent
315 usage.Options.parseOptions(self, *a, **kw)
316 class Opt(usage.Options):
318 ('foo', 'f', SubOpt, 'bar'),
321 o.parseOptions(['foo'])
322 self.failUnless(hasattr(o.subOptions, 'sawParent'))
323 self.assertEqual(o.subOptions.sawParent , o)
325 def test_subCommandInTwoPlaces(self):
327 The .parent pointer is correct even when the same Options class is
330 class SubOpt(usage.Options):
332 class OptFoo(usage.Options):
334 ('foo', 'f', SubOpt, 'quux'),
336 class OptBar(usage.Options):
338 ('bar', 'b', SubOpt, 'quux'),
341 oFoo.parseOptions(['foo'])
343 oBar.parseOptions(['bar'])
344 self.failUnless(hasattr(oFoo.subOptions, 'parent'))
345 self.failUnless(hasattr(oBar.subOptions, 'parent'))
346 self.failUnlessIdentical(oFoo.subOptions.parent, oFoo)
347 self.failUnlessIdentical(oBar.subOptions.parent, oBar)
350 class HelpStringTest(unittest.TestCase):
353 Instantiate a well-behaved Options class.
356 self.niceArgV = ("--long Alpha -n Beta "
357 "--shortless Gamma -f --myflag "
358 "--myparam Tofu").split()
360 self.nice = WellBehaved()
362 def test_noGoBoom(self):
364 __str__ shouldn't go boom.
371 def test_whitespaceStripFlagsAndParameters(self):
373 Extra whitespace in flag and parameters docs is stripped.
375 # We test this by making sure aflag and it's help string are on the
377 lines = [s for s in str(self.nice).splitlines() if s.find("aflag")>=0]
378 self.failUnless(len(lines) > 0)
379 self.failUnless(lines[0].find("flagallicious") >= 0)
382 class PortCoerceTestCase(unittest.TestCase):
384 Test the behavior of L{usage.portCoerce}.
386 def test_validCoerce(self):
388 Test the answers with valid input.
390 self.assertEqual(0, usage.portCoerce("0"))
391 self.assertEqual(3210, usage.portCoerce("3210"))
392 self.assertEqual(65535, usage.portCoerce("65535"))
394 def test_errorCoerce(self):
398 self.assertRaises(ValueError, usage.portCoerce, "")
399 self.assertRaises(ValueError, usage.portCoerce, "-21")
400 self.assertRaises(ValueError, usage.portCoerce, "212189")
401 self.assertRaises(ValueError, usage.portCoerce, "foo")
405 class ZshCompleterTestCase(unittest.TestCase):
407 Test the behavior of the various L{twisted.usage.Completer} classes
408 for producing output usable by zsh tab-completion system.
410 def test_completer(self):
412 Completer produces zsh shell-code that produces no completion matches.
414 c = usage.Completer()
415 got = c._shellCode('some-option', usage._ZSH)
416 self.assertEqual(got, ':some-option:')
418 c = usage.Completer(descr='some action', repeat=True)
419 got = c._shellCode('some-option', usage._ZSH)
420 self.assertEqual(got, '*:some action:')
423 def test_files(self):
425 CompleteFiles produces zsh shell-code that completes file names
428 c = usage.CompleteFiles()
429 got = c._shellCode('some-option', usage._ZSH)
430 self.assertEqual(got, ':some-option (*):_files -g "*"')
432 c = usage.CompleteFiles('*.py')
433 got = c._shellCode('some-option', usage._ZSH)
434 self.assertEqual(got, ':some-option (*.py):_files -g "*.py"')
436 c = usage.CompleteFiles('*.py', descr="some action", repeat=True)
437 got = c._shellCode('some-option', usage._ZSH)
438 self.assertEqual(got, '*:some action (*.py):_files -g "*.py"')
443 CompleteDirs produces zsh shell-code that completes directory names.
445 c = usage.CompleteDirs()
446 got = c._shellCode('some-option', usage._ZSH)
447 self.assertEqual(got, ':some-option:_directories')
449 c = usage.CompleteDirs(descr="some action", repeat=True)
450 got = c._shellCode('some-option', usage._ZSH)
451 self.assertEqual(got, '*:some action:_directories')
456 CompleteList produces zsh shell-code that completes words from a fixed
457 list of possibilities.
459 c = usage.CompleteList('ABC')
460 got = c._shellCode('some-option', usage._ZSH)
461 self.assertEqual(got, ':some-option:(A B C)')
463 c = usage.CompleteList(['1', '2', '3'])
464 got = c._shellCode('some-option', usage._ZSH)
465 self.assertEqual(got, ':some-option:(1 2 3)')
467 c = usage.CompleteList(['1', '2', '3'], descr='some action',
469 got = c._shellCode('some-option', usage._ZSH)
470 self.assertEqual(got, '*:some action:(1 2 3)')
473 def test_multiList(self):
475 CompleteMultiList produces zsh shell-code that completes multiple
476 comma-separated words from a fixed list of possibilities.
478 c = usage.CompleteMultiList('ABC')
479 got = c._shellCode('some-option', usage._ZSH)
480 self.assertEqual(got, ':some-option:_values -s , \'some-option\' A B C')
482 c = usage.CompleteMultiList(['1','2','3'])
483 got = c._shellCode('some-option', usage._ZSH)
484 self.assertEqual(got, ':some-option:_values -s , \'some-option\' 1 2 3')
486 c = usage.CompleteMultiList(['1','2','3'], descr='some action',
488 got = c._shellCode('some-option', usage._ZSH)
489 expected = '*:some action:_values -s , \'some action\' 1 2 3'
490 self.assertEqual(got, expected)
493 def test_usernames(self):
495 CompleteUsernames produces zsh shell-code that completes system
498 c = usage.CompleteUsernames()
499 out = c._shellCode('some-option', usage._ZSH)
500 self.assertEqual(out, ':some-option:_users')
502 c = usage.CompleteUsernames(descr='some action', repeat=True)
503 out = c._shellCode('some-option', usage._ZSH)
504 self.assertEqual(out, '*:some action:_users')
507 def test_groups(self):
509 CompleteGroups produces zsh shell-code that completes system group
512 c = usage.CompleteGroups()
513 out = c._shellCode('some-option', usage._ZSH)
514 self.assertEqual(out, ':group:_groups')
516 c = usage.CompleteGroups(descr='some action', repeat=True)
517 out = c._shellCode('some-option', usage._ZSH)
518 self.assertEqual(out, '*:some action:_groups')
521 def test_hostnames(self):
523 CompleteHostnames produces zsh shell-code that completes hostnames.
525 c = usage.CompleteHostnames()
526 out = c._shellCode('some-option', usage._ZSH)
527 self.assertEqual(out, ':some-option:_hosts')
529 c = usage.CompleteHostnames(descr='some action', repeat=True)
530 out = c._shellCode('some-option', usage._ZSH)
531 self.assertEqual(out, '*:some action:_hosts')
534 def test_userAtHost(self):
536 CompleteUserAtHost produces zsh shell-code that completes hostnames or
537 a word of the form <username>@<hostname>.
539 c = usage.CompleteUserAtHost()
540 out = c._shellCode('some-option', usage._ZSH)
541 self.assertTrue(out.startswith(':host | user@host:'))
543 c = usage.CompleteUserAtHost(descr='some action', repeat=True)
544 out = c._shellCode('some-option', usage._ZSH)
545 self.assertTrue(out.startswith('*:some action:'))
548 def test_netInterfaces(self):
550 CompleteNetInterfaces produces zsh shell-code that completes system
551 network interface names.
553 c = usage.CompleteNetInterfaces()
554 out = c._shellCode('some-option', usage._ZSH)
555 self.assertEqual(out, ':some-option:_net_interfaces')
557 c = usage.CompleteNetInterfaces(descr='some action', repeat=True)
558 out = c._shellCode('some-option', usage._ZSH)
559 self.assertEqual(out, '*:some action:_net_interfaces')
563 class CompleterNotImplementedTestCase(unittest.TestCase):
565 Using an unknown shell constant with the various Completer() classes
566 should raise NotImplementedError
568 def test_unknownShell(self):
570 Using an unknown shellType should raise NotImplementedError
572 classes = [usage.Completer, usage.CompleteFiles,
573 usage.CompleteDirs, usage.CompleteList,
574 usage.CompleteMultiList, usage.CompleteUsernames,
575 usage.CompleteGroups, usage.CompleteHostnames,
576 usage.CompleteUserAtHost, usage.CompleteNetInterfaces]
583 self.assertRaises(NotImplementedError, action._shellCode,
584 None, "bad_shell_type")