Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/mosquitto-make-reduced.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Mosquitto - Make (reduced features)

on:
workflow_dispatch:
push:
branches:
- master
- fixes
- develop
- release/*
tags:
- 'v[0-9]+.*'
pull_request:
branches:
- master
- fixes
- develop
- release/*

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Install third party dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
docbook-xsl \
lcov \
libargon2-dev \
libcjson-dev \
libcjson1 \
libcunit1-dev \
libgmock-dev \
microsocks \
python3-all \
python3-paho-mqtt \
python3-psutil \
uthash-dev \
xsltproc
-
uses: actions/checkout@v6
with:
submodules: 'true'
-
name: make
run: |
make \
WITH_EDITLINE=no \
WITH_HTTP_API=no \
WITH_PERSISTENCE=no \
WITH_SRV=no \
WITH_TLS=no \
WITH_TLS_PSK=no \
WITH_WEBSOCKETS=no
-
name: make test
run: |
make ptest \
WITH_EDITLINE=no \
WITH_HTTP_API=no \
WITH_PERSISTENCE=no \
WITH_SRV=no \
WITH_TLS=no \
WITH_TLS_PSK=no \
WITH_WEBSOCKETS=no
14 changes: 14 additions & 0 deletions config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ WITH_TLS:=yes
# WITH_TLS=yes.
# This must be disabled if using openssl < 1.0.
WITH_TLS_PSK:=yes
ifneq ($(WITH_TLS),yes)
override WITH_TLS_PSK:=no
endif

# Comment out to disable client threading support.
WITH_THREADING:=yes
Expand Down Expand Up @@ -82,6 +85,9 @@ WITH_SRV:=no
# Set to lws to build with old libwebsockets code
# Set to no to disable
WITH_WEBSOCKETS:=yes
ifneq ($(WITH_TLS),yes)
override WITH_WEBSOCKETS:=no
endif

# Build man page documentation by default.
WITH_DOCS:=yes
Expand Down Expand Up @@ -159,6 +165,14 @@ WITH_HTTP_API=yes
# End of user configuration
# =============================================================================

# Export WITH_* variables so test scripts can check them via the environment.
export WITH_HTTP_API
export WITH_PERSISTENCE
export WITH_SOCKS
export WITH_TLS
export WITH_TLS_PSK
export WITH_WEBSOCKETS


# Also bump lib/mosquitto.h, CMakeLists.txt,
# installer/mosquitto.nsi, installer/mosquitto64.nsi
Expand Down
19 changes: 19 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
# Convert CMake ON/OFF options to yes/no for test scripts
foreach(_var WITH_HTTP_API WITH_PERSISTENCE WITH_SOCKS WITH_TLS WITH_TLS_PSK WITH_WEBSOCKETS)
if(${_var})
set(${_var}_YESNO "yes")
else()
set(${_var}_YESNO "no")
endif()
endforeach()

set(TEST_ENVIRONMENT
"BUILD_ROOT=${CMAKE_BINARY_DIR}"
"WITH_HTTP_API=${WITH_HTTP_API_YESNO}"
"WITH_PERSISTENCE=${WITH_PERSISTENCE_YESNO}"
"WITH_SOCKS=${WITH_SOCKS_YESNO}"
"WITH_TLS=${WITH_TLS_YESNO}"
"WITH_TLS_PSK=${WITH_TLS_PSK_YESNO}"
"WITH_WEBSOCKETS=${WITH_WEBSOCKETS_YESNO}"
)

add_subdirectory(mock)

add_subdirectory(apps)
Expand Down
8 changes: 8 additions & 0 deletions test/apps/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,23 @@ test-compile:
$(MAKE) -C ctrl test-compile

test :
ifeq ($(WITH_PERSISTENCE),yes)
$(MAKE) -C db_dump test
endif
$(MAKE) -C ctrl test
ifeq ($(WITH_TLS),yes)
$(MAKE) -C passwd test
endif
$(MAKE) -C signal test

ptest :
ifeq ($(WITH_PERSISTENCE),yes)
$(MAKE) -C db_dump ptest
endif
$(MAKE) -C ctrl ptest
ifeq ($(WITH_TLS),yes)
$(MAKE) -C passwd ptest
endif
$(MAKE) -C signal ptest

reallyclean : clean
Expand Down
3 changes: 2 additions & 1 deletion test/apps/ctrl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ foreach(PY_TEST_FILE ${PY_TEST_FILES})
)
set_tests_properties(apps-${PY_TEST_NAME}
PROPERTIES
ENVIRONMENT "BUILD_ROOT=${CMAKE_BINARY_DIR}"
ENVIRONMENT "${TEST_ENVIRONMENT}"
SKIP_RETURN_CODE 77
)
endforeach()

Expand Down
8 changes: 7 additions & 1 deletion test/apps/ctrl/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ include ${R}/config.mk

LOCAL_CPPFLAGS+= \
-DWITH_THREADING \
-DWITH_TLS \
-I../ \
-I${R}/include \
-I${R} \
Expand All @@ -15,6 +14,11 @@ LOCAL_CPPFLAGS+= \
-I${R}/test/mock/apps/mosquitto_ctrl \
-I${R}/test/mock/lib

ifeq ($(WITH_TLS),yes)
LOCAL_CPPFLAGS+= \
-DWITH_TLS
endif

LOCAL_CXXFLAGS+=-std=c++20 -Wall -ggdb -D TEST_SOURCE_DIR='"$(realpath .)"'
LOCAL_LDFLAGS+=
LOCAL_LIBADD+=-lcjson -lgmock -lgtest_main -lgtest ${R}/libcommon/libmosquitto_common.a
Expand Down Expand Up @@ -150,7 +154,9 @@ endif
endif

test : test-mock
ifeq ($(WITH_TLS_PSK),yes)
./ctrl-args.py
endif
./ctrl-broker.py
./ctrl-dynsec.py

Expand Down
97 changes: 52 additions & 45 deletions test/apps/ctrl/ctrl-args.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Test parsing of command line args and errors. Does not test arg functionality.

from mosq_test_helper import *
import os

def do_test(args, rc_expected, response=None):
proc = subprocess.run([mosq_test.get_build_root()+"/apps/mosquitto_ctrl/mosquitto_ctrl"]
Expand All @@ -21,22 +22,24 @@ def do_test(args, rc_expected, response=None):
env = mosq_test.env_add_ld_library_path()

do_test(["-A"], 1, response="Error: -A argument given but no address specified.\n\n")
do_test(["--cafile"], 1, response="Error: --cafile argument given but no file specified.\n\n")
do_test(["--cafile", "missing", "broker", "listListeners"], 1, "Error: Problem setting TLS options: File not found.\n")
do_test(["--capath"], 1, response="Error: --capath argument given but no directory specified.\n\n")
do_test(["--cert"], 1, response="Error: --cert argument given but no file specified.\n\n")
do_test(["--cert", ssl_dir / "client.crt"], 1, response="Error: Both certfile and keyfile must be provided if one of them is set.\n")
do_test(["--help"], 1) # Gives generic help
do_test(["--key"], 1, response="Error: --key argument given but no file specified.\n\n")
do_test(["--key", ssl_dir / "client.key"], 1, response="Error: Both certfile and keyfile must be provided if one of them is set.\n")
do_test(["--ciphers"], 1, response="Error: --ciphers argument given but no ciphers specified.\n\n")
if os.environ.get('WITH_TLS') != 'no':
do_test(["--cafile"], 1, response="Error: --cafile argument given but no file specified.\n\n")
do_test(["--cafile", "missing", "broker", "listListeners"], 1, "Error: Problem setting TLS options: File not found.\n")
do_test(["--capath"], 1, response="Error: --capath argument given but no directory specified.\n\n")
do_test(["--cert"], 1, response="Error: --cert argument given but no file specified.\n\n")
do_test(["--cert", ssl_dir / "client.crt"], 1, response="Error: Both certfile and keyfile must be provided if one of them is set.\n")
do_test(["--key"], 1, response="Error: --key argument given but no file specified.\n\n")
do_test(["--key", ssl_dir / "client.key"], 1, response="Error: Both certfile and keyfile must be provided if one of them is set.\n")
do_test(["--ciphers"], 1, response="Error: --ciphers argument given but no ciphers specified.\n\n")
do_test(["-f"], 1, response="Error: -f argument given but no data file specified.\n\n")
do_test(["--help"], 1) # Gives generic help
do_test(["--host"], 1, response="Error: -h argument given but no host specified.\n\n")
do_test(["-i"], 1, response="Error: -i argument given but no id specified.\n\n")
do_test(["--keyform"], 1, response="Error: --keyform argument given but no keyform specified.\n\n")
do_test(["--keyform", "key"], 1, response="Error: If keyform is set, keyfile must be also specified.\n")
do_test(["--keyform", "key", "--cafile", "file", "--cert", "file", "--key", "file", "broker", "listListeners"], 1,
response="Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n")
if os.environ.get('WITH_TLS') != 'no':
do_test(["--keyform"], 1, response="Error: --keyform argument given but no keyform specified.\n\n")
do_test(["--keyform", "key"], 1, response="Error: If keyform is set, keyfile must be also specified.\n")
do_test(["--keyform", "key", "--cafile", "file", "--cert", "file", "--key", "file", "broker", "listListeners"], 1,
response="Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n")
do_test(['-L'], 1, response="Error: -L argument given but no URL specified.\n\n")
do_test(['-L', 'invalid://'], 1, response="Error: Unsupported URL scheme.\n\n")
do_test(['-L', 'mqtt://localhost'], 1, response="Error: Invalid URL for -L argument specified - topic missing.\n")
Expand All @@ -48,26 +51,29 @@ def do_test(args, rc_expected, response=None):
do_test(["-p", "-1"], 1, response="Error: Invalid port given: -1\n")
do_test(["-p", "65536"], 1, response="Error: Invalid port given: 65536\n")
do_test(["-P"], 1, response="Error: -P argument given but no password specified.\n\n")
do_test(["--proxy"], 1, response="Error: --proxy argument given but no proxy url specified.\n\n")
do_test(["--proxy", "mqtt://localhost"], 1, response="Error: Unsupported proxy protocol: mqtt://localhost\n")
do_test(["--proxy", "socks5h://"], 1, response="Error: Invalid proxy.\n")
do_test(["--proxy", "socks5h://localhost:0"], 1, response="Error: Invalid proxy port 0\n")
do_test(["--proxy", "socks5h://localhost:65536"], 1, response="Error: Invalid proxy port 65536\n")
do_test(["--proxy", "socks5h://username%@localhost"], 1, response="Error: Invalid URL encoding in username.\n")
do_test(["--proxy", "socks5h://username%41@localhost"], 1, response="Error: Invalid URL encoding in username.\n")
do_test(["--proxy", "socks5h://username:password%@localhost"], 1, response="Error: Invalid URL encoding in password.\n")
do_test(["--proxy", "socks5h://username:password%41@localhost"], 1, response="Error: Invalid URL encoding in password.\n")
do_test(["--psk"], 1, response="Error: --psk argument given but no key specified.\n\n")
do_test(["--psk", "missing.psk"], 1, response="Error: --psk-identity required if --psk used.\n")
do_test(["--psk-identity"], 1, response="Error: --psk-identity argument given but no identity specified.\n\n")
do_test(["--cafile", ssl_dir / "all-ca.crt", "--psk", "missing.psk", "--psk-identity", "identity"], 1, response="Error: Only one of --psk or --cafile/--capath may be used at once.\n")
if os.environ.get('WITH_SOCKS') != 'no':
do_test(["--proxy"], 1, response="Error: --proxy argument given but no proxy url specified.\n\n")
do_test(["--proxy", "mqtt://localhost"], 1, response="Error: Unsupported proxy protocol: mqtt://localhost\n")
do_test(["--proxy", "socks5h://"], 1, response="Error: Invalid proxy.\n")
do_test(["--proxy", "socks5h://localhost:0"], 1, response="Error: Invalid proxy port 0\n")
do_test(["--proxy", "socks5h://localhost:65536"], 1, response="Error: Invalid proxy port 65536\n")
do_test(["--proxy", "socks5h://username%@localhost"], 1, response="Error: Invalid URL encoding in username.\n")
do_test(["--proxy", "socks5h://username%41@localhost"], 1, response="Error: Invalid URL encoding in username.\n")
do_test(["--proxy", "socks5h://username:password%@localhost"], 1, response="Error: Invalid URL encoding in password.\n")
do_test(["--proxy", "socks5h://username:password%41@localhost"], 1, response="Error: Invalid URL encoding in password.\n")
if os.environ.get('WITH_TLS') != 'no' and os.environ.get('WITH_TLS_PSK') != 'no':
do_test(["--psk"], 1, response="Error: --psk argument given but no key specified.\n\n")
do_test(["--psk", "missing.psk"], 1, response="Error: --psk-identity required if --psk used.\n")
do_test(["--psk-identity"], 1, response="Error: --psk-identity argument given but no identity specified.\n\n")
do_test(["--cafile", ssl_dir / "all-ca.crt", "--psk", "missing.psk", "--psk-identity", "identity"], 1, response="Error: Only one of --psk or --cafile/--capath may be used at once.\n")
do_test(["-q"], 1, response="Error: -q argument given but no QoS specified.\n\n")
do_test(["-q", "-1"], 1, response="Error: Invalid QoS given: -1\n")
do_test(["-q", "3"], 1, response="Error: Invalid QoS given: 3\n")
do_test(["--tls-alpn"], 1, response="Error: --tls-alpn argument given but no protocol specified.\n\n")
do_test(["--tls-engine"], 1, response="Error: --tls-engine argument given but no engine_id specified.\n\n")
do_test(["--tls-engine-kpass-sha1"], 1, response="Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\n\n")
do_test(["--tls-version"], 1, response="Error: --tls-version argument given but no version specified.\n\n")
if os.environ.get('WITH_TLS') != 'no':
do_test(["--tls-alpn"], 1, response="Error: --tls-alpn argument given but no protocol specified.\n\n")
do_test(["--tls-engine"], 1, response="Error: --tls-engine argument given but no engine_id specified.\n\n")
do_test(["--tls-engine-kpass-sha1"], 1, response="Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\n\n")
do_test(["--tls-version"], 1, response="Error: --tls-version argument given but no version specified.\n\n")
do_test(["--username"], 1, response="Error: -u argument given but no username specified.\n\n")
do_test(["--unix"], 1, response="Error: --unix argument given but no socket path specified.\n\n")
do_test(["-V"], 1, response="Error: --protocol-version argument given but no version specified.\n\n")
Expand Down Expand Up @@ -135,22 +141,23 @@ def do_test(args, rc_expected, response=None):

# Env modification

# Missing file
env["HOME"] = "/tmp"
do_test(["--cert", ssl_dir / "client.crt"], 1, response="Error: Both certfile and keyfile must be provided if one of them is set.\n")
if os.environ.get('WITH_TLS') != 'no':
# Missing file
env["HOME"] = "/tmp"
do_test(["--cert", ssl_dir / "client.crt"], 1, response="Error: Both certfile and keyfile must be provided if one of them is set.\n")

# Invalid file
env["XDG_CONFIG_HOME"] = "."
with open("mosquitto_ctrl", "w") as f:
f.write(f"--cert {ssl_dir}/client.crt\n")
f.write(f"--key\n")
do_test(["broker"], 1, response="Error: --key argument given but no file specified.\n\n")
# Invalid file
env["XDG_CONFIG_HOME"] = "."
with open("mosquitto_ctrl", "w") as f:
f.write(f"--cert {ssl_dir}/client.crt\n")
f.write(f"--key\n")
do_test(["broker"], 1, response="Error: --key argument given but no file specified.\n\n")

# Empty file
env["XDG_CONFIG_HOME"] = "."
with open("mosquitto_ctrl", "w") as f:
pass
do_test(["--cert", ssl_dir / "client.crt"], 1, response="Error: Both certfile and keyfile must be provided if one of them is set.\n")
os.remove("mosquitto_ctrl")
# Empty file
env["XDG_CONFIG_HOME"] = "."
with open("mosquitto_ctrl", "w") as f:
pass
do_test(["--cert", ssl_dir / "client.crt"], 1, response="Error: Both certfile and keyfile must be provided if one of them is set.\n")
os.remove("mosquitto_ctrl")

exit(0)
4 changes: 4 additions & 0 deletions test/apps/ctrl/ctrl-broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@

from mosq_test_helper import *
import json
import os
import shutil

if os.environ.get('WITH_TLS') != 'yes':
exit(77)

def write_config(filename, ports):
with open(filename, 'w') as f:
f.write("enable_control_api true\n")
Expand Down
3 changes: 3 additions & 0 deletions test/apps/ctrl/ctrl-dynsec.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import os
import shutil

if os.environ.get('WITH_TLS') != 'yes':
exit(77)

def write_config(filename, ports):
with open(filename, 'w') as f:
f.write(f"global_plugin {mosq_test.get_build_root()}/plugins/dynamic-security/mosquitto_dynamic_security.so\n")
Expand Down
12 changes: 12 additions & 0 deletions test/apps/ctrl/ctrl_shell_options_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ TEST_F(CtrlShellOptionsTest, ConnectUrlMissingHost)

TEST_F(CtrlShellOptionsTest, ConnectTLS)
{
#ifndef WITH_TLS
GTEST_SKIP() << "TLS support not available.";
#else
mosq_config config{};
mosquitto mosq{};
const char host[] = "localhost";
Expand Down Expand Up @@ -258,6 +261,7 @@ TEST_F(CtrlShellOptionsTest, ConnectTLS)
expect_outputs(outputs, sizeof(outputs)/sizeof(char *));

ctrl_shell__main(&config);
#endif
}


Expand Down Expand Up @@ -296,6 +300,9 @@ TEST_F(CtrlShellOptionsTest, ConnectWebsockets)

TEST_F(CtrlShellOptionsTest, ConnectWebsocketsTLS)
{
#ifndef WITH_TLS
GTEST_SKIP() << "TLS support not available.";
#else
mosq_config config{};
mosquitto mosq{};
const char host[] = "localhost";
Expand Down Expand Up @@ -325,6 +332,7 @@ TEST_F(CtrlShellOptionsTest, ConnectWebsocketsTLS)
expect_outputs(outputs, sizeof(outputs)/sizeof(char *));

ctrl_shell__main(&config);
#endif
}


Expand Down Expand Up @@ -441,6 +449,9 @@ TEST_F(CtrlShellOptionsTest, ConnectCertNotFound)

TEST_F(CtrlShellOptionsTest, ConnectCertError)
{
#ifndef WITH_TLS
GTEST_SKIP() << "TLS support not available.";
#else
mosq_config config{};
mosquitto mosq{};
const char host[] = "localhost";
Expand Down Expand Up @@ -485,4 +496,5 @@ TEST_F(CtrlShellOptionsTest, ConnectCertError)
free(config.capath);
free(config.certfile);
free(config.keyfile);
#endif
}
Loading
Loading