summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/io/qprocess
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/io/qprocess')
-rw-r--r--tests/auto/corelib/io/qprocess/BLACKLIST6
-rw-r--r--tests/auto/corelib/io/qprocess/CMakeLists.txt6
-rw-r--r--tests/auto/corelib/io/qprocess/crasher.h4
-rw-r--r--tests/auto/corelib/io/qprocess/fileWriterProcess/main.cpp5
-rw-r--r--tests/auto/corelib/io/qprocess/test/CMakeLists.txt4
-rwxr-xr-xtests/auto/corelib/io/qprocess/testBatFiles/simple.bat4
-rwxr-xr-xtests/auto/corelib/io/qprocess/testBatFiles/with space.bat4
-rw-r--r--tests/auto/corelib/io/qprocess/testDetached/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testExitCodes/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testForwarding/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testForwardingHelper/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testGuiProcess/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessCrash/main.cpp3
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessDeadWhileReading/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessEOF/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessEcho/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessEcho2/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessEcho3/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessEchoGui/main_win.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessEnvironment/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessHang/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessNormal/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessOutput/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testProcessSpacesArgs/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testSetNamedPipeHandleState/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testSetWorkingDirectory/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testSoftExit/main_unix.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testSoftExit/main_win.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testSpaceInName/main.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testUnixProcessParameters/main.cpp63
-rw-r--r--tests/auto/corelib/io/qprocess/tst_qprocess.cpp508
31 files changed, 559 insertions, 90 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 9fbf7657fd..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)
diff --git a/tests/auto/corelib/io/qprocess/crasher.h b/tests/auto/corelib/io/qprocess/crasher.h
index d34d6dd058..f1ca82a86b 100644
--- a/tests/auto/corelib/io/qprocess/crasher.h
+++ b/tests/auto/corelib/io/qprocess/crasher.h
@@ -1,6 +1,6 @@
// 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 defined(_MSC_VER)
# include <intrin.h>
@@ -45,6 +45,8 @@ 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__))
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 2e707e8fe3..89893583ac 100644
--- a/tests/auto/corelib/io/qprocess/test/CMakeLists.txt
+++ b/tests/auto/corelib/io/qprocess/test/CMakeLists.txt
@@ -42,3 +42,7 @@ endif()
if(WIN32)
add_dependencies(tst_qprocess testProcessEchoGui testSetNamedPipeHandleState)
endif()
+
+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/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/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 43e196ab61..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>
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/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/main.cpp b/tests/auto/corelib/io/qprocess/testProcessCrash/main.cpp
index 474508c453..a5f1eef88a 100644
--- a/tests/auto/corelib/io/qprocess/testProcessCrash/main.cpp
+++ b/tests/auto/corelib/io/qprocess/testProcessCrash/main.cpp
@@ -1,12 +1,13 @@
// 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
#include "../crasher.h"
using namespace tst_QProcessCrash;
int main()
{
+ [[maybe_unused]] // NoCoreDumps may be an empty struct, not a RAII class
NoCoreDumps disableCoreDumps;
crash();
return 0;
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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/main.cpp b/tests/auto/corelib/io/qprocess/testUnixProcessParameters/main.cpp
index e701677403..42a173debe 100644
--- a/tests/auto/corelib/io/qprocess/testUnixProcessParameters/main.cpp
+++ b/tests/auto/corelib/io/qprocess/testUnixProcessParameters/main.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2023 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 <string_view>
@@ -29,18 +29,39 @@ int main(int argc, char **argv)
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") {
- // confirm it was not ignored
+ 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)
- return EXIT_SUCCESS;
- fprintf(stderr, "SIGUSR1 is SIG_IGN\n");
- return EXIT_FAILURE;
+ 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 it was ignored
+ // confirm SIGPIPE was ignored
struct sigaction action;
sigaction(SIGPIPE, nullptr, &action);
if (action.sa_handler == SIG_IGN)
@@ -49,7 +70,7 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
- if (cmd == "std-file-descriptors") {
+ if (cmd == "file-descriptors") {
int fd = atoi(argv[2]);
if (close(fd) < 0 && errno == EBADF)
return EXIT_SUCCESS;
@@ -57,6 +78,32 @@ int main(int argc, char **argv)
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 7c318f0ca7..5f35732979 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>
@@ -28,6 +28,10 @@
#include <stdlib.h>
+#include "crasher.h"
+
+using namespace Qt::StringLiterals;
+
typedef void (QProcess::*QProcessErrorSignal)(QProcess::ProcessError);
class tst_QProcess : public QObject
@@ -44,6 +48,7 @@ private slots:
void constructing();
void simpleStart();
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();
@@ -114,10 +122,18 @@ private slots:
#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();
@@ -156,6 +172,8 @@ protected slots:
void waitForBytesWrittenInABytesWrittenSlotSlot();
private:
+ QString nonExistentFileName = u"/this/file/cant/exist/hopefully"_s;
+
qint64 bytesAvailable;
QTemporaryDir m_temporaryDir;
bool haveWorkingVFork = false;
@@ -282,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;
@@ -1450,6 +1487,30 @@ 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)
@@ -1461,17 +1522,28 @@ static void childProcessModifier(int fd)
void tst_QProcess::setChildProcessModifier_data()
{
QTest::addColumn<bool>("detached");
- QTest::newRow("normal") << false;
- QTest::newRow("detached") << true;
+ 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]);
});
@@ -1495,14 +1567,52 @@ void tst_QProcess::setChildProcessModifier()
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 42;
+ throw MyException();
});
process.setProgram("testProcessNormal/testProcessNormal");
@@ -1510,7 +1620,9 @@ void tst_QProcess::throwInChildProcessModifier()
QVERIFY(!process.waitForStarted(5000));
QCOMPARE(process.state(), QProcess::NotRunning);
QCOMPARE(process.error(), QProcess::FailedToStart);
- QVERIFY2(process.errorString().contains("childProcessModifier"),
+ 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
@@ -1518,11 +1630,133 @@ void tst_QProcess::throwInChildProcessModifier()
QVERIFY(!process.waitForStarted(5000));
QCOMPARE(process.state(), QProcess::NotRunning);
QCOMPARE(process.error(), QProcess::FailedToStart);
- QVERIFY2(process.errorString().contains("childProcessModifier"),
+ 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";
+ 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");
@@ -1537,7 +1771,12 @@ void tst_QProcess::unixProcessParameters_data()
using P = QProcess::UnixProcessFlag;
addRow("reset-sighand", P::ResetSignalHandlers);
addRow("ignore-sigpipe", P::IgnoreSigPipe);
- addRow("std-file-descriptors", P::CloseNonStandardFileDescriptors);
+ 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()
@@ -1562,6 +1801,11 @@ void tst_QProcess::unixProcessParameters()
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()
{
@@ -1574,9 +1818,25 @@ void tst_QProcess::unixProcessParameters()
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
@@ -1585,12 +1845,39 @@ void tst_QProcess::unixProcessParameters()
process.start();
QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString()));
QVERIFY(process.waitForFinished(5000));
- QCOMPARE(process.readAllStandardError(), QString());
+
+ 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";
@@ -1599,6 +1886,8 @@ void tst_QProcess::unixProcessParametersAndChildModifier()
QAtomicInt vforkControl;
int pipes[2];
+ pid_t oldpgid = getpgrp();
+
QVERIFY2(pipe(pipes) == 0, qPrintable(qt_error_string()));
auto pipeGuard0 = qScopeGuard([=] { close(pipes[0]); });
{
@@ -1606,14 +1895,18 @@ void tst_QProcess::unixProcessParametersAndChildModifier()
// verify that our modifier runs before the parameters are applied
process.setChildProcessModifier([=, &vforkControl] {
+ const char *pgidmsg = "PGID mismatch. ";
+ if (getpgrp() != oldpgid)
+ write(pipes[1], pgidmsg, strlen(pgidmsg));
write(pipes[1], message, strlen(message));
vforkControl.storeRelaxed(1);
});
- auto flags = QProcess::UnixProcessFlag::CloseNonStandardFileDescriptors |
+ auto flags = QProcess::UnixProcessFlag::CloseFileDescriptors |
+ QProcess::UnixProcessFlag::CreateNewSession |
QProcess::UnixProcessFlag::UseVFork;
process.setUnixProcessParameters({ flags });
process.setProgram("testUnixProcessParameters/testUnixProcessParameters");
- process.setArguments({ "std-file-descriptors", QString::number(pipes[1]) });
+ process.setArguments({ "file-descriptors", QString::number(pipes[1]) });
process.start();
QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString()));
} // closes the writing end of the pipe
@@ -1630,6 +1923,40 @@ void tst_QProcess::unixProcessParametersAndChildModifier()
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()
@@ -2032,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);
@@ -2054,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;
}
@@ -2087,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
@@ -2108,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);
@@ -2133,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.";
@@ -2408,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());
@@ -2424,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).
@@ -2436,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
@@ -2575,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) {
@@ -2602,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());
@@ -2617,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) {
@@ -2627,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