Exemple #1
0
def command(mach: str, cmd: str) -> log.EventIO:
    """
    Log a command's execution.

    :param str mach: Name of the machine the command is run on
    :param str cmd: The command itself
    :rtype: EventIO
    :returns: A stream that the output of the command should
        be written to.
    """
    ev = log.EventIO(
        ["cmd", mach],
        "[" + c(mach).yellow + "] " + c(cmd).dark,
        verbosity=log.Verbosity.COMMAND,
        cmd=cmd,
    )

    if log.INTERACTIVE:
        if input(ev._prefix() +
                 c("  OK [Y/n]? ").magenta).upper() not in ("", "Y"):
            raise RuntimeError("Aborted by user")

    ev.prefix = "   ## "
    ev.verbosity = log.Verbosity.STDOUT
    return ev
Exemple #2
0
def testcase_begin(name: str) -> None:
    """
    Log a testcase's beginning.

    :param str name: Name of the testcase
    """
    log.EventIO(
        ["tc", "begin"],
        "Calling " + c(name).cyan.bold + " ...",
        verbosity=log.Verbosity.QUIET,
        name=name,
    )
    log.NESTING += 1
Exemple #3
0
def exception(name: str, trace: str) -> log.EventIO:
    ev = log.EventIO(
        ["exception"],
        log.c("Exception").red.bold + ":",
        verbosity=log.Verbosity.QUIET,
        name=name,
        trace=trace,
    )

    ev.prefix = "  "
    ev.write(trace)

    return ev
Exemple #4
0
def tbot_end(success: bool) -> None:
    log.message(
        log.c(
            log.u(
                "────────────────────────────────────────",
                "----------------------------------------",
            )).dark)

    if log.LOGFILE is not None:
        log.message(f"Log written to {log.LOGFILE.name!r}")

    msg = log.c("SUCCESS").green.bold if success else log.c("FAILURE").red.bold
    duration = time.monotonic() - log.START_TIME
    log.EventIO(
        ["tbot", "end"],
        msg + f" ({duration:.3f}s)",
        nest_first=log.u("└─", "\\-"),
        verbosity=log.Verbosity.QUIET,
        success=success,
        duration=duration,
    )
Exemple #5
0
def testcase_end(name: str, duration: float, success: bool = True) -> None:
    """
    Log a testcase's end.

    :param float duration: Time passed while this testcase ran
    :param bool success: Whether the testcase succeeded
    """
    if success:
        success_string = c("Done").green.bold
    else:
        success_string = c("Fail").red.bold

    log.EventIO(
        ["tc", "end"],
        success_string + f". ({duration:.3f}s)",
        nest_first=u("└─", "\\-"),
        verbosity=log.Verbosity.QUIET,
        name=name,
        duration=duration,
        success=success,
    )
    log.NESTING -= 1
Exemple #6
0
def testcase_end(
    name: str,
    duration: float,
    success: bool = True,
    skipped: typing.Optional[str] = None,
) -> None:
    """
    Log a testcase's end.

    :param float duration: Time passed while this testcase ran
    :param bool success: Whether the testcase succeeded
    :param str skipped: ``None`` if the testcase ran normally or a string (the
        reason) if it was skipped.  If a testcase was skipped, ``success`` is
        ignored.
    """
    if skipped is not None:
        message = c("Skipped").yellow.bold + f": {skipped}"
        skip_info = {"skip_reason": skipped}
    else:
        # Testcase ran normally
        skip_info = {}
        if success:
            message = c("Done").green.bold + f". ({duration:.3f}s)"
        else:
            message = c("Fail").red.bold + f". ({duration:.3f}s)"

    log.EventIO(
        ["tc", "end"],
        message,
        nest_first=u("└─", "\\-"),
        verbosity=log.Verbosity.QUIET,
        name=name,
        duration=duration,
        success=success,
        skipped=skipped is not None,
        **skip_info,
    )
    log.NESTING -= 1
Exemple #7
0
def main() -> None:  # noqa: C901
    """Tbot main entry point."""
    parser = argparse.ArgumentParser(prog=__about__.__title__,
                                     description=__about__.__summary__)

    parser.add_argument("testcase",
                        nargs="*",
                        help="testcase that should be run.")

    parser.add_argument("-b",
                        "--board",
                        help="use this board instead of the default.")

    parser.add_argument("-l",
                        "--lab",
                        help="use this lab instead of the default.")

    parser.add_argument(
        "-T",
        metavar="TC-DIR",
        dest="tcdirs",
        action="append",
        default=[],
        help="add a directory to the testcase search path.",
    )

    parser.add_argument(
        "-t",
        metavar="TC-FILE",
        dest="tcfiles",
        action="append",
        default=[],
        help="add a file to the testcase search path.",
    )

    parser.add_argument(
        "-f",
        metavar="FLAG",
        dest="flags",
        action="append",
        default=[],
        help="set a user defined flag to change testcase behaviour",
    )

    parser.add_argument("-v",
                        dest="verbosity",
                        action="count",
                        default=0,
                        help="increase the verbosity")

    parser.add_argument("-q",
                        dest="quiet",
                        action="count",
                        default=0,
                        help="decrease the verbosity")

    parser.add_argument("--version",
                        action="version",
                        version=f"%(prog)s {__about__.__version__}")

    parser.add_argument("--log",
                        metavar="LOGFILE",
                        help="Alternative location for the json log file")

    flags = [
        (["--list-testcases"],
         "list all testcases in the current search path."),
        (["--list-labs"], "list all available labs."),
        (["--list-boards"], "list all available boards."),
        (["--list-files"], "list all testcase files."),
        (["--list-flags"], "list all flags defined in lab or board config."),
        (["-s",
          "--show"], "show testcase signatures instead of running them."),
        (["-i", "--interactive"], "prompt before running each command."),
    ]

    for flag_names, flag_help in flags:
        parser.add_argument(*flag_names, action="store_true", help=flag_help)

    args = parser.parse_args()

    from tbot import log

    # Determine LogFile
    if args.log:
        log.LOGFILE = open(args.log, "w")
    else:
        logdir = pathlib.Path.cwd() / "log"
        logdir.mkdir(exist_ok=True)

        lab_name = "none" if args.lab is None else pathlib.Path(args.lab).stem
        board_name = "none" if args.board is None else pathlib.Path(
            args.board).stem

        prefix = f"{lab_name}-{board_name}"
        glob_pattern = f"{prefix}-*.json"
        new_num = sum(1 for _ in logdir.glob(glob_pattern)) + 1
        logfile = logdir / f"{prefix}-{new_num:04}.json"
        # Ensure logfile will not overwrite another one
        while logfile.exists():
            new_num += 1
            logfile = logdir / f"{prefix}-{new_num:04}.json"

        log.LOGFILE = open(logfile, "w")

    log.VERBOSITY = log.Verbosity(1 + args.verbosity - args.quiet)

    if args.list_labs:
        raise NotImplementedError()

    if args.list_boards:
        raise NotImplementedError()

    from tbot import loader

    files = loader.get_file_list(
        (pathlib.Path(d).resolve() for d in args.tcdirs),
        (pathlib.Path(f).resolve() for f in args.tcfiles),
    )

    if args.list_files:
        for f in files:
            print(f"{f}")
        return

    testcases = loader.collect_testcases(files)

    if args.list_testcases:
        for tc in testcases:
            print(tc)
        return

    if args.show:
        import textwrap
        import inspect

        for i, name in enumerate(args.testcase):
            if i != 0:
                print(log.c("\n=================================\n").dark)
            func = testcases[name]
            signature = f"def {name}{str(inspect.signature(func))}:\n    ..."
            try:
                import pygments
                from pygments.lexers import PythonLexer
                from pygments.formatters import TerminalFormatter

                print(
                    pygments.highlight(signature, PythonLexer(),
                                       TerminalFormatter()).strip())
            except ImportError:
                print(log.c(signature).bold.yellow)
            print(log.c(f"----------------").dark)
            print(
                log.c(
                    textwrap.dedent(
                        func.__doc__
                        or "No docstring available.").strip()).green)
        return

    if args.interactive:
        log.INTERACTIVE = True

    print(log.c("TBot").yellow.bold + " starting ...")

    import tbot

    for flag in args.flags:
        tbot.flags.add(flag)

    # Set the actual selected types, needs to be ignored by mypy
    # beause this is obviously not good python
    lab = None
    if args.lab is not None:
        lab = loader.load_module(pathlib.Path(args.lab).resolve())
        tbot.selectable.LabHost = lab.LAB  # type: ignore
    else:
        pass

    board = None
    if args.board is not None:
        board = loader.load_module(pathlib.Path(args.board).resolve())
        tbot.selectable.Board = board.BOARD  # type: ignore
        if hasattr(board, "UBOOT"):
            tbot.selectable.UBootMachine = board.UBOOT  # type: ignore
        if hasattr(board, "LINUX"):
            tbot.selectable.LinuxMachine = board.LINUX  # type: ignore
    else:
        pass

    if args.list_flags:
        import typing

        all_flags: typing.Dict[str, str] = dict()
        if lab is not None and "FLAGS" in lab.__dict__:
            all_flags.update(lab.__dict__["FLAGS"])

        if board is not None and "FLAGS" in board.__dict__:
            all_flags.update(board.__dict__["FLAGS"])

        width = max(map(len, flags))
        for name, description in all_flags.items():
            log.message(log.c(name.ljust(width)).blue + ": " + description)

    from tbot import log_event

    try:
        for tc in args.testcase:
            testcases[tc]()
    except Exception as e:  # noqa: E722
        import traceback

        trace = traceback.format_exc()
        with log.EventIO(
            ["exception"],
                log.c("Exception").red.bold + ":",
                verbosity=log.Verbosity.QUIET,
                name=e.__class__.__name__,
                trace=trace,
        ) as ev:
            ev.prefix = "  "
            ev.write(trace)

        log_event.tbot_end(False)
        sys.exit(1)
    except KeyboardInterrupt:
        log.message(
            log.c("Exception").red.bold + ":\n    Test run manually aborted.",
            verbosity=log.Verbosity.QUIET,
        )
        log_event.tbot_end(False)
        sys.exit(2)
    else:
        log_event.tbot_end(True)