From 8901b59d84edee988e45f20bccda5d0057040134 Mon Sep 17 00:00:00 2001 From: Vadim Peretokin Date: Mon, 10 Aug 2026 22:18:08 +0200 Subject: [PATCH] infrastructure: keep QTest's output visible when ctest runs on Windows (#9751) #### Brief overview of PR changes/additions - Appends `QT_ASSUME_STDERR_HAS_CONSOLE=1` to the `ENVIRONMENT` test property of every registered test - 95 of 95 confirmed with `ctest --show-only=json-v1` - leaving each test's existing `ASAN_OPTIONS`, `QT_QPA_PLATFORM` and `ENVIRONMENT_MODIFICATION` untouched. - One `cmake_language(DEFER CALL)` per directory that registers tests, rather than the variable copy-pasted into a dozen strings, so a test added later cannot miss it wherever in the file it lands. - Drops the 8 `QT_FORCE_STDERR_LOGGING` entries from the four workflows. `shouldLogToStderr()` is `forceStderrLogging() || stderrHasConsoleAttached()`, so the test property now covers what CI was setting by hand, and every one of those steps runs nothing but `ctest`. #### Motivation for adding to Mudlet Qt on Windows diverts QTest's output to `OutputDebugString` unless it believes stderr has a console attached, and an MSYS2 shell gives it none, so a failing test reported an exit code with no `FAIL!` lines, no compared values and no totals. Setting it as a test property fixes local runs and CI from one place instead of two. #### Other info (issues closed, discussion etc) **Test case:** full Linux `ctest` suite 95/95 pass; `ctest --show-only=json-v1` shows all 95 tests carrying the variable with no other property changed; `ctest -V` shows it in the test process environment. The Windows behaviour itself is not reproducible on Linux - it rests on the reporter's 219 vs 12233 byte A/B and on `QPlainTestLogger::outputMessage`, which only calls `OutputDebugStringA` when `!QtPrivate::shouldLogToStderr()`. Closes #9747 Assisted-by: Claude:claude-opus-5 --- .github/workflows/build-mudlet-pr.yml | 5 ----- .github/workflows/build-mudlet-win-pr.yml | 1 - .github/workflows/build-mudlet-win.yml | 2 -- .github/workflows/build-mudlet.yml | 5 ----- test/CMakeLists.txt | 13 +++++++++++++ test/functional_tests/CMakeLists.txt | 3 +++ 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-mudlet-pr.yml b/.github/workflows/build-mudlet-pr.yml index 85d36d56b..b8eaad714 100644 --- a/.github/workflows/build-mudlet-pr.yml +++ b/.github/workflows/build-mudlet-pr.yml @@ -417,7 +417,6 @@ jobs: run: ctest --output-on-failure env: QT_QPA_PLATFORM: offscreen - QT_FORCE_STDERR_LOGGING: 1 # This runner has Qt's FFmpeg backend and can decode, so TMediaLoopTest must # demonstrate every media behaviour rather than skip any of them. Without a floor # somewhere, a lost codec or a changed default backend would turn the whole file @@ -428,15 +427,11 @@ jobs: if: runner.os == 'macOS' working-directory: '${{runner.workspace}}/b/ninja' run: ctest --output-on-failure - env: - QT_FORCE_STDERR_LOGGING: 1 # the full ctest run above already covers every functional-labelled test - name: Run QTest if: matrix.run_tests != 'true' run: ctest --test-dir ${{runner.workspace}}/b/ninja -L functional --output-on-failure - env: - QT_FORCE_STDERR_LOGGING: 1 - name: add ssh-agent for release uploads if: (runner.os == 'Linux' || runner.os == 'macOS') && matrix.deploy == 'deploy' && startsWith(github.ref, 'refs/tags/Mudlet-') diff --git a/.github/workflows/build-mudlet-win-pr.yml b/.github/workflows/build-mudlet-win-pr.yml index dfbda7039..ebe425210 100644 --- a/.github/workflows/build-mudlet-win-pr.yml +++ b/.github/workflows/build-mudlet-win-pr.yml @@ -139,7 +139,6 @@ jobs: ctest --test-dir $GITHUB_WORKSPACE/build-$MSYSTEM/test --output-on-failure env: - QT_FORCE_STDERR_LOGGING: 1 # This runner has Qt's FFmpeg backend and can decode, so TMediaLoopTest must # demonstrate every media behaviour rather than skip any of them. Without a floor # somewhere, a lost codec or a changed default backend would turn the whole file diff --git a/.github/workflows/build-mudlet-win.yml b/.github/workflows/build-mudlet-win.yml index b07d37f99..109b5b634 100644 --- a/.github/workflows/build-mudlet-win.yml +++ b/.github/workflows/build-mudlet-win.yml @@ -115,8 +115,6 @@ jobs: export LUA_CPATH ctest --test-dir $GITHUB_WORKSPACE/build-$MSYSTEM/test --output-on-failure - env: - QT_FORCE_STDERR_LOGGING: 1 - name: (Windows) Run Lua tests timeout-minutes: 2 diff --git a/.github/workflows/build-mudlet.yml b/.github/workflows/build-mudlet.yml index 755ef11ba..e850ba3e9 100644 --- a/.github/workflows/build-mudlet.yml +++ b/.github/workflows/build-mudlet.yml @@ -403,21 +403,16 @@ jobs: run: ctest --output-on-failure env: QT_QPA_PLATFORM: offscreen - QT_FORCE_STDERR_LOGGING: 1 - name: (macOS) Run C++ tests if: runner.os == 'macOS' working-directory: '${{runner.workspace}}/b/ninja' run: ctest --output-on-failure - env: - QT_FORCE_STDERR_LOGGING: 1 # the full ctest run above already covers every functional-labelled test - name: Run QTest if: matrix.run_tests != 'true' run: ctest --test-dir ${{runner.workspace}}/b/ninja -L functional --output-on-failure - env: - QT_FORCE_STDERR_LOGGING: 1 - name: add ssh-agent for release uploads if: (runner.os == 'Linux' || runner.os == 'macOS') && matrix.deploy == 'deploy' && startsWith(github.ref, 'refs/tags/Mudlet-') diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f91b33bf1..e853a3663 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,6 +9,19 @@ endif() find_package(Qt6 6.8.2 REQUIRED COMPONENTS Test Network Widgets) +# On Windows Qt diverts QTest's stdout to OutputDebugString unless it believes +# stderr has a console attached, which an MSYS2 shell or a CI runner does not give +# it, so a failing test reports an exit code and nothing else (#9747). Deferred so +# that tests registered further down the file are covered too; TESTS does not +# descend into subdirectories, hence the second call in functional_tests. +function(restore_windows_test_output) + get_property(registeredTests DIRECTORY PROPERTY TESTS) + foreach(testName ${registeredTests}) + set_property(TEST ${testName} APPEND PROPERTY ENVIRONMENT "QT_ASSUME_STDERR_HAS_CONSOLE=1") + endforeach() +endfunction() +cmake_language(DEFER CALL restore_windows_test_output) + set(UNIT_TESTS TEntityResolverTest TEntityHandlerTest diff --git a/test/functional_tests/CMakeLists.txt b/test/functional_tests/CMakeLists.txt index 8587226c1..c9df0527a 100644 --- a/test/functional_tests/CMakeLists.txt +++ b/test/functional_tests/CMakeLists.txt @@ -2,6 +2,9 @@ if(NOT WIN32) include(${CMAKE_SOURCE_DIR}/src/cmake/EnableSanitizers.cmake) endif() +# Defined in the parent test/CMakeLists.txt; TESTS is per-directory, so call it here too. +cmake_language(DEFER CALL restore_windows_test_output) + set(FUNCTIONAL_TEST_SOURCES ConfigDirOverrideTest.cpp TelnetTextDisplayedTest.cpp