From 3dd726959e57cbecbe516db04108eab0286f6f09 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Mon, 26 Jan 2026 19:02:53 +0000 Subject: [PATCH] Enable and fix tests on Mac --- .github/workflows/macos.yml | 25 ++++++++++++++++++------- cmake/FindLineEditing.cmake | 10 ++++++++++ test/broker/08-ssl-bridge.py | 2 +- test/broker/16-config-huge.py | 6 +++++- test/client/02-subscribe-format.py | 6 ++++-- test/client/03-publish-tls.py | 4 ++-- test/mock/CMakeLists.txt | 1 + test/mosq_test.py | 17 +++++++++++++++++ 8 files changed, 58 insertions(+), 13 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 5528d625..0a0655e8 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -33,6 +33,11 @@ jobs: with: cmake-version: '3.31.6' + - name: Python test dependencies + uses: actions/setup-python@v6 + with: + cache: 'pip' + - name: Install Homebrew dependencies run: | brew update @@ -43,6 +48,7 @@ jobs: cunit \ docbook-xsl \ gcc \ + googletest \ libedit \ libmicrohttpd \ make \ @@ -51,19 +57,24 @@ jobs: - name: Configure CMake run: | - EDITLINE_PREFIX=$(brew --prefix libedit) + EDITLINE_DIR=$(brew --prefix libedit) + HOMEBREW_PREFIX=$(brew --prefix) cmake -B ${{github.workspace}}/build64 \ -G Ninja \ -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \ + -DCMAKE_PREFIX_PATH="$HOMEBREW_PREFIX" \ -DWITH_DOCS=OFF \ - -DWITH_WEBSOCKETS=ON \ - -DWITH_TESTS=OFF \ - -DCMAKE_INCLUDE_PATH="$EDITLINE_PREFIX/include" \ - -DCMAKE_LIBRARY_PATH="$EDITLINE_PREFIX/lib" \ - -DCMAKE_C_FLAGS="-I$EDITLINE_PREFIX/include" \ - -DCMAKE_EXE_LINKER_FLAGS="-L$EDITLINE_PREFIX/lib" \ -DOPENSSL_ROOT_DIR=$(brew --prefix openssl@3) - name: Build run: | cmake --build ${{github.workspace}}/build64 \ --config ${{env.BUILD_TYPE}} + + - name: Test + working-directory: build64/ + run: | + python3 -m venv venv + source venv/bin/activate + python3 -m pip install --upgrade pip + python3 -m pip install psutil + ctest --output-on-failure --repeat until-pass:5 diff --git a/cmake/FindLineEditing.cmake b/cmake/FindLineEditing.cmake index 8b548597..15df2db3 100644 --- a/cmake/FindLineEditing.cmake +++ b/cmake/FindLineEditing.cmake @@ -1,5 +1,13 @@ include(FindPackageHandleStandardArgs) +set(FIND_PATH_OPTS "") +if(APPLE) + list(APPEND FIND_PATH_OPTS + NO_CMAKE_SYSTEM_PATH + NO_SYSTEM_ENVIRONMENT_PATH + ) +endif() + # Checks an environment variable; note that the first check # does not require the usual CMake $-sign. if(DEFINED env{EDITLINE_DIR}) @@ -11,6 +19,7 @@ find_path( editline/readline.h HINTS EDITLINE_DIR + ${FIND_PATH_OPTS} ) find_library(EDITLINE_LIBRARY @@ -38,6 +47,7 @@ else() readline/readline.h HINTS READLINE_DIR + ${FIND_PATH_OPTS} ) find_library(READLINE_LIBRARY diff --git a/test/broker/08-ssl-bridge.py b/test/broker/08-ssl-bridge.py index a241bc2b..ce59a6f8 100755 --- a/test/broker/08-ssl-bridge.py +++ b/test/broker/08-ssl-bridge.py @@ -88,4 +88,4 @@ def do_test(address): exit(rc) do_test("127.0.0.1") -do_test("127.0.0.2") # tests non-matching certificate hostname with bridge_insecure +do_test(mosq_test.get_non_loopback_ip()) # tests non-matching certificate hostname with bridge_insecure diff --git a/test/broker/16-config-huge.py b/test/broker/16-config-huge.py index 06788cac..fcb7f4c9 100755 --- a/test/broker/16-config-huge.py +++ b/test/broker/16-config-huge.py @@ -5,6 +5,7 @@ # options being set. from mosq_test_helper import * +import platform import signal def write_acl(filename): @@ -123,7 +124,10 @@ def write_config(filename, ports, per_listener_settings, plugver, acl_file): f.write("listener %d\n" % (ports[2])) f.write("plugin_use auth\n") f.write("accept_protocol_versions 3,4,5\n") - f.write("bind_interface lo\n") + if platform.system() == "Darwin": + f.write("bind_interface lo0\n") + else: + f.write("bind_interface lo\n") f.write(f"cafile {ssl_dir}/all-ca.crt\n") f.write(f"certfile {ssl_dir}/server.crt\n") f.write(f"keyfile {ssl_dir}/server.key\n") diff --git a/test/client/02-subscribe-format.py b/test/client/02-subscribe-format.py index 1d5c3f78..9b3884c2 100755 --- a/test/client/02-subscribe-format.py +++ b/test/client/02-subscribe-format.py @@ -3,6 +3,7 @@ # from mosq_test_helper import * +import platform def do_test(format_str, expected_output, proto_ver=4, payload="message"): rc = 1 @@ -128,5 +129,6 @@ do_test('\\t', '\t\n') do_test('\\v', '\v\n') do_test('@@', '@\n') do_test('text', 'text\n') -do_test('%.3d', '2.718\n', payload=struct.pack('BBBBBBBB', 0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0x05, 0x40)) -do_test('%.3f', '0.707\n', payload=struct.pack('BBBB', 0xF4, 0xFD, 0x34, 0x3F)) +if platform.system() != 'Darwin': + do_test('%.3d', '2.718\n', payload=struct.pack('BBBBBBBB', 0x58, 0x39, 0xB4, 0xC8, 0x76, 0xBE, 0x05, 0x40)) + do_test('%.3f', '0.707\n', payload=struct.pack('BBBB', 0xF4, 0xFD, 0x34, 0x3F)) diff --git a/test/client/03-publish-tls.py b/test/client/03-publish-tls.py index 26e17d04..6bdcfc6a 100755 --- a/test/client/03-publish-tls.py +++ b/test/client/03-publish-tls.py @@ -82,5 +82,5 @@ def do_test(address, insecure_option, expect_ssl_fail): do_test("127.0.0.1", None, False) -do_test("127.0.0.2", None, True) -do_test("127.0.0.2", "--insecure", False) +do_test(mosq_test.get_non_loopback_ip(), None, True) +do_test(mosq_test.get_non_loopback_ip(), "--insecure", False) diff --git a/test/mock/CMakeLists.txt b/test/mock/CMakeLists.txt index 923f81e0..0372e229 100644 --- a/test/mock/CMakeLists.txt +++ b/test/mock/CMakeLists.txt @@ -7,6 +7,7 @@ if(EDITLINE_FOUND) target_include_directories(editline_mock PUBLIC ${mosquitto_SOURCE_DIR}/test/mock + ${LINEEDITING_INCLUDE_DIRS} ) target_link_libraries(editline_mock PRIVATE GTest::gmock) endif() diff --git a/test/mosq_test.py b/test/mosq_test.py index 1ffc3b00..42da1b9d 100644 --- a/test/mosq_test.py +++ b/test/mosq_test.py @@ -938,6 +938,23 @@ def client_test(client_cmd, client_args, callback, cb_data): exit(rc) +def get_non_loopback_ip(): + # https://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.settimeout(0) + try: + # doesn't even have to be reachable + s.connect(('10.254.254.254', 1)) + IP = s.getsockname()[0] + except Exception: + # Explicitly not 127.0.0.1 - we want something that doesn't match a + # certificate SAN + IP = '127.0.0.2' + finally: + s.close() + return IP + + # ============================================= # Websockets wrapper # =============================================