Migrating from tbot 0.7.1

With version 0.8, the machines API was rewritten and its interface was changed slightly in the process. To migrate your configs and testcases, please read through this page.

Testcases

The days of the old cumbersome testcases are gone, with the new decorators, things are much easier:

import tbot

@tbot.testcase
@tbot.with_lab
def a_wild_testcase(lh):
   lh.exec0("uname", "-a")

@tbot.testcase
@tbot.with_uboot
def check_uboot_version(ub):
   ub.exec0("version")

@tbot.testcase
@tbot.with_linux
def board_linux_network(lnx):
   lnx.exec0("ip", "address")

The decorators are essentially syntactic sugar for checking whether a parameter was given. If it was not, they will call the appropriate tbot.acquire_* functions. The first testcase example above would look like this without the decorator:

import tbot

@tbot.testcase
def a_wild_testcase(lh = None):
   if lh is None:
      lh = tbot.acquire_lab()

   with lh:
      lh.exec0("uname", "-a")

Machine Configuration

Base Classes

Machines now inherit from different classes than before. Read through the tbot.machine module docs for an overview of the new design. The biggest change is that instead of a single class, you now have to inherit from multiple base classes at once. The old classes map to the new ones like this:

Old (single) base class

New (multiple) base classes

tbot.machine.linux.lab.LocalLabHost

(Localhost as lab-host)

tbot.machine.linux.lab.SSHLabHost

(Remote ssh-connected lab-host)

tbot.machine.linux.SSHMachine

(Remote ssh-connected machine)

tbot.machine.linux.BuildMachine

Build-Host

(Used as a mixin with other machine classes)

tbot.machine.board.Board

(Hardware description of the board)

tbot.machine.board.UBootMachine

(U-Boot configuration)

tbot.machine.board.LinuxWithUBootMachine

(Linux booted from U-Boot)

tbot.machine.board.LinuxStandaloneMachine

(Linux booted directly after powerup)

tbot.machine.board.Connector
tbot.machine.linux.Ash [1]

Example

Here is a diff of a board config from old to new:

-from tbot.machine import board, linux
+from tbot.machine import connector, board, linux


-class BeagleBoneBlack(board.Board):
+class BeagleBoneBlack(connector.ConsoleConnector, board.PowerControl, board.Board):
     name = "bbb"

     def poweron(self):
-        self.lh.exec0("magic-power-controller-tool", "on")
+        self.host.exec0("magic-power-controller-tool", "on")

     def poweroff(self):
-        self.lh.exec0("magic-power-controller-tool", "off")
+        self.host.exec0("magic-power-controller-tool", "off")

-    def console_check(self):
-        if "off" not in self.lh.exec0("magic-power-controller-tool", "-l"):
-            raise Exception("Board is already on!")
+    def power_check(self):
+        return "off" in self.lh.exec0("magic-power-controller-tool", "-l")

-    def connect(self):
-        return self.lh.new_channel("picocom", "-b", str(115200), "/dev/ttyUSB0")
+    def connect(self, mach):
+        return mach.open_channel("picocom", "-b", str(115200), "/dev/ttyUSB0")

-class BBBUBoot(board.UBootMachine[BeagleBoneBlack]):
+class BBBUBoot(board.Connector, board.UBootAutobootIntercept, board.UBootShell):
     name = "bbb-uboot"
     prompt = "=> "

-class BBBLinux(board.LinuxWithUBootMachine[BeagleBoneBlack]):
+class BBBLinux(board.LinuxUbootConnector, board.LinuxBootLogin, linux.Ash):
     name = "bbb-linux"
     uboot = BBBUBoot
     username = "root"
     password = None

     def do_boot(self, ub):
         ub.env("autoload", "no")
         ub.exec0("dhcp")

-        return ["run", "netnfsboot"]
+        return ub.boot("run", "netnfsboot")

 BOARD = BeagleBoneBlack
 UBOOT = BBBUBoot
 LINUX = BBBLinux

Machine Interaction

There were a few minor changes to machine interaction as well.

  • The stdout=... argument was removed. Use tbot.machine.linux.RedirStdout instead:

     a_file = lh.fsroot / "tmp" / "somefile"
    
    -lh.exec0("echo", "bar", stdout=a_file)
    +lh.exec0("echo", "bar", linux.RedirStdout(a_file))
    
  • linux.Env("varname") was removed. You should query the variable using LinuxShell.env() instead:

    -lh.exec0("cd", linux.Env("HOME"))
    +homedir = lh.env("HOME")
    +lh.exec0("cd", homedir)
    
  • LabHost.new_channel() was removed. Instead, clone the machine and call open_channel() like this:

    with mach.clone() as cl:
        chan = cl.open_channel("telnet", "192.0.2.1")