From 39ea3b6ea59d43251408e40d34c5fc18978da22f Mon Sep 17 00:00:00 2001 From: Ralph Sennhauser Date: Tue, 7 Oct 2025 19:26:56 +0200 Subject: [PATCH] Use custom runner for tests The --libdir switch got lost at some point, which is useful for running tests with a system build. Further allow to switch the type of output at runtime avoiding an unnecessary rebuild. Finally allow to specify an output file, this means there is no need to redirect stdout which might break the CI in case the tests write something to stdout like in case of LOGERROR resulting the test result not being valid xml. Ref: #7534 Signed-off-by: Ralph Sennhauser --- build/jenkins/pipelines/freeBSD.Jenkinsfile | 4 +- build/jenkins/pipelines/linux.Jenkinsfile | 4 +- build/jenkins/pipelines/macOS.Jenkinsfile | 4 +- .../pipelines/static-analysis.Jenkinsfile | 2 +- build/jenkins/pipelines/windows.Jenkinsfile | 4 +- build/premake/cxxtest/cxxtest.lua | 7 +- build/premake/premake5.lua | 8 +- source/CxxTestRunner.tpl | 96 +++++++++++++++++++ 8 files changed, 108 insertions(+), 21 deletions(-) create mode 100644 source/CxxTestRunner.tpl diff --git a/build/jenkins/pipelines/freeBSD.Jenkinsfile b/build/jenkins/pipelines/freeBSD.Jenkinsfile index cc66567492..a32cc30a71 100644 --- a/build/jenkins/pipelines/freeBSD.Jenkinsfile +++ b/build/jenkins/pipelines/freeBSD.Jenkinsfile @@ -47,7 +47,7 @@ pipeline { sh 'git lfs pull -I "binaries/data/mods/_test.*"' sh "libraries/build-source-libs.sh ${JOBS} 2> freebsd-prebuild-errors.log" - sh 'build/workspaces/update-workspaces.sh --jenkins-tests 2>> freebsd-prebuild-errors.log' + sh 'build/workspaces/update-workspaces.sh 2>> freebsd-prebuild-errors.log' script { if (params.CLEANBUILD) { @@ -103,7 +103,7 @@ pipeline { stage('Release Tests') { steps { timeout(time: 15) { - sh './binaries/system/test > cxxtest.xml' + sh './binaries/system/test --format junit --output cxxtest.xml' } } post { diff --git a/build/jenkins/pipelines/linux.Jenkinsfile b/build/jenkins/pipelines/linux.Jenkinsfile index c93c8e60a5..fd667c2bf8 100644 --- a/build/jenkins/pipelines/linux.Jenkinsfile +++ b/build/jenkins/pipelines/linux.Jenkinsfile @@ -112,7 +112,7 @@ pipeline { steps { sh "libraries/build-source-libs.sh ${JOBS} 2> ${JENKINS_COMPILER}-prebuild-errors.log" - sh "build/workspaces/update-workspaces.sh --jenkins-tests 2>> ${JENKINS_COMPILER}-prebuild-errors.log" + sh "build/workspaces/update-workspaces.sh 2>> ${JENKINS_COMPILER}-prebuild-errors.log" script { if (params.CLEANBUILD) { @@ -171,7 +171,7 @@ pipeline { timeout(time: 15) { script { def bin = env.BUILD_TYPE == 'debug' ? 'test_dbg' : 'test' - sh "binaries/system/${bin} > cxxtest.xml" + sh "binaries/system/${bin} --format junit --output cxxtest.xml" } } } diff --git a/build/jenkins/pipelines/macOS.Jenkinsfile b/build/jenkins/pipelines/macOS.Jenkinsfile index 593c02cf08..0642383af9 100644 --- a/build/jenkins/pipelines/macOS.Jenkinsfile +++ b/build/jenkins/pipelines/macOS.Jenkinsfile @@ -53,7 +53,7 @@ pipeline { sh 'git lfs pull -I "binaries/data/mods/_test.*"' sh "libraries/build-macos-libs.sh ${JOBS} 2> macos-prebuild-errors.log" - sh 'build/workspaces/update-workspaces.sh --jenkins-tests 2>> macos-prebuild-errors.log' + sh 'build/workspaces/update-workspaces.sh 2>> macos-prebuild-errors.log' script { if (params.CLEANBUILD) { @@ -111,7 +111,7 @@ pipeline { timeout(time: 15) { script { def bin = env.BUILD_TYPE == 'debug' ? 'test_dbg' : 'test' - sh "./binaries/system/${bin} > cxxtest.xml" + sh "./binaries/system/${bin} --format junit --output cxxtest.xml" } } } diff --git a/build/jenkins/pipelines/static-analysis.Jenkinsfile b/build/jenkins/pipelines/static-analysis.Jenkinsfile index b64ce3a770..ada8f6c573 100644 --- a/build/jenkins/pipelines/static-analysis.Jenkinsfile +++ b/build/jenkins/pipelines/static-analysis.Jenkinsfile @@ -37,7 +37,7 @@ pipeline { steps { ws('/zpool0/docker-coverage') { dir('build/workspaces/') { - sh './update-workspaces.sh -j1 --jenkins-tests --coverage' + sh './update-workspaces.sh -j1 --coverage' dir('gcc/') { // Reset everything in case there were ever leftovers sh 'lcov --directory . --zerocounters' diff --git a/build/jenkins/pipelines/windows.Jenkinsfile b/build/jenkins/pipelines/windows.Jenkinsfile index b887d05aa1..61b729bb45 100644 --- a/build/jenkins/pipelines/windows.Jenkinsfile +++ b/build/jenkins/pipelines/windows.Jenkinsfile @@ -93,7 +93,7 @@ pipeline { } bat "(robocopy /MIR /NDL /NJH /NJS /NP /NS /NC E:\\wxWidgets-3.2.8\\lib libraries\\${ARCH}\\wxwidgets\\lib) ^& IF %ERRORLEVEL% LEQ 1 exit 0" bat "(robocopy /MIR /NDL /NJH /NJS /NP /NS /NC E:\\wxWidgets-3.2.8\\include libraries\\${ARCH}\\wxwidgets\\include) ^& IF %ERRORLEVEL% LEQ 1 exit 0" - bat 'cd build\\workspaces && update-workspaces.bat --jenkins-tests' + bat 'cd build\\workspaces && update-workspaces.bat' script { if (params.CLEANBUILD) { @@ -143,7 +143,7 @@ pipeline { timeout(time: 15) { script { def bin = env.BUILD_TYPE == 'Debug' ? 'test_dbg' : 'test' - bat "cd binaries\\system && ${bin}.exe > cxxtest.xml" + bat "cd binaries\\system && ${bin}.exe --format junit --output cxxtest.xml" } } } diff --git a/build/premake/cxxtest/cxxtest.lua b/build/premake/cxxtest/cxxtest.lua index c9e35d5c26..c8a9a8037d 100644 --- a/build/premake/cxxtest/cxxtest.lua +++ b/build/premake/cxxtest/cxxtest.lua @@ -2,7 +2,6 @@ local m = {} m._VERSION = "1.1.0-dev" m.exepath = nil -m.runner = "ErrorPrinter" m.options = "" m.rootoptions = "" @@ -20,8 +19,6 @@ end -- this module. function m.init(have_std, have_eh, runner, includes, root_includes) - m.runner = runner - if have_std then m.options = m.options.." --have-std" end @@ -53,7 +50,7 @@ function m.init(have_std, have_eh, runner, includes, root_includes) buildmessage 'Generating test root file' buildcommands { "{MKDIR} %{wks.location}/generated", - m.exepath.." --root "..m.rootoptions.." --runner="..m.runner.." -o %{wks.location}/generated/test_root.cpp" + m.exepath .. " --root " .. m.rootoptions .. " --template ../../../source/CxxTestRunner.tpl -o %{wks.location}/generated/test_root.cpp" } cleancommands { "{DELETE} %{wks.location}/generated/test_root.cpp" } end @@ -70,7 +67,7 @@ function m.configure_project(hdrfiles) prebuildmessage 'Generating test root file' prebuildcommands { "{MKDIR} %{wks.location}/generated", - m.exepath.." --root "..m.rootoptions.." --runner="..m.runner.." -o %{wks.location}/generated/test_root.cpp" + m.exepath .. " --root " .. m.rootoptions .. " --template ../../../source/CxxTestRunner.tpl -o %{wks.location}/generated/test_root.cpp" } end diff --git a/build/premake/premake5.lua b/build/premake/premake5.lua index dfc1a52f9e..357b08fb40 100644 --- a/build/premake/premake5.lua +++ b/build/premake/premake5.lua @@ -42,7 +42,6 @@ print("") newoption { category = "Pyrogenesis", trigger = "android", description = "Use non-working Android cross-compiling mode" } newoption { category = "Pyrogenesis", trigger = "coverage", description = "Enable code coverage data collection (GCC only)" } newoption { category = "Pyrogenesis", trigger = "gles", description = "Use non-working OpenGL ES 2.0 mode" } -newoption { category = "Pyrogenesis", trigger = "jenkins-tests", description = "Configure CxxTest to use the XmlPrinter runner which produces Jenkins-compatible output" } newoption { category = "Pyrogenesis", trigger = "minimal-flags", description = "Only set compiler/linker flags that are really needed. Has no effect on Windows builds" } newoption { category = "Pyrogenesis", trigger = "outpath", description = "Location for generated project files", default="../workspaces/default" } newoption { category = "Pyrogenesis", trigger = "sanitize-address", description = "Enable ASAN if available" } @@ -1521,11 +1520,6 @@ function setup_tests() end end - local runner = "ErrorPrinter" - if _OPTIONS["jenkins-tests"] then - runner = "XmlPrinter" - end - local include_files = { -- Precompiled headers - the header is added to all generated .cpp files -- note that the header isn't actually precompiled here, only #included @@ -1541,7 +1535,7 @@ function setup_tests() table.insert(test_root_include_files, "lib/external_libraries/libsdl.h") end - cxxtest.init(true, true, runner, include_files, test_root_include_files) + cxxtest.init(true, true, nil, include_files, test_root_include_files) local target_type = get_main_project_target_type() project_create("test", target_type) diff --git a/source/CxxTestRunner.tpl b/source/CxxTestRunner.tpl new file mode 100644 index 0000000000..af5053c7a4 --- /dev/null +++ b/source/CxxTestRunner.tpl @@ -0,0 +1,96 @@ +// vim: set filetype=cpp : + +#include +#include + +#include +#include +#include +#include + + +void usage(const char* prog) { + const std::string help = std::string("Usage: ") + prog + R"( [options] + +--libdir dir Where to find extra shared libraries, used for running tests in staging area +--format junit|simple simple: default, good for cli - junit: for use by CI +--output file File to write to, defaults to stdout +--help This message +)"; + std::printf("%s\n", help.c_str()); +} + +int main(int argc, char **argv) +{ + + bool xml = false; + std::ostream* output = &std::cout; + std::ofstream out; + + for (int i = 1; i < argc; ++i) + { + if (std::strcmp(argv[i], "--libdir") == 0) + { + if (++i >= argc) + { + std::printf("Option libdir requires an optarg\n\n"); + usage(argv[0]); + std::exit(1); + } + + DllLoader::OverrideLibdir(argv[i]); + } + else if (std::strcmp(argv[i], "--format") == 0) + { + if (++i >= argc) + { + std::printf("Option format requires an optarg\n\n"); + usage(argv[0]); + std::exit(1); + } + + if (std::strcmp(argv[i], "junit") == 0) + xml = true; + else if (std::strcmp(argv[i], "simple") == 0) + xml = false; + else + { + std::printf("Unkown format: %s\n\n", argv[i]); + usage(argv[0]); + std::exit(1); + } + } + + else if (std::strcmp(argv[i], "--output") == 0) + { + if (++i >= argc) + { + std::printf("Option output requires an optarg\n\n"); + usage(argv[0]); + std::exit(1); + } + + out.open(argv[i]); + output = &out; + } + else if (std::strcmp(argv[i], "--help") == 0) + { + usage(argv[0]); + std::exit(0); + } + else + { + std::printf("Unkown argument: %s\n\n", argv[i]); + usage(argv[0]); + std::exit(1); + } + } + + if (xml) + return CxxTest::XmlPrinter(*output).run(); + + return CxxTest::ErrorPrinter(*output).run(); +} + +// The CxxTest "world" +