tbot.tc - Builtin Testcases

tbot come bundled with a few builtin testcases. Here is an overview:

Common

tbot.tc.testsuite(*args: Callable, **kwargs: Any)None[source]

Run a number of tests and report how many of them succeeded.

Parameters
  • args – Testcases

  • kwargs – Named-Arguments that should be given to each testcase. Be aware that this requires all testcases to have compatible signatures.

Example:

import tbot
from tbot import tc, machine

@tbot.testcase
def test_a(lab: machine.linux.LinuxShell) -> None:
    lab.exec0("echo", "Test", "A")

@tbot.testcase
def test_b(lab: machine.linux.LinuxShell) -> None:
    lab.exec0("uname", "-a")

@tbot.testcase
def all_tests() -> None:
    with tbot.acquire_lab() as lh:
        tc.testsuite(
            test_a,
            test_b,

            lab=lh,
        )

Shell

tbot.tc.shell.copy(p1: tbot.machine.linux.path.Path[H1], p2: tbot.machine.linux.path.Path[H2])None[source]

Copy a file, possibly from one host to another.

The following transfers are currently supported:

  • H 🢥 H (transfer without changing host)

  • lab-host 🢥 ssh-machine (SSHConnector, using scp)

  • ssh-machine 🢥 lab-host (Using scp)

  • local-host 🢥 paramiko-host (ParamikoConnector, using scp)

  • local-host 🢥 ssh-machine (SSHConnector, using scp)

  • paramiko-host/ssh-machine 🢥 local-host (Using scp)

The following transfers are not supported:

  • ssh-machine 🢥 ssh-machine (There is no guarantee that two remote hosts can connect to each other. If you need this, transfer to the lab-host first and then to the other remote)

  • lab-host 🢥 board-machine (Transfers over serial are not (yet) implemented. To ‘upload’ files, connect to your target via ssh or use a tftp download)

Parameters
  • p1 (linux.Path) – Exisiting path to be copied

  • p2 (linux.Path) – Target where p1 should be copied

tbot.tc.shell.check_for_tool(host: tbot.machine.linux.linux_shell.LinuxShell, tool: str)bool[source]

Check whether a certain tool/program is installed on a host.

Results from previous invocations are cached.

Example:

if shell.check_for_tool(lh, "wget"):
    lh.exec0("wget", download_url, "-O", lh.workdir / "download.tgz")
elif shell.check_for_tool(lh, "curl"):
    lh.exec0("curl", download_url, "-o", lh.workdir / "download.tgz")
else:
    raise Exception("Need either 'wget' or 'curl'!")
Parameters
  • host (linux.LinuxShell) – The host to ceck on.

  • tool (str) – Name of the binary to check for.

Return type

bool

Returns

True if the tool was found and False otherwise.

Git

class tbot.tc.git.GitRepository(target: tbot.machine.linux.path.Path[H], url: Optional[str] = None, *, clean: bool = True, fetch: bool = True, rev: Optional[str] = None)[source]

Bases: tbot.machine.linux.path.Path

Git repository.

Initialize a git repository from either a remote or an existing repo.

There are two modes in which a repo can be initialized:

  1. Only supplying target: tbot assumes, that a repo exists at target already and will fail if this is not the case.

  2. Also supplying url: If target is not already a git repo, one will be created by cloning url.

If clean is True, the repo will be hard reset and all untracked files/ changes will be removed. If rev is also given, it will be checked out.

If fetch is True and url is given, the latest upstream revision will be checked out.

Parameters
  • target (linux.Path) – Where the repository is supposed to be.

  • url (str) – Optional remote url. Whether this is set specifies the mode the repo is initialized in.

  • clean (bool) – Whether to clean the working tree. Defaults to True.

  • fetch (bool) – Whether to fetch remote. Defaults to True.

  • rev (str) – Optional revision to checkout. Only has an effect if clean is also set. If you don’t want to clean, but still perform a checkout, call checkout().

Changed in version 0.6.1: GitRepository now fetches latest changes from remote by default.

git(*args: Union[str, tbot.machine.linux.path.Path[H], tbot.machine.linux.special.Special]) → Tuple[int, str][source]

Run a git subcommand.

Behaves like calling git -C <path/to/repo> <*args>.

Parameters

args – Command line parameters. First one should be a git subcommand.

Return type

tuple[int, str]

Returns

Retcode and command output

git0(*args: Union[str, tbot.machine.linux.path.Path[H], tbot.machine.linux.special.Special])str[source]

Run a git subcommand and ensure its retcode is zero.

Behaves like calling git -C <path/to/repo> <*args>.

Parameters

args – Command line parameters. First one should be a git subcommand.

Return type

str

Returns

Command output

Raises

CommandFailedException – If the command exited with a non-zero return code

property head

Return the current HEAD of this repo.

property symbolic_head

Return the current HEAD of this repo, as a symbolic if possible.

checkout(rev: str)None[source]

Checkout a revision or branch.

Parameters

rev (str) – Revision or branch name to be checked out.

reset(rev: str, mode: tbot.tc.git.ResetMode = <ResetMode.MIXED: '--mixed'>)None[source]

Call git --reset.

Parameters
  • rev (str) – Revision to reset to

  • mode (ResetMode) – Reset mode to be used. Refer to the git-reset man-page for more info.

clean(force: bool = True, untracked: bool = False, noignore: bool = False)None[source]

Call git clean.

Parameters
  • force (bool) – -f

  • untracked (bool) – -d

  • noignore (bool) – -x

Refer to the git-clean man-page for more info.

add(f: tbot.machine.linux.path.Path[H])None[source]

Add a file to the index.

commit(msg: str, author: Optional[str] = None)None[source]

Commit changes.

Parameters
  • msg (str) – Commit message

  • author (str) – Optional commit author in the Author Name <email@address> format.

am(patch: tbot.machine.linux.path.Path[H])int[source]

Apply one or multiple patches.

Parameters

patch (linux.Path) – Either a path to a .patch file or to a directory containing patch files.

Return type

int

Returns

Number of patches applied

apply(patch: tbot.machine.linux.path.Path[H])int[source]

Apply one or multiple patches to the working tree.

Parameters

patch (linux.Path) – Either a path to a .patch file or to a directory containing patch files.

Return type

int

Returns

Number of patches applied

New in version 0.6.4.

bisect(good: str, test: Callable[…, bool])str[source]

Run a git bisect to find the commit that introduced an error.

Todo

Add back the bisect example.

Parameters
  • good (str) – A known good commit, the current head will be assumed as bad.

  • test – A function to check the state of the current commit. Should return True if it is good and False if it is bad. An exception is interpreded as an unexpected error while checking.

Return type

str

Returns

The first bad commit

Kconfig

tbot has a few testcases to manipulate a kconfig-file, as used in Linux or U-Boot. These are:

tbot.tc.kconfig.enable(conf: tbot.machine.linux.path.Path, name: str)None[source]

Enable a kconfig option.

Example:

kconfig.enable(repo / ".config", "CONFIG_AUTO_COMPLETE")
Parameters
  • conf (linux.Path) – Path to kconfig file (usually .config)

  • name (str) – Name of the option (with leading CONFIG_)

tbot.tc.kconfig.module(conf: tbot.machine.linux.path.Path, name: str)None[source]

Set a kconfig option to be built as module.

Example:

kconfig.module(repo / ".config", "CONFIG_BLK_DEV_NVME")
Parameters
  • conf (linux.Path) – Path to kconfig file (usually .config)

  • name (str) – Name of the option (with leading CONFIG_)

tbot.tc.kconfig.disable(conf: tbot.machine.linux.path.Path, name: str)None[source]

Disable a kconfig option.

Example:

kconfig.disable(repo / ".config", "CONFIG_AUTO_COMPLETE")
Parameters
  • conf (linux.Path) – Path to kconfig file (usually .config)

  • name (str) – Name of the option (with leading CONFIG_)

tbot.tc.kconfig.set_string_value(conf: tbot.machine.linux.path.Path, name: str, value: str)None[source]

Set a kconfig string value.

Example:

kconfig.set_string_value(repo / ".config", "CONFIG_LOCALVERSION", "-test")
Parameters
  • conf (linux.Path) – Path to kconfig file (usually .config)

  • name (str) – Name of the option (with leading CONFIG_)

  • value (str) – New string value

tbot.tc.kconfig.set_hex_value(conf: tbot.machine.linux.path.Path, name: str, value: int)None[source]

Set a kconfig hex value.

Example:

kconfig.set_hex_value(repo / ".config", "CONFIG_SYS_BASE", 0x10000)
Parameters
  • conf (linux.Path) – Path to kconfig file (usually .config)

  • name (str) – Name of the option (with leading CONFIG_)

  • value (int) – Integer value that should be set (will be converted to hex)

U-Boot

tbot has testcases to automatically build U-Boot for your board. These integrate nicely with the Board Config.

tbot.tc.uboot.build(builder: Optional[tbot.tc.uboot.build.UBootBuilder] = None, *, clean: bool = True, repo: Optional[tbot.tc.git.GitRepository[BH]] = None, unpatched_repo: Optional[tbot.tc.git.GitRepository[BH]] = None, path: Optional[tbot.machine.linux.path.Path[BH]] = None, host: Optional[BH] = None, lab: Optional[tbot.machine.linux.lab.Lab] = None)tbot.tc.git.GitRepository[BH]

Build U-Boot.

There are a few ways this testcase can be called:

  • From the commandline as uboot_build or in a testcase without any arguments: tbot will use the configured build-host and builder config (see UBootBuilder) to attempt building U-Boot. You can use the clean parameter to specify whether the build should reuse existing artifacts or start from scratch.

  • Specifying just the lab parameter: Use lab as the lab-host from where tbot should connect to its default build-host.

  • Specifying just the host parameter: Build U-Boot on host.

  • Just the path parameter: Checkout U-Boot to path on path’s associated host (which must be a build-host).

  • Only the unpatched_repo: Apply the patch step onto an already checked out revision before attempting the build.

  • Just the repo parameter: Use the already checked-out revision that is assumed to already have necessary patches applied.

In any case, tbot will attempt building U-Boot and if it succeeded, the testcase will return the git repo. Depending on the way it was called, it will skip certain steps (See list above). This can be used to build eg. with a pre-configured checkout or build in a bisect-run.

You can only specify one of repo, unpatched_repo, path, host or lab!

Parameters
  • clean (bool) – Whether the U-Boot tree should be cleand of all leftovers from previous builds.

  • repo (git.GitRepository) – Build from existing, checkout-out revision.

  • unpatched_repo (git.GitRepository) – Build from existing, checkout-out revision, but also apply patches.

  • path (linux.Path) – Checkout U-Boot to path.

  • host (linux.BuildMachine) – Build U-Boot on this host.

  • lab (linux.Lab) – Build U-Boot on the default build-host of this lab.

Return type

git.GitRepository

Returns

Location of the U-Boot tree containing build artifacts

tbot.tc.uboot.checkout(builder: Optional[tbot.tc.uboot.build.UBootBuilder] = None, *, clean: bool = True, rev: Optional[str] = None, path: Optional[tbot.machine.linux.path.Path[H]] = None, host: Optional[H] = None)tbot.tc.git.GitRepository[H]

Just checkout and patch a version of U-Boot without attempting to build it.

This function can either be called with path which will make it checkout U-Boot to path or with host, which will checkout U-Boot to the path defined in do_repo_path().

Only on of path or host is allowed!

class tbot.tc.uboot.UBootBuilder[source]

Bases: abc.ABC

U-Boot build process description.

You will usually define it in your board config like this:

class MyUBootBuilder(tbot.tc.uboot.UBootBuilder):
    name = "my-board"
    defconfig = "myboard_defconfig"
    toolchain = "generic-armv7a-hf"

To make tbot aware of this config, you need to tell it in your U-Boot config:

class MyUBootMachine(
    board.Connector,
    board.UBootShell,
):
    # Create a builder instance
    build = MyUBootBuilder()

If you’ve done everything correctly, calling the uboot_checkout or uboot_build testcases should then checkout and build U-Boot for your board!

You can also manually trigger the checkout/build of a certain builder using the checkout() and build() methods.

abstract property name

Name of this builder.

remote = 'https://gitlab.denx.de/u-boot/u-boot.git'

Where to fetch U-Boot from.

revision = None

Optionally, which revision to check out. Can be any branch, tag, or commit.

defconfig: Optional[str] = None

Defconfig for this board.

toolchain: Optional[str] = None

Name of the toolchain to be used.

Must exist on the selected build-host.

do_repo_path(bh: H) → tbot.machine.linux.path.Path[H][source]

Build-Step that defines where the U-Boot build-directory is.

The default path is $workdir/uboot-$name. Overwrite this step to set a custom path:

def do_repo_path(self, bh: linux.Builder) -> linux.Path:
    return bh.workdir / "projects" / "foo" / "uboot"
Parameters

bh (linux.Builder) – Selected build-host. The returned path must be associated with this machine.

Return type

linux.Path

Returns

Path to the U-Boot build directory

do_checkout(target: tbot.machine.linux.path.Path[H], clean: bool, rev: Optional[str])tbot.tc.git.GitRepository[H][source]

Build-Step that defines how to checkout U-Boot.

Overwrite this step if you have a custom checkout procedure:

def do_checkout(self, target: linux.Path, clean: bool) -> git.GitRepository:
    return git.GitRepository(
        target=target,
        url=self.remote,
        clean=clean,
        rev="v2018.09",
    )
Parameters
  • target (linux.Path) – Where to checkout U-Boot to. This build-step must be able to deal with an already checked out U-Boot source.

  • clean (bool) – Whether this build-step should clean the source-dir (like git clean -fdx).

  • rev (str) – Revision to check out, or None to use the current revision.

Return type

tbot.tc.git.GitRepository

Returns

A git repo of the checked out U-Boot sources

do_patch(repo: tbot.tc.git.GitRepository[H])None[source]

Build-Step to patch the checked out U-Boot tree.

If you need to apply patches ontop of upstream U-Boot, you should do so in this step:

def do_patch(self, repo: git.GitRepository) -> None:
    repo.am(linux.Path(repo.host, "/path/to/patches"))
do_toolchain(bh: BH) → ContextManager[source]

Build-Step to enable the toolchain.

This step should return a context-manager for a sub-shell which has the toolchain enabled. By default this step returns bh.enable(self.toolchain).

do_configure(bh: BH, repo: tbot.tc.git.GitRepository[BH])None[source]

Build-Step to generate the build configuration.

By default, this steps runs make ${self.defconfig}.

do_build(bh: BH, repo: tbot.tc.git.GitRepository[BH])None[source]

Build-Step to actually build U-Boot.

By default, this steps runs make -j $(nproc).

checkout(*, clean: bool = True, path: Optional[tbot.machine.linux.path.Path[H]] = None, host: Optional[H] = None)tbot.tc.git.GitRepository[H][source]

Just checkout and patch a version of U-Boot without attempting to build it.

See tbot.tc.uboot.checkout().

build(*, clean: bool = True, repo: Optional[tbot.tc.git.GitRepository[BH]] = None, unpatched_repo: Optional[tbot.tc.git.GitRepository[BH]] = None, path: Optional[tbot.machine.linux.path.Path[BH]] = None, host: Optional[BH] = None, lab: Optional[tbot.machine.linux.lab.Lab] = None)tbot.tc.git.GitRepository[BH][source]

Build U-Boot.

See tbot.tc.uboot.build().

U-Boot test/py

tbot can also run U-Boot’s test/py when U-Boot building is properly configured. The uboot_testpy testcase is used for this:

tbot.tc.uboot.testpy(lh: Optional[tbot.machine.linux.lab.Lab], *, build_host: Optional[tbot.machine.linux.build.Builder] = None, uboot_builder: Optional[tbot.tc.uboot.build.UBootBuilder] = None, boardenv: Optional[str] = None, testpy_args: List[str] = [])None[source]

Run U-Boot’s test/py test-framework against the selected board.

This testcase can be called from the command-line as uboot_testpy.

Parameters
  • build_host (tbot.machine.linux.Builder) – Optional build-host where U-Boot should be built (and in this case, where test/py will run). By default, tbot.acquire_lab().build() is used.

  • uboot_builder (tbot.tc.uboot.UBootBuilder) – Optional configuration for U-Boot checkout. By default, tbot.acquire_uboot().build is used (exactly like uboot_build does).

  • boardenv (str) – Optional contents for the boardenv.py file. If this option is not given, UBootBuilder.testpy_boardenv is used (or nothing).

  • testpy_args (list(str)) – Additional arguments to be passed to test/py. Can be used, for example, to limit which tests should be run (using testpy_args=["-k", "sf"]).

Example:

The following additions to a board-config make it possible to call tbot ... -vv uboot_testpy:

from tbot.tc import uboot

class DHComUBootBuilder(uboot.UBootBuilder):
    name = "dhcom-pdk2"
    defconfig = "dh_imx6_defconfig"
    toolchain = "imx6q"

    testpy_boardenv = r"""# Config for dhcom pdk2 board

# A list of sections of Flash memory to be tested.
env__sf_configs = (
    {
        # Where in SPI Flash should the test operate.
        'offset': 0x140000,
        # This value is optional.
        #   If present, specifies if the test can write to Flash offset
        #   If missing, defaults to False.
        'writeable': True,
    },
)
"""


class DHComUBoot(board.Connector, board.UBootShell):
    name = "dhcom-uboot"
    prompt = "=> "

    # Don't forget this!
    build = DHComUBootBuilder()