ci: add hardware tests for T1

This enables to run device tests against an actual device connected to
our CI. It uses https://github.com/mmahut/tpmb to enter bootloader and
then it uploads a debug firmware to the device. The debug mode is the
used to "click" the buttons but we might improve this and actually use
the hardware buttons instead.
pull/945/head
Marek Mahut 5 years ago committed by Tomas Susanka
parent bc4e8eaa16
commit 4264e87319

@ -0,0 +1,8 @@
# Hardware tests
Hardware tests are device tests that run against an actual device instead of an emulator.
This works thanks to [tpmb](https://github.com/mmahut/tpmb), which is a small arduino
device capable of pushing an actual buttons on the device. Currently T1 is supported
but TT might follow.
See `ci/test.yml` "hardware legacy device test" what exactly is run.

@ -0,0 +1,20 @@
import configparser
import sys
from device.t1 import TrezorOne
def main(file: str = None):
config = configparser.ConfigParser()
config.read_file(open("hardware.cfg"))
t1 = TrezorOne(
config["t1"]["location"], config["t1"]["port"], config["t1"]["arduino_serial"],
)
t1.update_firmware(file)
if __name__ == "__main__":
file = None
if len(sys.argv) == 2:
file = sys.argv[1]
main(file)

@ -0,0 +1,14 @@
with import <nixpkgs> {};
stdenv.mkDerivation rec {
name = "trezor-firmware-hardware-tests";
buildInputs = [
uhubctl
ffmpeg
pipenv
libusb1
dejavu_fonts
];
LD_LIBRARY_PATH = "${libusb1}/lib";
NIX_ENFORCE_PURITY = 0;
}

@ -0,0 +1,46 @@
import datetime
import os
import time
import serial
class Device:
def __init__(self, uhub_location, uhub_port, arduino_serial):
self.uhub_location = uhub_location
self.uhub_port = uhub_port
self.arduino_serial = arduino_serial
self.serial = serial.Serial(arduino_serial, 9600)
def power_on(self):
self.now()
print("[hardware/usb] Turning power on...")
os.system(
"uhubctl -l {} -p {} -a on > /dev/null".format(
self.uhub_location, self.uhub_port
)
)
self.wait(3)
def power_off(self):
self.now()
print("[hardware/usb] Turning power off...")
os.system(
"uhubctl -l {} -p {} -r 100 -a off > /dev/null".format(
self.uhub_location, self.uhub_port
)
)
self.wait(3)
def touch(self, location, action):
raise NotImplementedError
@staticmethod
def wait(seconds):
Device.now()
print("[software] Waiting for {} seconds...".format(seconds))
time.sleep(seconds)
@staticmethod
def now():
print("\n[timestamp] {}".format(datetime.datetime.now()))

@ -0,0 +1,47 @@
import os
from .device import Device
class TrezorOne(Device):
def touch(self, location, action):
self.now()
print(
"[hardware/trezor] Touching the {} button by {}...".format(location, action)
)
self.serial.write(("{} {}\n".format(location, action)).encode())
def update_firmware(self, file=None):
if file:
unofficial = True
trezorctlcmd = "trezorctl firmware-update -s -f {} &".format(file)
print("[software/trezorctl] Updating the firmware to {}...".format(file))
else:
unofficial = False
trezorctlcmd = "trezorctl firmware-update &"
print("[software/trezorctl] Updating the firmware to latest...")
self._enter_bootloader()
os.system(trezorctlcmd)
self.wait(3)
self.touch("right", "click")
self.wait(20)
if unofficial:
self.touch("right", "click")
self.wait(10)
self.power_off()
self.power_on()
if unofficial:
self.touch("right", "click")
self.wait(5)
self.touch("right", "click")
self.wait(5)
os.system("trezorctl get-features|grep version")
def _enter_bootloader(self):
self.power_off()
self.touch("all", "press")
self.wait(2)
self.power_on()
self.wait(2)
self.touch("all", "unpress")

@ -0,0 +1,4 @@
[t1]
location = 3-1.4
port = 3
arduino_serial = /dev/ttyACM0

@ -0,0 +1,20 @@
#!/usr/bin/env bash
if [ $# -ne 2 ]
then
echo "Usage: $0 commit_id [start|stop]"
fi
OUTPUTFILE=video_$1_$(date +%s).mp4
INPUTDEVICE=/dev/video0
if [ "$2" == "start" ]; then
echo "[software/video] Starting record to $OUTPUTFILE"
ffmpeg -loglevel panic -f oss -f video4linux2 -i $INPUTDEVICE \
-vf "drawtext=font=Dejavu Sans: \
text='$1 | %{localtime} | %{pts}': x=(w-tw)/2: y=h-(2*lh): fontcolor=white: box=1: boxcolor=0x00000000@1: fontsize=15" $OUTPUTFILE &
export VPID=$!
elif [ "$2" == "stop" ]; then
echo "[software/video] Stopping the recording of $OUTPUTFILE"
pkill ffmpeg
sync
fi

@ -319,3 +319,25 @@ storage test:
junit: tests/junit.xml
expire_in: 1 week
when: always
# Hardware
hardware legacy device test:
stage: test
extends: .legacy_job
tags:
- tpmb
dependencies:
- legacy fw debug build
script:
- cd ci/hardware_tests
- nix-shell --run "./record_video.sh ${CI_COMMIT_SHORT_SHA} start"
- nix-shell --run "cd ../.. && pipenv sync"
- nix-shell --run "pipenv run python bootstrap.py"
- nix-shell --run "pipenv run python bootstrap.py ../../trezor-*.bin"
- nix-shell --run "pipenv run pytest ../../tests/device_tests"
- nix-shell --run "./record_video.sh ${CI_COMMIT_SHORT_SHA} stop"
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_SHORT_SHA"
paths:
- ci/hardware_tests/video*.mp4
expire_in: 2 days

Loading…
Cancel
Save