Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit 47fecd5

Browse files
committed
Create a soc-pytest example and add jumpstarter pytest class
1 parent a7847c7 commit 47fecd5

18 files changed

Lines changed: 640 additions & 7 deletions

examples/soc-pytest/README.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Jumpstarter SOC testing with pytest example
2+
3+
This example aims to demonstrate Jumpstarter in an a simple
4+
SOC testing scenario using pytest.
5+
6+
The following drivers will be utilized:
7+
- DUTLink: for power, storage and console control of the target
8+
- UStreamer: with an hdmi capture card plus a webcam for video snapshits
9+
10+
This example requires the following hardware:
11+
- 1x Raspberry Pi 4
12+
- 1x dutlink (DUTLink could be replaced by a composite set of power, storage mux and serial port interface)
13+
- 1x HDMI Capture card
14+
- 1x Webcam
15+
16+
# Running the example (distributed env)
17+
18+
1) Setup an environment with the required hardware, and customize the exporter.yaml
19+
2) Setup the exporter to be run from a container (TODO: link)
20+
3) Label the exporter in k8s with the `board=rpi4` label
21+
4) Prepare the images by running `make` in the `image` directory
22+
5) Run the tests in this directory by running:
23+
```bash
24+
$ cd jumpstarter_example_soc_pytest
25+
$ uv run pytest -s
26+
================================================================== test session starts ===================================================================
27+
platform linux -- Python 3.12.3, pytest-8.3.3, pluggy-1.5.0
28+
rootdir: /home/majopela/jumpstarter/examples/soc-pytest
29+
configfile: pyproject.toml
30+
plugins: anyio-4.6.2.post1, cov-5.0.0
31+
collected 6 items
32+
33+
test_on_rpi4.py::TestResource::test_setup_device
34+
--------------------------------------------------------------------- live log setup ---------------------------------------------------------------------
35+
INFO jumpstarter.client.lease:lease.py:35 Leasing Exporter matching labels {'board': 'rpi4'} for seconds: 1800
36+
37+
INFO jumpstarter.client.lease:lease.py:42 Lease c33b74ff-ad92-42a6-aa88-2c8a944a297c created
38+
INFO jumpstarter.client.lease:lease.py:46 Polling Lease c33b74ff-ad92-42a6-aa88-2c8a944a297c
39+
INFO jumpstarter.client.lease:lease.py:51 Lease c33b74ff-ad92-42a6-aa88-2c8a944a297c acquired
40+
INFO jumpstarter.client.lease:lease.py:73 Connecting to Lease with name c33b74ff-ad92-42a6-aa88-2c8a944a297c
41+
--------------------------------------------------------------------- live log call ----------------------------------------------------------------------
42+
INFO /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/test_on_rpi4.py:test_on_rpi4.py:51 Setting up device
43+
read: 2.45GB [00:49, 52.8MB/s]
44+
INFO jumpstarter.testing.utils:utils.py:15 Waiting for login prompt
45+
46+
RPi: BOOTLOADER release VERSION:817717 DATE: 2023/01/11 TIME: 17:40:52
47+
BOOTMODE: 0x06 partition 1 build-ts BUILD_TIMESTAMP=1673458852 serial c3656a7d boardrev d03114 stc 608563
48+
..
49+
Starting start4.elf @ 0xfeb00200 partition 1
50+
+
51+
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd083]
52+
...
53+
...
54+
55+
Raspbian GNU/Linux 12 rpitest ttyS0
56+
57+
rpitest login: root
58+
Password:
59+
Linux rpitest 6.6.31+rpt-rpi-v8 #1 SMP PREEMPT Debian 1:6.6.31-1+rpt1 (2024-05-29) aarch64
60+
61+
The programs included with the Debian GNU/Linux system are free software;
62+
the exact distribution terms for each program are described in the
63+
individual files in /usr/share/doc/*/copyright.
64+
65+
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
66+
permitted by applicable law.
67+
root@rpitest:~#INFO jumpstarter.testing.utils:utils.py:21 Logged in
68+
INFO /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/test_on_rpi4.py:test_on_rpi4.py:112 Attempting a soft power off
69+
poweroff
70+
root@rpitest:~# Stopping session-1.scope - Session 1 of User root...
71+
...
72+
[ 28.964752] reboot: Power down
73+
PASSED
74+
test_on_rpi4.py::TestResource::test_tpm2_device
75+
--------------------------------------------------------------------- live log setup ---------------------------------------------------------------------
76+
INFO jumpstarter.testing.utils:utils.py:15 Waiting for login prompt
77+
INFO jumpstarter.testing.utils:utils.py:21 Logged in
78+
--------------------------------------------------------------------- live log call ----------------------------------------------------------------------
79+
INFO /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/test_on_rpi4.py:test_on_rpi4.py:78 Running command: apt-get install -y tpm2-tools
80+
apt-get install -y tpm2-tools
81+
...
82+
root@rpitest:~# INFO /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/test_on_rpi4.py:test_on_rpi4.py:78 Running command: tpm2_createprimary -C e -c primary.ctx
83+
tpm2_createprimary -C e -c primary.ctx
84+
name-alg:
85+
value: sha256
86+
raw: 0xb
87+
attributes:
88+
value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt
89+
raw: 0x30072
90+
type:
91+
value: rsa
92+
raw: 0x1
93+
exponent: 65537
94+
bits: 2048
95+
scheme:
96+
value: null
97+
raw: 0x10
98+
scheme-halg:
99+
value: (null)
100+
raw: 0x0
101+
sym-alg:
102+
value: aes
103+
raw: 0x6
104+
sym-mode:
105+
value: cfb
106+
raw: 0x43
107+
sym-keybits: 128
108+
rsa: efe8d8387679d50d7cea501f4302834eebd4c4b3ec7f7b6a40128c63f3e9fb6e9203429dba4e1221d4d40039ff757dc3cbec638c79e11fe5cb4cc159a5e15a3d785b179f3081ada24f6370bad9b81ad2ddcba2e137bb62a454069d37da7cd1e3a06cb7fe03fc8386b055746b5396ee3b44aa1e40dae4e6257c763a53f7eb60a29df18ee14bce38d376434d89e9c95a79d1563833a48db8016c130f6246f24e023b8874e6f2f8bb1fbfe8ad9a1a0ef71b7fc0ed412056a40a225b6f352ea32aa9564c56bef09df7107b871db136aa530ae479b0b09256373479716416bc18fc7544df8c5de99383c37193f5e016bca7ab39231a69c6d4255d93aed66527bb261d
109+
root@rpitest:~# INFO /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/test_on_rpi4.py:test_on_rpi4.py:78 Running command: tpm2_create -G rsa -u key.pub -r key.priv -C primary.ctx
110+
tpm2_create -G rsa -u key.pub -r key.priv -C primary.ctx
111+
name-alg:
112+
value: sha256
113+
raw: 0xb
114+
attributes:
115+
value: fixedtpm|fixedparent|sensitivedataorigin|userwithauth|decrypt|sign
116+
raw: 0x60072
117+
type:
118+
value: rsa
119+
raw: 0x1
120+
exponent: 65537
121+
bits: 2048
122+
scheme:
123+
value: null
124+
raw: 0x10
125+
scheme-halg:
126+
value: (null)
127+
raw: 0x0
128+
sym-alg:
129+
value: null
130+
raw: 0x10
131+
sym-mode:
132+
value: (null)
133+
raw: 0x0
134+
sym-keybits: 0
135+
rsa: c8cebe46344bbed17c39a497c3e5c53406be142ce741697641d940b77a835b3956c4ce0c5949688ff44a5d8ef847097e1870589ff4afcd401d2b7814b9a57ecc1f750b8a759b4e4f59915d8dda68c5463c8392870a59e21a02481e4d9b8d7ad27dd915850a587b6ff1a87fa98c578a0188e74c2731e39456c4e2e7f3158a878a294f82105a6ead9e397c15cd80c8b587c9a3f47513680cbe5f5fb5a0a41830566e5b70f312fa5e28fc780f45e72d4c8aa42fc2ea9d19e1068815493e2acda90cd6f7dabede223b494f916bd0c67682d4d5b4073b80954c0bab0ac612ae243f92c1d85ab3a7840d1d4aa7390f6155edb3341f229fbc015a8637d16230da03920f
136+
root@rpitest:~# INFO /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/test_on_rpi4.py:test_on_rpi4.py:78 Running command: tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx
137+
tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx
138+
name: 000b0395380f392a3ef0773853ed245ed1a2ba94d26261d846268146f2f4de148cf0
139+
root@rpitest:~# INFO /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/test_on_rpi4.py:test_on_rpi4.py:78 Running command: echo my message > message.dat
140+
echo my message > message.dat
141+
root@rpitest:~# INFO /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/test_on_rpi4.py:test_on_rpi4.py:78 Running command: tpm2_sign -c key.ctx -g sha256 -o sig.rssa message.dat
142+
tpm2_sign -c key.ctx -g sha256 -o sig.rssa message.dat
143+
root@rpitest:~# INFO /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/test_on_rpi4.py:test_on_rpi4.py:78 Running command: tpm2_verifysignature -c key.ctx -g sha256 -s sig.rssa -m message.dat
144+
.dat_verifysignature -c key.ctx -g sha256 -s sig.rssa -m message
145+
root@rpitest:~# echo result: $?
146+
result: 0
147+
PASSED
148+
------------------------------------------------------------------- live log teardown --------------------------------------------------------------------
149+
INFO /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/test_on_rpi4.py:test_on_rpi4.py:112 Attempting a soft power off
150+
poweroff
151+
root@rpitest:~# poweroff
152+
...
153+
[ 80.068761] reboot: Power down
154+
155+
test_on_rpi4.py::TestResource::test_power_off_camera PASSED
156+
test_on_rpi4.py::TestResource::test_power_on_camera PASSED
157+
test_on_rpi4.py::TestResource::test_power_on_hdmi
158+
--------------------------------------------------------------------- live log call ----------------------------------------------------------------------
159+
INFO imagehash:imagehash.py:79 video comparing snapshot test_booting_empty_ok.jpeg: snapshot f0f0f0f0f0f0f0f0, ref f0f0f0f0f0f0f0f0, diff: 0
160+
INFO imagehash:imagehash.py:79 video comparing snapshot test_booting_rainbow_ok.jpeg: snapshot 3c3c3c1c1c1c1c1c, ref 3c3c3c1c1c1c1c1c, diff: 0
161+
INFO imagehash:imagehash.py:79 video comparing snapshot test_booting_raspberries_ok.jpeg: snapshot c000000000000000, ref c000000000000000, diff: 0
162+
PASSED
163+
test_on_rpi4.py::TestResource::test_login_console_hdmi
164+
--------------------------------------------------------------------- live log setup ---------------------------------------------------------------------
165+
INFO jumpstarter.testing.utils:utils.py:15 Waiting for login prompt
166+
INFO jumpstarter.testing.utils:utils.py:21 Logged in
167+
--------------------------------------------------------------------- live log call ----------------------------------------------------------------------
168+
INFO imagehash:imagehash.py:79 video comparing snapshot test_booted_ok.jpeg: snapshot c0c0000000000000, ref c0c0000000000000, diff: 0
169+
PASSED
170+
------------------------------------------------------------------- live log teardown --------------------------------------------------------------------
171+
INFO /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/test_on_rpi4.py:test_on_rpi4.py:112 Attempting a soft power off
172+
INFO jumpstarter.client.lease:lease.py:63 Releasing Lease c33b74ff-ad92-42a6-aa88-2c8a944a297c
173+
174+
175+
============================================================= 6 passed in 303.59s (0:05:03) ==============================================================
176+
```
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apiVersion: jumpstarter.dev/v1alpha1
2+
kind: ExporterConfig
3+
endpoint: grpc.jumpstarter.example.com:443
4+
token: xxxxx
5+
export:
6+
# a DUTLink interface to the DUT
7+
dutlink:
8+
type: jumpstarter_driver_dutlink.driver.Dutlink
9+
config:
10+
storage_device: "/dev/disk/by-id/usb-SanDisk_3.2_Gen_1_5B4C0AB025C0-0:0"
11+
# an HDMI to USB capture card
12+
video:
13+
type: jumpstarter_driver_ustreamer.driver.UStreamer
14+
config:
15+
args:
16+
device: '/dev/v4l/by-path/pci-0000:00:14.0-usbv2-0:3:1.0-video-index0'
17+
resolution: 1920x1080
18+
# a USB camera pointing to the DUT
19+
camera:
20+
type: jumpstarter_driver_ustreamer.driver.UStreamer
21+
config:
22+
args:
23+
device: '/dev/v4l/by-path/pci-0000:00:14.0-usbv2-0:4:1.0-video-index0'
24+
resolution: 1280x720
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
all: prepare-image
2+
3+
###############################################################################
4+
# Image preparation targets
5+
###############################################################################
6+
7+
download-image:
8+
scripts/download-latest-raspbian
9+
10+
prepare-image: images/latest.raw mount
11+
scripts/prepare-latest-raw
12+
touch images/.prepared
13+
umount mnt
14+
15+
images/.prepared:
16+
make prepare-image
17+
18+
images/latest.raw.xz:
19+
make download-image
20+
21+
images/latest.raw: images/latest.raw.xz
22+
xz -d -f -v -T0 -k $^
23+
touch images/latest.raw
24+
rm -f images/.prepared
25+
26+
clean-image:
27+
rm -f images/.prepared
28+
rm -f images/latest.raw
29+
30+
clean-images: clean-image
31+
rm -rf images/download.raspberrypi.org
32+
rm -rf images/latest.raw.xz
33+
34+
clean: clean-image clean-images
35+
36+
###############################################################################
37+
# Image manipulation targets
38+
###############################################################################
39+
40+
mnt:
41+
mkdir -p $@
42+
43+
umount:
44+
umount mnt || true
45+
46+
mount: umount images/latest.raw mnt
47+
guestmount -a images/latest.raw -m /dev/sda2 -m /dev/sda1:/boot/firmware -o allow_other --rw mnt
48+
49+
50+
###############################################################################
51+
# phony targets are targets which don't produce files, just for utility
52+
###############################################################################
53+
54+
55+
.PHONY: download-image prepare-image
56+
.PHONY: mount umount
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Image preparation scripts
2+
3+
This directory contains scripts to prepare the image for the example SOC test,
4+
running `make` should:
5+
6+
* Download a minimal raspbian image
7+
* Inject the settings for the test to be performed (enable UART, setup basic password, tpm dtb)
8+
9+
You will need guestmount installed, sudo permissions.
10+
11+
fuse must be configured to enable `user_allow_other` in `/etc/fuse.conf`.
12+
13+
14+
```bash
15+
$ make
16+
make download-image
17+
make[1]: Entering directory '/home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/image'
18+
scripts/download-latest-raspbian
19+
https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2024-07-04/2024-07-04-raspios-bookworm-armhf-lite.img.xz
20+
--2024-10-17 12:12:43-- https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2024-07-04/2024-07-04-raspios-bookworm-armhf-lite.img.xz
21+
Resolving downloads.raspberrypi.org (downloads.raspberrypi.org)... 2a00:1098:80:56::2:1, 2a00:1098:80:56::1:1, 2a00:1098:82:47::1, ...
22+
Connecting to downloads.raspberrypi.org (downloads.raspberrypi.org)|2a00:1098:80:56::2:1|:443... connected.
23+
HTTP request sent, awaiting response... 200 OK
24+
Length: 523828628 (500M) [application/x-xz]
25+
Saving to: ‘./images/downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2024-07-04/2024-07-04-raspios-bookworm-armhf-lite.img.xz’
26+
27+
downloads.raspberrypi.org/raspios_lite_armhf/ima 100%[=======================================================================================================>] 499.56M 77.9MB/s in 6.6s
28+
29+
2024-10-17 12:12:53 (75.3 MB/s) - ‘./images/downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2024-07-04/2024-07-04-raspios-bookworm-armhf-lite.img.xz’ saved [523828628/523828628]
30+
31+
FINISHED --2024-10-17 12:12:53--
32+
Total wall clock time: 10s
33+
Downloaded: 1 files, 500M in 6.6s (75.3 MB/s)
34+
Latest image: ./images/downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2024-07-04/2024-07-04-raspios-bookworm-armhf-lite.img.xz
35+
Updating link from latest.raw.xz -> ./images/downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2024-07-04/2024-07-04-raspios-bookworm-armhf-lite.img.xz
36+
make[1]: Leaving directory '/home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/image'
37+
xz -d -f -v -T0 -k images/latest.raw.xz
38+
images/latest.raw.xz (1/1)
39+
100 % 499.6 MiB / 2,512.0 MiB = 0.199 102 MiB/s 0:24
40+
touch images/latest.raw
41+
rm -f images/.prepared
42+
umount mnt || true
43+
umount: /home/majopela/jumpstarter/examples/soc-pytest/jumpstarter_example_soc_pytest/image/mnt: not mounted.
44+
guestmount -a images/latest.raw -m /dev/sda2 -m /dev/sda1:/boot/firmware -o allow_other --rw mnt
45+
scripts/prepare-latest-raw
46+
+ sudo sed -i 's/console=serial0,115200 console=tty1/console=serial0,115200/g' mnt/boot/firmware/cmdline.txt
47+
+ cat mnt/boot/firmware/cmdline.txt
48+
console=serial0,115200 root=PARTUUID=d28ec40f-02 rootfstype=ext4 fsck.repair=yes rootwait quiet init=/usr/lib/raspberrypi-sys-mods/firstboot
49+
+ cat
50+
+ sudo tee mnt/boot/firmware/custom.toml
51+
# Raspberry Pi First Boot Setup
52+
[system]
53+
hostname = "rpitest"
54+
55+
[user]
56+
name = "root"
57+
password = "changeme"
58+
password_encrypted = false
59+
60+
[ssh]
61+
enabled = false
62+
63+
[wlan]
64+
country = "es"
65+
66+
[locale]
67+
keymap = "es"
68+
timezone = "Europe/Madrid"
69+
+ cat
70+
+ sudo tee -a mnt/boot/firmware/config.txt
71+
dtparam=spi=on
72+
dtoverlay=tpm-slb9670
73+
enable_uart=1
74+
touch images/.prepared
75+
umount mnt
76+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*

examples/soc-pytest/jumpstarter_example_soc_pytest/image/images/.gitkeep

Whitespace-only changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/bash
2+
set -e
3+
#LATEST_URL=$(wget -O /dev/null -o - --max-redirect=0 https://downloads.raspberrypi.org/raspios_lite_armhf_latest 2>/dev/null| sed -n "s/^Location: \(.*\) \[following\]$/\1/p")
4+
LATEST_URL=$(curl https://downloads.raspberrypi.org/raspios_lite_armhf_latest -v 2>&1 | sed -n "s/< location: \(.*\)\r$/\1/p")
5+
echo $LATEST_URL
6+
CACHE="./images"
7+
wget "${LATEST_URL}" -np -m -A '*img.xz' -c -P "${CACHE}"
8+
# use the latest compose image
9+
LATEST_IMG=$(ls -Art "${CACHE}/downloads.raspberrypi.org/raspios_lite_armhf/images"/*/*.img.xz | tail -n 1)
10+
11+
echo "Latest image: ${LATEST_IMG}"
12+
13+
# calculate full path to LATEST_IMG
14+
LATEST_IMG_FULLPATH=$(readlink -f ${LATEST_IMG})
15+
EXISTING_LINK=$(readlink "${CACHE}/latest.raw.xz" || true )
16+
# if the link has changed, update the link
17+
if [[ "${LATEST_IMG_FULLPATH}" != "${EXISTING_LINK}" ]]; then
18+
echo "Updating link from latest.raw.xz -> ${LATEST_IMG}"
19+
ln -fs "${LATEST_IMG_FULLPATH}" "${CACHE}/latest.raw.xz"
20+
else
21+
echo "We are up-to-date."
22+
fi
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/sh
2+
set -x
3+
# all output to serial port
4+
sudo sed -i 's/console=serial0,115200 console=tty1/console=serial0,115200/g' mnt/boot/firmware/cmdline.txt
5+
cat mnt/boot/firmware/cmdline.txt
6+
7+
cat << EOF | sudo tee mnt/boot/firmware/custom.toml
8+
# Raspberry Pi First Boot Setup
9+
[system]
10+
hostname = "rpitest"
11+
12+
[user]
13+
name = "root"
14+
password = "changeme"
15+
password_encrypted = false
16+
17+
[ssh]
18+
enabled = false
19+
20+
[wlan]
21+
country = "es"
22+
23+
[locale]
24+
keymap = "es"
25+
timezone = "Europe/Madrid"
26+
EOF
27+
28+
cat << EOF | sudo tee -a mnt/boot/firmware/config.txt
29+
dtparam=spi=on
30+
dtoverlay=tpm-slb9670
31+
enable_uart=1
32+
EOF
37.3 KB
Loading
47.9 KB
Loading

0 commit comments

Comments
 (0)