diff options
Diffstat (limited to 'tests/auto/corelib/io/qprocess')
53 files changed, 996 insertions, 185 deletions
diff --git a/tests/auto/corelib/io/qprocess/BLACKLIST b/tests/auto/corelib/io/qprocess/BLACKLIST deleted file mode 100644 index 682bcbc60c..0000000000 --- a/tests/auto/corelib/io/qprocess/BLACKLIST +++ /dev/null @@ -1,6 +0,0 @@ -[lockupsInStartDetached] -redhatenterpriselinuxworkstation-6.6 -# QTBUG-48455 -[fileWriterProcess] -windows-10 msvc-2017 - diff --git a/tests/auto/corelib/io/qprocess/CMakeLists.txt b/tests/auto/corelib/io/qprocess/CMakeLists.txt index 016fcf6666..17dea9688f 100644 --- a/tests/auto/corelib/io/qprocess/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(qprocess LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + add_subdirectory(test) add_subdirectory(testProcessCrash) add_subdirectory(testProcessEcho) @@ -28,3 +34,6 @@ if(WIN32) add_subdirectory(testProcessEchoGui) add_subdirectory(testSetNamedPipeHandleState) endif() +if(UNIX) + add_subdirectory(testUnixProcessParameters) +endif() diff --git a/tests/auto/corelib/io/qprocess/crasher.h b/tests/auto/corelib/io/qprocess/crasher.h new file mode 100644 index 0000000000..f1ca82a86b --- /dev/null +++ b/tests/auto/corelib/io/qprocess/crasher.h @@ -0,0 +1,60 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2020 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#if defined(_MSC_VER) +# include <intrin.h> +#endif +#if __has_include(<signal.h>) +# include <signal.h> +#endif +#if __has_include(<sys/resource.h>) +# include <sys/resource.h> +#endif + +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +namespace tst_QProcessCrash { +struct NoCoreDumps +{ +#if defined(RLIMIT_CORE) + struct rlimit rlim; + NoCoreDumps() + { + if (getrlimit(RLIMIT_CORE, &rlim) == 0 && rlim.rlim_cur != 0) { + struct rlimit newrlim = rlim; + newrlim.rlim_cur = 0; + setrlimit(RLIMIT_CORE, &newrlim); + } + } + ~NoCoreDumps() + { + setrlimit(RLIMIT_CORE, &rlim); + } +#endif // RLIMIT_CORE +}; + +void crashFallback(volatile int *ptr = nullptr) +{ + *ptr = 0; +} + +void crash() +{ +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) + __ud2(); +#elif defined(_MSC_VER) && defined(_M_ARM64) + __debugbreak(); +#elif __has_builtin(__builtin_trap) + __builtin_trap(); +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + asm("ud2"); +#elif defined(SIGILL) + raise(SIGILL); +#endif + + crashFallback(); +} +} // namespace tst_QProcessCrash diff --git a/tests/auto/corelib/io/qprocess/fileWriterProcess/CMakeLists.txt b/tests/auto/corelib/io/qprocess/fileWriterProcess/CMakeLists.txt index 75c30672fa..d431ba2c3f 100644 --- a/tests/auto/corelib/io/qprocess/fileWriterProcess/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/fileWriterProcess/CMakeLists.txt @@ -1,8 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from fileWriterProcess.pro. - ##################################################################### ## fileWriterProcess Binary: ##################################################################### diff --git a/tests/auto/corelib/io/qprocess/fileWriterProcess/main.cpp b/tests/auto/corelib/io/qprocess/fileWriterProcess/main.cpp index 01e49400af..0349a48067 100644 --- a/tests/auto/corelib/io/qprocess/fileWriterProcess/main.cpp +++ b/tests/auto/corelib/io/qprocess/fileWriterProcess/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QCoreApplication> #include <QFile> @@ -9,7 +9,8 @@ int main(int argc, char **argv) { QCoreApplication ca(argc, argv); QFile f; - f.open(stdin, QIODevice::ReadOnly); + if (!f.open(stdin, QIODevice::ReadOnly)) + return 1; QByteArray input; char buf[1024]; qint64 len; diff --git a/tests/auto/corelib/io/qprocess/test/CMakeLists.txt b/tests/auto/corelib/io/qprocess/test/CMakeLists.txt index f3cf9637fc..89893583ac 100644 --- a/tests/auto/corelib/io/qprocess/test/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/test/CMakeLists.txt @@ -43,5 +43,6 @@ if(WIN32) add_dependencies(tst_qprocess testProcessEchoGui testSetNamedPipeHandleState) endif() -#### Keys ignored in scope 2:.:.:test.pro:WIN32: -# TESTDATA = "../testBatFiles/*" +if(UNIX) + add_dependencies(tst_qprocess testUnixProcessParameters) +endif() diff --git a/tests/auto/corelib/io/qprocess/testBatFiles/simple.bat b/tests/auto/corelib/io/qprocess/testBatFiles/simple.bat index 900f7ae356..8567e16850 100755 --- a/tests/auto/corelib/io/qprocess/testBatFiles/simple.bat +++ b/tests/auto/corelib/io/qprocess/testBatFiles/simple.bat @@ -1,2 +1,4 @@ +:: Copyright (C) 2024 The Qt Company Ltd. +:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 @echo off -echo Hello
\ No newline at end of file +echo Hello diff --git a/tests/auto/corelib/io/qprocess/testBatFiles/with space.bat b/tests/auto/corelib/io/qprocess/testBatFiles/with space.bat index 900f7ae356..8567e16850 100755 --- a/tests/auto/corelib/io/qprocess/testBatFiles/with space.bat +++ b/tests/auto/corelib/io/qprocess/testBatFiles/with space.bat @@ -1,2 +1,4 @@ +:: Copyright (C) 2024 The Qt Company Ltd. +:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 @echo off -echo Hello
\ No newline at end of file +echo Hello diff --git a/tests/auto/corelib/io/qprocess/testDetached/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testDetached/CMakeLists.txt index c72e2738a3..2439fc67a7 100644 --- a/tests/auto/corelib/io/qprocess/testDetached/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testDetached/CMakeLists.txt @@ -1,8 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from testDetached.pro. - ##################################################################### ## testDetached Binary: ##################################################################### diff --git a/tests/auto/corelib/io/qprocess/testDetached/main.cpp b/tests/auto/corelib/io/qprocess/testDetached/main.cpp index 4282621f0b..9b0b3855a0 100644 --- a/tests/auto/corelib/io/qprocess/testDetached/main.cpp +++ b/tests/auto/corelib/io/qprocess/testDetached/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QCoreApplication> #include <QDebug> #include <QStringList> diff --git a/tests/auto/corelib/io/qprocess/testExitCodes/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testExitCodes/CMakeLists.txt index 80fae82a39..bc03f52d00 100644 --- a/tests/auto/corelib/io/qprocess/testExitCodes/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testExitCodes/CMakeLists.txt @@ -5,4 +5,9 @@ ## testExitCodes Binary: ##################################################################### -add_executable(testExitCodes main.cpp) +qt_internal_add_executable(testExitCodes + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testExitCodes/main.cpp b/tests/auto/corelib/io/qprocess/testExitCodes/main.cpp index bd416d8fa3..3f186d2272 100644 --- a/tests/auto/corelib/io/qprocess/testExitCodes/main.cpp +++ b/tests/auto/corelib/io/qprocess/testExitCodes/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <stdlib.h> diff --git a/tests/auto/corelib/io/qprocess/testForwarding/main.cpp b/tests/auto/corelib/io/qprocess/testForwarding/main.cpp index cc1bb8163f..774a8622e1 100644 --- a/tests/auto/corelib/io/qprocess/testForwarding/main.cpp +++ b/tests/auto/corelib/io/qprocess/testForwarding/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QCoreApplication> #include <QtCore/QDeadlineTimer> @@ -13,7 +13,7 @@ static bool waitForDoneFileWritten(const QString &filePath, int msecs = 30000) { QDeadlineTimer t(msecs); do { - QThread::msleep(250); + QThread::sleep(std::chrono::milliseconds{250}); QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) continue; diff --git a/tests/auto/corelib/io/qprocess/testForwardingHelper/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testForwardingHelper/CMakeLists.txt index b5d9096a21..9f5f064307 100644 --- a/tests/auto/corelib/io/qprocess/testForwardingHelper/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testForwardingHelper/CMakeLists.txt @@ -5,4 +5,9 @@ ## testForwardingHelper Binary: ##################################################################### -add_executable(testForwardingHelper main.cpp) +qt_internal_add_executable(testForwardingHelper + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testForwardingHelper/main.cpp b/tests/auto/corelib/io/qprocess/testForwardingHelper/main.cpp index 3ed5ae7360..4b4d9d4715 100644 --- a/tests/auto/corelib/io/qprocess/testForwardingHelper/main.cpp +++ b/tests/auto/corelib/io/qprocess/testForwardingHelper/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <fstream> #include <stdio.h> diff --git a/tests/auto/corelib/io/qprocess/testGuiProcess/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testGuiProcess/CMakeLists.txt index c0dac373bd..781f49e515 100644 --- a/tests/auto/corelib/io/qprocess/testGuiProcess/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testGuiProcess/CMakeLists.txt @@ -1,8 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from testGuiProcess.pro. - ##################################################################### ## testGuiProcess Binary: ##################################################################### diff --git a/tests/auto/corelib/io/qprocess/testGuiProcess/main.cpp b/tests/auto/corelib/io/qprocess/testGuiProcess/main.cpp index eff79edbdc..1c65268511 100644 --- a/tests/auto/corelib/io/qprocess/testGuiProcess/main.cpp +++ b/tests/auto/corelib/io/qprocess/testGuiProcess/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtWidgets/QApplication> #include <QtWidgets/QLabel> #include <stdio.h> diff --git a/tests/auto/corelib/io/qprocess/testProcessCrash/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessCrash/CMakeLists.txt index 0aeaa68e0d..2f167899eb 100644 --- a/tests/auto/corelib/io/qprocess/testProcessCrash/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessCrash/CMakeLists.txt @@ -5,4 +5,9 @@ ## testProcessCrash Binary: ##################################################################### -add_executable(testProcessCrash main.cpp) +qt_internal_add_executable(testProcessCrash + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testProcessCrash/main.cpp b/tests/auto/corelib/io/qprocess/testProcessCrash/main.cpp index ddf0ef0ad7..a5f1eef88a 100644 --- a/tests/auto/corelib/io/qprocess/testProcessCrash/main.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessCrash/main.cpp @@ -1,50 +1,14 @@ // Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2020 Intel Corporation. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#if __has_include(<sys/resource.h>) -# include <sys/resource.h> -# if defined(RLIMIT_CORE) -static bool disableCoreDumps() -{ - // Unix: set our core dump limit to zero to request no dialogs. - if (struct rlimit rlim; getrlimit(RLIMIT_CORE, &rlim) == 0) { - rlim.rlim_cur = 0; - setrlimit(RLIMIT_CORE, &rlim); - } - return true; -} -static bool disabledCoreDumps = disableCoreDumps(); -# endif // RLIMIT_CORE -#endif // <sys/resource.h> - -void crashFallback(volatile int *ptr = nullptr) -{ - *ptr = 0; -} - -#if defined(_MSC_VER) -# include <intrin.h> - -int main() -{ -# if defined(_M_IX86) || defined(_M_X64) - __ud2(); -# endif - - crashFallback(); -} -#elif defined(__MINGW32__) -int main() -{ - asm("ud2"); - crashFallback(); -} -#else -# include <stdlib.h> +#include "../crasher.h" +using namespace tst_QProcessCrash; int main() { - abort(); + [[maybe_unused]] // NoCoreDumps may be an empty struct, not a RAII class + NoCoreDumps disableCoreDumps; + crash(); + return 0; } -#endif diff --git a/tests/auto/corelib/io/qprocess/testProcessDeadWhileReading/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessDeadWhileReading/CMakeLists.txt index 9d1d1372b4..bdd82c6419 100644 --- a/tests/auto/corelib/io/qprocess/testProcessDeadWhileReading/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessDeadWhileReading/CMakeLists.txt @@ -5,4 +5,9 @@ ## testProcessDeadWhileReading Binary: ##################################################################### -add_executable(testProcessDeadWhileReading main.cpp) +qt_internal_add_executable(testProcessDeadWhileReading + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testProcessDeadWhileReading/main.cpp b/tests/auto/corelib/io/qprocess/testProcessDeadWhileReading/main.cpp index e13927a380..0add9d56de 100644 --- a/tests/auto/corelib/io/qprocess/testProcessDeadWhileReading/main.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessDeadWhileReading/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <stdio.h> diff --git a/tests/auto/corelib/io/qprocess/testProcessEOF/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessEOF/CMakeLists.txt index c7c7558c04..8914bf181d 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEOF/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessEOF/CMakeLists.txt @@ -5,4 +5,9 @@ ## testProcessEOF Binary: ##################################################################### -add_executable(testProcessEOF main.cpp) +qt_internal_add_executable(testProcessEOF + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testProcessEOF/main.cpp b/tests/auto/corelib/io/qprocess/testProcessEOF/main.cpp index c997fbe5c2..a0efdbd271 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEOF/main.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessEOF/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <stdio.h> diff --git a/tests/auto/corelib/io/qprocess/testProcessEcho/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessEcho/CMakeLists.txt index 30c42b8797..071156f759 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEcho/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessEcho/CMakeLists.txt @@ -5,4 +5,9 @@ ## testProcessEcho Binary: ##################################################################### -add_executable(testProcessEcho main.cpp) +qt_internal_add_executable(testProcessEcho + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testProcessEcho/main.cpp b/tests/auto/corelib/io/qprocess/testProcessEcho/main.cpp index b970704b1e..7b87380900 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEcho/main.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessEcho/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <stdio.h> diff --git a/tests/auto/corelib/io/qprocess/testProcessEcho2/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessEcho2/CMakeLists.txt index d349b6af57..9f71b1ebb6 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEcho2/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessEcho2/CMakeLists.txt @@ -5,4 +5,9 @@ ## testProcessEcho2 Binary: ##################################################################### -add_executable(testProcessEcho2 main.cpp) +qt_internal_add_executable(testProcessEcho2 + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testProcessEcho2/main.cpp b/tests/auto/corelib/io/qprocess/testProcessEcho2/main.cpp index 8420601d6e..b67480e6bb 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEcho2/main.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessEcho2/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <stdio.h> diff --git a/tests/auto/corelib/io/qprocess/testProcessEcho3/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessEcho3/CMakeLists.txt index 67cdd8d315..badc9ef82d 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEcho3/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessEcho3/CMakeLists.txt @@ -5,4 +5,9 @@ ## testProcessEcho3 Binary: ##################################################################### -add_executable(testProcessEcho3 main.cpp) +qt_internal_add_executable(testProcessEcho3 + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testProcessEcho3/main.cpp b/tests/auto/corelib/io/qprocess/testProcessEcho3/main.cpp index 7f2c78e20d..161f8bd547 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEcho3/main.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessEcho3/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <stdio.h> diff --git a/tests/auto/corelib/io/qprocess/testProcessEchoGui/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessEchoGui/CMakeLists.txt index 6254d4f61b..43663e62e8 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEchoGui/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessEchoGui/CMakeLists.txt @@ -5,5 +5,11 @@ ## testProcessEchoGui Binary: ##################################################################### -add_executable(testProcessEchoGui WIN32 main_win.cpp) +qt_internal_add_executable(testProcessEchoGui + GUI + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main_win.cpp +) target_link_libraries(testProcessEchoGui PRIVATE user32) diff --git a/tests/auto/corelib/io/qprocess/testProcessEchoGui/main_win.cpp b/tests/auto/corelib/io/qprocess/testProcessEchoGui/main_win.cpp index eab3275a38..ab54e4a328 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEchoGui/main_win.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessEchoGui/main_win.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <windows.h> diff --git a/tests/auto/corelib/io/qprocess/testProcessEnvironment/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessEnvironment/CMakeLists.txt index 6f0cdd5ffb..9069bbc7c8 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEnvironment/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessEnvironment/CMakeLists.txt @@ -5,4 +5,9 @@ ## testProcessEnvironment Binary: ##################################################################### -add_executable(testProcessEnvironment main.cpp) +qt_internal_add_executable(testProcessEnvironment + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testProcessEnvironment/main.cpp b/tests/auto/corelib/io/qprocess/testProcessEnvironment/main.cpp index d5a289fb49..dae98c3af6 100644 --- a/tests/auto/corelib/io/qprocess/testProcessEnvironment/main.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessEnvironment/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <stdio.h> #include <stdlib.h> diff --git a/tests/auto/corelib/io/qprocess/testProcessHang/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessHang/CMakeLists.txt index ba8a92ab30..cb6f490c40 100644 --- a/tests/auto/corelib/io/qprocess/testProcessHang/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessHang/CMakeLists.txt @@ -5,4 +5,9 @@ ## testProcessHang Binary: ##################################################################### -add_executable(testProcessHang main.cpp) +qt_internal_add_executable(testProcessHang + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testProcessHang/main.cpp b/tests/auto/corelib/io/qprocess/testProcessHang/main.cpp index bbfc7af19d..89fcadfb8f 100644 --- a/tests/auto/corelib/io/qprocess/testProcessHang/main.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessHang/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 Intel Corporation. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <stdio.h> diff --git a/tests/auto/corelib/io/qprocess/testProcessNormal/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessNormal/CMakeLists.txt index f71dfde8e0..7eb6388792 100644 --- a/tests/auto/corelib/io/qprocess/testProcessNormal/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessNormal/CMakeLists.txt @@ -5,4 +5,9 @@ ## testProcessNormal Binary: ##################################################################### -add_executable(testProcessNormal main.cpp) +qt_internal_add_executable(testProcessNormal + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testProcessNormal/main.cpp b/tests/auto/corelib/io/qprocess/testProcessNormal/main.cpp index ea78ca4353..a714713cf2 100644 --- a/tests/auto/corelib/io/qprocess/testProcessNormal/main.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessNormal/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only int main() diff --git a/tests/auto/corelib/io/qprocess/testProcessOutput/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessOutput/CMakeLists.txt index b74a870f4e..78005b2b93 100644 --- a/tests/auto/corelib/io/qprocess/testProcessOutput/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessOutput/CMakeLists.txt @@ -5,4 +5,9 @@ ## testProcessOutput Binary: ##################################################################### -add_executable(testProcessOutput main.cpp) +qt_internal_add_executable(testProcessOutput + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testProcessOutput/main.cpp b/tests/auto/corelib/io/qprocess/testProcessOutput/main.cpp index 914d2ed26c..08d059e0f9 100644 --- a/tests/auto/corelib/io/qprocess/testProcessOutput/main.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessOutput/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <stdio.h> diff --git a/tests/auto/corelib/io/qprocess/testProcessSpacesArgs/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testProcessSpacesArgs/CMakeLists.txt index a0ce882db1..5ea7bf53f4 100644 --- a/tests/auto/corelib/io/qprocess/testProcessSpacesArgs/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testProcessSpacesArgs/CMakeLists.txt @@ -1,10 +1,25 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -add_executable(nospace main.cpp) +qt_internal_add_executable(nospace + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) -add_executable(onespace main.cpp) +qt_internal_add_executable(onespace + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) set_target_properties(onespace PROPERTIES OUTPUT_NAME "one space") -add_executable(twospaces main.cpp) +qt_internal_add_executable(twospaces + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) set_target_properties(twospaces PROPERTIES OUTPUT_NAME "two space s") diff --git a/tests/auto/corelib/io/qprocess/testProcessSpacesArgs/main.cpp b/tests/auto/corelib/io/qprocess/testProcessSpacesArgs/main.cpp index c04c7171d5..2c319fa9af 100644 --- a/tests/auto/corelib/io/qprocess/testProcessSpacesArgs/main.cpp +++ b/tests/auto/corelib/io/qprocess/testProcessSpacesArgs/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <stdio.h> diff --git a/tests/auto/corelib/io/qprocess/testSetNamedPipeHandleState/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testSetNamedPipeHandleState/CMakeLists.txt index 9e4f0f2e56..5e532aa017 100644 --- a/tests/auto/corelib/io/qprocess/testSetNamedPipeHandleState/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testSetNamedPipeHandleState/CMakeLists.txt @@ -5,5 +5,10 @@ ## testSetNamedPipeHandleState Binary: ##################################################################### -add_executable(testSetNamedPipeHandleState main.cpp) +qt_internal_add_executable(testSetNamedPipeHandleState + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) target_link_libraries(testSetNamedPipeHandleState PRIVATE kernel32.lib) diff --git a/tests/auto/corelib/io/qprocess/testSetNamedPipeHandleState/main.cpp b/tests/auto/corelib/io/qprocess/testSetNamedPipeHandleState/main.cpp index 3f7260dd4e..ace55e9058 100644 --- a/tests/auto/corelib/io/qprocess/testSetNamedPipeHandleState/main.cpp +++ b/tests/auto/corelib/io/qprocess/testSetNamedPipeHandleState/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <windows.h> diff --git a/tests/auto/corelib/io/qprocess/testSetWorkingDirectory/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testSetWorkingDirectory/CMakeLists.txt index e4a4fa39ab..f5b9c22c90 100644 --- a/tests/auto/corelib/io/qprocess/testSetWorkingDirectory/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testSetWorkingDirectory/CMakeLists.txt @@ -1,8 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# Generated from testSetWorkingDirectory.pro. - ##################################################################### ## testSetWorkingDirectory Binary: ##################################################################### diff --git a/tests/auto/corelib/io/qprocess/testSetWorkingDirectory/main.cpp b/tests/auto/corelib/io/qprocess/testSetWorkingDirectory/main.cpp index 86f5d65a46..c12ca2de7a 100644 --- a/tests/auto/corelib/io/qprocess/testSetWorkingDirectory/main.cpp +++ b/tests/auto/corelib/io/qprocess/testSetWorkingDirectory/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QCoreApplication> diff --git a/tests/auto/corelib/io/qprocess/testSoftExit/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testSoftExit/CMakeLists.txt index c9fa43d073..44099dcab1 100644 --- a/tests/auto/corelib/io/qprocess/testSoftExit/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testSoftExit/CMakeLists.txt @@ -5,7 +5,10 @@ ## testSoftExit Binary: ##################################################################### -add_executable(testSoftExit) +qt_internal_add_executable(testSoftExit + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None +) qt_internal_extend_target(testSoftExit CONDITION WIN32 SOURCES diff --git a/tests/auto/corelib/io/qprocess/testSoftExit/main_unix.cpp b/tests/auto/corelib/io/qprocess/testSoftExit/main_unix.cpp index fce8206170..6d5078e75e 100644 --- a/tests/auto/corelib/io/qprocess/testSoftExit/main_unix.cpp +++ b/tests/auto/corelib/io/qprocess/testSoftExit/main_unix.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <sys/types.h> diff --git a/tests/auto/corelib/io/qprocess/testSoftExit/main_win.cpp b/tests/auto/corelib/io/qprocess/testSoftExit/main_win.cpp index 29de8a5129..cbef8418e6 100644 --- a/tests/auto/corelib/io/qprocess/testSoftExit/main_win.cpp +++ b/tests/auto/corelib/io/qprocess/testSoftExit/main_win.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <windows.h> diff --git a/tests/auto/corelib/io/qprocess/testSpaceInName/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testSpaceInName/CMakeLists.txt index 4223030f46..9cc05bfc3d 100644 --- a/tests/auto/corelib/io/qprocess/testSpaceInName/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/testSpaceInName/CMakeLists.txt @@ -5,6 +5,11 @@ ## testSpaceInName Binary: ##################################################################### -add_executable(testSpaceInName main.cpp) +qt_internal_add_executable(testSpaceInName + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) set_property(TARGET testSpaceInName PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../test Space In Name") diff --git a/tests/auto/corelib/io/qprocess/testSpaceInName/main.cpp b/tests/auto/corelib/io/qprocess/testSpaceInName/main.cpp index f86f575a3e..ca54a6bfde 100644 --- a/tests/auto/corelib/io/qprocess/testSpaceInName/main.cpp +++ b/tests/auto/corelib/io/qprocess/testSpaceInName/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <stdio.h> diff --git a/tests/auto/corelib/io/qprocess/testUnixProcessParameters/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testUnixProcessParameters/CMakeLists.txt new file mode 100644 index 0000000000..9b6c48933c --- /dev/null +++ b/tests/auto/corelib/io/qprocess/testUnixProcessParameters/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (C) 2023 Intel Corporation. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## testProcessNormal Binary: +##################################################################### + +qt_internal_add_executable(testUnixProcessParameters + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testUnixProcessParameters/main.cpp b/tests/auto/corelib/io/qprocess/testUnixProcessParameters/main.cpp new file mode 100644 index 0000000000..42a173debe --- /dev/null +++ b/tests/auto/corelib/io/qprocess/testUnixProcessParameters/main.cpp @@ -0,0 +1,109 @@ +// Copyright (C) 2023 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <string_view> + +#include <errno.h> +#include <fcntl.h> +#include <sched.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/resource.h> +#include <unistd.h> + +int main(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s command [extra]\nSee source code for commands\n", + argv[0]); + return EXIT_FAILURE; + } + + std::string_view cmd = argv[1]; + errno = 0; + + if (cmd.size() == 0) { + // just checking that we did get here + return EXIT_SUCCESS; + } + + if (cmd == "reset-ids") { + if (getuid() == geteuid() && getgid() == getegid()) + return EXIT_SUCCESS; + fprintf(stderr, "Real: %d %d; Effective: %d %d\n", + getuid(), getgid(), geteuid(), getegid()); + return EXIT_FAILURE; + } + + if (cmd == "reset-sighand") { + bool ok = true; + + // confirm our signal block mask is empty + sigset_t set; + sigprocmask(SIG_SETMASK, nullptr, &set); + for (int signo = 1; signo < NSIG; ++signo) { + if (sigismember(&set, signo)) { + fprintf(stderr, "'%s' is blocked.\n", strsignal(signo)); + ok = false; + } + } + + // confirm SIGUSR1 was not ignored + struct sigaction action; + sigaction(SIGUSR1, nullptr, &action); + if (action.sa_handler != SIG_DFL) { + fprintf(stderr, "SIGUSR1 is SIG_IGN\n"); + ok = false; + } + return ok ? EXIT_SUCCESS : EXIT_FAILURE; + } + + if (cmd == "ignore-sigpipe") { + // confirm SIGPIPE was ignored + struct sigaction action; + sigaction(SIGPIPE, nullptr, &action); + if (action.sa_handler == SIG_IGN) + return EXIT_SUCCESS; + fprintf(stderr, "SIGPIPE is SIG_DFL\n"); + return EXIT_FAILURE; + } + + if (cmd == "file-descriptors") { + int fd = atoi(argv[2]); + if (close(fd) < 0 && errno == EBADF) + return EXIT_SUCCESS; + fprintf(stderr, "%d is a valid file descriptor\n", fd); + return EXIT_FAILURE; + } + + if (cmd == "file-descriptors2") { + int fd1 = atoi(argv[2]); // should be open + int fd2 = atoi(argv[3]); // should be closed + if (close(fd1) < 0) + fprintf(stderr, "%d was not a valid file descriptor\n", fd1); + if (close(fd2) == 0 || errno != EBADF) + fprintf(stderr, "%d is a valid file descriptor\n", fd2); + return EXIT_SUCCESS; + } + + if (cmd == "noctty") { + int fd = open("/dev/tty", O_RDONLY); + if (fd == -1) + return EXIT_SUCCESS; + fprintf(stderr, "Could open /dev/tty\n"); + return EXIT_FAILURE; + } + + if (cmd == "setsid") { + pid_t pgid = getpgrp(); + if (pgid == getpid()) + return EXIT_SUCCESS; + fprintf(stderr, "Process group was %d\n", pgid); + return EXIT_FAILURE; + } + + fprintf(stderr, "Unknown command \"%s\"", cmd.data()); + return EXIT_FAILURE; +} diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp index 8e559eff95..316c388bc8 100644 --- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp @@ -1,6 +1,6 @@ // Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2022 Intel Corporation. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QTestEventLoop> @@ -21,12 +21,17 @@ #include <qplatformdefs.h> #ifdef Q_OS_UNIX # include <private/qcore_unix_p.h> +# include <sys/wait.h> #endif #include <QtTest/private/qemulationdetector_p.h> #include <stdlib.h> +#include "crasher.h" + +using namespace Qt::StringLiterals; + typedef void (QProcess::*QProcessErrorSignal)(QProcess::ProcessError); class tst_QProcess : public QObject @@ -42,8 +47,8 @@ private slots: void getSetCheck(); void constructing(); void simpleStart(); - void setChildProcessModifier(); void startCommand(); + void startCommandEmptyString(); void startWithOpen(); void startWithOldOpen(); void execute(); @@ -84,8 +89,11 @@ private slots: void environmentIsSorted(); void spaceInName(); void setStandardInputFile(); + void setStandardInputFileFailure(); void setStandardOutputFile_data(); void setStandardOutputFile(); + void setStandardOutputFileFailure_data() { setStandardOutputFile_data(); } + void setStandardOutputFileFailure(); void setStandardOutputFileNullDevice(); void setStandardOutputFileAndWaitForBytesWritten(); void setStandardOutputProcess_data(); @@ -111,6 +119,22 @@ private slots: void nativeArguments(); void createProcessArgumentsModifier(); #endif // Q_OS_WIN +#if defined(Q_OS_UNIX) + void setChildProcessModifier_data(); + void setChildProcessModifier(); + void failChildProcessModifier_data() { setChildProcessModifier_data(); } + void failChildProcessModifier(); + void throwInChildProcessModifier(); + void terminateInChildProcessModifier_data(); + void terminateInChildProcessModifier(); + void raiseInChildProcessModifier(); + void unixProcessParameters_data(); + void unixProcessParameters(); + void impossibleUnixProcessParameters_data(); + void impossibleUnixProcessParameters(); + void unixProcessParametersAndChildModifier(); + void unixProcessParametersOtherFileDescriptors(); +#endif void exitCodeTest(); void systemEnvironment(); void lockupsInStartDetached(); @@ -148,8 +172,11 @@ protected slots: void waitForBytesWrittenInABytesWrittenSlotSlot(); private: + QString nonExistentFileName = u"/this/file/cant/exist/hopefully"_s; + qint64 bytesAvailable; QTemporaryDir m_temporaryDir; + bool haveWorkingVFork = false; }; void tst_QProcess::initTestCase() @@ -161,6 +188,12 @@ void tst_QProcess::initTestCase() // chdir to our testdata path and execute helper apps relative to that. QString testdata_dir = QFileInfo(QFINDTESTDATA("testProcessNormal")).absolutePath(); QVERIFY2(QDir::setCurrent(testdata_dir), qPrintable("Could not chdir to " + testdata_dir)); + +#if defined(Q_OS_LINUX) && QT_CONFIG(forkfd_pidfd) + // see detect_clone_pidfd_support() in forkfd_linux.c for explanation + waitid(/*P_PIDFD*/ idtype_t(3), INT_MAX, NULL, WEXITED|WNOHANG); + haveWorkingVFork = (errno == EBADF); +#endif } void tst_QProcess::cleanupTestCase() @@ -253,44 +286,6 @@ void tst_QProcess::simpleStart() QCOMPARE(qvariant_cast<QProcess::ProcessState>(spy.at(2).at(0)), QProcess::NotRunning); } -#ifdef Q_OS_UNIX -static const char messageFromChildProcess[] = "Message from the child process"; -static void childProcessModifier(int fd) -{ - QT_WRITE(fd, messageFromChildProcess, sizeof(messageFromChildProcess) - 1); - QT_CLOSE(fd); -} -#endif - -void tst_QProcess::setChildProcessModifier() -{ -#ifdef Q_OS_UNIX - int pipes[2] = { -1 , -1 }; - QVERIFY(qt_safe_pipe(pipes) == 0); - - QProcess process; - process.setChildProcessModifier([pipes]() { - ::childProcessModifier(pipes[1]); - }); - process.start("testProcessNormal/testProcessNormal"); - if (process.state() != QProcess::Starting) - QCOMPARE(process.state(), QProcess::Running); - QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); - - char buf[sizeof messageFromChildProcess] = {}; - qt_safe_close(pipes[1]); - QCOMPARE(qt_safe_read(pipes[0], buf, sizeof(buf)), qint64(sizeof(messageFromChildProcess)) - 1); - QCOMPARE(buf, messageFromChildProcess); - qt_safe_close(pipes[0]); - - QVERIFY2(process.waitForFinished(5000), qPrintable(process.errorString())); - QCOMPARE(process.exitStatus(), QProcess::NormalExit); - QCOMPARE(process.exitCode(), 0); -#else - QSKIP("Unix-only test"); -#endif -} - void tst_QProcess::startCommand() { QProcess process; @@ -305,6 +300,25 @@ void tst_QProcess::startCommand() QCOMPARE(actual, expected); } +void tst_QProcess::startCommandEmptyString() +{ + static const char warningMsg[] = + "QProcess::startCommand: empty or whitespace-only command was provided"; + QProcess process; + + QTest::ignoreMessage(QtWarningMsg, warningMsg); + process.startCommand(""); + QVERIFY(!process.waitForStarted()); + + QTest::ignoreMessage(QtWarningMsg, warningMsg); + process.startCommand(" "); + QVERIFY(!process.waitForStarted()); + + QTest::ignoreMessage(QtWarningMsg, warningMsg); + process.startCommand("\t\n"); + QVERIFY(!process.waitForStarted()); +} + void tst_QProcess::startWithOpen() { QProcess p; @@ -1472,6 +1486,479 @@ void tst_QProcess::createProcessArgumentsModifier() } #endif // Q_OS_WIN +#ifdef Q_OS_UNIX +static constexpr int sigs[] = { SIGABRT, SIGILL, SIGSEGV }; +struct DisableCrashLogger +{ + // disable core dumps too + tst_QProcessCrash::NoCoreDumps disableCoreDumps {}; + std::array<struct sigaction, std::size(sigs)> oldhandlers; + DisableCrashLogger() + { + struct sigaction def = {}; + def.sa_handler = SIG_DFL; + for (uint i = 0; i < std::size(sigs); ++i) + sigaction(sigs[i], &def, &oldhandlers[i]); + } + ~DisableCrashLogger() + { + // restore them + for (uint i = 0; i < std::size(sigs); ++i) + sigaction(sigs[i], &oldhandlers[i], nullptr); + } +}; + +QT_BEGIN_NAMESPACE +Q_AUTOTEST_EXPORT bool _qprocessUsingVfork() noexcept; +QT_END_NAMESPACE +static constexpr char messageFromChildProcess[] = "Message from the child process"; +static_assert(std::char_traits<char>::length(messageFromChildProcess) <= PIPE_BUF); +static void childProcessModifier(int fd) +{ + QT_WRITE(fd, messageFromChildProcess, strlen(messageFromChildProcess)); + QT_CLOSE(fd); +} + +void tst_QProcess::setChildProcessModifier_data() +{ + QTest::addColumn<bool>("detached"); + QTest::addColumn<bool>("useVfork"); + QTest::newRow("normal") << false << false; + QTest::newRow("detached") << true << false; + +#ifdef QT_BUILD_INTERNAL + if (_qprocessUsingVfork()) { + QTest::newRow("normal-vfork") << false << true; + QTest::newRow("detached-vfork") << true << true; + } +#endif +} + +void tst_QProcess::setChildProcessModifier() +{ + QFETCH(bool, detached); + QFETCH(bool, useVfork); + int pipes[2] = { -1 , -1 }; + QVERIFY(qt_safe_pipe(pipes) == 0); + + QProcess process; + if (useVfork) + process.setUnixProcessParameters(QProcess::UnixProcessFlag::UseVFork); + process.setChildProcessModifier([pipes]() { + ::childProcessModifier(pipes[1]); + }); + process.setProgram("testProcessNormal/testProcessNormal"); + if (detached) { + process.startDetached(); + } else { + process.start("testProcessNormal/testProcessNormal"); + if (process.state() != QProcess::Starting) + QCOMPARE(process.state(), QProcess::Running); + QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); + QVERIFY2(process.waitForFinished(5000), qPrintable(process.errorString())); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); + QCOMPARE(process.exitCode(), 0); + } + + char buf[sizeof messageFromChildProcess] = {}; + qt_safe_close(pipes[1]); + QCOMPARE(qt_safe_read(pipes[0], buf, sizeof(buf)), qint64(sizeof(messageFromChildProcess)) - 1); + QCOMPARE(buf, messageFromChildProcess); + qt_safe_close(pipes[0]); +} + +void tst_QProcess::failChildProcessModifier() +{ + static const char failureMsg[] = + "Some error message from the child process would go here if this were a " + "real application"; + static_assert(sizeof(failureMsg) < _POSIX_PIPE_BUF / 2, + "Implementation detail: the length of the message is limited"); + + QFETCH(bool, detached); + QFETCH(bool, useVfork); + + QProcess process; + if (useVfork) + process.setUnixProcessParameters(QProcess::UnixProcessFlag::UseVFork); + process.setChildProcessModifier([&process]() { + process.failChildProcessModifier(failureMsg, EPERM); + }); + process.setProgram("testProcessNormal/testProcessNormal"); + + if (detached) { + qint64 pid; + QVERIFY(!process.startDetached(&pid)); + QCOMPARE(pid, -1); + } else { + process.start(); + QVERIFY(!process.waitForStarted(5000)); + } + + QString errMsg = process.errorString(); + QVERIFY2(errMsg.startsWith("Child process modifier reported error: "_L1 + failureMsg), + qPrintable(errMsg)); + QVERIFY2(errMsg.endsWith(strerror(EPERM)), qPrintable(errMsg)); +} + +void tst_QProcess::throwInChildProcessModifier() +{ +#ifndef __cpp_exceptions + Q_SKIP("Exceptions disabled."); +#else + static constexpr char What[] = "tst_QProcess::throwInChildProcessModifier()::MyException"; + struct MyException : std::exception { + const char *what() const noexcept override { return What; } + }; + QProcess process; + process.setChildProcessModifier([]() { + throw MyException(); + }); + process.setProgram("testProcessNormal/testProcessNormal"); + + process.start(); + QVERIFY(!process.waitForStarted(5000)); + QCOMPARE(process.state(), QProcess::NotRunning); + QCOMPARE(process.error(), QProcess::FailedToStart); + QVERIFY2(process.errorString().contains("Child process modifier threw an exception"), + qPrintable(process.errorString())); + QVERIFY2(process.errorString().contains(What), + qPrintable(process.errorString())); + + // try again, to ensure QProcess internal state wasn't corrupted + process.start(); + QVERIFY(!process.waitForStarted(5000)); + QCOMPARE(process.state(), QProcess::NotRunning); + QCOMPARE(process.error(), QProcess::FailedToStart); + QVERIFY2(process.errorString().contains("Child process modifier threw an exception"), + qPrintable(process.errorString())); + QVERIFY2(process.errorString().contains(What), + qPrintable(process.errorString())); +#endif +} + +void tst_QProcess::terminateInChildProcessModifier_data() +{ + using F = std::function<void(void)>; + QTest::addColumn<F>("function"); + QTest::addColumn<QProcess::ExitStatus>("exitStatus"); + QTest::addColumn<bool>("stderrIsEmpty"); + + QTest::newRow("_exit") << F([]() { _exit(0); }) << QProcess::NormalExit << true; + QTest::newRow("abort") << F(std::abort) << QProcess::CrashExit << true; + QTest::newRow("sigkill") << F([]() { raise(SIGKILL); }) << QProcess::CrashExit << true; + QTest::newRow("terminate") << F(std::terminate) << QProcess::CrashExit + << (std::get_terminate() == std::abort); + QTest::newRow("crash") << F([]() { tst_QProcessCrash::crash(); }) << QProcess::CrashExit << true; +} + +void tst_QProcess::terminateInChildProcessModifier() +{ + QFETCH(std::function<void(void)>, function); + QFETCH(QProcess::ExitStatus, exitStatus); + QFETCH(bool, stderrIsEmpty); + + // temporarily disable QTest's crash logger + DisableCrashLogger disableCrashLogging; + + // testForwardingHelper prints to both stdout and stderr, so if we fail to + // fail we should be able to tell too + QProcess process; + process.setChildProcessModifier(function); + process.setProgram("testForwardingHelper/testForwardingHelper"); + process.setArguments({ "/dev/null" }); + + // temporarily disable QTest's crash logger while starting the child process + { + DisableCrashLogger d; + process.start(); + } + + QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); + QVERIFY2(process.waitForFinished(5000), qPrintable(process.errorString())); + QCOMPARE(process.exitStatus(), exitStatus); + QCOMPARE(process.readAllStandardOutput(), QByteArray()); + + // some environments print extra stuff to stderr when we crash +#ifndef Q_OS_QNX + if (!QTestPrivate::isRunningArmOnX86()) { + QByteArray standardError = process.readAllStandardError(); + QVERIFY2(standardError.isEmpty() == stderrIsEmpty, + "stderr was: " + standardError); + } +#endif +} + +void tst_QProcess::raiseInChildProcessModifier() +{ +#ifdef QT_BUILD_INTERNAL + // This is similar to the above, but knowing that raise() doesn't unblock + // signals, unlike abort(), this implies that + // 1) the raise() in the child modifier will not run our handler + // 2) the write() to stdout after that will run + // 3) QProcess resets the signal handlers to the defaults, then unblocks + // 4) at that point, the signal will be delivered to the child, but our + // handler is no longer active so there'll be no write() to stderr + // + // Note for maintenance: if in the future this test causes the parent + // process to die with SIGUSR1, it means the C library is buggy and is + // using a cached PID in the child process after vfork(). + if (!QT_PREPEND_NAMESPACE(_qprocessUsingVfork())) + QSKIP("QProcess will only block Unix signals when using vfork()"); + + // we use SIGUSR1 because QtTest doesn't log it and because its default + // action is termination, not core dumping + struct SigUsr1Handler { + SigUsr1Handler() + { + struct sigaction sa = {}; + sa.sa_flags = SA_RESETHAND; + sa.sa_handler = [](int) { + static const char msg[] = "SIGUSR1 handler was run"; + (void)write(STDERR_FILENO, msg, strlen(msg)); + raise(SIGUSR1); // re-raise + }; + sigaction(SIGUSR1, &sa, nullptr); + } + ~SigUsr1Handler() { restore(); } + static void restore() { signal(SIGUSR1, SIG_DFL); } + } sigUsr1Handler; + + QProcess process; + + // QProcess will block signals with UseVFork + process.setUnixProcessParameters(QProcess::UnixProcessFlag::UseVFork | + QProcess::UnixProcessFlag::ResetSignalHandlers); + process.setChildProcessModifier([]() { + raise(SIGUSR1); + ::childProcessModifier(STDOUT_FILENO); + }); + + // testForwardingHelper prints to both stdout and stderr, so if we fail to + // fail we should be able to tell too + process.setProgram("testForwardingHelper/testForwardingHelper"); + process.setArguments({ "/dev/null" }); + + process.start(); + QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); + QVERIFY2(process.waitForFinished(5000), qPrintable(process.errorString())); + QCOMPARE(process.error(), QProcess::Crashed); + + // ensure the write() from the child modifier DID get run + QCOMPARE(process.readAllStandardOutput(), messageFromChildProcess); + + // some environments print extra stuff to stderr when we crash + if (!QTestPrivate::isRunningArmOnX86()) { + // and write() from the SIGUSR1 handler did not + QCOMPARE(process.readAllStandardError(), QByteArray()); + } +#else + QSKIP("Requires QT_BUILD_INTERNAL symbols"); +#endif +} + +void tst_QProcess::unixProcessParameters_data() +{ + QTest::addColumn<QProcess::UnixProcessParameters>("params"); + QTest::addColumn<QString>("cmd"); + QTest::newRow("defaults") << QProcess::UnixProcessParameters{} << QString(); + + auto addRow = [](const char *cmd, QProcess::UnixProcessFlags flags) { + QProcess::UnixProcessParameters params = {}; + params.flags = flags; + QTest::addRow("%s", cmd) << params << cmd; + }; + using P = QProcess::UnixProcessFlag; + addRow("reset-sighand", P::ResetSignalHandlers); + addRow("ignore-sigpipe", P::IgnoreSigPipe); + addRow("file-descriptors", P::CloseFileDescriptors); + addRow("setsid", P::CreateNewSession); + addRow("reset-ids", P::ResetIds); + + // On FreeBSD, we need to be session leader to disconnect from the CTTY + addRow("noctty", P::DisconnectControllingTerminal | P::CreateNewSession); +} + +void tst_QProcess::unixProcessParameters() +{ + QFETCH(QProcess::UnixProcessParameters, params); + QFETCH(QString, cmd); + + // set up a few things + struct Scope { + int devnull; + struct sigaction old_sigusr1, old_sigpipe; + Scope() + { + int fd = open("/dev/null", O_RDONLY); + devnull = fcntl(fd, F_DUPFD, 100); + close(fd); + + // we ignore SIGUSR1 and reset SIGPIPE to Terminate + struct sigaction act = {}; + sigemptyset(&act.sa_mask); + act.sa_handler = SIG_IGN; + sigaction(SIGUSR1, &act, &old_sigusr1); + act.sa_handler = SIG_DFL; + sigaction(SIGPIPE, &act, &old_sigpipe); + + // and we block SIGUSR2 + sigset_t *set = &act.sa_mask; // reuse this sigset_t + sigaddset(set, SIGUSR2); + sigprocmask(SIG_BLOCK, set, nullptr); + } + ~Scope() + { + if (devnull != -1) + dismiss(); + } + void dismiss() + { + close(devnull); + sigaction(SIGUSR1, &old_sigusr1, nullptr); + sigaction(SIGPIPE, &old_sigpipe, nullptr); + devnull = -1; + + sigset_t *set = &old_sigusr1.sa_mask; // reuse this sigset_t + sigaddset(set, SIGUSR2); + sigprocmask(SIG_BLOCK, set, nullptr); + } + } scope; + + if (params.flags & QProcess::UnixProcessFlag::ResetIds) { + if (getuid() == geteuid() && getgid() == getegid()) + qInfo("Process has identical real and effective IDs; this test will do nothing"); + } + + if (params.flags & QProcess::UnixProcessFlag::DisconnectControllingTerminal) { + if (int fd = open("/dev/tty", O_RDONLY); fd < 0) { + qInfo("Process has no controlling terminal; this test will do nothing"); + close(fd); + } + } + + QProcess process; + process.setUnixProcessParameters(params); + process.setStandardInputFile(QProcess::nullDevice()); // so we can't mess with SIGPIPE + process.setProgram("testUnixProcessParameters/testUnixProcessParameters"); + process.setArguments({ cmd, QString::number(scope.devnull) }); + process.start(); + QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); + QVERIFY(process.waitForFinished(5000)); + + const QString stdErr = process.readAllStandardError(); + QCOMPARE(stdErr, QString()); + QCOMPARE(process.readAll(), QString()); + QCOMPARE(process.exitCode(), 0); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); +} + +void tst_QProcess::impossibleUnixProcessParameters_data() +{ + using P = QProcess::UnixProcessParameters; + QTest::addColumn<P>("params"); + QTest::newRow("setsid") << P{ QProcess::UnixProcessFlag::CreateNewSession }; +} + +void tst_QProcess::impossibleUnixProcessParameters() +{ + QFETCH(QProcess::UnixProcessParameters, params); + + QProcess process; + if (params.flags & QProcess::UnixProcessFlag::CreateNewSession) { + process.setChildProcessModifier([]() { + // double setsid() should cause the second to fail + setsid(); + }); + } + process.setUnixProcessParameters(params); + process.start("testProcessNormal/testProcessNormal"); + + QVERIFY(!process.waitForStarted(5000)); + qDebug() << process.errorString(); +} + +void tst_QProcess::unixProcessParametersAndChildModifier() +{ + static constexpr char message[] = "Message from the handler function\n"; + static_assert(std::char_traits<char>::length(message) <= PIPE_BUF); + QProcess process; + QAtomicInt vforkControl; + int pipes[2]; + + pid_t oldpgid = getpgrp(); + + QVERIFY2(pipe(pipes) == 0, qPrintable(qt_error_string())); + auto pipeGuard0 = qScopeGuard([=] { close(pipes[0]); }); + { + auto pipeGuard1 = qScopeGuard([=] { close(pipes[1]); }); + + // verify that our modifier runs before the parameters are applied + process.setChildProcessModifier([=, &vforkControl] { + const char *pgidmsg = "PGID mismatch. "; + if (getpgrp() != oldpgid) + (void)write(pipes[1], pgidmsg, strlen(pgidmsg)); + (void)write(pipes[1], message, strlen(message)); + vforkControl.storeRelaxed(1); + }); + auto flags = QProcess::UnixProcessFlag::CloseFileDescriptors | + QProcess::UnixProcessFlag::CreateNewSession | + QProcess::UnixProcessFlag::UseVFork; + process.setUnixProcessParameters({ flags }); + process.setProgram("testUnixProcessParameters/testUnixProcessParameters"); + process.setArguments({ "file-descriptors", QString::number(pipes[1]) }); + process.start(); + QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); + } // closes the writing end of the pipe + + QVERIFY(process.waitForFinished(5000)); + QCOMPARE(process.readAllStandardError(), QString()); + QCOMPARE(process.readAll(), QString()); + + char buf[2 * sizeof(message)]; + int r = read(pipes[0], buf, sizeof(buf)); + QVERIFY2(r >= 0, qPrintable(qt_error_string())); + QCOMPARE(QByteArrayView(buf, r), message); + + if (haveWorkingVFork) + QVERIFY2(vforkControl.loadRelaxed(), "QProcess doesn't appear to have used vfork()"); +} + +void tst_QProcess::unixProcessParametersOtherFileDescriptors() +{ + constexpr int TargetFileDescriptor = 3; + int fd1 = open("/dev/null", O_RDONLY); + int devnull = fcntl(fd1, F_DUPFD, 100); // instead of F_DUPFD_CLOEXEC + close(fd1); + + auto closeFds = qScopeGuard([&] { + close(devnull); + }); + + QProcess process; + QProcess::UnixProcessParameters params; + params.flags = QProcess::UnixProcessFlag::CloseFileDescriptors + | QProcess::UnixProcessFlag::UseVFork; + params.lowestFileDescriptorToClose = 4; + process.setUnixProcessParameters(params); + process.setChildProcessModifier([devnull, &process]() { + if (dup2(devnull, TargetFileDescriptor) != TargetFileDescriptor) + process.failChildProcessModifier("dup2", errno); + }); + process.setProgram("testUnixProcessParameters/testUnixProcessParameters"); + process.setArguments({ "file-descriptors2", QString::number(TargetFileDescriptor), + QString::number(devnull) }); + process.start(); + + QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); + QVERIFY(process.waitForFinished(5000)); + QCOMPARE(process.readAllStandardError(), QString()); + QCOMPARE(process.readAll(), QString()); + QCOMPARE(process.exitCode(), 0); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); +} +#endif + void tst_QProcess::exitCodeTest() { for (int i = 0; i < 255; ++i) { @@ -1501,7 +1988,7 @@ void tst_QProcess::failToStart() // to many processes here will cause test failures later on. #if defined Q_OS_HPUX const int attempts = 15; -#elif defined Q_OS_MAC +#elif defined Q_OS_DARWIN const int attempts = 15; #else const int attempts = 50; @@ -1872,12 +2359,21 @@ void tst_QProcess::setStandardInputFile() QProcess process; QFile file(m_temporaryDir.path() + QLatin1String("/data-sif")); + QSignalSpy stateSpy(&process, &QProcess::stateChanged); + QSignalSpy errorOccurredSpy(&process, &QProcess::errorOccurred); + QVERIFY(file.open(QIODevice::WriteOnly)); file.write(data, sizeof data); file.close(); process.setStandardInputFile(file.fileName()); process.start("testProcessEcho/testProcessEcho"); + QVERIFY(process.waitForStarted()); + QCOMPARE(errorOccurredSpy.size(), 0); + QCOMPARE(stateSpy.size(), 2); + QCOMPARE(stateSpy[0][0].value<QProcess::ProcessState>(), QProcess::Starting); + QCOMPARE(stateSpy[1][0].value<QProcess::ProcessState>(), QProcess::Running); + stateSpy.clear(); QVERIFY(process.waitForFinished()); QCOMPARE(process.exitStatus(), QProcess::NormalExit); @@ -1894,31 +2390,50 @@ void tst_QProcess::setStandardInputFile() QCOMPARE(all.size(), 0); } +void tst_QProcess::setStandardInputFileFailure() +{ + QProcess process; + process.setStandardInputFile(nonExistentFileName); + + QSignalSpy stateSpy(&process, &QProcess::stateChanged); + QSignalSpy errorOccurredSpy(&process, &QProcess::errorOccurred); + + process.start("testProcessEcho/testProcessEcho"); + QVERIFY(!process.waitForStarted()); + + QCOMPARE(errorOccurredSpy.size(), 1); + QCOMPARE(errorOccurredSpy[0][0].value<QProcess::ProcessError>(), QProcess::FailedToStart); + + QCOMPARE(stateSpy.size(), 2); + QCOMPARE(stateSpy[0][0].value<QProcess::ProcessState>(), QProcess::Starting); + QCOMPARE(stateSpy[1][0].value<QProcess::ProcessState>(), QProcess::NotRunning); +} + void tst_QProcess::setStandardOutputFile_data() { - QTest::addColumn<int>("channelToTest"); - QTest::addColumn<int>("_channelMode"); + QTest::addColumn<QProcess::ProcessChannel>("channelToTest"); + QTest::addColumn<QProcess::ProcessChannelMode>("channelMode"); QTest::addColumn<bool>("append"); - QTest::newRow("stdout-truncate") << int(QProcess::StandardOutput) - << int(QProcess::SeparateChannels) + QTest::newRow("stdout-truncate") << QProcess::StandardOutput + << QProcess::SeparateChannels << false; - QTest::newRow("stdout-append") << int(QProcess::StandardOutput) - << int(QProcess::SeparateChannels) + QTest::newRow("stdout-append") << QProcess::StandardOutput + << QProcess::SeparateChannels << true; - QTest::newRow("stderr-truncate") << int(QProcess::StandardError) - << int(QProcess::SeparateChannels) + QTest::newRow("stderr-truncate") << QProcess::StandardError + << QProcess::SeparateChannels << false; - QTest::newRow("stderr-append") << int(QProcess::StandardError) - << int(QProcess::SeparateChannels) + QTest::newRow("stderr-append") << QProcess::StandardError + << QProcess::SeparateChannels << true; - QTest::newRow("merged-truncate") << int(QProcess::StandardOutput) - << int(QProcess::MergedChannels) + QTest::newRow("merged-truncate") << QProcess::StandardOutput + << QProcess::MergedChannels << false; - QTest::newRow("merged-append") << int(QProcess::StandardOutput) - << int(QProcess::MergedChannels) + QTest::newRow("merged-append") << QProcess::StandardOutput + << QProcess::MergedChannels << true; } @@ -1927,11 +2442,10 @@ void tst_QProcess::setStandardOutputFile() static const char data[] = "Original data. "; static const char testdata[] = "Test data."; - QFETCH(int, channelToTest); - QFETCH(int, _channelMode); + QFETCH(QProcess::ProcessChannel, channelToTest); + QFETCH(QProcess::ProcessChannelMode, channelMode); QFETCH(bool, append); - QProcess::ProcessChannelMode channelMode = QProcess::ProcessChannelMode(_channelMode); QIODevice::OpenMode mode = append ? QIODevice::Append : QIODevice::Truncate; // create the destination file with data @@ -1948,7 +2462,17 @@ void tst_QProcess::setStandardOutputFile() else process.setStandardErrorFile(file.fileName(), mode); + QSignalSpy stateSpy(&process, &QProcess::stateChanged); + QSignalSpy errorOccurredSpy(&process, &QProcess::errorOccurred); + process.start("testProcessEcho2/testProcessEcho2"); + QVERIFY(process.waitForStarted()); + QCOMPARE(errorOccurredSpy.size(), 0); + QCOMPARE(stateSpy.size(), 2); + QCOMPARE(stateSpy[0][0].value<QProcess::ProcessState>(), QProcess::Starting); + QCOMPARE(stateSpy[1][0].value<QProcess::ProcessState>(), QProcess::Running); + stateSpy.clear(); + process.write(testdata, sizeof testdata); QVERIFY(process.waitForFinished()); QCOMPARE(process.exitStatus(), QProcess::NormalExit); @@ -1973,6 +2497,34 @@ void tst_QProcess::setStandardOutputFile() QCOMPARE(all.size(), expectedsize); } +void tst_QProcess::setStandardOutputFileFailure() +{ + QFETCH(QProcess::ProcessChannel, channelToTest); + QFETCH(QProcess::ProcessChannelMode, channelMode); + QFETCH(bool, append); + + QIODevice::OpenMode mode = append ? QIODevice::Append : QIODevice::Truncate; + + // run the process + QProcess process; + process.setProcessChannelMode(channelMode); + if (channelToTest == QProcess::StandardOutput) + process.setStandardOutputFile(nonExistentFileName, mode); + else + process.setStandardErrorFile(nonExistentFileName, mode); + + QSignalSpy stateSpy(&process, &QProcess::stateChanged); + QSignalSpy errorOccurredSpy(&process, &QProcess::errorOccurred); + + process.start("testProcessEcho2/testProcessEcho2"); + QVERIFY(!process.waitForStarted()); + QCOMPARE(errorOccurredSpy.size(), 1); + QCOMPARE(errorOccurredSpy[0][0].value<QProcess::ProcessError>(), QProcess::FailedToStart); + QCOMPARE(stateSpy.size(), 2); + QCOMPARE(stateSpy[0][0].value<QProcess::ProcessState>(), QProcess::Starting); + QCOMPARE(stateSpy[1][0].value<QProcess::ProcessState>(), QProcess::NotRunning); +} + void tst_QProcess::setStandardOutputFileNullDevice() { static const char testdata[] = "Test data."; @@ -2248,13 +2800,21 @@ void tst_QProcess::setWorkingDirectory() void tst_QProcess::setNonExistentWorkingDirectory() { QProcess process; - process.setWorkingDirectory("this/directory/should/not/exist/for/sure"); + process.setWorkingDirectory(nonExistentFileName); + + QSignalSpy stateSpy(&process, &QProcess::stateChanged); + QSignalSpy errorOccurredSpy(&process, &QProcess::errorOccurred); // use absolute path because on Windows, the executable is relative to the parent's CWD // while on Unix with fork it's relative to the child's (with posix_spawn, it could be either). process.start(QFileInfo("testSetWorkingDirectory/testSetWorkingDirectory").absoluteFilePath()); + QVERIFY(!process.waitForFinished()); - QCOMPARE(int(process.error()), int(QProcess::FailedToStart)); + QCOMPARE(errorOccurredSpy.size(), 1); + QCOMPARE(process.error(), QProcess::FailedToStart); + QCOMPARE(stateSpy.size(), 2); + QCOMPARE(stateSpy[0][0].value<QProcess::ProcessState>(), QProcess::Starting); + QCOMPARE(stateSpy[1][0].value<QProcess::ProcessState>(), QProcess::NotRunning); #ifdef Q_OS_UNIX QVERIFY2(process.errorString().startsWith("chdir:"), process.errorString().toLocal8Bit()); @@ -2264,7 +2824,9 @@ void tst_QProcess::setNonExistentWorkingDirectory() void tst_QProcess::detachedSetNonExistentWorkingDirectory() { QProcess process; - process.setWorkingDirectory("this/directory/should/not/exist/for/sure"); + process.setWorkingDirectory(nonExistentFileName); + + QSignalSpy errorOccurredSpy(&process, &QProcess::errorOccurred); // use absolute path because on Windows, the executable is relative to the parent's CWD // while on Unix with fork it's relative to the child's (with posix_spawn, it could be either). @@ -2276,6 +2838,9 @@ void tst_QProcess::detachedSetNonExistentWorkingDirectory() QCOMPARE(process.error(), QProcess::FailedToStart); QVERIFY(process.errorString() != "Unknown error"); + QCOMPARE(errorOccurredSpy.size(), 1); + QCOMPARE(process.error(), QProcess::FailedToStart); + #ifdef Q_OS_UNIX QVERIFY2(process.errorString().startsWith("chdir:"), process.errorString().toLocal8Bit()); #endif @@ -2363,7 +2928,7 @@ public: public slots: void block() { - QThread::sleep(1); + QThread::sleep(std::chrono::seconds{1}); } }; @@ -2415,23 +2980,23 @@ void tst_QProcess::startStopStartStop() //----------------------------------------------------------------------------- void tst_QProcess::startStopStartStopBuffers_data() { - QTest::addColumn<int>("channelMode1"); - QTest::addColumn<int>("channelMode2"); + QTest::addColumn<QProcess::ProcessChannelMode>("channelMode1"); + QTest::addColumn<QProcess::ProcessChannelMode>("channelMode2"); - QTest::newRow("separate-separate") << int(QProcess::SeparateChannels) << int(QProcess::SeparateChannels); - QTest::newRow("separate-merged") << int(QProcess::SeparateChannels) << int(QProcess::MergedChannels); - QTest::newRow("merged-separate") << int(QProcess::MergedChannels) << int(QProcess::SeparateChannels); - QTest::newRow("merged-merged") << int(QProcess::MergedChannels) << int(QProcess::MergedChannels); - QTest::newRow("merged-forwarded") << int(QProcess::MergedChannels) << int(QProcess::ForwardedChannels); + QTest::newRow("separate-separate") << QProcess::SeparateChannels << QProcess::SeparateChannels; + QTest::newRow("separate-merged") << QProcess::SeparateChannels << QProcess::MergedChannels; + QTest::newRow("merged-separate") << QProcess::MergedChannels << QProcess::SeparateChannels; + QTest::newRow("merged-merged") << QProcess::MergedChannels << QProcess::MergedChannels; + QTest::newRow("merged-forwarded") << QProcess::MergedChannels << QProcess::ForwardedChannels; } void tst_QProcess::startStopStartStopBuffers() { - QFETCH(int, channelMode1); - QFETCH(int, channelMode2); + QFETCH(QProcess::ProcessChannelMode, channelMode1); + QFETCH(QProcess::ProcessChannelMode, channelMode2); QProcess process; - process.setProcessChannelMode(QProcess::ProcessChannelMode(channelMode1)); + process.setProcessChannelMode(channelMode1); process.start("testProcessHang/testProcessHang"); QVERIFY2(process.waitForReadyRead(), process.errorString().toLocal8Bit()); if (channelMode1 == QProcess::SeparateChannels || channelMode1 == QProcess::ForwardedOutputChannel) { @@ -2442,14 +3007,18 @@ void tst_QProcess::startStopStartStopBuffers() } // We want to test that the write buffer still has bytes after the child - // exiting. We do that by writing to a child process that never reads. We - // just have to write more data than a pipe can hold, so that even if - // QProcess finds the pipe writable (during waitForFinished() or in the - // QWindowsPipeWriter thread), some data will remain. The worst case I know - // of is Linux, which defaults to 64 kB of buffer. + // exits. We can do that by writing data until the OS stops consuming data, + // indicating that the pipe buffers are full. The initial value of 128 kB + // should make this loop typicall run only once; the worst case I know of + // is Linux, which defaults to 64 kB of buffer. - process.write(QByteArray(128 * 1024, 'a')); - QVERIFY(process.bytesToWrite() > 0); + QByteArray chunk(128 * 1024, 'a'); + do { + process.write(chunk); + QVERIFY(process.bytesToWrite() > 0); + process.waitForBytesWritten(1); + } while (process.bytesToWrite() == 0); + chunk = {}; process.kill(); QVERIFY(process.waitForFinished()); @@ -2457,7 +3026,8 @@ void tst_QProcess::startStopStartStopBuffers() #ifndef Q_OS_WIN // confirm that our buffers are still full // Note: this doesn't work on Windows because our buffers are drained into - // QWindowsPipeWriter before being sent to the child process. + // QWindowsPipeWriter before being sent to the child process and are lost + // in waitForFinished() -> processFinished() -> cleanup(). QVERIFY(process.bytesToWrite() > 0); QVERIFY(process.bytesAvailable() > 0); // channelMode1 is not ForwardedChannels if (channelMode1 == QProcess::SeparateChannels || channelMode1 == QProcess::ForwardedOutputChannel) { @@ -2467,7 +3037,7 @@ void tst_QProcess::startStopStartStopBuffers() } #endif - process.setProcessChannelMode(QProcess::ProcessChannelMode(channelMode2)); + process.setProcessChannelMode(channelMode2); process.start("testProcessEcho2/testProcessEcho2", {}, QIODevice::ReadWrite | QIODevice::Text); // the buffers should now be empty |