.. _quickstart: Quick Start =========== This quick-start guide will introduce you step-by-step to setting up a tbot environment for automating your development workflow. Please start by installing tbot as detailed in :ref:`installation`. This guide assumes you have an embedded Linux system like a BeagleBone or Raspberry Pi (or some other board) with a serial console you can access. .. note:: This version of the quick-start guide uses ``newbot``, tbot's new commandline tool. It is easier to use than the old CLI tool (``tbot``). At some point in the future, the old tool will be removed. For migration, please first read this guide to familiarize yourself with the new tool. Then, you can find additional information in :ref:`cli` about switching to ``newbot``. 1. Directory Structure ---------------------- To start, let's set up a basic directory structure. Importantly, you can do this completely different - here is just a simple and proven suggestion. .. code-block:: shell-session $ mkdir tbot-example && cd tbot-example && git init $ mkdir -p config/ tc/ $ touch config/my_config.py tc/interactive.py tc/examples.py $ tree ./ ├──config/ │ └──my_config.py └──tc/ ├──examples.py └──interactive.py There are two directories here: - ``config/``: This directory is a Python module containing all configurations. For now, there is only one. - ``tc/``: This directory is a Python module containing "testcases". "Testcase" just means a callable routine which does something. "Task" might also be an adequate description. Next, we will fill ``tc/interactive.py`` with some "testcase" code. This will help with the next step. Please add the following code to ``tc/interactive.py``. I will explain what it does later in this guide. .. code-block:: python import tbot @tbot.testcase def board() -> None: """Open an interactive session on the board's serial console.""" with tbot.ctx.request(tbot.role.Board) as b: b.interactive() @tbot.testcase def linux() -> None: """Open an interactive session on the board's Linux shell.""" with tbot.ctx.request(tbot.role.BoardLinux) as lnx: lnx.interactive() If your board uses U-Boot and you also intend to interact with it, you can also add this one: .. code-block:: python @tbot.testcase def uboot() -> None: """Open an interactive session on the board's U-Boot shell.""" tbot.ctx.teardown_if_alive(tbot.role.BoardLinux) with tbot.ctx.request(tbot.role.BoardUBoot) as ub: ub.interactive() 2. Simple Testcases ------------------- Now it is time to write some testcases ourselves. Again, testcase just means "callable function"/"task" in tbot. Start with the following skeleton in ``tc/examples.py``: .. code-block:: python import tbot @tbot.testcase def hello_world(): tbot.log.message("Hello World!") You can now run this testcase like this: .. html-console::
$ newbot tc.examples.hello_world tbot starting ... ├─Calling hello_world ... │ ├─Hello World! │ └─Done. (0.000s) ├───────────────────────────────────────── └─SUCCESS (0.218s)As you can see, you need to pass a sort of module path to ``newbot`` to run a testcase. Under the hood, you can imagine tbot is doing nothing more than: .. code-block:: python # newbot tc.examples.hello_world # ... essentially does: import tc.examples tc.examples.hello_world() The next testcase will run some commands on the localhost (= the machine tbot is running on). To do this, we first need to introduce the concept of machines: 3. Machines ----------- Any "host" or board tbot can interact with is called a *machine*. We can interact with them by instantiating a machine and then calling methods on this instance. To make everything a bit more generic, instantiation usually happens by *requesting* a **role**. A role is later filled in with a concrete *machine* in the configuration. A few roles have default machines attached, so for now, no config is needed. In practice: .. code-block:: python import tbot @tbot.testcase def hello_machine(): with tbot.ctx.request(tbot.role.LocalHost) as lo: lo.exec0("uname", "-a") host = lo.exec0("hostname") tbot.log.message(f"This host is called: {host}") Add this ``hello_machine()`` testcase to ``tc/examples.py`` and run it: .. html-console::
$ newbot tc.examples.hello_machine tbot starting ... ├─Calling hello_machine ... │ ├─[local] uname -a │ │ ## Linux sandvich 5.17.4-arch1-1 #1 SMP PREEMPT Wed, 20 Apr 2022 18:29:28 +0000 x86_64 GNU/Linux │ ├─[local] hostname │ │ ## sandvich │ └─Done. (0.070s) ├───────────────────────────────────────── └─SUCCESS (0.210s)In this case, the role :py:class:`tbot.role.LocalHost` is used. It describes the host tbot is running on. After requesting an instance of it, we can use methods on the instance to run commands. :py:meth:`exec0()
$ newbot -c config.my_config tc.interactive.board tbot starting ... ├─Calling board ... │ ├─[local] picocom -q -b 115200 /dev/ttyUSB1 │ ├─Entering interactive shell (CTRL+D to exit) ... U-Boot 2022.01 (Apr 06 2022 - 14:09:55 +0000) CPU: Freescale i.MX6UL rev1.1 528 MHz (running at 396 MHz) Reset cause: POR Scanning mmc 0:1... Found U-Boot script /boot/boot.scr 1691 bytes read in 2 ms (825.2 KiB/s) ## Executing script at 86000000 5490104 bytes read in 124 ms (42.2 MiB/s) Kernel image @ 0x82000000 [ 0x000000 - 0x53c5b8 ] ## Flattened Device Tree blob at 84000000 Booting using the fdt blob at 0x84000000 Loading Device Tree to 8ef71000, end 8ef7b0d2 ... OK Starting kernel ... [ 2.658999] sd 0:0:0:0: [sda] No Caching mode page found [ 2.664394] sd 0:0:0:0: [sda] Assuming drive cache: write through Xyz Linux 2022.04 test ttymxc5 test login: root root@emb-imx6ul:~# uname -a Linux test 5.10.99 #1 Tue Feb 8 17:30:41 UTC 2022 armv7l GNU/Linux root@emb-imx6ul:~# │ └─Done. (72.616s) ├───────────────────────────────────────── └─SUCCESS (72.676s)The ``-c`` argument is used to tell ``newbot`` which configuration to load. Again, there is little magic here. You can imagine tbot is just doing this: .. code-block:: python # newbot -c config.my_config # ... essentially does: import config.my_config config.my_config.register_machines(tbot.ctx) After that, the listed testcases are called like before. A thing worth mentioning is that you can pass ``-c`` multiple times. This means you can modularize your configuration and mix-and-match from the commandline. 5. Machines in depth -------------------- Machines in tbot have two core parts: - A "connector" which defines how to open the connection to this machine. This can be opening a serial console (:py:class:`~tbot.machine.connector.ConsoleConnector`) or ssh-ing to a server (:py:class:`~tbot.machine.connector.SSHConnector`), for example. - A "shell" which defines how to interact with the machine once the connection is established. On Linux, this could be :py:class:`linux.Bash
$ newbot -c config.my_config tc.examples.board_linux tbot starting ... ├─Calling board_linux ... │ ├─[local] picocom -q -b 115200 /dev/ttyUSB0 │ ├─POWERON (my-board) │ ├─[local] sispmctl -o 3 │ │ ## Accessing Gembird #0 USB device 002 │ │ ## Switched outlet 3 on │ ├─LINUX (my-board-linux) │ │ <> │ │ <> U-Boot 2022.01 (Apr 06 2022 - 14:09:55 +0000) │ │ <> │ │ <> CPU: Freescale i.MX6UL rev1.1 528 MHz (running at 396 MHz) │ │ <> Reset cause: POR │ │ <> Scanning mmc 0:1... │ │ <> Found U-Boot script /boot/boot.scr │ │ <> 1691 bytes read in 2 ms (825.2 KiB/s) │ │ <> ## Executing script at 86000000 │ │ <> 5490104 bytes read in 124 ms (42.2 MiB/s) │ │ <> Kernel image @ 0x82000000 [ 0x000000 - 0x53c5b8 ] │ │ <> ## Flattened Device Tree blob at 84000000 │ │ <> Booting using the fdt blob at 0x84000000 │ │ <> Loading Device Tree to 8ef71000, end 8ef7b0d2 ... OK │ │ <> │ │ <> Starting kernel ... │ │ <> │ │ <> [ 2.658999] sd 0:0:0:0: [sda] No Caching mode page found │ │ <> [ 2.664394] sd 0:0:0:0: [sda] Assuming drive cache: write through │ │ <> │ │ <> Xyz Linux 2022.04 test ttymxc5 │ │ <> │ │ <> test login: │ ├─[my-board-linux] ip address │ │ ## 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 │ │ ## inet 127.0.0.1/8 scope host lo │ │ ## valid_lft forever preferred_lft forever │ │ ## 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000 │ │ ## inet 10.0.0.100/16 brd 10.10.255.255 scope global dynamic eth0 │ │ ## valid_lft 85290sec preferred_lft 85290sec │ ├─[my-board-linux] uname -a │ │ ## Linux test 5.10.99 #1 Tue Feb 8 17:30:41 UTC 2022 armv7l GNU/Linux │ ├─[my-board-linux] cat /etc/os-release │ │ ## ID=test │ │ ## NAME="Xyz Linux" │ │ ## VERSION="2022.04" │ │ ## VERSION_ID=2022.04 │ │ ## PRETTY_NAME="Xyz Linux 2022.04" │ ├─POWEROFF (my-board) │ ├─[local] sispmctl -f 4 │ │ ## Accessing Gembird #0 USB device 002 │ │ ## Switched outlet 4 off │ └─Done. (32.864s) ├───────────────────────────────────────── └─SUCCESS (32.925s)That's it for a basic overview of tbot! Here are links to resources you could dive into next: - :ref:`cli`: The ``newbot`` commandline interface - :ref:`configuration`: More details on configuration - :ref:`context`: The mechanism for requesting machine instances