Ejemplo n.º 1
0
def test_default_help_text(help_builder):
    """All different parts for the default help."""
    cmd1 = create_command("cmd1",
                          "Cmd help which is very long but whatever.",
                          common_=True)
    cmd2 = create_command("command-2", "Cmd help.", common_=True)
    cmd3 = create_command("cmd3",
                          "Extremely " + "super crazy long " * 5 + " help.",
                          common_=True)
    cmd4 = create_command("cmd4", "Some help.")
    cmd5 = create_command("cmd5", "More help.")
    cmd6 = create_command("cmd6-really-long", "More help.", common_=True)
    cmd7 = create_command("cmd7", "More help.")

    command_groups = [
        CommandGroup("group1", [cmd6, cmd2]),
        CommandGroup("group3", [cmd7]),
        CommandGroup("group2", [cmd3, cmd4, cmd5, cmd1]),
    ]
    fake_summary = textwrap.dedent("""
        This is the summary for
        the whole program.
    """)
    global_options = [
        ("-h, --help", "Show this help message and exit."),
        ("-q, --quiet", "Only show warnings and errors, not progress."),
    ]

    help_builder.init("testapp", fake_summary, command_groups)
    text = help_builder.get_full_help(global_options)

    expected = textwrap.dedent("""\
        Usage:
            testapp [help] <command>

        Summary:
            This is the summary for
            the whole program.

        Global options:
                  -h, --help:  Show this help message and exit.
                 -q, --quiet:  Only show warnings and errors, not progress.

        Starter commands:
                        cmd1:  Cmd help which is very long but whatever.
                        cmd3:  Extremely super crazy long super crazy long super
                               crazy long super crazy long super crazy long
                               help.
            cmd6-really-long:  More help.
                   command-2:  Cmd help.

        Commands can be classified as follows:
                      group1:  cmd6-really-long, command-2
                      group2:  cmd1, cmd3, cmd4, cmd5
                      group3:  cmd7

        For more information about a command, run 'charmcraft help <command>'.
        For a summary of all commands, run 'charmcraft help --all'.
    """)
    assert text == expected
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
def test_command_help_text_no_parameters(config, help_builder):
    """All different parts for a specific command help that doesn't have parameters."""
    overview = textwrap.dedent("""
        Quite some long text.

        Multiline!
    """)
    cmd1 = create_command("somecommand",
                          "Command one line help.",
                          overview_=overview)
    cmd2 = create_command("other-cmd-2", "Some help.")
    cmd3 = create_command("other-cmd-3", "Some help.")
    cmd4 = create_command("other-cmd-4", "Some help.")
    command_groups = [
        CommandGroup("group1", [cmd1, cmd2, cmd4]),
        CommandGroup("group2", [cmd3]),
    ]

    options = [
        ("-h, --help", "Show this help message and exit."),
        ("-q, --quiet", "Only show warnings and errors, not progress."),
        ("--name", "The name of the charm."),
        ("--revision", "The revision to release (defaults to latest)."),
    ]

    help_builder.init("testapp", "general summary", command_groups)
    text = help_builder.get_command_help(cmd1(config), options)

    expected = textwrap.dedent("""\
        Usage:
            charmcraft somecommand [options]

        Summary:
            Quite some long text.

            Multiline!

        Options:
             -h, --help:  Show this help message and exit.
            -q, --quiet:  Only show warnings and errors, not progress.
                 --name:  The name of the charm.
             --revision:  The revision to release (defaults to latest).

        See also:
            other-cmd-2
            other-cmd-4

        For a summary of all commands, run 'charmcraft help --all'.
    """)
    assert text == expected
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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 = [CommandGroup("title", [MyCommand1, MyCommand2])]
    dispatcher = Dispatcher(groups)
    dispatcher.pre_parse_args(["command1"])
    dispatcher.load_command("config")
    dispatcher.run()
    assert isinstance(MyCommand1._executed[0], argparse.Namespace)
Ejemplo n.º 6
0
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",
    ]
Ejemplo n.º 7
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
Ejemplo n.º 8
0
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"
Ejemplo n.º 9
0
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]
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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."
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
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."
Ejemplo n.º 14
0
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"
Ejemplo n.º 15
0
def test_dispatcher_generic_setup_paramglobal_without_param_confusing(options):
    """Generic parameter handling for a param type global arg confusing the command as the arg."""
    cmd = create_command("somecommand")
    groups = [CommandGroup("title", [cmd])]
    extra = GlobalArgument("globalparam", "option", "-g", "--globalparam", "Test global param.")
    dispatcher = Dispatcher(groups, [extra])
    with patch("charmcraft.helptexts.HelpBuilder.get_full_help") as mock_helper:
        mock_helper.return_value = "help text"
        with pytest.raises(ArgumentParsingError) as err:
            dispatcher.pre_parse_args(options)

    # generic usage message because "no command" (as 'somecommand' was consumed by --globalparam)
    assert str(err.value) == "help text"
Ejemplo n.º 16
0
def test_dispatcher_build_commands_repeated():
    """Error while loading commands with repeated name."""

    class Foo(BaseCommand):
        help_msg = "some help"
        name = "repeated"

    class Bar(BaseCommand):
        help_msg = "some help"
        name = "cool"

    class Baz(BaseCommand):
        help_msg = "some help"
        name = "repeated"

    groups = [
        CommandGroup("whatever title", [Foo, Bar]),
        CommandGroup("other title", [Baz]),
    ]
    expected_msg = "Multiple commands with same name: (Foo|Baz) and (Baz|Foo)"
    with pytest.raises(RuntimeError, match=expected_msg):
        Dispatcher(groups)
Ejemplo n.º 17
0
def test_main_load_config_not_present_ok():
    """Command ends indicating the return code to be used."""

    class MyCommand(BaseCommand):
        help_msg = "some help"
        name = "cmdname"

        def run(self, parsed_args):
            assert self.config.type is None
            assert not self.config.project.config_provided

    with patch("charmcraft.main.COMMAND_GROUPS", [CommandGroup("title", [MyCommand])]):
        retcode = main(["charmcraft", "cmdname", "--project-dir=/whatever"])
    assert retcode == 0
Ejemplo n.º 18
0
def test_main_load_config_not_present_but_needed(capsys):
    """Command ends indicating the return code to be used."""
    cmd = create_command("cmdname", needs_config_=True)
    with patch("charmcraft.main.COMMAND_GROUPS", [CommandGroup("title", [cmd])]):
        retcode = main(["charmcraft", "cmdname", "--project-dir=/whatever"])
    assert retcode == 1

    out, err = capsys.readouterr()
    assert not out
    assert err == (
        "The specified command needs a valid 'charmcraft.yaml' configuration file (in "
        "the current directory or where specified with --project-dir option); see "
        "the reference: https://discourse.charmhub.io/t/charmcraft-configuration/4138\n"
    )
Ejemplo n.º 19
0
def test_command_help_text_loneranger(config, help_builder):
    """All different parts for a specific command that's the only one in its group."""
    overview = textwrap.dedent("""
        Quite some long text.
    """)
    cmd1 = create_command("somecommand",
                          "Command one line help.",
                          overview_=overview)
    cmd2 = create_command("other-cmd-2", "Some help.")
    command_groups = [
        CommandGroup("group1", [cmd1]),
        CommandGroup("group2", [cmd2]),
    ]

    options = [
        ("-h, --help", "Show this help message and exit."),
        ("-q, --quiet", "Only show warnings and errors, not progress."),
    ]

    help_builder.init("testapp", "general summary", command_groups)
    text = help_builder.get_command_help(cmd1(config), options)

    expected = textwrap.dedent("""\
        Usage:
            charmcraft somecommand [options]

        Summary:
            Quite some long text.

        Options:
             -h, --help:  Show this help message and exit.
            -q, --quiet:  Only show warnings and errors, not progress.

        For a summary of all commands, run 'charmcraft help --all'.
    """)
    assert text == expected
Ejemplo n.º 20
0
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 = [CommandGroup("title", [MyCommand])]
    dispatcher = Dispatcher(groups)
    dispatcher.pre_parse_args(["cmdname"])
    dispatcher.load_command("config")
    with pytest.raises(ValueError):
        dispatcher.run()
Ejemplo n.º 21
0
def test_dispatcher_command_return_code():
    """Command ends indicating the return code to be used."""

    class MyCommand(BaseCommand):
        help_msg = "some help"
        name = "cmdname"

        def run(self, parsed_args):
            return 17

    groups = [CommandGroup("title", [MyCommand])]
    dispatcher = Dispatcher(groups)
    dispatcher.pre_parse_args(["cmdname"])
    dispatcher.load_command("config")
    retcode = dispatcher.run()
    assert retcode == 17
Ejemplo n.º 22
0
def test_tool_exec_help_command_on_command_wrong():
    """Execute charmcraft asking for help on a command which does not exist."""
    command_groups = [CommandGroup("group", [])]
    dispatcher = Dispatcher(command_groups)

    with patch("charmcraft.helptexts.HelpBuilder.get_usage_message") as mock:
        mock.return_value = "test help"
        with pytest.raises(ArgumentParsingError) as cm:
            dispatcher.pre_parse_args(["help", "wrongcommand"])
    error = cm.value

    # check the given information to the help text builder
    assert mock.call_args[0] == (
        "command 'wrongcommand' not found to provide help for", )

    # check the result of the help builder is what is shown
    assert str(error) == "test help"
Ejemplo n.º 23
0
def test_main_load_config_ok(create_config):
    """Command is properly executed, after loading and receiving the config."""
    tmp_path = create_config(
        """
        type: charm
    """
    )

    class MyCommand(BaseCommand):
        help_msg = "some help"
        name = "cmdname"

        def run(self, parsed_args):
            assert self.config.type == "charm"

    with patch("charmcraft.main.COMMAND_GROUPS", [CommandGroup("title", [MyCommand])]):
        retcode = main(["charmcraft", "cmdname", f"--project-dir={tmp_path}"])
    assert retcode == 0
Ejemplo n.º 24
0
def test_command_help_text_with_parameters(config, help_builder):
    """All different parts for a specific command help that has parameters."""
    overview = textwrap.dedent("""
        Quite some long text.
    """)
    cmd1 = create_command("somecommand",
                          "Command one line help.",
                          overview_=overview)
    cmd2 = create_command("other-cmd-2", "Some help.")
    command_groups = [
        CommandGroup("group1", [cmd1, cmd2]),
    ]

    options = [
        ("-h, --help", "Show this help message and exit."),
        ("name", "The name of the charm."),
        ("--revision", "The revision to release (defaults to latest)."),
        ("extraparam", "Another parameter.."),
        ("--other-option", "Other option."),
    ]

    help_builder.init("testapp", "general summary", command_groups)
    text = help_builder.get_command_help(cmd1(config), options)

    expected = textwrap.dedent("""\
        Usage:
            charmcraft somecommand [options] <name> <extraparam>

        Summary:
            Quite some long text.

        Options:
                -h, --help:  Show this help message and exit.
                --revision:  The revision to release (defaults to latest).
            --other-option:  Other option.

        See also:
            other-cmd-2

        For a summary of all commands, run 'charmcraft help --all'.
    """)
    assert text == expected
Ejemplo n.º 25
0
def test_tool_exec_command_wrong_option(help_builder):
    """Execute a correct command but with a wrong option."""
    cmd = create_command("somecommand", "This command does that.")
    command_groups = [CommandGroup("group", [cmd])]
    dispatcher = Dispatcher(command_groups)
    dispatcher.pre_parse_args(["somecommand", "--whatever"])

    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: unrecognized arguments: --whatever
        """)

    error = cm.value
    assert str(error) == expected
Ejemplo n.º 26
0
def test_tool_exec_help_command_all():
    """Execute charmcraft asking for detailed help."""
    command_groups = [CommandGroup("group", [])]
    dispatcher = Dispatcher(command_groups)

    with patch("charmcraft.helptexts.HelpBuilder.get_detailed_help") as mock:
        mock.return_value = "test help"
        with pytest.raises(ProvideHelpException) as cm:
            dispatcher.pre_parse_args(["help", "--all"])

    # check the result of the 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 sorted(x[0] for x in args[0]) == [
        "-h, --help",
        "-q, --quiet",
        "-t, --trace",
        "-v, --verbose",
    ]
Ejemplo n.º 27
0
def test_tool_exec_help_command_on_command_complex():
    """Execute charmcraft asking for help on a command with parameters and options."""
    def fill_parser(self, parser):
        parser.add_argument("param1", help="help on param1")
        parser.add_argument("param2", help="help on param2")
        parser.add_argument("param3",
                            metavar="transformed3",
                            help="help on param2")
        parser.add_argument("--option1", help="help on option1")
        parser.add_argument("-o2", "--option2", help="help on option2")

    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(["help", "somecommand"])

    # check the result of the 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
    expected_options = [
        "--option1",
        "-h, --help",
        "-o2, --option2",
        "-q, --quiet",
        "-t, --trace",
        "-v, --verbose",
        "param1",
        "param2",
        "transformed3",
    ]
    assert sorted(x[0] for x in args[1]) == expected_options
Ejemplo n.º 28
0
def test_tool_exec_help_command_on_command_ok():
    """Execute charmcraft asking for help on a command ok."""
    cmd = create_command("somecommand", "This command does that.")
    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(["help", "somecommand"])

    # check the result of the 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 isinstance(args[0], cmd)
    assert sorted(x[0] for x in args[1]) == [
        "-h, --help",
        "-q, --quiet",
        "-t, --trace",
        "-v, --verbose",
    ]
Ejemplo n.º 29
0
def test_dispatcher_command_execution_ok():
    """Command execution depends of the indicated name in command line, return code ok."""

    class MyCommandControl(BaseCommand):
        help_msg = "some help"

        def run(self, parsed_args):
            self._executed.append(parsed_args)

    class MyCommand1(MyCommandControl):
        name = "name1"
        _executed = []

    class MyCommand2(MyCommandControl):
        name = "name2"
        _executed = []

    groups = [CommandGroup("title", [MyCommand1, MyCommand2])]
    dispatcher = Dispatcher(groups)
    dispatcher.pre_parse_args(["name2"])
    dispatcher.load_command("config")
    dispatcher.run()
    assert MyCommand1._executed == []
    assert isinstance(MyCommand2._executed[0], argparse.Namespace)
Ejemplo n.º 30
0
def test_dispatcher_pre_parsing():
    """Parses and return global arguments."""
    groups = [CommandGroup("title", [create_command("somecommand")])]
    dispatcher = Dispatcher(groups)
    global_args = dispatcher.pre_parse_args(["-q", "somecommand"])
    assert global_args == {"help": False, "verbose": False, "quiet": True, "trace": False}