Skip to content

Commit 6ddbfe8

Browse files
committed
Improved testing for Broker
1 parent 731b96b commit 6ddbfe8

5 files changed

Lines changed: 370 additions & 204 deletions

File tree

.github/workflows/broker-check.yml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: Broker Build Test
2+
3+
on:
4+
push:
5+
branches: [ 'master', 'main', 'release/**' ]
6+
pull_request:
7+
branches: [ '*' ]
8+
9+
jobs:
10+
build:
11+
12+
runs-on: ubuntu-22.04
13+
timeout-minutes: 5
14+
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
include:
19+
- name: "Broker default (dynamic alloc)"
20+
cflags: ""
21+
- name: "Broker static memory"
22+
cflags: "-DWOLFMQTT_STATIC_MEMORY"
23+
24+
steps:
25+
- name: Install dependencies
26+
run: |
27+
export DEBIAN_FRONTEND=noninteractive
28+
sudo apt-get update
29+
sudo apt-get install -y mosquitto-clients
30+
31+
- uses: actions/checkout@master
32+
with:
33+
repository: wolfssl/wolfssl
34+
path: wolfssl
35+
- name: wolfssl autogen
36+
working-directory: ./wolfssl
37+
run: ./autogen.sh
38+
- name: wolfssl configure
39+
working-directory: ./wolfssl
40+
run: ./configure --enable-enckeys
41+
- name: wolfssl make
42+
working-directory: ./wolfssl
43+
run: make
44+
- name: wolfssl make install
45+
working-directory: ./wolfssl
46+
run: sudo make install
47+
48+
- uses: actions/checkout@master
49+
- name: wolfmqtt autogen
50+
run: ./autogen.sh
51+
52+
- name: "wolfmqtt configure (${{ matrix.name }})"
53+
run: ./configure --enable-broker CFLAGS="${{ matrix.cflags }}"
54+
- name: wolfmqtt make
55+
run: make
56+
57+
- name: "Run broker tests (${{ matrix.name }})"
58+
run: ./scripts/broker.test
59+
60+
- name: Show logs on failure
61+
if: failure() || cancelled()
62+
run: |
63+
ls -la /tmp/tmp.* 2>/dev/null || true
64+
for d in /tmp/tmp.*; do
65+
if [ -d "$d" ]; then
66+
echo "=== Logs in $d ==="
67+
cat "$d"/*.log 2>/dev/null || true
68+
fi
69+
done

scripts/broker.test

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
#!/bin/bash
2+
3+
# wolfMQTT Broker test
4+
# Tests the wolfMQTT broker with pub/sub, retained messages, LWT, QoS 1,
5+
# client ID takeover, and multi-client pairs.
6+
7+
name="MQTT Broker"
8+
broker_bin="src/mqtt_broker"
9+
pub_bin="examples/pub-sub/mqtt-pub"
10+
sub_bin="examples/pub-sub/mqtt-sub"
11+
no_pid=-1
12+
broker_pid=$no_pid
13+
14+
do_cleanup() {
15+
if [ $broker_pid != $no_pid ]; then
16+
kill -9 $broker_pid 2>/dev/null
17+
echo "Killed broker PID $broker_pid"
18+
broker_pid=$no_pid
19+
fi
20+
# Kill any lingering test processes
21+
for pid in "${TEST_PIDS[@]:-}"; do
22+
kill "$pid" 2>/dev/null || true
23+
done
24+
wait 2>/dev/null || true
25+
if [ -n "${TMP_DIR:-}" ] && [ -d "${TMP_DIR}" ]; then
26+
if [ "${KEEP_LOGS:-0}" = "1" ]; then
27+
echo "Logs preserved in ${TMP_DIR}"
28+
else
29+
rm -rf "${TMP_DIR}"
30+
fi
31+
fi
32+
if [ $1 -ne 0 ]; then
33+
exit 1
34+
fi
35+
}
36+
37+
# Check for broker binary
38+
[ ! -x ./$broker_bin ] && echo -e "\n\n$broker_bin doesn't exist" && exit 1
39+
40+
# Check for pub/sub binaries (needed for multi-client test)
41+
has_pubsub=no
42+
if [ -x ./$pub_bin ] && [ -x ./$sub_bin ]; then
43+
has_pubsub=yes
44+
fi
45+
46+
# Generate a random port
47+
generate_port() {
48+
if [[ "$OSTYPE" == "linux"* ]]; then
49+
port=$(($(od -An -N2 /dev/urandom) % (65535-49152) + 49152))
50+
elif [[ "$OSTYPE" == "darwin"* ]]; then
51+
port=$(($(od -An -N2 /dev/random) % (65535-49152) + 49152))
52+
else
53+
port=11883
54+
fi
55+
echo "Using port $port"
56+
}
57+
58+
check_broker() {
59+
timeout 10 sh -c 'until nc -v -z $0 $1; do sleep 1; done' localhost $port
60+
}
61+
62+
generate_port
63+
64+
TEST_PIDS=()
65+
TMP_DIR="$(mktemp -d)"
66+
FAIL=0
67+
68+
echo "=== wolfMQTT Broker Test Suite ==="
69+
70+
# Start broker
71+
./$broker_bin -p $port &
72+
broker_pid=$!
73+
echo "Broker PID is $broker_pid"
74+
check_broker
75+
76+
# --- Test 1: Basic pub/sub with mosquitto_pub/mosquitto_sub ---
77+
echo ""
78+
echo "--- Test 1: Basic pub/sub ---"
79+
if command -v mosquitto_sub >/dev/null 2>&1 && command -v mosquitto_pub >/dev/null 2>&1; then
80+
mosquitto_sub -h 127.0.0.1 -p $port -t "test/hello" -C 1 -W 5 >"${TMP_DIR}/t1_sub.log" 2>&1 &
81+
T1_PID=$!
82+
sleep 1
83+
mosquitto_pub -h 127.0.0.1 -p $port -t "test/hello" -m "world"
84+
wait $T1_PID 2>/dev/null
85+
if grep -q "world" "${TMP_DIR}/t1_sub.log" 2>/dev/null; then
86+
echo "PASS: Basic pub/sub"
87+
else
88+
echo "FAIL: Basic pub/sub"
89+
FAIL=1
90+
fi
91+
else
92+
echo "SKIP: mosquitto_pub/mosquitto_sub not found"
93+
fi
94+
95+
# --- Test 2: Retained messages ---
96+
echo ""
97+
echo "--- Test 2: Retained messages ---"
98+
if command -v mosquitto_sub >/dev/null 2>&1 && command -v mosquitto_pub >/dev/null 2>&1; then
99+
mosquitto_pub -h 127.0.0.1 -p $port -t "test/retained" -m "stored_msg" -r
100+
sleep 1
101+
mosquitto_sub -h 127.0.0.1 -p $port -t "test/retained" -C 1 -W 5 >"${TMP_DIR}/t2_sub.log" 2>&1
102+
if grep -q "stored_msg" "${TMP_DIR}/t2_sub.log" 2>/dev/null; then
103+
echo "PASS: Retained message delivered on subscribe"
104+
else
105+
echo "FAIL: Retained message not delivered"
106+
FAIL=1
107+
fi
108+
else
109+
echo "SKIP: mosquitto_pub/mosquitto_sub not found"
110+
fi
111+
112+
# --- Test 3: QoS 1 forwarding ---
113+
echo ""
114+
echo "--- Test 3: QoS 1 forwarding ---"
115+
if command -v mosquitto_sub >/dev/null 2>&1 && command -v mosquitto_pub >/dev/null 2>&1; then
116+
mosquitto_sub -h 127.0.0.1 -p $port -t "test/qos1" -q 1 -C 1 -W 5 >"${TMP_DIR}/t3_sub.log" 2>&1 &
117+
T3_PID=$!
118+
sleep 1
119+
mosquitto_pub -h 127.0.0.1 -p $port -t "test/qos1" -m "qos1_msg" -q 1
120+
wait $T3_PID 2>/dev/null
121+
if grep -q "qos1_msg" "${TMP_DIR}/t3_sub.log" 2>/dev/null; then
122+
echo "PASS: QoS 1 forwarding"
123+
else
124+
echo "FAIL: QoS 1 forwarding"
125+
FAIL=1
126+
fi
127+
else
128+
echo "SKIP: mosquitto_pub/mosquitto_sub not found"
129+
fi
130+
131+
# --- Test 4: Last Will and Testament ---
132+
echo ""
133+
echo "--- Test 4: Last Will and Testament ---"
134+
if command -v mosquitto_sub >/dev/null 2>&1; then
135+
mosquitto_sub -h 127.0.0.1 -p $port -t "test/will" -C 1 -W 10 >"${TMP_DIR}/t4_sub.log" 2>&1 &
136+
T4_SUB_PID=$!
137+
sleep 1
138+
# Connect a client with a will, then kill it abruptly
139+
mosquitto_sub -h 127.0.0.1 -p $port -t "test/nothing" \
140+
--will-topic "test/will" --will-payload "client_died" -W 3 >"${TMP_DIR}/t4_will.log" 2>&1 &
141+
T4_WILL_PID=$!
142+
sleep 1
143+
kill -9 $T4_WILL_PID 2>/dev/null
144+
wait $T4_WILL_PID 2>/dev/null || true
145+
wait $T4_SUB_PID 2>/dev/null || true
146+
if grep -q "client_died" "${TMP_DIR}/t4_sub.log" 2>/dev/null; then
147+
echo "PASS: LWT delivered on abnormal disconnect"
148+
else
149+
echo "FAIL: LWT not delivered"
150+
FAIL=1
151+
fi
152+
else
153+
echo "SKIP: mosquitto_sub not found"
154+
fi
155+
156+
# --- Test 5: Client ID takeover ---
157+
echo ""
158+
echo "--- Test 5: Client ID takeover ---"
159+
if command -v mosquitto_sub >/dev/null 2>&1 && command -v mosquitto_pub >/dev/null 2>&1; then
160+
mosquitto_sub -h 127.0.0.1 -p $port -t "test/takeover" -i "takeoverClient" -W 5 >"${TMP_DIR}/t5_old.log" 2>&1 &
161+
T5_OLD_PID=$!
162+
sleep 1
163+
# Connect second client with same ID - should disconnect old
164+
mosquitto_sub -h 127.0.0.1 -p $port -t "test/takeover" -i "takeoverClient" -C 1 -W 5 >"${TMP_DIR}/t5_new.log" 2>&1 &
165+
T5_NEW_PID=$!
166+
sleep 1
167+
# Old client should have been disconnected
168+
wait $T5_OLD_PID 2>/dev/null
169+
T5_OLD_EXIT=$?
170+
# Send message to new client
171+
mosquitto_pub -h 127.0.0.1 -p $port -t "test/takeover" -m "after_takeover"
172+
wait $T5_NEW_PID 2>/dev/null || true
173+
if [ $T5_OLD_EXIT -ne 0 ]; then
174+
echo "PASS: Client ID takeover (old client disconnected)"
175+
else
176+
echo "FAIL: Old client was not disconnected"
177+
FAIL=1
178+
fi
179+
else
180+
echo "SKIP: mosquitto_pub/mosquitto_sub not found"
181+
fi
182+
183+
# --- Test 6: Multi-client paired pub/sub ---
184+
echo ""
185+
echo "--- Test 6: Multi-client paired pub/sub ---"
186+
if [ "$has_pubsub" = "yes" ]; then
187+
NUM_PAIRS=2
188+
NUM_CLIENTS=$((NUM_PAIRS * 2))
189+
SUB_LOGS=()
190+
191+
start_sub() {
192+
local client_id="$1"
193+
local topic="$2"
194+
local log="$3"
195+
stdbuf -oL ./${sub_bin} -T -h localhost -p ${port} -i "${client_id}" \
196+
-n "${topic}" -q 0 >"${log}" 2>&1 &
197+
TEST_PIDS+=("$!")
198+
SUB_LOGS+=("${log}")
199+
}
200+
201+
run_pub() {
202+
local client_id="$1"
203+
local topic="$2"
204+
local msg="$3"
205+
./${pub_bin} -T -h localhost -p ${port} -i "${client_id}" -n "${topic}" \
206+
-m "${msg}" -q 0 >/dev/null 2>&1
207+
}
208+
209+
# Start subscribers
210+
for p in $(seq 0 $((NUM_PAIRS - 1))); do
211+
start_sub "sub_${p}_a" "pair/${p}/a" "${TMP_DIR}/sub_${p}_a.log"
212+
start_sub "sub_${p}_b" "pair/${p}/b" "${TMP_DIR}/sub_${p}_b.log"
213+
done
214+
215+
# Wait for subscribers to be ready
216+
echo "Waiting for ${NUM_CLIENTS} subscribers to connect..."
217+
ELAPSED=0
218+
while [ $ELAPSED -lt 15 ]; do
219+
READY=0
220+
for log in "${SUB_LOGS[@]}"; do
221+
if grep -q "MQTT Waiting for message" "${log}" 2>/dev/null; then
222+
READY=$((READY + 1))
223+
fi
224+
done
225+
if [ $READY -eq ${#SUB_LOGS[@]} ]; then
226+
echo "All subscribers ready after ${ELAPSED}s"
227+
break
228+
fi
229+
sleep 1
230+
ELAPSED=$((ELAPSED + 1))
231+
done
232+
233+
# Publish messages
234+
for p in $(seq 0 $((NUM_PAIRS - 1))); do
235+
run_pub "pub_${p}_a" "pair/${p}/b" "hello_from_${p}_a"
236+
run_pub "pub_${p}_b" "pair/${p}/a" "hello_from_${p}_b"
237+
done
238+
239+
# Wait for delivery
240+
sleep 3
241+
242+
# Check results
243+
T6_PASS=0
244+
T6_FAIL=0
245+
for p in $(seq 0 $((NUM_PAIRS - 1))); do
246+
if grep -q "hello_from_${p}_b" "${TMP_DIR}/sub_${p}_a.log" 2>/dev/null; then
247+
T6_PASS=$((T6_PASS + 1))
248+
else
249+
T6_FAIL=$((T6_FAIL + 1))
250+
fi
251+
if grep -q "hello_from_${p}_a" "${TMP_DIR}/sub_${p}_b.log" 2>/dev/null; then
252+
T6_PASS=$((T6_PASS + 1))
253+
else
254+
T6_FAIL=$((T6_FAIL + 1))
255+
fi
256+
done
257+
258+
# Clean up subscriber processes
259+
for pid in "${TEST_PIDS[@]:-}"; do
260+
kill "$pid" 2>/dev/null || true
261+
wait "$pid" 2>/dev/null || true
262+
done
263+
TEST_PIDS=()
264+
265+
if [ $T6_FAIL -eq 0 ]; then
266+
echo "PASS: Multi-client pub/sub (${T6_PASS}/${NUM_CLIENTS} messages delivered)"
267+
else
268+
echo "FAIL: Multi-client pub/sub (${T6_FAIL} messages missing)"
269+
FAIL=1
270+
fi
271+
else
272+
echo "SKIP: mqtt-pub/mqtt-sub not built"
273+
fi
274+
275+
# --- Summary ---
276+
echo ""
277+
if [ $FAIL -ne 0 ]; then
278+
KEEP_LOGS=1
279+
echo "$name Tests FAILED"
280+
do_cleanup "-1"
281+
fi
282+
283+
do_cleanup "0"
284+
echo "$name Tests Passed"
285+
exit 0

0 commit comments

Comments
 (0)