def test_tool_exec_command_dash_help_missing_params(help_option): """Execute a command (which needs params) asking for help.""" def fill_parser(self, parser): parser.add_argument('mandatory') cmd = create_command('somecommand', 'This command does that.') cmd.fill_parser = fill_parser command_groups = COMMAND_GROUPS + [('group', 'help text', [cmd])] dispatcher = Dispatcher(['somecommand', help_option], command_groups) with patch('charmcraft.helptexts.get_command_help') as mock: mock.return_value = 'test help' with pytest.raises(CommandError) as cm: dispatcher.run() error = cm.value # check the given information to the builder args = mock.call_args[0] assert args[0] == COMMAND_GROUPS assert args[1].__class__ == cmd assert {x[0] for x in args[2] } == {'-h, --help', '-v, --verbose', '-q, --quiet', 'mandatory'} # check the result of the full help builder is what is shown assert error.argsparsing assert str(error) == "test help"
def test_dispatcher_commands_are_not_loaded_if_not_needed(): class MyCommand1(BaseCommand): """Expected to be executed.""" name = "command1" help_msg = "some help" _executed = [] def run(self, parsed_args): self._executed.append(parsed_args) class MyCommand2(BaseCommand): """Expected to not be instantiated, or parse args, or run.""" name = "command2" help_msg = "some help" def __init__(self, *args): raise AssertionError def fill_parser(self, parser): raise AssertionError def run(self, parsed_args): raise AssertionError groups = [("test-group", "title", [MyCommand1, MyCommand2])] dispatcher = Dispatcher(["command1"], groups) dispatcher.run() assert isinstance(MyCommand1._executed[0], argparse.Namespace)
def test_tool_exec_command_bad_option_type(help_builder): """Execute a correct command but giving the valid option a bad value.""" def fill_parser(self, parser): parser.add_argument("--number", type=int) cmd = create_command("somecommand", "This command does that.") cmd.fill_parser = fill_parser command_groups = [CommandGroup("group", [cmd])] dispatcher = Dispatcher(command_groups) dispatcher.pre_parse_args(["somecommand", "--number=foo"]) help_builder.init("testapp", "general summary", command_groups) with pytest.raises(ArgumentParsingError) as cm: dispatcher.load_command("config") expected = textwrap.dedent("""\ Usage: testapp [options] command [args]... Try 'testapp somecommand -h' for help. Error: argument --number: invalid int value: 'foo' """) error = cm.value assert str(error) == expected
def test_tool_exec_command_dash_help_reverse(help_option): """Execute a command (that needs no params) asking for help.""" cmd = create_command('somecommand', 'This command does that.') command_groups = COMMAND_GROUPS + [('group', 'help text', [cmd])] dispatcher = Dispatcher([help_option, 'somecommand'], command_groups) with patch('charmcraft.helptexts.get_command_help') as mock: mock.return_value = 'test help' with pytest.raises(CommandError) as cm: dispatcher.run() error = cm.value # check the given information to the builder args = mock.call_args[0] assert args[0] == COMMAND_GROUPS assert args[1].__class__ == cmd assert sorted(x[0] for x in args[2]) == [ '-h, --help', '-p, --project-dir', '-q, --quiet', '-v, --verbose' ] # check the result of the full help builder is what is shown assert error.argsparsing assert str(error) == "test help" assert error.retcode == 0
def test_tool_exec_command_bad_option_type(): """Execute a correct command but giving the valid option a bad value.""" def fill_parser(self, parser): parser.add_argument("--number", type=int) cmd = create_command("somecommand", "This command does that.") cmd.fill_parser = fill_parser command_groups = [("group", "help text", [cmd])] with pytest.raises(CommandError) as cm: Dispatcher(["somecommand", "--number=foo"], command_groups) expected = textwrap.dedent( """\ Usage: charmcraft [options] command [args]... Try 'charmcraft somecommand -h' for help. Error: argument --number: invalid int value: 'foo' """ ) error = cm.value assert error.argsparsing assert str(error) == expected assert error.retcode == 1
def test_tool_exec_help_command_on_command_ok(): """Execute charmcraft asking for help on a command ok.""" dispatcher = Dispatcher(["help", "version"], COMMAND_GROUPS) with patch("charmcraft.helptexts.get_command_help") as mock: mock.return_value = "test help" with pytest.raises(CommandError) as cm: dispatcher.run() error = cm.value # check the given information to the builder args = mock.call_args[0] assert args[0] == COMMAND_GROUPS assert args[1].__class__ == VersionCommand assert sorted(x[0] for x in args[2]) == [ "-h, --help", "-p, --project-dir", "-q, --quiet", "-v, --verbose", ] # check the result of the full help builder is what is shown assert error.argsparsing assert str(error) == "test help" assert error.retcode == 0
def test_dispatcher_global_arguments_default(): """The dispatcher uses the default global arguments.""" cmd = create_command("somecommand") groups = [CommandGroup("title", [cmd])] dispatcher = Dispatcher(groups) assert dispatcher.global_arguments == _DEFAULT_GLOBAL_ARGS
def test_tool_exec_command_dash_help_missing_params(help_option): """Execute a command (which needs params) asking for help.""" def fill_parser(self, parser): parser.add_argument("mandatory") cmd = create_command("somecommand", "This command does that.") cmd.fill_parser = fill_parser command_groups = [CommandGroup("group", [cmd])] dispatcher = Dispatcher(command_groups) with patch("charmcraft.helptexts.HelpBuilder.get_command_help") as mock: mock.return_value = "test help" with pytest.raises(ProvideHelpException) as cm: dispatcher.pre_parse_args(["somecommand", help_option]) # check the result of the full help builder is what is transmitted assert str(cm.value) == "test help" # check the given information to the help text builder args = mock.call_args[0] assert args[0].__class__ == cmd assert sorted(x[0] for x in args[1]) == [ "-h, --help", "-q, --quiet", "-t, --trace", "-v, --verbose", "mandatory", ]
def test_dispatcher_generic_setup_projectdir_with_param(options): """Generic parameter handling for 'project dir' with the param, directly or after the cmd.""" cmd = create_command("somecommand") groups = [("test-group", "title", [cmd])] with patch("charmcraft.config.load") as config_mock: Dispatcher(options, groups) config_mock.assert_called_once_with("foobar")
def test_dispatcher_generic_setup_quiet(options): """Generic parameter handling for quiet log setup, directly or after the command.""" cmd = create_command("somecommand") groups = [("test-group", "title", [cmd])] logsetup.message_handler.mode = None Dispatcher(options, groups) assert logsetup.message_handler.mode == logsetup.message_handler.QUIET
def test_tool_exec_command_dash_help_missing_params(help_option): """Execute a command (which needs params) asking for help.""" def fill_parser(self, parser): parser.add_argument("mandatory") cmd = create_command("somecommand", "This command does that.") cmd.fill_parser = fill_parser command_groups = COMMAND_GROUPS + [("group", "help text", [cmd])] dispatcher = Dispatcher(["somecommand", help_option], command_groups) with patch("charmcraft.helptexts.get_command_help") as mock: mock.return_value = "test help" with pytest.raises(CommandError) as cm: dispatcher.run() error = cm.value # check the given information to the builder args = mock.call_args[0] assert args[0] == COMMAND_GROUPS assert args[1].__class__ == cmd assert sorted(x[0] for x in args[2]) == [ "-h, --help", "-p, --project-dir", "-q, --quiet", "-v, --verbose", "mandatory", ] # check the result of the full help builder is what is shown assert error.argsparsing assert str(error) == "test help" assert error.retcode == 0
def test_dispatcher_generic_setup_verbose(options): """Generic parameter handling for verbose log setup, directly or after the command.""" cmd = create_command('somecommand') groups = [('test-group', 'title', [cmd])] logsetup.message_handler.mode = None Dispatcher(options, groups) assert logsetup.message_handler.mode == logsetup.message_handler.VERBOSE
def test_dispatcher_generic_setup_projectdir_without_param_simple(options): """Generic parameter handling for 'project dir' without the requested parameter.""" cmd = create_command("somecommand") groups = [("test-group", "title", [cmd])] with pytest.raises(CommandError) as err: Dispatcher(options, groups) assert str(err.value) == "The 'project-dir' option expects one argument."
def test_dispatcher_generic_setup_trace(options): """Generic parameter handling for trace log setup, directly or after the command.""" cmd = create_command("somecommand") groups = [CommandGroup("title", [cmd])] emit.set_mode(EmitterMode.NORMAL) # this is how `main` will init the Emitter dispatcher = Dispatcher(groups) dispatcher.pre_parse_args(options) assert emit.get_mode() == EmitterMode.TRACE
def test_dispatcher_generic_setup_paramglobal_with_param(options): """Generic parameter handling for a param type global arg, directly or after the cmd.""" cmd = create_command("somecommand") groups = [CommandGroup("title", [cmd])] extra = GlobalArgument("globalparam", "option", "-g", "--globalparam", "Test global param.") dispatcher = Dispatcher(groups, [extra]) global_args = dispatcher.pre_parse_args(options) assert global_args["globalparam"] == "foobar"
def test_dispatcher_generic_setup_mutually_exclusive(options): """Disallow mutually exclusive generic options.""" cmd = create_command("somecommand") groups = [CommandGroup("title", [cmd])] dispatcher = Dispatcher(groups) with pytest.raises(ArgumentParsingError) as err: dispatcher.pre_parse_args(options) assert str(err.value) == "The 'verbose', 'trace' and 'quiet' options are mutually exclusive."
def test_dispatcher_generic_setup_default(): """Generic parameter handling for default values.""" cmd = create_command("somecommand") groups = [CommandGroup("title", [cmd])] emit.set_mode(EmitterMode.NORMAL) # this is how `main` will init the Emitter dispatcher = Dispatcher(groups) dispatcher.pre_parse_args(["somecommand"]) assert emit.get_mode() == EmitterMode.NORMAL
def test_dispatcher_generic_setup_mutually_exclusive(options): """Disallow mutually exclusive generic options.""" cmd = create_command('somecommand') groups = [('test-group', 'title', [cmd])] # test the system exit, which is done automatically by argparse with pytest.raises(CommandError) as err: Dispatcher(options, groups) assert str(err.value) == "The 'verbose' and 'quiet' options are mutually exclusive."
def test_dispatcher_global_arguments_extra_arguments(): """The dispatcher uses the default global arguments.""" cmd = create_command("somecommand") groups = [CommandGroup("title", [cmd])] extra_arg = GlobalArgument("other", "flag", "-o", "--other", "Other stuff") dispatcher = Dispatcher(groups, extra_global_args=[extra_arg]) assert dispatcher.global_arguments == _DEFAULT_GLOBAL_ARGS + [extra_arg]
def test_dispatcher_generic_setup_mutually_exclusive(options): """Disallow mutually exclusive generic options.""" cmd = create_command('somecommand') groups = [('test-group', 'title', [cmd])] # test the system exit, which is done automatically by argparse with pytest.raises(SystemExit) as cm: Dispatcher(options, groups) assert cm.value.code == 2
def test_dispatcher_generic_setup_quiet(options): """Generic parameter handling for silent log setup, directly of after the command.""" cmd = create_command('somecommand') groups = [('test-group', 'title', [cmd])] dispatcher = Dispatcher(options, groups) logsetup.message_handler.mode = None dispatcher.run() assert logsetup.message_handler.mode == logsetup.message_handler.QUIET
def test_dispatcher_no_command(): """When no command is given we should get the "usage" help.""" groups = [('test-group', 'title', [create_command('cmd-name', 'cmd help')])] dispatcher = Dispatcher([], groups) fake_stdout = io.StringIO() with patch('sys.stdout', fake_stdout): retcode = dispatcher.run() assert retcode == 1 assert "usage: charmcraft" in fake_stdout.getvalue()
def test_dispatcher_generic_setup_projectdir_without_param_confusing(options): """Generic parameter handling for 'project dir' taking confusingly the command as the arg.""" cmd = create_command("somecommand") groups = [("test-group", "title", [cmd])] with pytest.raises(CommandError) as err: Dispatcher(options, groups) # generic usage message because "no command" (as 'somecommand' was consumed by --project-dir) assert "Usage" in str(err.value)
def test_dispatcher_generic_setup_paramglobal_without_param_simple(options): """Generic parameter handling for a param type global arg without the requested parameter.""" cmd = create_command("somecommand") groups = [CommandGroup("title", [cmd])] extra = GlobalArgument("globalparam", "option", "-g", "--globalparam", "Test global param.") dispatcher = Dispatcher(groups, [extra]) with pytest.raises(ArgumentParsingError) as err: dispatcher.pre_parse_args(options) assert str(err.value) == "The 'globalparam' option expects one argument."
def test_dispatcher_generic_setup_default(): """Generic parameter handling for default values.""" cmd = create_command("somecommand") groups = [("test-group", "title", [cmd])] logsetup.message_handler.mode = None with patch("charmcraft.config.load") as config_mock: Dispatcher(["somecommand"], groups) assert logsetup.message_handler.mode is None config_mock.assert_called_once_with(None)
def test_dispatcher_command_loading(): """Parses and return global arguments.""" cmd = create_command("somecommand") groups = [CommandGroup("title", [cmd])] dispatcher = Dispatcher(groups) dispatcher.pre_parse_args(["somecommand"]) command = dispatcher.load_command("test-config") assert isinstance(command, cmd) assert command.config == "test-config"
def test_dispatcher_config_not_needed(): """Command does not needs a config.""" class MyCommand(BaseCommand): help_msg = "some help" name = 'cmdname' def run(self, parsed_args): pass groups = [('test-group', 'title', [MyCommand])] dispatcher = Dispatcher(['cmdname'], groups) dispatcher.run()
def test_dispatcher_build_commands_ok(): """Correct command loading.""" cmd0, cmd1, cmd2 = [create_command("cmd-name-{}".format(n), "cmd help") for n in range(3)] groups = [ CommandGroup("whatever title", [cmd0]), CommandGroup("other title", [cmd1, cmd2]), ] dispatcher = Dispatcher(groups) assert len(dispatcher.commands) == 3 for cmd in [cmd0, cmd1, cmd2]: expected_class = dispatcher.commands[cmd.name] assert expected_class == cmd
def test_dispatcher_config_not_needed(): """Command does not needs a config.""" class MyCommand(BaseCommand): help_msg = "some help" name = "cmdname" def run(self, parsed_args): pass groups = [("test-group", "title", [MyCommand])] dispatcher = Dispatcher(["cmdname"], groups) dispatcher.run()
def test_dispatcher_command_execution_crash(): """Command crashing doesn't pass through, we inform nicely.""" class MyCommand(BaseCommand): help_msg = "some help" name = "cmdname" def run(self, parsed_args): raise ValueError() groups = [("test-group", "title", [MyCommand])] dispatcher = Dispatcher(["cmdname"], groups) with pytest.raises(ValueError): dispatcher.run()