Skip to content

Commit 8536a7a

Browse files
committed
feat(examples): websocket autobahn test suit integration
1 parent ed569d8 commit 8536a7a

File tree

22 files changed

+2494
-10
lines changed

22 files changed

+2494
-10
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
name: "autobahn: build/linux-tests"
2+
3+
on:
4+
push:
5+
# branches: [ master, main ]
6+
pull_request:
7+
types: [opened, synchronize, reopened, labeled]
8+
9+
jobs:
10+
linux_autobahn:
11+
runs-on: ubuntu-22.04
12+
if: github.event_name == 'push' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'autobahn'))
13+
14+
env:
15+
TEST_DIR: components/esp_websocket_client/examples/autobahn-testsuite
16+
TESTEE_DIR: components/esp_websocket_client/examples/autobahn-testsuite/testee
17+
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
with:
22+
submodules: recursive
23+
24+
- name: Start Autobahn Fuzzing Server
25+
run: |
26+
# Ensure reports directory exists before starting fuzzing server
27+
mkdir -p ${{ env.TEST_DIR }}/reports/clients
28+
29+
# Get host IP address that containers can use to reach the server
30+
# On GitHub Actions, get the IP of the default interface
31+
HOST_IP=$(ip route get 8.8.8.8 | grep -oP 'src \K\S+' || hostname -I | awk '{print $1}' || echo "172.17.0.1")
32+
echo "Host IP address: $HOST_IP"
33+
echo "HOST_IP=$HOST_IP" >> $GITHUB_ENV
34+
35+
# Start fuzzing server on host network so it's accessible from containers
36+
docker run -d \
37+
--name fuzzing-server \
38+
--network host \
39+
-v ${{ github.workspace }}/${{ env.TEST_DIR }}/config:/config:ro \
40+
-v ${{ github.workspace }}/${{ env.TEST_DIR }}/reports:/reports \
41+
crossbario/autobahn-testsuite:latest \
42+
wstest -m fuzzingserver -s /config/fuzzingserver.json
43+
44+
echo "Waiting for http://localhost:9001/info (max 120s)..."
45+
for i in {1..5}; do
46+
if curl -f http://localhost:9001/info >/dev/null 2>&1; then
47+
echo "Fuzzing server ready!"
48+
exit 0
49+
fi
50+
echo "Attempt $i/60 – waiting 2s..."
51+
sleep 2
52+
done
53+
54+
echo "SERVER START FAILED. Container Logs:"
55+
docker logs fuzzing-server
56+
exit 1
57+
58+
- name: Build testee (Linux target)
59+
working-directory: ${{ env.TESTEE_DIR }}
60+
run: |
61+
# Build in Docker container to match GLIBC version
62+
docker run --rm \
63+
-v ${{ github.workspace }}:/work \
64+
-w /work/${{ env.TESTEE_DIR }} \
65+
espressif/idf:latest \
66+
bash -c "
67+
. \$IDF_PATH/export.sh
68+
# Prepare sdkconfig.defaults BEFORE set-target (set-target reads from it)
69+
70+
cp sdkconfig.ci.linux sdkconfig.defaults
71+
72+
echo 'Building...'
73+
idf.py build
74+
"
75+
76+
- name: Verify fuzzing server connectivity
77+
run: |
78+
# Get host IP for connection test
79+
HOST_IP=${HOST_IP:-$(ip route get 8.8.8.8 | grep -oP 'src \K\S+' || hostname -I | awk '{print $1}' || echo "172.17.0.1")}
80+
echo "Testing connectivity to fuzzing server at $HOST_IP:9001"
81+
82+
# Verify the server is accessible from inside the container
83+
docker run --rm \
84+
--network host \
85+
espressif/idf:latest \
86+
bash -c "
87+
echo '=== Checking fuzzing server connectivity ==='
88+
echo 'Checking http://$HOST_IP:9001/info...'
89+
curl -v http://$HOST_IP:9001/info || {
90+
echo 'ERROR: Cannot connect to fuzzing server from container!'
91+
echo 'Tried IP: $HOST_IP'
92+
echo 'This suggests a network configuration issue.'
93+
exit 1
94+
}
95+
echo ''
96+
echo 'Fuzzing server is accessible, proceeding with tests...'
97+
"
98+
99+
- name: Run Autobahn tests
100+
run: |
101+
# Run in Docker container to match GLIBC version
102+
# Fuzzing server is on host network, accessible via localhost from container
103+
docker run --rm \
104+
--network host \
105+
-v ${{ github.workspace }}:/work \
106+
-w /work/${{ env.TESTEE_DIR }}/build \
107+
espressif/idf:latest \
108+
bash -c "
109+
# Install gdb for debugging if segfault occurs
110+
apt-get update && apt-get install -y file curl net-tools || true
111+
112+
# Get host IP address for WebSocket connection
113+
HOST_IP=\$(ip route get 8.8.8.8 2>/dev/null | grep -oP 'src \\K\\S+' || hostname -I | awk '{print \$1}' || echo '172.17.0.1')
114+
echo '=== Verifying server is reachable before starting testee ==='
115+
echo \"Testing connection to http://\${HOST_IP}:9001/info...\"
116+
curl -f http://\${HOST_IP}:9001/info || {
117+
echo \"ERROR: Server not reachable from testee container at \${HOST_IP}:9001!\"
118+
exit 1
119+
}
120+
echo ''
121+
echo '=== Running autobahn_testee.elf ==='
122+
WS_URI=\"ws://\${HOST_IP}:9001\"
123+
echo \"Sending URI: \${WS_URI}\"
124+
# Use printf to ensure newline is included, and add a small delay to ensure stdin is ready
125+
(sleep 0.5; printf \"\${WS_URI}\\n\") | timeout 30m ./autobahn_testee.elf || {
126+
EXIT_CODE=\$?
127+
echo '=== Test failed ==='
128+
exit \$EXIT_CODE
129+
}
130+
echo ''
131+
echo 'All ~2800 Autobahn tests passed!'
132+
"
133+
134+
- name: Show reports
135+
if: always()
136+
working-directory: ${{ env.TEST_DIR }}
137+
run: |
138+
if [ -d reports/clients ]; then
139+
ls -la reports/clients/
140+
else
141+
echo "No reports"
142+
fi
143+
144+
- name: Generate summary
145+
if: always()
146+
working-directory: ${{ env.TEST_DIR }}
147+
run: python3 scripts/generate_summary.py || true
148+
149+
- name: Upload reports
150+
if: always()
151+
uses: actions/upload-artifact@v4
152+
with:
153+
name: autobahn-reports-linux-${{ github.run_id }}
154+
path: ${{ env.TEST_DIR }}/reports/**
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
name: "autobahn: build/target-tests"
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
types: [opened, synchronize, reopened, labeled]
9+
10+
jobs:
11+
build_autobahn:
12+
# Run on push to master or if PR has 'websocket' label
13+
if: contains(github.event.pull_request.labels.*.name, 'autobahn') || github.event_name == 'push'
14+
name: Build
15+
strategy:
16+
matrix:
17+
#idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "latest"]
18+
idf_ver: [ "latest"]
19+
idf_target: ["esp32"]
20+
runs-on: ubuntu-22.04
21+
container: espressif/idf:${{ matrix.idf_ver }}
22+
env:
23+
TEST_DIR: components/esp_websocket_client/examples/autobahn-testsuite/testee
24+
steps:
25+
- name: Checkout esp-protocols
26+
uses: actions/checkout@v4
27+
with:
28+
submodules: recursive
29+
- name: Build autobahn testee with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
30+
working-directory: ${{ env.TEST_DIR }}
31+
env:
32+
IDF_TARGET: ${{ matrix.idf_target }}
33+
shell: bash
34+
run: |
35+
. ${IDF_PATH}/export.sh
36+
test -f sdkconfig.ci.target.plain_tcp && cat sdkconfig.ci.target.plain_tcp >> sdkconfig.defaults || echo "No sdkconfig.ci.plain_tcp"
37+
idf.py set-target ${{ matrix.idf_target }}
38+
idf.py build
39+
- name: Merge binaries with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
40+
working-directory: ${{ env.TEST_DIR }}/build
41+
env:
42+
IDF_TARGET: ${{ matrix.idf_target }}
43+
shell: bash
44+
run: |
45+
. ${IDF_PATH}/export.sh
46+
esptool.py --chip ${{ matrix.idf_target }} merge_bin --fill-flash-size 4MB -o flash_image.bin @flash_args
47+
- uses: actions/upload-artifact@v4
48+
with:
49+
name: autobahn_testee_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
50+
path: |
51+
${{ env.TEST_DIR }}/build/bootloader/bootloader.bin
52+
${{ env.TEST_DIR }}/build/partition_table/partition-table.bin
53+
${{ env.TEST_DIR }}/build/*.bin
54+
${{ env.TEST_DIR }}/build/*.elf
55+
${{ env.TEST_DIR }}/build/flasher_args.json
56+
${{ env.TEST_DIR }}/build/config/sdkconfig.h
57+
${{ env.TEST_DIR }}/build/config/sdkconfig.json
58+
if-no-files-found: error
59+
60+
# run-target-autobahn:
61+
# # Skip running on forks since it won't have access to secrets
62+
# if: |
63+
# github.repository == 'espressif/esp-protocols' &&
64+
# ( contains(github.event.pull_request.labels.*.name, 'autobahn') || github.event_name == 'push' )
65+
# name: Target test
66+
# needs: build_autobahn
67+
# strategy:
68+
# fail-fast: false
69+
# matrix:
70+
# idf_ver: ["latest"]
71+
# idf_target: ["esp32"]
72+
# runs-on:
73+
# - self-hosted
74+
# - ESP32-ETHERNET-KIT
75+
# env:
76+
# TEST_DIR: components/esp_websocket_client/examples/autobahn-testsuite
77+
# TESTEE_DIR: components/esp_websocket_client/examples/autobahn-testsuite/testee
78+
# steps:
79+
# - uses: actions/checkout@v4
80+
# with:
81+
# submodules: recursive
82+
# - uses: actions/download-artifact@v4
83+
# with:
84+
# name: autobahn_testee_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
85+
# path: ${{ env.TESTEE_DIR }}/build
86+
# - name: Install Docker Compose
87+
# run: |
88+
# sudo apt-get update
89+
# sudo apt-get install -y docker-compose-plugin || sudo apt-get install -y docker-compose
90+
# # Ensure user has permission to use Docker (if not already in docker group)
91+
# sudo usermod -aG docker $USER || true
92+
# # Start Docker service if not running
93+
# sudo systemctl start docker || true
94+
# - name: Start Autobahn Fuzzing Server
95+
# working-directory: ${{ env.TEST_DIR }}
96+
# run: |
97+
# # Get host IP address for ESP32 to connect to
98+
# HOST_IP=$(hostname -I | awk '{print $1}')
99+
# echo "HOST_IP=$HOST_IP" >> $GITHUB_ENV
100+
# echo "Autobahn server will be accessible at ws://$HOST_IP:9001"
101+
#
102+
# # Start the fuzzing server using pre-built image
103+
# # For CI, we may need to specify platform if architecture differs
104+
# echo "Starting Autobahn fuzzing server..."
105+
# # Set platform for CI if needed (uncomment if you get exec format error)
106+
# # export DOCKER_DEFAULT_PLATFORM=linux/amd64
107+
# docker compose up -d || docker-compose up -d
108+
#
109+
# # Wait for server to be ready
110+
# echo "Waiting for fuzzing server to start..."
111+
# sleep 10
112+
#
113+
# # Check if container is running and healthy
114+
# if ! docker ps | grep -q ws-fuzzing-server; then
115+
# echo "Error: Fuzzing server failed to start"
116+
# echo "Container logs:"
117+
# docker compose logs || docker-compose logs
118+
# echo "Checking available Python executables in container:"
119+
# docker compose run --rm fuzzing-server which python python3 || true
120+
# exit 1
121+
# fi
122+
#
123+
# # Verify the server is actually responding
124+
# echo "Checking if server is responding..."
125+
# sleep 5
126+
# if ! curl -s http://localhost:8080 > /dev/null 2>&1; then
127+
# echo "Warning: Server may not be fully ready, but container is running"
128+
# docker compose logs --tail=20 || docker-compose logs --tail=20
129+
# fi
130+
#
131+
# echo "✓ Fuzzing server started successfully"
132+
# - name: Flash ESP32 Testee
133+
# working-directory: ${{ env.TESTEE_DIR }}/build
134+
# env:
135+
# IDF_TARGET: ${{ matrix.idf_target }}
136+
# run: |
137+
# python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 flash_image.bin
138+
# - name: Run Autobahn Tests
139+
# working-directory: ${{ env.TESTEE_DIR }}
140+
# env:
141+
# PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
142+
# run: |
143+
# # Detect ESP32 port if not set in environment
144+
# if [ -z "${ESP_PORT:-}" ]; then
145+
# for port in /dev/ttyUSB* /dev/ttyACM*; do
146+
# if [ -e "$port" ]; then
147+
# export ESP_PORT="$port"
148+
# echo "Detected ESP32 port: $ESP_PORT"
149+
# break
150+
# fi
151+
# done
152+
# fi
153+
#
154+
# # Default to /dev/ttyUSB0 if still not found
155+
# export ESP_PORT="${ESP_PORT:-/dev/ttyUSB0}"
156+
#
157+
# if [ ! -e "$ESP_PORT" ]; then
158+
# echo "Error: ESP32 port not found. Please set ESP_PORT environment variable."
159+
# echo "Available ports:"
160+
# ls -la /dev/tty* || true
161+
# exit 1
162+
# fi
163+
#
164+
# echo "Using ESP32 port: $ESP_PORT"
165+
# export PYENV_ROOT="$HOME/.pyenv"
166+
# export PATH="$PYENV_ROOT/bin:$PATH"
167+
# eval "$(pyenv init --path)"
168+
# eval "$(pyenv init -)"
169+
# if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
170+
# echo "Installing Python 3.12.6..."
171+
# pyenv install -s 3.12.6
172+
# fi
173+
# if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
174+
# echo "Creating pyenv virtualenv 'myenv'..."
175+
# pyenv virtualenv 3.12.6 myenv
176+
# fi
177+
# pyenv activate myenv
178+
# python --version
179+
# pip install --prefer-binary pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pytest-custom_exit_code esptool pyserial
180+
# pip install --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
181+
#
182+
# echo "Starting Autobahn test suite on ESP32..."
183+
# echo "Tests may take 15-30 minutes to complete..."
184+
#
185+
# # Send server URI via serial (stdin) and monitor for completion
186+
# # Script is in the parent directory (TEST_DIR) from TESTEE_DIR
187+
# SERVER_URI="ws://$HOST_IP:9001"
188+
# echo "Sending server URI to ESP32: $SERVER_URI"
189+
# python3 ../scripts/monitor_serial.py --port "$ESP_PORT" --uri "$SERVER_URI" --timeout 2400
190+
# - name: Collect Test Reports
191+
# working-directory: ${{ env.TEST_DIR }}
192+
# if: always()
193+
# run: |
194+
# # Stop the fuzzing server
195+
# docker compose down || docker-compose down
196+
#
197+
# # Check if reports were generated
198+
# if [ -d "reports/clients" ]; then
199+
# echo "✓ Test reports found"
200+
# ls -la reports/clients/
201+
# else
202+
# echo "⚠ No test reports found in reports/clients/"
203+
# fi
204+
# - name: Generate Test Summary
205+
# working-directory: ${{ env.TEST_DIR }}
206+
# if: always()
207+
# run: |
208+
# # Generate summary from test results
209+
# # Check for JSON files in both reports/ and reports/clients/
210+
# if [ -d "reports" ] && ( [ -n "$(ls -A reports/*.json 2>/dev/null)" ] || [ -n "$(ls -A reports/clients/*.json 2>/dev/null)" ] ); then
211+
# echo "Generating test summary..."
212+
# python3 scripts/generate_summary.py
213+
# echo ""
214+
# echo "Summary generated successfully!"
215+
# if [ -f "reports/summary.html" ]; then
216+
# echo "HTML summary available at: reports/summary.html"
217+
# fi
218+
# else
219+
# echo "⚠ No JSON test results found, skipping summary generation"
220+
# fi
221+
# - uses: actions/upload-artifact@v4
222+
# if: always()
223+
# with:
224+
# name: autobahn_reports_${{ matrix.idf_target }}_${{ matrix.idf_ver }}
225+
# path: |
226+
# ${{ env.TEST_DIR }}/reports/**
227+
# if-no-files-found: warn

common_components/linux_compat/esp_timer/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
idf_component_register(SRCS esp_timer_linux.c timer_task.cpp
2-
INCLUDE_DIRS include)
2+
INCLUDE_DIRS include
3+
REQUIRES esp_common)
34

45
set_target_properties(${COMPONENT_LIB} PROPERTIES
56
CXX_STANDARD 17

common_components/linux_compat/esp_timer/include/esp_timer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <stdbool.h>
99
#include <stdint.h>
1010
#include "bsd/string.h"
11+
#include "esp_err.h"
1112

1213
#ifdef __cplusplus
1314
extern "C" {

0 commit comments

Comments
 (0)