Skip to content

Commit 932d114

Browse files
committed
Add e2e test suite and rename chromium-browser to chromium
Add QEMU-based e2e testing using the shared CustomPiOS distro_testing framework. Tests verify SSH boot, lighttpd serving FullPageDashboard, and Chromium kiosk displaying the correct page on a virtual X display (Xvfb). The screenshot hook captures the actual framebuffer via xwd to validate the full display pipeline. CI workflow updated to build armhf + arm64 matrices with an e2e-test job for arm64. Also rename chromium-browser to chromium in start_chroot_script and start_chromium_browser (cherry-pick from PR #696) for Trixie compatibility.
1 parent fbad3f8 commit 932d114

11 files changed

Lines changed: 406 additions & 33 deletions

File tree

.github/workflows/main.yml

Lines changed: 131 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,138 @@
11
name: Build Image
22

3-
on: push
3+
on:
4+
repository_dispatch:
5+
push:
46

57
jobs:
68
build:
79
runs-on: ubuntu-latest
10+
strategy:
11+
fail-fast: false
12+
matrix:
13+
include:
14+
- board: raspberrypiarmhf
15+
arch: armhf
16+
- board: raspberrypiarm64
17+
arch: arm64
818
steps:
9-
- name: Update apt
10-
run: sudo apt-get update
11-
- name: Install Dependencies
12-
run: sudo apt install coreutils p7zip-full qemu-user-static python3-git
13-
- name: Checkout CustomPiOS
14-
uses: actions/checkout@v2
15-
with:
16-
repository: 'guysoft/CustomPiOS'
17-
path: CustomPiOS
18-
- name: Checkout Project Repository
19-
uses: actions/checkout@v2
20-
with:
21-
repository: ${{ github.repository }}
22-
path: repository
23-
submodules: true
24-
- name: Download Raspbian Image
25-
run: cd repository/src/image && wget -q -c --trust-server-names 'https://downloads.raspberrypi.org/raspios_lite_armhf_latest'
26-
- name: Update CustomPiOS Paths
27-
run: cd repository/src && ../../CustomPiOS/src/update-custompios-paths
28-
- name: Build Image
29-
run: sudo modprobe loop && cd repository/src && sudo bash -x ./build_dist
30-
- name: Copy Output
31-
run: cp ${{ github.workspace }}/repository/src/workspace/*-raspios-*-lite.img build.img
32-
- name: Zip Output
33-
run: gzip build.img
34-
- uses: actions/upload-artifact@v4
35-
with:
36-
name: build.img.gz
37-
path: build.img.gz
19+
- name: Install Dependencies
20+
run: |
21+
sudo apt-get update
22+
sudo apt-get install -y coreutils p7zip-full qemu-user-static \
23+
python3-git python3-yaml
24+
25+
- name: Checkout CustomPiOS
26+
uses: actions/checkout@v4
27+
with:
28+
repository: 'guysoft/CustomPiOS'
29+
path: CustomPiOS
30+
31+
- name: Checkout Project Repository
32+
uses: actions/checkout@v4
33+
with:
34+
path: repository
35+
submodules: true
36+
37+
- name: Update CustomPiOS Paths
38+
run: |
39+
cd repository/src
40+
../../CustomPiOS/src/update-custompios-paths
41+
42+
- name: Download Base Image
43+
run: |
44+
cd repository/src
45+
export DIST_PATH=$(pwd)
46+
export CUSTOM_PI_OS_PATH=$(cat custompios_path)
47+
export BASE_BOARD=${{ matrix.board }}
48+
$CUSTOM_PI_OS_PATH/base_image_downloader_wrapper.sh
49+
50+
- name: Build Image
51+
run: |
52+
sudo modprobe loop
53+
cd repository/src
54+
sudo BASE_BOARD=${{ matrix.board }} bash -x ./build_dist
55+
56+
- name: Copy output
57+
id: copy
58+
run: |
59+
source repository/src/config
60+
NOW=$(date +"%Y-%m-%d-%H%M")
61+
IMAGE="${NOW}-fullpageos-${DIST_VERSION}-${{ matrix.arch }}"
62+
cp repository/src/workspace/*.img ${IMAGE}.img
63+
echo "image=${IMAGE}" >> $GITHUB_OUTPUT
64+
65+
- uses: actions/upload-artifact@v4
66+
with:
67+
name: fullpageos-${{ matrix.arch }}
68+
path: ${{ steps.copy.outputs.image }}.img
69+
70+
e2e-test:
71+
needs: build
72+
runs-on: ubuntu-latest
73+
timeout-minutes: 30
74+
steps:
75+
- uses: actions/checkout@v4
76+
77+
- name: Checkout CustomPiOS
78+
uses: actions/checkout@v4
79+
with:
80+
repository: 'guysoft/CustomPiOS'
81+
path: CustomPiOS
82+
83+
- name: Download arm64 image from build
84+
uses: actions/download-artifact@v4
85+
with:
86+
name: fullpageos-arm64
87+
path: image/
88+
89+
- name: Prepare testing context
90+
run: |
91+
mkdir -p testing/custompios
92+
cp -r CustomPiOS/src/distro_testing/scripts testing/custompios/scripts
93+
cp -r CustomPiOS/src/distro_testing/tests testing/custompios/tests
94+
95+
- name: Build test Docker image
96+
run: DOCKER_BUILDKIT=0 docker build -t fullpageos-e2e-test ./testing/
97+
98+
- name: Start E2E test container
99+
run: |
100+
mkdir -p artifacts
101+
IMG=$(find image/ -name '*.img' | head -1)
102+
docker run -d --name fullpageos-test \
103+
-v "$PWD/artifacts:/output" \
104+
-v "$(realpath $IMG):/input/image.img:ro" \
105+
-e ARTIFACTS_DIR=/output \
106+
-e DISTRO_NAME="FullPageOS" \
107+
-e QEMU_EXTRA_ARGS="-device virtio-gpu-pci" \
108+
-e KEEP_ALIVE=true \
109+
fullpageos-e2e-test
110+
111+
- name: Wait for tests to complete
112+
run: |
113+
for i in $(seq 1 180); do
114+
[ -f artifacts/exit-code ] && break
115+
sleep 5
116+
done
117+
if [ ! -f artifacts/exit-code ]; then
118+
echo "ERROR: Tests did not complete within 15 minutes"
119+
docker logs fullpageos-test 2>&1 | tail -80
120+
exit 1
121+
fi
122+
echo "Tests finished with exit code: $(cat artifacts/exit-code)"
123+
cat artifacts/test-results.txt 2>/dev/null || true
124+
125+
- name: Collect logs and stop container
126+
if: always()
127+
run: |
128+
docker logs fullpageos-test > artifacts/container.log 2>&1 || true
129+
docker stop fullpageos-test 2>/dev/null || true
130+
131+
- name: Check test result
132+
run: exit "$(cat artifacts/exit-code 2>/dev/null || echo 1)"
133+
134+
- uses: actions/upload-artifact@v4
135+
if: always()
136+
with:
137+
name: e2e-test-results
138+
path: artifacts/

src/modules/fullpageos/filesystem/opt/custompios/scripts/start_chromium_browser

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ flags=(
1616
)
1717

1818
# Standard behavior - runs chromium
19-
chromium-browser "${flags[@]}" --app=$(/opt/custompios/scripts/get_url)
19+
chromium "${flags[@]}" --app=$(/opt/custompios/scripts/get_url)
2020
exit;
2121

2222
# Remove the two lines above to enable signage mode - refresh the browser whenever errors are seen in log
2323

24-
chromium-browser --enable-logging --log-level=2 --v=0 "${flags[@]}" --app=$(/opt/custompios/scripts/get_url) &
24+
chromium --enable-logging --log-level=2 --v=0 "${flags[@]}" --app=$(/opt/custompios/scripts/get_url) &
2525

2626
export logfile="/home/$(id -nu 1000)/.config/chromium/chrome_debug.log"
2727

src/modules/fullpageos/start_chroot_script

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ apt-get -y --force-yes install git screen checkinstall avahi-daemon libavahi-com
3838

3939
if [ "$FULLPAGEOS_INCLUDE_CHROMIUM" == "yes" ]
4040
then
41-
apt-get install -y --force-yes chromium-browser
41+
apt-get install -y --force-yes chromium
4242
sed -i 's@%BROWSER_START_SCRIPT%@/opt/custompios/scripts/start_chromium_browser@g' /opt/custompios/scripts/run_onepageos
4343
fi
4444

testing/.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
images/

testing/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
images/
2+
custompios/
3+
*.png

testing/Dockerfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
FROM ptrsr/pi-ci:latest
2+
3+
ENV LIBGUESTFS_BACKEND=direct
4+
5+
RUN apt-get update && apt-get install -y --no-install-recommends \
6+
sshpass openssh-client curl socat imagemagick \
7+
&& rm -rf /var/lib/apt/lists/*
8+
9+
# Shared framework from CustomPiOS (copied into build context by CI)
10+
COPY custompios/scripts/ /test/scripts/
11+
COPY custompios/tests/ /test/tests/
12+
13+
# FullPageOS-specific tests and hooks
14+
COPY tests/ /test/tests/
15+
COPY hooks/ /test/hooks/
16+
17+
RUN chmod +x /test/scripts/*.sh /test/tests/*.sh; \
18+
chmod +x /test/hooks/*.sh 2>/dev/null || true
19+
20+
ENTRYPOINT ["/test/scripts/entrypoint.sh"]

testing/hooks/post-boot.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/bin/bash
2+
set -e
3+
SSH_HOST="${1:-localhost}"
4+
SSH_PORT="${2:-2222}"
5+
6+
SSH_CMD="sshpass -p raspberry ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p $SSH_PORT pi@$SSH_HOST"
7+
8+
echo "Installing Xvfb and configuring virtual display..."
9+
10+
# Install xvfb and x11-apps (for xwd screenshot capture)
11+
$SSH_CMD "sudo apt-get update -qq && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq xvfb x11-apps 2>&1 | tail -3"
12+
13+
# In QEMU virt, logind's seat0 has CanGraphical=no (no real display device),
14+
# so lightdm won't start an X session. Start Xvfb and the GUI session directly.
15+
echo "Starting Xvfb virtual display..."
16+
$SSH_CMD "sudo Xvfb :0 -screen 0 1280x720x24 &"
17+
sleep 2
18+
19+
echo "Starting GUI session (matchbox + chromium kiosk)..."
20+
$SSH_CMD "sudo -u pi bash -c 'export DISPLAY=:0; export HOME=/home/pi; /opt/custompios/scripts/start_gui' &"
21+
22+
# Wait for Chromium to appear on the display
23+
echo "Waiting for Chromium to start..."
24+
for i in $(seq 1 30); do
25+
PGREP=$($SSH_CMD "pgrep -f 'chromium.*--kiosk' || true" 2>/dev/null)
26+
if [ -n "$PGREP" ]; then
27+
echo " Chromium running on display (pid: $PGREP)"
28+
break
29+
fi
30+
sleep 5
31+
done
32+
33+
# Give the page time to render
34+
echo " Waiting for page to load..."
35+
sleep 15
36+
37+
echo "Post-boot setup complete"

testing/hooks/prepare-image.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
set -e
3+
IMAGE_FILE="${1:?Usage: $0 <image.qcow2>}"
4+
5+
export LIBGUESTFS_BACKEND=direct
6+
export LIBGUESTFS_DEBUG=0
7+
export LIBGUESTFS_TRACE=0
8+
9+
echo '=== FullPageOS-specific image patches ==='
10+
11+
guestfish -a "$IMAGE_FILE" <<GFEOF
12+
run
13+
mount /dev/sda2 /
14+
15+
# Remove fbturbo X11 config that conflicts with virtio-gpu
16+
-rm /usr/share/X11/xorg.conf.d/99-fbturbo.conf
17+
-rm /usr/share/X11/xorg.conf.d/99-fbturbo.~
18+
19+
# Disable services that timeout waiting for Pi-specific hardware in QEMU:
20+
# - zram (no /dev/zram0 in virt)
21+
# - rpi-eeprom-update (no Pi EEPROM)
22+
# - copy-network-manager config for wlan0 (no wifi)
23+
# - DRI device dependencies (virtio-gpu may not create card0/renderD128 in time)
24+
-rm /etc/systemd/system/multi-user.target.wants/dphys-swapfile.service
25+
-rm /etc/systemd/system/swap.target.wants/mkswap.service
26+
-rm /etc/systemd/system/swap.target.wants/sys-subsystem-net-devices-wlan0.device
27+
-rm /lib/systemd/system/rpi-eeprom-update.service
28+
-rm /etc/systemd/system/multi-user.target.wants/rpi-eeprom-update.service
29+
30+
# Mask services that block boot waiting for Pi hardware
31+
ln-sf /dev/null /etc/systemd/system/systemd-zram-setup@.service
32+
ln-sf /dev/null /etc/systemd/system/copy-network-manager-conf@.service
33+
34+
# Mask DRI device wait units -- virtio-gpu may not create /dev/dri/* in QEMU virt
35+
ln-sf /dev/null /etc/systemd/system/dev-dri-card0.device
36+
ln-sf /dev/null /etc/systemd/system/dev-dri-renderD128.device
37+
38+
# Pre-create /etc/gpu_enabled so enable_gpu script skips its reboot
39+
touch /etc/gpu_enabled
40+
41+
# Disable the enable_gpu_first_boot service entirely in QEMU
42+
ln-sf /dev/null /etc/systemd/system/enable_gpu_first_boot.service
43+
44+
# Xvfb will be installed and configured via post-boot.sh hook (needs apt-get).
45+
# Pre-create the lightdm conf directory so the hook can write to it.
46+
mkdir-p /etc/lightdm/lightdm.conf.d
47+
48+
umount /
49+
GFEOF
50+
51+
echo 'FullPageOS patches applied'

testing/hooks/screenshot.sh

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/bin/bash
2+
set -e
3+
SSH_HOST="${1:-localhost}"
4+
SSH_PORT="${2:-2222}"
5+
ARTIFACTS_DIR="${3:-/output}"
6+
7+
SSH_CMD="sshpass -p raspberry ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p $SSH_PORT pi@$SSH_HOST"
8+
SCP_CMD="sshpass -p raspberry scp -o StrictHostKeyChecking=no -P $SSH_PORT"
9+
10+
echo "Capturing X display screenshot from the running desktop..."
11+
12+
# Wait for Chromium to be rendering on the display (give the page time to load)
13+
echo " Waiting for Chromium to settle..."
14+
sleep 10
15+
16+
# Capture the actual X display using xwd (part of x11-apps, installed with xterm)
17+
DISPLAY_SCREENSHOT=false
18+
$SSH_CMD "DISPLAY=:0 XAUTHORITY=/var/run/lightdm/root/:0 xwd -root -out /tmp/display.xwd" 2>/dev/null && DISPLAY_SCREENSHOT=true
19+
20+
if [ "$DISPLAY_SCREENSHOT" = false ]; then
21+
# Try alternative Xauthority paths
22+
$SSH_CMD "DISPLAY=:0 xwd -root -out /tmp/display.xwd" 2>/dev/null && DISPLAY_SCREENSHOT=true
23+
fi
24+
25+
if [ "$DISPLAY_SCREENSHOT" = true ] && $SSH_CMD "test -s /tmp/display.xwd" 2>/dev/null; then
26+
# Convert xwd to png inside the guest (ImageMagick's convert may not be there, try import)
27+
$SSH_CMD "convert /tmp/display.xwd /tmp/display-screenshot.png 2>/dev/null || cp /tmp/display.xwd /tmp/display-screenshot.xwd" || true
28+
29+
if $SSH_CMD "test -f /tmp/display-screenshot.png" 2>/dev/null; then
30+
$SCP_CMD "pi@${SSH_HOST}:/tmp/display-screenshot.png" "$ARTIFACTS_DIR/screenshot.png"
31+
echo "Display screenshot saved (PNG)"
32+
elif $SSH_CMD "test -f /tmp/display-screenshot.xwd" 2>/dev/null; then
33+
$SCP_CMD "pi@${SSH_HOST}:/tmp/display-screenshot.xwd" "$ARTIFACTS_DIR/screenshot.xwd"
34+
# Convert on the host side if imagemagick is available
35+
if command -v convert &>/dev/null; then
36+
convert "$ARTIFACTS_DIR/screenshot.xwd" "$ARTIFACTS_DIR/screenshot.png" 2>/dev/null && \
37+
rm -f "$ARTIFACTS_DIR/screenshot.xwd" && \
38+
echo "Display screenshot saved (converted to PNG on host)" || \
39+
echo "Display screenshot saved (XWD format)"
40+
else
41+
echo "Display screenshot saved (XWD format)"
42+
fi
43+
fi
44+
else
45+
echo " xwd capture failed, falling back to Chromium headless..."
46+
SCREENSHOT_URL="${SCREENSHOT_URL:-http://localhost/FullPageDashboard}"
47+
$SSH_CMD "chromium --headless --disable-gpu --no-sandbox --screenshot=/tmp/chromium-screenshot.png --window-size=1280,720 '$SCREENSHOT_URL'" 2>/dev/null || true
48+
sleep 2
49+
if $SSH_CMD "test -f /tmp/chromium-screenshot.png" 2>/dev/null; then
50+
$SCP_CMD "pi@${SSH_HOST}:/tmp/chromium-screenshot.png" "$ARTIFACTS_DIR/screenshot.png"
51+
echo "Chromium headless screenshot saved (fallback)"
52+
else
53+
echo "All screenshot methods failed"
54+
exit 1
55+
fi
56+
fi

0 commit comments

Comments
 (0)