494 lines
17 KiB
Python
494 lines
17 KiB
Python
from __future__ import unicode_literals
|
|
|
|
import argparse
|
|
import unittest
|
|
|
|
import mock
|
|
|
|
from mopidy import commands
|
|
|
|
|
|
class ConfigOverrideTypeTest(unittest.TestCase):
|
|
def test_valid_override(self):
|
|
expected = (b'section', b'key', b'value')
|
|
self.assertEqual(
|
|
expected, commands.config_override_type(b'section/key=value'))
|
|
self.assertEqual(
|
|
expected, commands.config_override_type(b'section/key=value '))
|
|
self.assertEqual(
|
|
expected, commands.config_override_type(b'section/key =value'))
|
|
self.assertEqual(
|
|
expected, commands.config_override_type(b'section /key=value'))
|
|
|
|
def test_valid_override_is_bytes(self):
|
|
section, key, value = commands.config_override_type(
|
|
b'section/key=value')
|
|
self.assertIsInstance(section, bytes)
|
|
self.assertIsInstance(key, bytes)
|
|
self.assertIsInstance(value, bytes)
|
|
|
|
def test_empty_override(self):
|
|
expected = ('section', 'key', '')
|
|
self.assertEqual(
|
|
expected, commands.config_override_type(b'section/key='))
|
|
self.assertEqual(
|
|
expected, commands.config_override_type(b'section/key= '))
|
|
|
|
def test_invalid_override(self):
|
|
self.assertRaises(
|
|
argparse.ArgumentTypeError,
|
|
commands.config_override_type, b'section/key')
|
|
self.assertRaises(
|
|
argparse.ArgumentTypeError,
|
|
commands.config_override_type, b'section=')
|
|
self.assertRaises(
|
|
argparse.ArgumentTypeError,
|
|
commands.config_override_type, b'section')
|
|
|
|
|
|
class CommandParsingTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.exit_patcher = mock.patch.object(commands.Command, 'exit')
|
|
self.exit_mock = self.exit_patcher.start()
|
|
self.exit_mock.side_effect = SystemExit
|
|
|
|
def tearDown(self):
|
|
self.exit_patcher.stop()
|
|
|
|
def test_command_parsing_returns_namespace(self):
|
|
cmd = commands.Command()
|
|
self.assertIsInstance(cmd.parse([]), argparse.Namespace)
|
|
|
|
def test_command_parsing_does_not_contain_args(self):
|
|
cmd = commands.Command()
|
|
result = cmd.parse([])
|
|
self.assertFalse(hasattr(result, '_args'))
|
|
|
|
def test_unknown_options_bails(self):
|
|
cmd = commands.Command()
|
|
with self.assertRaises(SystemExit):
|
|
cmd.parse(['--foobar'])
|
|
|
|
def test_invalid_sub_command_bails(self):
|
|
cmd = commands.Command()
|
|
with self.assertRaises(SystemExit):
|
|
cmd.parse(['foo'])
|
|
|
|
def test_command_arguments(self):
|
|
cmd = commands.Command()
|
|
cmd.add_argument('--bar')
|
|
|
|
result = cmd.parse(['--bar', 'baz'])
|
|
self.assertEqual(result.bar, 'baz')
|
|
|
|
def test_command_arguments_and_sub_command(self):
|
|
child = commands.Command()
|
|
child.add_argument('--baz')
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_argument('--bar')
|
|
cmd.add_child('foo', child)
|
|
|
|
result = cmd.parse(['--bar', 'baz', 'foo'])
|
|
self.assertEqual(result.bar, 'baz')
|
|
self.assertEqual(result.baz, None)
|
|
|
|
def test_subcommand_may_have_positional(self):
|
|
child = commands.Command()
|
|
child.add_argument('bar')
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_child('foo', child)
|
|
|
|
result = cmd.parse(['foo', 'baz'])
|
|
self.assertEqual(result.bar, 'baz')
|
|
|
|
def test_subcommand_may_have_remainder(self):
|
|
child = commands.Command()
|
|
child.add_argument('bar', nargs=argparse.REMAINDER)
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_child('foo', child)
|
|
|
|
result = cmd.parse(['foo', 'baz', 'bep', 'bop'])
|
|
self.assertEqual(result.bar, ['baz', 'bep', 'bop'])
|
|
|
|
def test_result_stores_choosen_command(self):
|
|
child = commands.Command()
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_child('foo', child)
|
|
|
|
result = cmd.parse(['foo'])
|
|
self.assertEqual(result.command, child)
|
|
|
|
result = cmd.parse([])
|
|
self.assertEqual(result.command, cmd)
|
|
|
|
child2 = commands.Command()
|
|
cmd.add_child('bar', child2)
|
|
|
|
subchild = commands.Command()
|
|
child.add_child('baz', subchild)
|
|
|
|
result = cmd.parse(['bar'])
|
|
self.assertEqual(result.command, child2)
|
|
|
|
result = cmd.parse(['foo', 'baz'])
|
|
self.assertEqual(result.command, subchild)
|
|
|
|
def test_invalid_type(self):
|
|
cmd = commands.Command()
|
|
cmd.add_argument('--bar', type=int)
|
|
|
|
with self.assertRaises(SystemExit):
|
|
cmd.parse(['--bar', b'zero'], prog='foo')
|
|
|
|
self.exit_mock.assert_called_once_with(
|
|
1, "argument --bar: invalid int value: 'zero'",
|
|
'usage: foo [--bar BAR]')
|
|
|
|
@mock.patch('sys.argv')
|
|
def test_command_error_usage_prog(self, argv_mock):
|
|
argv_mock.__getitem__.return_value = '/usr/bin/foo'
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_argument('--bar', required=True)
|
|
|
|
with self.assertRaises(SystemExit):
|
|
cmd.parse([])
|
|
self.exit_mock.assert_called_once_with(
|
|
mock.ANY, mock.ANY, 'usage: foo --bar BAR')
|
|
|
|
self.exit_mock.reset_mock()
|
|
with self.assertRaises(SystemExit):
|
|
cmd.parse([], prog='baz')
|
|
|
|
self.exit_mock.assert_called_once_with(
|
|
mock.ANY, mock.ANY, 'usage: baz --bar BAR')
|
|
|
|
def test_missing_required(self):
|
|
cmd = commands.Command()
|
|
cmd.add_argument('--bar', required=True)
|
|
|
|
with self.assertRaises(SystemExit):
|
|
cmd.parse([], prog='foo')
|
|
|
|
self.exit_mock.assert_called_once_with(
|
|
1, 'argument --bar is required', 'usage: foo --bar BAR')
|
|
|
|
def test_missing_positionals(self):
|
|
cmd = commands.Command()
|
|
cmd.add_argument('bar')
|
|
|
|
with self.assertRaises(SystemExit):
|
|
cmd.parse([], prog='foo')
|
|
|
|
self.exit_mock.assert_called_once_with(
|
|
1, 'too few arguments', 'usage: foo bar')
|
|
|
|
def test_missing_positionals_subcommand(self):
|
|
child = commands.Command()
|
|
child.add_argument('baz')
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_child('bar', child)
|
|
|
|
with self.assertRaises(SystemExit):
|
|
cmd.parse(['bar'], prog='foo')
|
|
|
|
self.exit_mock.assert_called_once_with(
|
|
1, 'too few arguments', 'usage: foo bar baz')
|
|
|
|
def test_unknown_command(self):
|
|
cmd = commands.Command()
|
|
|
|
with self.assertRaises(SystemExit):
|
|
cmd.parse(['--help'], prog='foo')
|
|
|
|
self.exit_mock.assert_called_once_with(
|
|
1, 'unrecognized arguments: --help', 'usage: foo')
|
|
|
|
def test_invalid_subcommand(self):
|
|
cmd = commands.Command()
|
|
cmd.add_child('baz', commands.Command())
|
|
|
|
with self.assertRaises(SystemExit):
|
|
cmd.parse(['bar'], prog='foo')
|
|
|
|
self.exit_mock.assert_called_once_with(
|
|
1, 'unrecognized command: bar', 'usage: foo')
|
|
|
|
def test_set(self):
|
|
cmd = commands.Command()
|
|
cmd.set(foo='bar')
|
|
|
|
result = cmd.parse([])
|
|
self.assertEqual(result.foo, 'bar')
|
|
|
|
def test_set_propegate(self):
|
|
child = commands.Command()
|
|
|
|
cmd = commands.Command()
|
|
cmd.set(foo='bar')
|
|
cmd.add_child('command', child)
|
|
|
|
result = cmd.parse(['command'])
|
|
self.assertEqual(result.foo, 'bar')
|
|
|
|
def test_innermost_set_wins(self):
|
|
child = commands.Command()
|
|
child.set(foo='bar', baz=1)
|
|
|
|
cmd = commands.Command()
|
|
cmd.set(foo='baz', baz=None)
|
|
cmd.add_child('command', child)
|
|
|
|
result = cmd.parse(['command'])
|
|
self.assertEqual(result.foo, 'bar')
|
|
self.assertEqual(result.baz, 1)
|
|
|
|
def test_help_action_works(self):
|
|
cmd = commands.Command()
|
|
cmd.add_argument('-h', action='help')
|
|
cmd.format_help = mock.Mock()
|
|
|
|
with self.assertRaises(SystemExit):
|
|
cmd.parse(['-h'])
|
|
|
|
cmd.format_help.assert_called_once_with(mock.ANY)
|
|
self.exit_mock.assert_called_once_with(0, cmd.format_help.return_value)
|
|
|
|
|
|
class UsageTest(unittest.TestCase):
|
|
@mock.patch('sys.argv')
|
|
def test_prog_name_default_and_override(self, argv_mock):
|
|
argv_mock.__getitem__.return_value = '/usr/bin/foo'
|
|
cmd = commands.Command()
|
|
self.assertEqual('usage: foo', cmd.format_usage().strip())
|
|
self.assertEqual('usage: baz', cmd.format_usage('baz').strip())
|
|
|
|
def test_basic_usage(self):
|
|
cmd = commands.Command()
|
|
self.assertEqual('usage: foo', cmd.format_usage('foo').strip())
|
|
|
|
cmd.add_argument('-h', '--help', action='store_true')
|
|
self.assertEqual('usage: foo [-h]', cmd.format_usage('foo').strip())
|
|
|
|
cmd.add_argument('bar')
|
|
self.assertEqual('usage: foo [-h] bar',
|
|
cmd.format_usage('foo').strip())
|
|
|
|
def test_nested_usage(self):
|
|
child = commands.Command()
|
|
cmd = commands.Command()
|
|
cmd.add_child('bar', child)
|
|
|
|
self.assertEqual('usage: foo', cmd.format_usage('foo').strip())
|
|
self.assertEqual('usage: foo bar', cmd.format_usage('foo bar').strip())
|
|
|
|
cmd.add_argument('-h', '--help', action='store_true')
|
|
self.assertEqual('usage: foo bar',
|
|
child.format_usage('foo bar').strip())
|
|
|
|
child.add_argument('-h', '--help', action='store_true')
|
|
self.assertEqual('usage: foo bar [-h]',
|
|
child.format_usage('foo bar').strip())
|
|
|
|
|
|
class HelpTest(unittest.TestCase):
|
|
@mock.patch('sys.argv')
|
|
def test_prog_name_default_and_override(self, argv_mock):
|
|
argv_mock.__getitem__.return_value = '/usr/bin/foo'
|
|
cmd = commands.Command()
|
|
self.assertEqual('usage: foo', cmd.format_help().strip())
|
|
self.assertEqual('usage: bar', cmd.format_help('bar').strip())
|
|
|
|
def test_command_without_documenation_or_options(self):
|
|
cmd = commands.Command()
|
|
self.assertEqual('usage: bar', cmd.format_help('bar').strip())
|
|
|
|
def test_command_with_option(self):
|
|
cmd = commands.Command()
|
|
cmd.add_argument('-h', '--help', action='store_true',
|
|
help='show this message')
|
|
|
|
expected = ('usage: foo [-h]\n\n'
|
|
'OPTIONS:\n\n'
|
|
' -h, --help show this message')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
def test_command_with_option_and_positional(self):
|
|
cmd = commands.Command()
|
|
cmd.add_argument('-h', '--help', action='store_true',
|
|
help='show this message')
|
|
cmd.add_argument('bar', help='some help text')
|
|
|
|
expected = ('usage: foo [-h] bar\n\n'
|
|
'OPTIONS:\n\n'
|
|
' -h, --help show this message\n'
|
|
' bar some help text')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
def test_command_with_documentation(self):
|
|
cmd = commands.Command()
|
|
cmd.help = 'some text about everything this command does.'
|
|
|
|
expected = ('usage: foo\n\n'
|
|
'some text about everything this command does.')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
def test_command_with_documentation_and_option(self):
|
|
cmd = commands.Command()
|
|
cmd.help = 'some text about everything this command does.'
|
|
cmd.add_argument('-h', '--help', action='store_true',
|
|
help='show this message')
|
|
|
|
expected = ('usage: foo [-h]\n\n'
|
|
'some text about everything this command does.\n\n'
|
|
'OPTIONS:\n\n'
|
|
' -h, --help show this message')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
def test_subcommand_without_documentation_or_options(self):
|
|
child = commands.Command()
|
|
cmd = commands.Command()
|
|
cmd.add_child('bar', child)
|
|
|
|
self.assertEqual('usage: foo', cmd.format_help('foo').strip())
|
|
|
|
def test_subcommand_with_documentation_shown(self):
|
|
child = commands.Command()
|
|
child.help = 'some text about everything this command does.'
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_child('bar', child)
|
|
expected = ('usage: foo\n\n'
|
|
'COMMANDS:\n\n'
|
|
'bar\n\n'
|
|
' some text about everything this command does.')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
def test_subcommand_with_options_shown(self):
|
|
child = commands.Command()
|
|
child.add_argument('-h', '--help', action='store_true',
|
|
help='show this message')
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_child('bar', child)
|
|
|
|
expected = ('usage: foo\n\n'
|
|
'COMMANDS:\n\n'
|
|
'bar [-h]\n\n'
|
|
' -h, --help show this message')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
def test_subcommand_with_positional_shown(self):
|
|
child = commands.Command()
|
|
child.add_argument('baz', help='the great and wonderful')
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_child('bar', child)
|
|
|
|
expected = ('usage: foo\n\n'
|
|
'COMMANDS:\n\n'
|
|
'bar baz\n\n'
|
|
' baz the great and wonderful')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
def test_subcommand_with_options_and_documentation(self):
|
|
child = commands.Command()
|
|
child.help = ' some text about everything this command does.'
|
|
child.add_argument('-h', '--help', action='store_true',
|
|
help='show this message')
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_child('bar', child)
|
|
|
|
expected = ('usage: foo\n\n'
|
|
'COMMANDS:\n\n'
|
|
'bar [-h]\n\n'
|
|
' some text about everything this command does.\n\n'
|
|
' -h, --help show this message')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
def test_nested_subcommands_with_options(self):
|
|
subchild = commands.Command()
|
|
subchild.add_argument('--test', help='the great and wonderful')
|
|
|
|
child = commands.Command()
|
|
child.add_child('baz', subchild)
|
|
child.add_argument('-h', '--help', action='store_true',
|
|
help='show this message')
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_child('bar', child)
|
|
|
|
expected = ('usage: foo\n\n'
|
|
'COMMANDS:\n\n'
|
|
'bar [-h]\n\n'
|
|
' -h, --help show this message\n\n'
|
|
'bar baz [--test TEST]\n\n'
|
|
' --test TEST the great and wonderful')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
def test_nested_subcommands_skipped_intermediate(self):
|
|
subchild = commands.Command()
|
|
subchild.add_argument('--test', help='the great and wonderful')
|
|
|
|
child = commands.Command()
|
|
child.add_child('baz', subchild)
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_child('bar', child)
|
|
|
|
expected = ('usage: foo\n\n'
|
|
'COMMANDS:\n\n'
|
|
'bar baz [--test TEST]\n\n'
|
|
' --test TEST the great and wonderful')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
def test_command_with_option_and_subcommand_with_option(self):
|
|
child = commands.Command()
|
|
child.add_argument('--test', help='the great and wonderful')
|
|
|
|
cmd = commands.Command()
|
|
cmd.add_argument('-h', '--help', action='store_true',
|
|
help='show this message')
|
|
cmd.add_child('bar', child)
|
|
|
|
expected = ('usage: foo [-h]\n\n'
|
|
'OPTIONS:\n\n'
|
|
' -h, --help show this message\n\n'
|
|
'COMMANDS:\n\n'
|
|
'bar [--test TEST]\n\n'
|
|
' --test TEST the great and wonderful')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
def test_command_with_options_doc_and_subcommand_with_option_and_doc(self):
|
|
child = commands.Command()
|
|
child.help = 'some text about this sub-command.'
|
|
child.add_argument('--test', help='the great and wonderful')
|
|
|
|
cmd = commands.Command()
|
|
cmd.help = 'some text about everything this command does.'
|
|
cmd.add_argument('-h', '--help', action='store_true',
|
|
help='show this message')
|
|
cmd.add_child('bar', child)
|
|
|
|
expected = ('usage: foo [-h]\n\n'
|
|
'some text about everything this command does.\n\n'
|
|
'OPTIONS:\n\n'
|
|
' -h, --help show this message\n\n'
|
|
'COMMANDS:\n\n'
|
|
'bar [--test TEST]\n\n'
|
|
' some text about this sub-command.\n\n'
|
|
' --test TEST the great and wonderful')
|
|
self.assertEqual(expected, cmd.format_help('foo').strip())
|
|
|
|
|
|
class RunTest(unittest.TestCase):
|
|
def test_default_implmentation_raises_error(self):
|
|
with self.assertRaises(NotImplementedError):
|
|
commands.Command().run()
|