def assert_bash_exec(
        bash: pexpect.spawn, cmd: str, want_output: bool = False) -> str:

    # Send command
    bash.sendline(cmd)
    bash.expect_exact(cmd)

    # Find prompt, output is before it
    bash.expect_exact("\r\n" + PS1)
    output = bash.before

    # Retrieve exit status
    echo = "echo $?"
    bash.sendline(echo)
    got = bash.expect([
        r"^%s\r\n(\d+)\r\n%s" % (re.escape(echo), re.escape(PS1)),
        PS1,
        pexpect.EOF,
        pexpect.TIMEOUT,
    ])
    status = bash.match.group(1) if got == 0 else "unknown"

    assert status == "0", \
        'Error running "%s": exit status=%s, output="%s"' % \
        (cmd, status, output)
    if output:
        assert want_output, \
            'Unexpected output from "%s": exit status=%s, output="%s"' % \
            (cmd, status, output)
    else:
        assert not want_output, \
            'Expected output from "%s": exit status=%s, output="%s"' % \
            (cmd, status, output)

    return output
Exemple #2
0
def is_bash_type(bash: pexpect.spawn, cmd: str) -> bool:
    typecmd = "type %s &>/dev/null && echo -n 0 || echo -n 1" % cmd
    bash.sendline(typecmd)
    bash.expect_exact(typecmd + "\r\n")
    result = bash.expect_exact(["0", "1"]) == 0
    bash.expect_exact(PS1)
    return result
def is_bash_type(bash: pexpect.spawn, cmd: str) -> bool:
    typecmd = "type %s &>/dev/null && echo -n 0 || echo -n 1" % cmd
    bash.sendline(typecmd)
    bash.expect_exact(typecmd + "\r\n")
    result = bash.expect_exact(["0", "1"]) == 0
    bash.expect_exact(PS1)
    return result
def assert_complete(bash: pexpect.spawn, cmd: str,
                    **kwargs) -> CompletionResult:
    skipif = kwargs.get("skipif")
    if skipif:
        try:
            assert_bash_exec(bash, skipif, want_output=None)
        except AssertionError:
            pass
        else:
            pytest.skip(skipif)
    xfail = kwargs.get("xfail")
    if xfail:
        try:
            assert_bash_exec(bash, xfail, want_output=None)
        except AssertionError:
            pass
        else:
            pytest.xfail(xfail)

    with bash_env_saved(bash, sendintr=True) as bash_env:

        cwd = kwargs.get("cwd")
        if cwd:
            bash_env.chdir(str(cwd))

        for k, v in kwargs.get("env", {}).items():
            bash_env.write_env(k, v, quote=False)

        for k, v in kwargs.get("shopt", {}).items():
            bash_env.shopt(k, v)

        bash.send(cmd + "\t")
        # Sleep a bit if requested, to avoid `.*` matching too early
        time.sleep(kwargs.get("sleep_after_tab", 0))
        bash.expect_exact(kwargs.get("rendered_cmd", cmd))
        bash.send(MAGIC_MARK)
        got = bash.expect([
            # 0: multiple lines, result in .before
            r"\r\n" + re.escape(PS1 + cmd) + ".*" + re.escape(MAGIC_MARK),
            # 1: no completion
            r"^" + re.escape(MAGIC_MARK),
            # 2: on same line, result in .match
            r"^([^\r]+)%s$" % re.escape(MAGIC_MARK),
            pexpect.EOF,
            pexpect.TIMEOUT,
        ])
        if got == 0:
            output = bash.before
            if output.endswith(MAGIC_MARK):
                output = bash.before[:-len(MAGIC_MARK)]
            return CompletionResult(output)
        elif got == 2:
            output = bash.match.group(1)
            return CompletionResult(output)
        else:
            # TODO: warn about EOF/TIMEOUT?
            return CompletionResult()
Exemple #5
0
def assert_bash_exec(
    bash: pexpect.spawn,
    cmd: str,
    want_output: Optional[bool] = False,
    want_newline=True,
) -> str:
    """
    :param want_output: if None, don't care if got output or not
    """

    # Send command
    bash.sendline(cmd)
    bash.expect_exact(cmd)

    # Find prompt, output is before it
    bash.expect_exact("%s%s" % ("\r\n" if want_newline else "", PS1))
    output = bash.before

    # Retrieve exit status
    echo = "echo $?"
    bash.sendline(echo)
    got = bash.expect(
        [
            r"^%s\r\n(\d+)\r\n%s" % (re.escape(echo), re.escape(PS1)),
            PS1,
            pexpect.EOF,
            pexpect.TIMEOUT,
        ]
    )
    status = bash.match.group(1) if got == 0 else "unknown"

    assert status == "0", 'Error running "%s": exit status=%s, output="%s"' % (
        cmd,
        status,
        output,
    )
    if want_output is not None:
        if output:
            assert (
                want_output
            ), 'Unexpected output from "%s": exit status=%s, output="%s"' % (
                cmd,
                status,
                output,
            )
        else:
            assert (
                not want_output
            ), 'Expected output from "%s": exit status=%s, output="%s"' % (
                cmd,
                status,
                output,
            )

    return output
Exemple #6
0
def assert_complete_at_point(
    bash: pexpect.spawn, cmd: str, trail: str
) -> CompletionResult:
    # TODO: merge to assert_complete
    fullcmd = "%s%s%s" % (
        cmd,
        trail,
        "\002" * len(trail),
    )  # \002 = ^B = cursor left
    bash.send(fullcmd + "\t")
    bash.send(MAGIC_MARK)
    bash.expect_exact(fullcmd.replace("\002", "\b"))

    got = bash.expect_exact(
        [
            # 0: multiple lines, result in .before
            PS1 + fullcmd.replace("\002", "\b"),
            # 1: no completion
            MAGIC_MARK,
            pexpect.EOF,
            pexpect.TIMEOUT,
        ]
    )
    if got == 0:
        output = bash.before
        result = CompletionResult(output)

        # At this point, something weird happens. For most test setups, as
        # expected (pun intended!), MAGIC_MARK follows as is. But for some
        # others (e.g. CentOS 6, Ubuntu 14 test containers), we get MAGIC_MARK
        # one character a time, followed each time by trail and the corresponding
        # number of \b's. Don't know why, but accept it until/if someone finds out.
        # Or just be fine with it indefinitely, the visible and practical end
        # result on a terminal is the same anyway.
        repeat = "(%s%s)?" % (re.escape(trail), "\b" * len(trail))
        fullexpected = "".join(
            "%s%s" % (re.escape(x), repeat) for x in MAGIC_MARK
        )
        bash.expect(fullexpected)
    else:
        # TODO: warn about EOF/TIMEOUT?
        result = CompletionResult()

    return result
Exemple #7
0
def assert_bash_exec(bash: pexpect.spawn,
                     cmd: str,
                     want_output: bool = False) -> str:

    # Send command
    bash.sendline(cmd)
    bash.expect_exact(cmd)

    # Find prompt, output is before it
    bash.expect_exact("\r\n" + PS1)
    output = bash.before

    # Retrieve exit status
    echo = "echo $?"
    bash.sendline(echo)
    got = bash.expect([
        r"^%s\r\n(\d+)\r\n%s" % (re.escape(echo), re.escape(PS1)),
        PS1,
        pexpect.EOF,
        pexpect.TIMEOUT,
    ])
    status = bash.match.group(1) if got == 0 else "unknown"

    assert status == "0", 'Error running "%s": exit status=%s, output="%s"' % (
        cmd,
        status,
        output,
    )
    if output:
        assert want_output, (
            'Unexpected output from "%s": exit status=%s, output="%s"' %
            (cmd, status, output))
    else:
        assert not want_output, (
            'Expected output from "%s": exit status=%s, output="%s"' %
            (cmd, status, output))

    return output
Exemple #8
0
def assert_complete(bash: pexpect.spawn, cmd: str,
                    **kwargs) -> CompletionResult:
    skipif = kwargs.get("skipif")
    if skipif:
        try:
            assert_bash_exec(bash, skipif)
        except AssertionError:
            pass
        else:
            pytest.skip(skipif)
    xfail = kwargs.get("xfail")
    if xfail:
        try:
            assert_bash_exec(bash, xfail)
        except AssertionError:
            pass
        else:
            pytest.xfail(xfail)
    cwd = kwargs.get("cwd")
    if cwd:
        assert_bash_exec(bash, "cd '%s'" % cwd)
    env_prefix = "_BASHCOMP_TEST_"
    env = kwargs.get("env", {})
    if env:
        # Back up environment and apply new one
        assert_bash_exec(
            bash,
            " ".join('%s%s="$%s"' % (env_prefix, k, k) for k in env.keys()),
        )
        assert_bash_exec(
            bash,
            "export %s" % " ".join("%s=%s" % (k, v) for k, v in env.items()),
        )
    bash.send(cmd + "\t")
    bash.expect_exact(cmd)
    bash.send(MAGIC_MARK)
    got = bash.expect([
        # 0: multiple lines, result in .before
        r"\r\n" + re.escape(PS1 + cmd) + ".*" + MAGIC_MARK,
        # 1: no completion
        r"^" + MAGIC_MARK,
        # 2: on same line, result in .match
        r"^([^\r]+)%s$" % MAGIC_MARK,
        pexpect.EOF,
        pexpect.TIMEOUT,
    ])
    if got == 0:
        output = bash.before
        if output.endswith(MAGIC_MARK):
            output = bash.before[:-len(MAGIC_MARK)]
        result = CompletionResult(output)
    elif got == 2:
        output = bash.match.group(1)
        result = CompletionResult(output, [shlex.split(cmd + output)[-1]])
    else:
        # TODO: warn about EOF/TIMEOUT?
        result = CompletionResult("", [])
    bash.sendintr()
    bash.expect_exact(PS1)
    if env:
        # Restore environment, and clean up backup
        # TODO: Test with declare -p if a var was set, backup only if yes, and
        #       similarly restore only backed up vars. Should remove some need
        #       for ignore_env.
        assert_bash_exec(
            bash,
            "export %s" % " ".join('%s="$%s%s"' % (k, env_prefix, k)
                                   for k in env.keys()),
        )
        assert_bash_exec(
            bash,
            "unset -v %s" % " ".join("%s%s" % (env_prefix, k)
                                     for k in env.keys()),
        )
    if cwd:
        assert_bash_exec(bash, "cd - >/dev/null")
    return result
Exemple #9
0
def slow_sendline(exp: spawn, string: str):
    """Send one character at a time and wait for echo. The expect session deadlocks if we don't do this."""
    for i in range(len(string)):
        exp.send(string[i])
        exp.expect_exact(string[i])
    exp.send('\n')
Exemple #10
0
def wait_for_pexpect_process(process: pexpect.spawn) -> int:
    process.expect_exact(pexpect.EOF)
    return process.wait()
def assert_complete(
        bash: pexpect.spawn, cmd: str, **kwargs) -> CompletionResult:
    skipif = kwargs.get("skipif")
    if skipif:
        try:
            assert_bash_exec(bash, skipif)
        except AssertionError:
            pass
        else:
            pytest.skip(skipif)
            return CompletionResult("", [])
    cwd = kwargs.get("cwd")
    if cwd:
        assert_bash_exec(bash, "cd '%s'" % cwd)
    env_prefix = "_BASHCOMP_TEST_"
    env = kwargs.get("env", {})
    if env:
        # Back up environment and apply new one
        assert_bash_exec(bash, " ".join(
            '%s%s="$%s"' % (env_prefix, k, k) for k in env.keys()
        ))
        assert_bash_exec(bash, "export %s" % " ".join(
            "%s=%s" % (k, v) for k, v in env.items()
        ))
    bash.send(cmd + "\t")
    bash.expect_exact(cmd)
    bash.send(MAGIC_MARK)
    got = bash.expect([
        r"\r\n" + re.escape(PS1 + cmd),  # 0: multiple lines, result in .before
        r"^" + MAGIC_MARK,               # 1: no completion
        r"^[^\r]+",                      # 2: on same line, result in .after
        pexpect.EOF,
        pexpect.TIMEOUT,
    ])
    if got == 0:
        line = bash.before
        if line.endswith(MAGIC_MARK):
            line = bash.before[:-len(MAGIC_MARK)]
        result = CompletionResult(
            line,
            sorted(x for x in re.split(r" {2,}|\r\n", line) if x),
        )
    elif got == 2:
        line = bash.after
        if line.endswith(MAGIC_MARK):
            line = bash.after[:-len(MAGIC_MARK)]
        result = CompletionResult(
            line,
            [shlex.split(cmd + line)[-1]],
        )
    else:
        # TODO: warn about EOF/TIMEOUT?
        result = CompletionResult("", [])
    bash.sendintr()
    bash.expect_exact(PS1)
    if env:
        # Restore environment, and clean up backup
        # TODO: Test with declare -p if a var was set, backup only if yes, and
        #       similarly restore only backed up vars. Should remove some need
        #       for ignore_env.
        assert_bash_exec(bash, "export %s" % " ".join(
            '%s="$%s%s"' % (k, env_prefix, k) for k in env.keys()
        ))
        assert_bash_exec(bash, "unset -v %s" % " ".join(
            "%s%s" % (env_prefix, k) for k in env.keys()
        ))
    if cwd:
        assert_bash_exec(bash, "cd - >/dev/null")
    return result