2022-11-22 08:35:43 +01:00
|
|
|
// Copyright (C) 2022 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2022-11-22 08:35:43 +01:00
|
|
|
|
2022-11-29 10:41:30 +01:00
|
|
|
#include <app/app_version.h>
|
|
|
|
|
2022-11-22 08:35:43 +01:00
|
|
|
#include <utils/commandline.h>
|
2022-11-29 10:41:30 +01:00
|
|
|
#include <utils/environment.h>
|
2022-11-24 09:16:40 +01:00
|
|
|
#include <utils/hostosinfo.h>
|
2022-11-29 10:41:30 +01:00
|
|
|
#include <utils/launcherinterface.h>
|
2023-03-13 13:56:50 +01:00
|
|
|
#include <utils/macroexpander.h>
|
2023-05-03 17:05:35 +02:00
|
|
|
#include <utils/process.h>
|
2022-11-29 10:41:30 +01:00
|
|
|
#include <utils/processinterface.h>
|
|
|
|
#include <utils/temporarydirectory.h>
|
2022-11-22 08:35:43 +01:00
|
|
|
|
|
|
|
#include <QObject>
|
|
|
|
#include <QtTest>
|
|
|
|
|
2022-11-29 10:41:30 +01:00
|
|
|
#include <iostream>
|
|
|
|
|
2022-11-22 08:35:43 +01:00
|
|
|
using namespace Utils;
|
|
|
|
|
2022-11-29 10:41:30 +01:00
|
|
|
FilePath self;
|
|
|
|
|
2022-11-22 08:35:43 +01:00
|
|
|
class tst_CommandLine : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
private:
|
2022-11-29 10:41:30 +01:00
|
|
|
Environment testEnv = Environment::systemEnvironment();
|
|
|
|
QString newLine;
|
|
|
|
|
|
|
|
QString run(const CommandLine &cmd)
|
|
|
|
{
|
2023-05-03 16:00:22 +02:00
|
|
|
Process p;
|
2022-11-29 10:41:30 +01:00
|
|
|
p.setCommand(cmd);
|
|
|
|
p.setEnvironment(testEnv);
|
|
|
|
p.runBlocking();
|
2024-01-21 17:17:46 +01:00
|
|
|
return QString::fromUtf8(p.rawStdOut());
|
2022-11-29 10:41:30 +01:00
|
|
|
}
|
|
|
|
|
2022-11-22 08:35:43 +01:00
|
|
|
private slots:
|
2022-11-29 10:41:30 +01:00
|
|
|
void initTestCase()
|
|
|
|
{
|
|
|
|
TemporaryDirectory::setMasterTemporaryDirectory(
|
|
|
|
QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX");
|
|
|
|
|
|
|
|
const QString libExecPath(qApp->applicationDirPath() + '/'
|
|
|
|
+ QLatin1String(TEST_RELATIVE_LIBEXEC_PATH));
|
|
|
|
LauncherInterface::setPathToLauncher(libExecPath);
|
|
|
|
|
|
|
|
testEnv.appendOrSet("TEST_ECHO", "1");
|
|
|
|
|
|
|
|
if (HostOsInfo::isWindowsHost())
|
|
|
|
newLine = "\r\n";
|
|
|
|
else
|
|
|
|
newLine = "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
void cleanupTestCase() { Singleton::deleteAll(); }
|
|
|
|
|
|
|
|
void testSpace()
|
|
|
|
{
|
|
|
|
CommandLine cmd(self, {"With Space"});
|
|
|
|
QString expected = "With Space" + newLine;
|
|
|
|
QCOMPARE(run(cmd), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testQuote()
|
|
|
|
{
|
|
|
|
QStringList args = {"\"With <\"Quote\"> % ^^ \"", "Hallo ??"};
|
|
|
|
CommandLine cmd(self, args);
|
|
|
|
QString expected = args.join(newLine) + newLine;
|
|
|
|
QCOMPARE(run(cmd), expected);
|
|
|
|
}
|
2022-11-22 08:35:43 +01:00
|
|
|
|
|
|
|
void testAnd()
|
|
|
|
{
|
2022-11-29 10:41:30 +01:00
|
|
|
QStringList args = {"foo", "bar", "baz"};
|
|
|
|
CommandLine cmd(self, {args[0]});
|
|
|
|
CommandLine cmd2(self, args.sliced(1));
|
2022-11-22 08:35:43 +01:00
|
|
|
|
|
|
|
cmd.addCommandLineWithAnd(cmd2);
|
|
|
|
|
2022-11-29 10:41:30 +01:00
|
|
|
QString expected = args.join(newLine) + newLine;
|
2022-11-23 14:57:12 +01:00
|
|
|
|
2022-11-29 10:41:30 +01:00
|
|
|
QCOMPARE(run(cmd), expected);
|
2022-11-22 08:35:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void testAndComplex()
|
|
|
|
{
|
2022-11-29 10:41:30 +01:00
|
|
|
QStringList args = {"foo", "long with space", "bar\"", "blizz is 'great"};
|
|
|
|
CommandLine cmd(self, args.sliced(0, 2));
|
|
|
|
CommandLine cmd2(self, args.sliced(2, 2));
|
2022-11-22 08:35:43 +01:00
|
|
|
|
|
|
|
cmd.addCommandLineWithAnd(cmd2);
|
2022-11-29 10:41:30 +01:00
|
|
|
QString expected = args.join(newLine) + newLine;
|
|
|
|
QString actual = run(cmd);
|
|
|
|
QCOMPARE(actual, expected);
|
2022-11-22 08:35:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void testAndAdd()
|
|
|
|
{
|
2022-11-24 09:16:40 +01:00
|
|
|
if (HostOsInfo::isWindowsHost())
|
2022-11-29 10:41:30 +01:00
|
|
|
QSKIP("The test does not yet work on Windows.");
|
|
|
|
|
|
|
|
QStringList args = {"foo", "long with space", "bar", "blizz is great"};
|
|
|
|
|
|
|
|
CommandLine cmd(self, args.sliced(0, 2));
|
|
|
|
CommandLine cmd2(self, args.sliced(2, 2));
|
2022-11-24 09:16:40 +01:00
|
|
|
|
2022-11-22 08:35:43 +01:00
|
|
|
cmd.addCommandLineWithAnd(cmd2);
|
|
|
|
|
2022-11-29 10:41:30 +01:00
|
|
|
CommandLine shell;
|
|
|
|
if (HostOsInfo::isWindowsHost()) {
|
|
|
|
shell.setExecutable(FilePath::fromUserInput(qEnvironmentVariable("COMSPEC")));
|
|
|
|
shell.addArgs({"/v:off", "/s", "/c"});
|
|
|
|
} else {
|
|
|
|
shell.setExecutable(FilePath::fromUserInput("/bin/sh"));
|
|
|
|
shell.addArgs({"-c"});
|
|
|
|
}
|
2022-11-22 08:35:43 +01:00
|
|
|
|
2022-11-29 10:41:30 +01:00
|
|
|
shell.addCommandLineAsSingleArg(cmd);
|
2022-11-23 14:57:12 +01:00
|
|
|
|
2022-11-29 10:41:30 +01:00
|
|
|
QString expected = args.join(newLine) + newLine;
|
|
|
|
QString actual = run(shell);
|
|
|
|
QCOMPARE(actual, expected);
|
2022-11-22 08:35:43 +01:00
|
|
|
}
|
2023-03-13 13:56:50 +01:00
|
|
|
|
|
|
|
void testFromUserInput_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("input");
|
|
|
|
QTest::addColumn<QString>("executable");
|
|
|
|
QTest::addColumn<QString>("arguments");
|
|
|
|
|
|
|
|
QTest::newRow("empty") << ""
|
|
|
|
<< ""
|
|
|
|
<< "";
|
|
|
|
QTest::newRow("command") << "command"
|
|
|
|
<< "command"
|
|
|
|
<< "";
|
|
|
|
QTest::newRow("command-with-args") << "command and args"
|
|
|
|
<< "command"
|
|
|
|
<< "and args";
|
|
|
|
|
|
|
|
if (!HostOsInfo::isWindowsHost()) {
|
|
|
|
QTest::newRow("command-with-space-slash") << "command\\ with-space and args"
|
|
|
|
<< "command with-space"
|
|
|
|
<< "and args";
|
|
|
|
QTest::newRow("command-with-space-single-quote") << "'command with-space' and args"
|
|
|
|
<< "command with-space"
|
|
|
|
<< "and args";
|
|
|
|
}
|
|
|
|
QTest::newRow("command-with-space-double-quote") << "\"command with-space\" and args"
|
|
|
|
<< "command with-space"
|
|
|
|
<< "and args";
|
|
|
|
|
|
|
|
QTest::newRow("command-with-space-double-quote-in-name")
|
|
|
|
<< "\"command\\\"with-quote\" and args"
|
|
|
|
<< "command\"with-quote"
|
|
|
|
<< "and args";
|
|
|
|
|
|
|
|
QTest::newRow("inside-space-quoted") << "command\" \"withspace args here"
|
|
|
|
<< "command withspace"
|
|
|
|
<< "args here";
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFromUserInput()
|
|
|
|
{
|
|
|
|
QFETCH(QString, input);
|
|
|
|
QFETCH(QString, executable);
|
|
|
|
QFETCH(QString, arguments);
|
|
|
|
|
|
|
|
CommandLine cmd = CommandLine::fromUserInput(input);
|
|
|
|
QCOMPARE(cmd.executable(), FilePath::fromUserInput(executable));
|
|
|
|
QCOMPARE(cmd.arguments(), arguments);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testFromInputFails()
|
|
|
|
{
|
|
|
|
if (HostOsInfo::isWindowsHost())
|
|
|
|
QSKIP("The test does not work on Windows.");
|
|
|
|
|
|
|
|
CommandLine cmd = CommandLine::fromUserInput("command\\\\\\ with-space and args");
|
|
|
|
QEXPECT_FAIL("",
|
|
|
|
"CommandLine::fromUserInput (and FilePath::fromUserInput) does not handle "
|
|
|
|
"backslashes correctly",
|
|
|
|
Continue);
|
|
|
|
QCOMPARE(cmd.executable().fileName(), "command\\ with-space");
|
|
|
|
QCOMPARE(cmd.arguments(), "and args");
|
|
|
|
}
|
|
|
|
|
2023-06-15 08:20:56 +02:00
|
|
|
void testFromInputWithMacro_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("input");
|
|
|
|
QTest::addColumn<QString>("expectedExecutable");
|
|
|
|
QTest::addColumn<QString>("expectedArguments");
|
|
|
|
|
|
|
|
QTest::newRow("simple") << "command %{hello}"
|
|
|
|
<< "command"
|
2023-06-16 15:14:29 +02:00
|
|
|
<< "hello world";
|
2023-06-15 08:20:56 +02:00
|
|
|
|
|
|
|
QTest::newRow("simple-quoted")
|
|
|
|
<< "command \"%{hello}\""
|
|
|
|
<< "command" << (HostOsInfo::isWindowsHost() ? "\"hello world\"" : "'hello world'");
|
|
|
|
|
|
|
|
QTest::newRow("quoted-with-extra")
|
|
|
|
<< "command \"%{hello}, he said\""
|
|
|
|
<< "command"
|
|
|
|
<< (HostOsInfo::isWindowsHost() ? "\"hello world, he said\"" : "'hello world, he said'");
|
|
|
|
|
|
|
|
QTest::newRow("convert-to-quote-win")
|
|
|
|
<< "command 'this is a test'"
|
|
|
|
<< "command"
|
|
|
|
<< (HostOsInfo::isWindowsHost() ? "\"this is a test\"" : "'this is a test'");
|
|
|
|
}
|
|
|
|
|
2023-03-13 13:56:50 +01:00
|
|
|
void testFromInputWithMacro()
|
|
|
|
{
|
2023-06-15 08:20:56 +02:00
|
|
|
QFETCH(QString, input);
|
|
|
|
QFETCH(QString, expectedExecutable);
|
|
|
|
QFETCH(QString, expectedArguments);
|
|
|
|
|
2023-03-13 13:56:50 +01:00
|
|
|
MacroExpander expander;
|
|
|
|
expander.registerVariable("hello", "world var", [] { return "hello world"; });
|
|
|
|
|
2023-06-15 08:20:56 +02:00
|
|
|
CommandLine cmd = CommandLine::fromUserInput(input, &expander);
|
|
|
|
QCOMPARE(cmd.executable().toUserOutput(), expectedExecutable);
|
|
|
|
|
2023-06-16 15:14:29 +02:00
|
|
|
if (HostOsInfo::isWindowsHost()) {
|
2023-06-15 08:20:56 +02:00
|
|
|
QEXPECT_FAIL("convert-to-quote-win",
|
|
|
|
"Windows should convert single to double quotes",
|
|
|
|
Continue);
|
2023-06-16 15:14:29 +02:00
|
|
|
}
|
2023-06-15 08:20:56 +02:00
|
|
|
|
|
|
|
QCOMPARE(cmd.arguments(), expectedArguments);
|
|
|
|
}
|
|
|
|
|
|
|
|
void testMultiCommand_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("input");
|
|
|
|
QTest::addColumn<QString>("executable");
|
|
|
|
QTest::addColumn<QString>("arguments");
|
|
|
|
|
|
|
|
QTest::newRow("command-and-command") << "command1 && command2"
|
|
|
|
<< "command1"
|
|
|
|
<< "&& command2";
|
|
|
|
|
|
|
|
QTest::newRow("command-and-command-nospace") << "command1&&command2"
|
|
|
|
<< "command1"
|
|
|
|
<< "&&command2";
|
|
|
|
|
|
|
|
QTest::newRow("command-semicolon-command") << "command1 ; command2"
|
|
|
|
<< "command1"
|
|
|
|
<< "; command2";
|
|
|
|
|
|
|
|
QTest::newRow("command-or-command") << "command1 || command2"
|
|
|
|
<< "command1"
|
|
|
|
<< "|| command2";
|
|
|
|
}
|
|
|
|
|
|
|
|
void testMultiCommand()
|
|
|
|
{
|
|
|
|
QFETCH(QString, input);
|
|
|
|
QFETCH(QString, executable);
|
|
|
|
QFETCH(QString, arguments);
|
|
|
|
|
|
|
|
CommandLine cmdLine = CommandLine::fromUserInput(input);
|
|
|
|
|
|
|
|
QEXPECT_FAIL(
|
|
|
|
"command-and-command-nospace",
|
|
|
|
"CommandLine::fromUserInput does not handle multi-command without space correctly",
|
|
|
|
Abort);
|
2023-03-13 13:56:50 +01:00
|
|
|
|
2023-06-15 08:20:56 +02:00
|
|
|
QCOMPARE(cmdLine.executable().path(), executable);
|
|
|
|
QCOMPARE(cmdLine.arguments(), arguments);
|
2023-03-13 13:56:50 +01:00
|
|
|
}
|
2022-11-22 08:35:43 +01:00
|
|
|
};
|
|
|
|
|
2022-11-29 10:41:30 +01:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
QCoreApplication app(argc, argv);
|
|
|
|
|
|
|
|
self = FilePath::fromString(argv[0]);
|
|
|
|
|
|
|
|
if (qEnvironmentVariableIsSet("TEST_ECHO")) {
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
|
|
std::cout << argv[i] << std::endl;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTLIB_SELFCOVERAGE_START(tst_CommandLine)
|
|
|
|
QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<tst_CommandLine>();
|
|
|
|
|
|
|
|
tst_CommandLine tc;
|
|
|
|
QTEST_SET_MAIN_SOURCE_PATH
|
|
|
|
return QTest::qExec(&tc, argc, argv);
|
|
|
|
}
|
2022-11-22 08:35:43 +01:00
|
|
|
|
|
|
|
#include "tst_commandline.moc"
|