From 67f4a720a891fc52b2a38629d73a249fe7ddc209 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 17 Oct 2024 12:33:09 +0200 Subject: [PATCH] Utils: Remove AbstractMacroExpander It was only used by its own tests. Instead move the functionality into MacroExpander(Private). The existing unittests are moved into tst_expander. Change-Id: Ia54f659efa7976b17f47a0084900f98acc834939 Reviewed-by: hjk --- src/libs/utils/commandline.cpp | 8 +- src/libs/utils/commandline.h | 7 +- src/libs/utils/macroexpander.cpp | 127 +++++- src/libs/utils/macroexpander.h | 5 +- src/libs/utils/stringutils.cpp | 120 ----- src/libs/utils/stringutils.h | 23 - tests/auto/utils/expander/tst_expander.cpp | 422 ++++++++++++++++-- tests/auto/utils/process/tst_process.cpp | 304 ------------- .../utils/stringutils/tst_stringutils.cpp | 119 ----- 9 files changed, 522 insertions(+), 613 deletions(-) diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp index b4fa60481ed..d5ad99a4178 100644 --- a/src/libs/utils/commandline.cpp +++ b/src/libs/utils/commandline.cpp @@ -766,7 +766,7 @@ static int quoteArgInternalWin(QString &ret, int bslashes) * \return false if the string could not be parsed and therefore no safe * substitution was possible */ -bool ProcessArgs::expandMacros(QString *cmd, AbstractMacroExpander *mx, OsType osType) +bool ProcessArgs::expandMacros(QString *cmd, const FindMacro &findMacro, OsType osType) { QString str = *cmd; if (str.isEmpty()) @@ -775,7 +775,7 @@ bool ProcessArgs::expandMacros(QString *cmd, AbstractMacroExpander *mx, OsType o QString rsts; int varLen; int varPos = 0; - if (!(varLen = mx->findMacro(str, &varPos, &rsts))) + if (!(varLen = findMacro(str, &varPos, &rsts))) return true; int pos = 0; @@ -839,7 +839,7 @@ bool ProcessArgs::expandMacros(QString *cmd, AbstractMacroExpander *mx, OsType o str.replace(pos, varLen, rsts); pos += rsts.length(); varPos = pos; - if (!(varLen = mx->findMacro(str, &varPos, &rsts))) { + if (!(varLen = findMacro(str, &varPos, &rsts))) { // Don't leave immediately, as we may be in CrtNeedWord state which could // be still resolved, or we may have inserted trailing backslashes. varPos = INT_MAX; @@ -954,7 +954,7 @@ bool ProcessArgs::expandMacros(QString *cmd, AbstractMacroExpander *mx, OsType o str.replace(pos, varLen, rsts); pos += rsts.length(); varPos = pos; - if (!(varLen = mx->findMacro(str, &varPos, &rsts))) + if (!(varLen = findMacro(str, &varPos, &rsts))) break; continue; } diff --git a/src/libs/utils/commandline.h b/src/libs/utils/commandline.h index 92f9a996246..84a32384efb 100644 --- a/src/libs/utils/commandline.h +++ b/src/libs/utils/commandline.h @@ -59,9 +59,12 @@ public: static QStringList splitArgs(const QString &cmd, OsType osType, bool abortOnMeta = false, SplitError *err = nullptr, const Environment *env = nullptr, const QString *pwd = nullptr); + + using FindMacro = std::function; + //! Safely replace the expandos in a shell command - static bool expandMacros(QString *cmd, AbstractMacroExpander *mx, - OsType osType = HostOsInfo::hostOs()); + static bool expandMacros( + QString *cmd, const FindMacro &findMacro, OsType osType = HostOsInfo::hostOs()); /*! Iterate over arguments from a command line. * Assumes that the name of the actual command is *not* part of the line. diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index 09bd95bc7bd..63360c93f09 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace Utils { namespace Internal { @@ -27,12 +28,112 @@ const char kNativePathPostfix[] = ":NativePath"; const char kFileNamePostfix[] = ":FileName"; const char kFileBaseNamePostfix[] = ":FileBaseName"; -class MacroExpanderPrivate : public AbstractMacroExpander +class MacroExpanderPrivate { public: MacroExpanderPrivate() = default; - bool resolveMacro(const QString &name, QString *ret, QSet &seen) override + static bool validateVarName(const QString &varName) { return !varName.startsWith("JS:"); } + + bool expandNestedMacros(const QString &str, int *pos, QString *ret) + { + QString varName; + QString pattern, replace; + QString defaultValue; + QString *currArg = &varName; + QChar prev; + QChar c; + QChar replacementChar; + bool replaceAll = false; + + int i = *pos; + int strLen = str.length(); + varName.reserve(strLen - i); + for (; i < strLen; prev = c) { + c = str.at(i++); + if (c == '\\' && i < strLen) { + c = str.at(i++); + // For the replacement, do not skip the escape sequence when followed by a digit. + // This is needed for enabling convenient capture group replacement, + // like %{var/(.)(.)/\2\1}, without escaping the placeholders. + if (currArg == &replace && c.isDigit()) + *currArg += '\\'; + *currArg += c; + } else if (c == '}') { + if (varName.isEmpty()) { // replace "%{}" with "%" + *ret = QString('%'); + *pos = i; + return true; + } + QSet seen; + if (resolveMacro(varName, ret, seen)) { + *pos = i; + if (!pattern.isEmpty() && currArg == &replace) { + const QRegularExpression regexp(pattern); + if (regexp.isValid()) { + if (replaceAll) { + ret->replace(regexp, replace); + } else { + // There isn't an API for replacing once... + const QRegularExpressionMatch match = regexp.match(*ret); + if (match.hasMatch()) { + *ret = ret->left(match.capturedStart(0)) + + match.captured(0).replace(regexp, replace) + + ret->mid(match.capturedEnd(0)); + } + } + } + } + return true; + } + if (!defaultValue.isEmpty()) { + *pos = i; + *ret = defaultValue; + return true; + } + return false; + } else if (c == '{' && prev == '%') { + if (!expandNestedMacros(str, &i, ret)) + return false; + varName.chop(1); + varName += *ret; + } else if (currArg == &varName && c == '-' && prev == ':' && validateVarName(varName)) { + varName.chop(1); + currArg = &defaultValue; + } else if (currArg == &varName && (c == '/' || c == '#') && validateVarName(varName)) { + replacementChar = c; + currArg = &pattern; + if (i < strLen && str.at(i) == replacementChar) { + ++i; + replaceAll = true; + } + } else if (currArg == &pattern && c == replacementChar) { + currArg = &replace; + } else { + *currArg += c; + } + } + return false; + } + + int findMacro(const QString &str, int *pos, QString *ret) + { + forever { + int openPos = str.indexOf("%{", *pos); + if (openPos < 0) + return 0; + int varPos = openPos + 2; + if (expandNestedMacros(str, &varPos, ret)) { + *pos = openPos; + return varPos - openPos; + } + // An actual expansion may be nested into a "false" one, + // so we continue right after the last %{. + *pos = openPos + 2; + } + } + + bool resolveMacro(const QString &name, QString *ret, QSet &seen) { // Prevent loops: const int count = seen.count(); @@ -229,7 +330,7 @@ MacroExpander::~MacroExpander() */ bool MacroExpander::resolveMacro(const QString &name, QString *ret) const { - QSet seen; + QSet seen; return d->resolveMacro(name, ret, seen); } @@ -242,6 +343,16 @@ QString MacroExpander::value(const QByteArray &variable, bool *found) const return d->value(variable, found); } +static void expandMacros(QString *str, MacroExpanderPrivate *mx) +{ + QString rsts; + + for (int pos = 0; int len = mx->findMacro(*str, &pos, &rsts);) { + str->replace(pos, len, rsts); + pos += rsts.length(); + } +} + /*! * Returns \a stringWithVariables with all variables replaced by their values. * See the MacroExpander overview documentation for other ways to expand variables. @@ -261,7 +372,7 @@ QString MacroExpander::expand(const QString &stringWithVariables) const ++d->m_lockDepth; QString res = stringWithVariables; - Utils::expandMacros(&res, d); + expandMacros(&res, d); --d->m_lockDepth; @@ -302,10 +413,14 @@ QVariant MacroExpander::expandVariant(const QVariant &v) const return v; } -QString MacroExpander::expandProcessArgs(const QString &argsWithVariables) const +QString MacroExpander::expandProcessArgs(const QString &argsWithVariables, Utils::OsType osType) const { QString result = argsWithVariables; - const bool ok = ProcessArgs::expandMacros(&result, d); + const bool ok = ProcessArgs::expandMacros( + &result, + [this](const QString &str, int *pos, QString *ret) { return d->findMacro(str, pos, ret); }, + osType); + QTC_ASSERT(ok, qCDebug(expanderLog) << "Expanding failed: " << argsWithVariables); return result; } diff --git a/src/libs/utils/macroexpander.h b/src/libs/utils/macroexpander.h index ad9a21a938b..5608b39e7a5 100644 --- a/src/libs/utils/macroexpander.h +++ b/src/libs/utils/macroexpander.h @@ -5,6 +5,8 @@ #include "utils_global.h" +#include "hostosinfo.h" + #include #include @@ -35,7 +37,8 @@ public: QByteArray expand(const QByteArray &stringWithVariables) const; QVariant expandVariant(const QVariant &v) const; - QString expandProcessArgs(const QString &argsWithVariables) const; + QString expandProcessArgs( + const QString &argsWithVariables, Utils::OsType osType = Utils::HostOsInfo::hostOs()) const; using PrefixFunction = std::function; using ResolverFunction = std::function; diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp index d21c7cb9bc1..2aa2f760427 100644 --- a/src/libs/utils/stringutils.cpp +++ b/src/libs/utils/stringutils.cpp @@ -78,126 +78,6 @@ QTCREATOR_UTILS_EXPORT QString commonPrefix(const QStringList &strings) return strings.at(0).left(commonLength); } -static bool validateVarName(const QString &varName) -{ - return !varName.startsWith("JS:"); -} - -bool AbstractMacroExpander::expandNestedMacros(const QString &str, int *pos, QString *ret) -{ - QString varName; - QString pattern, replace; - QString defaultValue; - QString *currArg = &varName; - QChar prev; - QChar c; - QChar replacementChar; - bool replaceAll = false; - - int i = *pos; - int strLen = str.length(); - varName.reserve(strLen - i); - for (; i < strLen; prev = c) { - c = str.at(i++); - if (c == '\\' && i < strLen) { - c = str.at(i++); - // For the replacement, do not skip the escape sequence when followed by a digit. - // This is needed for enabling convenient capture group replacement, - // like %{var/(.)(.)/\2\1}, without escaping the placeholders. - if (currArg == &replace && c.isDigit()) - *currArg += '\\'; - *currArg += c; - } else if (c == '}') { - if (varName.isEmpty()) { // replace "%{}" with "%" - *ret = QString('%'); - *pos = i; - return true; - } - QSet seen; - if (resolveMacro(varName, ret, seen)) { - *pos = i; - if (!pattern.isEmpty() && currArg == &replace) { - const QRegularExpression regexp(pattern); - if (regexp.isValid()) { - if (replaceAll) { - ret->replace(regexp, replace); - } else { - // There isn't an API for replacing once... - const QRegularExpressionMatch match = regexp.match(*ret); - if (match.hasMatch()) { - *ret = ret->left(match.capturedStart(0)) - + match.captured(0).replace(regexp, replace) - + ret->mid(match.capturedEnd(0)); - } - } - } - } - return true; - } - if (!defaultValue.isEmpty()) { - *pos = i; - *ret = defaultValue; - return true; - } - return false; - } else if (c == '{' && prev == '%') { - if (!expandNestedMacros(str, &i, ret)) - return false; - varName.chop(1); - varName += *ret; - } else if (currArg == &varName && c == '-' && prev == ':' && validateVarName(varName)) { - varName.chop(1); - currArg = &defaultValue; - } else if (currArg == &varName && (c == '/' || c == '#') && validateVarName(varName)) { - replacementChar = c; - currArg = &pattern; - if (i < strLen && str.at(i) == replacementChar) { - ++i; - replaceAll = true; - } - } else if (currArg == &pattern && c == replacementChar) { - currArg = &replace; - } else { - *currArg += c; - } - } - return false; -} - -int AbstractMacroExpander::findMacro(const QString &str, int *pos, QString *ret) -{ - forever { - int openPos = str.indexOf("%{", *pos); - if (openPos < 0) - return 0; - int varPos = openPos + 2; - if (expandNestedMacros(str, &varPos, ret)) { - *pos = openPos; - return varPos - openPos; - } - // An actual expansion may be nested into a "false" one, - // so we continue right after the last %{. - *pos = openPos + 2; - } -} - -QTCREATOR_UTILS_EXPORT void expandMacros(QString *str, AbstractMacroExpander *mx) -{ - QString rsts; - - for (int pos = 0; int len = mx->findMacro(*str, &pos, &rsts); ) { - str->replace(pos, len, rsts); - pos += rsts.length(); - } -} - -QTCREATOR_UTILS_EXPORT QString expandMacros(const QString &str, AbstractMacroExpander *mx) -{ - QString ret = str; - expandMacros(&ret, mx); - return ret; -} - QTCREATOR_UTILS_EXPORT QString stripAccelerator(const QString &text) { QString res = text; diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h index 141ab7f1040..7b21f869fff 100644 --- a/src/libs/utils/stringutils.h +++ b/src/libs/utils/stringutils.h @@ -41,29 +41,6 @@ QTCREATOR_UTILS_EXPORT bool readMultiLineString(const QJsonValue &value, QString // Compare case insensitive and use case sensitive comparison in case of that being equal. QTCREATOR_UTILS_EXPORT int caseFriendlyCompare(const QString &a, const QString &b); -class QTCREATOR_UTILS_EXPORT AbstractMacroExpander -{ -public: - virtual ~AbstractMacroExpander() {} - // Not const, as it may change the state of the expander. - //! Find an expando to replace and provide a replacement string. - //! \param str The string to scan - //! \param pos Position to start scan on input, found position on output - //! \param ret Replacement string on output - //! \return Length of string part to replace, zero if no (further) matches found - virtual int findMacro(const QString &str, int *pos, QString *ret); - //! Provide a replacement string for an expando - //! \param name The name of the expando - //! \param ret Replacement string on output - //! \return True if the expando was found - virtual bool resolveMacro(const QString &name, QString *ret, QSet &seen) = 0; -private: - bool expandNestedMacros(const QString &str, int *pos, QString *ret); -}; - -QTCREATOR_UTILS_EXPORT void expandMacros(QString *str, AbstractMacroExpander *mx); -QTCREATOR_UTILS_EXPORT QString expandMacros(const QString &str, AbstractMacroExpander *mx); - QTCREATOR_UTILS_EXPORT int parseUsedPortFromNetstatOutput(const QByteArray &line); QTCREATOR_UTILS_EXPORT QString appendHelper(const QString &base, int n); diff --git a/tests/auto/utils/expander/tst_expander.cpp b/tests/auto/utils/expander/tst_expander.cpp index a1dac554fb8..33cb2d0bd43 100644 --- a/tests/auto/utils/expander/tst_expander.cpp +++ b/tests/auto/utils/expander/tst_expander.cpp @@ -173,6 +173,7 @@ private slots: { QTest::addColumn("input"); QTest::addColumn("expected"); + QTest::addColumn("os"); QTest::addColumn>("setup"); std::function empty; @@ -184,57 +185,63 @@ private slots: expander.registerVariable("WithSpace", "", [] { return "This has spaces"; }); }; - QTest::newRow("empty") << QString() << QString() << empty; - QTest::newRow("no expansion") << QString("foo") << QString("foo") << empty; + QTest::newRow("empty") << QString() << QString() << Utils::OsTypeLinux << empty; + QTest::newRow("no expansion") + << QString("foo") << QString("foo") << Utils::OsTypeLinux << empty; QTest::newRow("simple-expansion") - << QString("cat %{file}") << QString("cat foo.txt") << file; + << QString("cat %{file}") << QString("cat foo.txt") << Utils::OsTypeLinux << file; QTest::newRow("with-ticks") - << QString("echo -n 'foo %{file}'") << QString("echo -n 'foo foo.txt'") << file; + << QString("echo -n 'foo %{file}'") << QString("echo -n 'foo foo.txt'") + << Utils::OsTypeLinux << file; - QTest::newRow("with-ticks-env") << QString("file=%{file} echo -n 'foo \"$file\"'") - << QString("file=foo.txt echo -n 'foo \"$file\"'") << file; + QTest::newRow("with-ticks-env") + << QString("file=%{file} echo -n 'foo \"$file\"'") + << QString("file=foo.txt echo -n 'foo \"$file\"'") << Utils::OsTypeLinux << file; - if (Utils::HostOsInfo::isWindowsHost()) { - QTest::newRow("with-spaces") - << QString("echo %{WithSpace}") << QString("echo \"This has spaces\"") << withspace; + QTest::newRow("with-spaces") + << QString("echo %{WithSpace}") << QString("echo 'This has spaces'") + << Utils::OsTypeLinux << withspace; - QTest::newRow("with-spaces-manual") - << QString("echo \"Some: %{WithSpace}\"") - << QString("echo \"Some: This has spaces\"") << withspace; + QTest::newRow("with-spaces-pre-quoted") + << QString("echo 'Some: %{WithSpace}'") << QString("echo 'Some: This has spaces'") + << Utils::OsTypeLinux << withspace; - QTest::newRow("with-spaces-nested") - << QString("cmd /k \"echo %{WithSpace}\"") - << QString("cmd /k \"echo This has spaces\"") << withspace; - } else { - QTest::newRow("with-spaces") - << QString("echo %{WithSpace}") << QString("echo 'This has spaces'") << withspace; + QTest::newRow("with-spaces-nested") + << QString("sh -c 'echo %{WithSpace}'") << QString("sh -c 'echo This has spaces'") + << Utils::OsTypeLinux << withspace; - QTest::newRow("with-spaces-pre-quoted") - << QString("echo 'Some: %{WithSpace}'") << QString("echo 'Some: This has spaces'") - << withspace; + // Due to security concerns, backslash-escaping an expando is treated as a quoting error + QTest::newRow("backslash-escaping") + << QString("echo \\%{file}") << QString("echo \\%{file}") << Utils::OsTypeLinux << file; - QTest::newRow("with-spaces-nested") - << QString("sh -c 'echo %{WithSpace}'") << QString("sh -c 'echo This has spaces'") - << withspace; + QTest::newRow("expando-within-shell-substitution") + << QString("${VAR:-%{file}}") << QString("${VAR:-foo.txt}") << Utils::OsTypeLinux + << file; + QTest::newRow("expando-within-shell-substitution-with-space") + << QString("echo \"Some: ${VAR:-%{WithSpace}}\"") + << QString("echo \"Some: ${VAR:-This has spaces}\"") << Utils::OsTypeLinux << withspace; - // Due to security concerns, backslash-escaping an expando is treated as a quoting error - QTest::newRow("backslash-escaping") - << QString("echo \\%{file}") << QString("echo \\%{file}") << file; + // Windows tests + QTest::newRow("with-spaces") + << QString("echo %{WithSpace}") << QString("echo \"This has spaces\"") + << Utils::OsTypeWindows << withspace; - QTest::newRow("expando-within-shell-substitution") - << QString("${VAR:-%{file}}") << QString("${VAR:-foo.txt}") << file; - QTest::newRow("expando-within-shell-substitution-with-space") - << QString("echo \"Some: ${VAR:-%{WithSpace}}\"") - << QString("echo \"Some: ${VAR:-This has spaces}\"") << withspace; - } + QTest::newRow("with-spaces-manual") + << QString("echo \"Some: %{WithSpace}\"") << QString("echo \"Some: This has spaces\"") + << Utils::OsTypeWindows << withspace; + + QTest::newRow("with-spaces-nested") + << QString("cmd /k \"echo %{WithSpace}\"") << QString("cmd /k \"echo This has spaces\"") + << Utils::OsTypeWindows << withspace; } void expandCommandArgs() { QFETCH(QString, input); QFETCH(QString, expected); + QFETCH(OsType, os); QFETCH(std::function, setup); MacroExpander expander; @@ -242,7 +249,354 @@ private slots: if (setup) setup(expander); - QCOMPARE(expander.expandProcessArgs(input), expected); + QCOMPARE(expander.expandProcessArgs(input, os), expected); + } + + void expandProcessArgs_data() + { + QTest::addColumn("in"); + QTest::addColumn("out"); + QTest::addColumn("os"); + QChar sp(QLatin1Char(' ')); + + struct Val + { + const char *in; + const char *out; + OsType os; + }; + + const std::array vals + = {Val{"plain", 0, OsTypeWindows}, + Val{"%{a}", "hi", OsTypeWindows}, + Val{"%{aa}", "\"hi ho\"", OsTypeWindows}, + Val{"%{b}", "h\\i", OsTypeWindows}, + Val{"%{c}", "\\hi", OsTypeWindows}, + Val{"%{d}", "hi\\", OsTypeWindows}, + Val{"%{ba}", "\"h\\i ho\"", OsTypeWindows}, + Val{"%{ca}", "\"\\hi ho\"", OsTypeWindows}, + Val{"%{da}", "\"hi ho\\\\\"", OsTypeWindows}, // or "\"hi ho\"\\" + Val{"%{e}", "\"h\"\\^\"\"i\"", OsTypeWindows}, + Val{"%{f}", "\"\"\\^\"\"hi\"", OsTypeWindows}, + Val{"%{g}", "\"hi\"\\^\"\"\"", OsTypeWindows}, + Val{"%{h}", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, + Val{"%{i}", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, + Val{"%{j}", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"%{k}", "\"&special;\"", OsTypeWindows}, + Val{"%{x}", "\\", OsTypeWindows}, + Val{"%{y}", "\"\"\\^\"\"\"", OsTypeWindows}, + Val{"%{z}", "\"\"", OsTypeWindows}, + Val{"^%{z}%{z}", "^%{z}%{z}", OsTypeWindows}, // stupid user check + + Val{"quoted", 0, OsTypeWindows}, + Val{"\"%{a}\"", "\"hi\"", OsTypeWindows}, + Val{"\"%{aa}\"", "\"hi ho\"", OsTypeWindows}, + Val{"\"%{b}\"", "\"h\\i\"", OsTypeWindows}, + Val{"\"%{c}\"", "\"\\hi\"", OsTypeWindows}, + Val{"\"%{d}\"", "\"hi\\\\\"", OsTypeWindows}, + Val{"\"%{ba}\"", "\"h\\i ho\"", OsTypeWindows}, + Val{"\"%{ca}\"", "\"\\hi ho\"", OsTypeWindows}, + Val{"\"%{da}\"", "\"hi ho\\\\\"", OsTypeWindows}, + Val{"\"%{e}\"", "\"h\"\\^\"\"i\"", OsTypeWindows}, + Val{"\"%{f}\"", "\"\"\\^\"\"hi\"", OsTypeWindows}, + Val{"\"%{g}\"", "\"hi\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{h}\"", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, + Val{"\"%{i}\"", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, + Val{"\"%{j}\"", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{k}\"", "\"&special;\"", OsTypeWindows}, + Val{"\"%{x}\"", "\"\\\\\"", OsTypeWindows}, + Val{"\"%{y}\"", "\"\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{z}\"", "\"\"", OsTypeWindows}, + + Val{"leading bs", 0, OsTypeWindows}, + Val{"\\%{a}", "\\hi", OsTypeWindows}, + Val{"\\%{aa}", "\\\\\"hi ho\"", OsTypeWindows}, + Val{"\\%{b}", "\\h\\i", OsTypeWindows}, + Val{"\\%{c}", "\\\\hi", OsTypeWindows}, + Val{"\\%{d}", "\\hi\\", OsTypeWindows}, + Val{"\\%{ba}", "\\\\\"h\\i ho\"", OsTypeWindows}, + Val{"\\%{ca}", "\\\\\"\\hi ho\"", OsTypeWindows}, + Val{"\\%{da}", "\\\\\"hi ho\\\\\"", OsTypeWindows}, + Val{"\\%{e}", "\\\\\"h\"\\^\"\"i\"", OsTypeWindows}, + Val{"\\%{f}", "\\\\\"\"\\^\"\"hi\"", OsTypeWindows}, + Val{"\\%{g}", "\\\\\"hi\"\\^\"\"\"", OsTypeWindows}, + Val{"\\%{h}", "\\\\\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, + Val{"\\%{i}", "\\\\\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, + Val{"\\%{j}", "\\\\\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"\\%{x}", "\\\\", OsTypeWindows}, + Val{"\\%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, + Val{"\\%{z}", "\\", OsTypeWindows}, + + Val{"trailing bs", 0, OsTypeWindows}, + Val{"%{a}\\", "hi\\", OsTypeWindows}, + Val{"%{aa}\\", "\"hi ho\"\\", OsTypeWindows}, + Val{"%{b}\\", "h\\i\\", OsTypeWindows}, + Val{"%{c}\\", "\\hi\\", OsTypeWindows}, + Val{"%{d}\\", "hi\\\\", OsTypeWindows}, + Val{"%{ba}\\", "\"h\\i ho\"\\", OsTypeWindows}, + Val{"%{ca}\\", "\"\\hi ho\"\\", OsTypeWindows}, + Val{"%{da}\\", "\"hi ho\\\\\"\\", OsTypeWindows}, + Val{"%{e}\\", "\"h\"\\^\"\"i\"\\", OsTypeWindows}, + Val{"%{f}\\", "\"\"\\^\"\"hi\"\\", OsTypeWindows}, + Val{"%{g}\\", "\"hi\"\\^\"\"\"\\", OsTypeWindows}, + Val{"%{h}\\", "\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows}, + Val{"%{i}\\", "\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows}, + Val{"%{j}\\", "\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows}, + Val{"%{x}\\", "\\\\", OsTypeWindows}, + Val{"%{y}\\", "\"\"\\^\"\"\"\\", OsTypeWindows}, + Val{"%{z}\\", "\\", OsTypeWindows}, + + Val{"bs-enclosed", 0, OsTypeWindows}, + Val{"\\%{a}\\", "\\hi\\", OsTypeWindows}, + Val{"\\%{aa}\\", "\\\\\"hi ho\"\\", OsTypeWindows}, + Val{"\\%{b}\\", "\\h\\i\\", OsTypeWindows}, + Val{"\\%{c}\\", "\\\\hi\\", OsTypeWindows}, + Val{"\\%{d}\\", "\\hi\\\\", OsTypeWindows}, + Val{"\\%{ba}\\", "\\\\\"h\\i ho\"\\", OsTypeWindows}, + Val{"\\%{ca}\\", "\\\\\"\\hi ho\"\\", OsTypeWindows}, + Val{"\\%{da}\\", "\\\\\"hi ho\\\\\"\\", OsTypeWindows}, + Val{"\\%{e}\\", "\\\\\"h\"\\^\"\"i\"\\", OsTypeWindows}, + Val{"\\%{f}\\", "\\\\\"\"\\^\"\"hi\"\\", OsTypeWindows}, + Val{"\\%{g}\\", "\\\\\"hi\"\\^\"\"\"\\", OsTypeWindows}, + Val{"\\%{h}\\", "\\\\\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows}, + Val{"\\%{i}\\", "\\\\\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows}, + Val{"\\%{j}\\", "\\\\\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows}, + Val{"\\%{x}\\", "\\\\\\", OsTypeWindows}, + Val{"\\%{y}\\", "\\\\\"\"\\^\"\"\"\\", OsTypeWindows}, + Val{"\\%{z}\\", "\\\\", OsTypeWindows}, + + Val{"bs-enclosed and trailing literal quote", 0, OsTypeWindows}, + Val{"\\%{a}\\\\\\^\"", "\\hi\\\\\\^\"", OsTypeWindows}, + Val{"\\%{aa}\\\\\\^\"", "\\\\\"hi ho\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{b}\\\\\\^\"", "\\h\\i\\\\\\^\"", OsTypeWindows}, + Val{"\\%{c}\\\\\\^\"", "\\\\hi\\\\\\^\"", OsTypeWindows}, + Val{"\\%{d}\\\\\\^\"", "\\hi\\\\\\\\\\^\"", OsTypeWindows}, + Val{"\\%{ba}\\\\\\^\"", "\\\\\"h\\i ho\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{ca}\\\\\\^\"", "\\\\\"\\hi ho\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{da}\\\\\\^\"", "\\\\\"hi ho\\\\\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{e}\\\\\\^\"", "\\\\\"h\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{f}\\\\\\^\"", "\\\\\"\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{g}\\\\\\^\"", "\\\\\"hi\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{h}\\\\\\^\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{i}\\\\\\^\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{j}\\\\\\^\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{x}\\\\\\^\"", "\\\\\\\\\\\\\\^\"", OsTypeWindows}, + Val{"\\%{y}\\\\\\^\"", "\\\\\"\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, + Val{"\\%{z}\\\\\\^\"", "\\\\\\\\\\^\"", OsTypeWindows}, + + Val{"bs-enclosed and trailing unclosed quote", 0, OsTypeWindows}, + Val{"\\%{a}\\\\\"", "\\hi\\\\\"", OsTypeWindows}, + Val{"\\%{aa}\\\\\"", "\\\\\"hi ho\"\\\\\"", OsTypeWindows}, + Val{"\\%{b}\\\\\"", "\\h\\i\\\\\"", OsTypeWindows}, + Val{"\\%{c}\\\\\"", "\\\\hi\\\\\"", OsTypeWindows}, + Val{"\\%{d}\\\\\"", "\\hi\\\\\\\\\"", OsTypeWindows}, + Val{"\\%{ba}\\\\\"", "\\\\\"h\\i ho\"\\\\\"", OsTypeWindows}, + Val{"\\%{ca}\\\\\"", "\\\\\"\\hi ho\"\\\\\"", OsTypeWindows}, + Val{"\\%{da}\\\\\"", "\\\\\"hi ho\\\\\"\\\\\"", OsTypeWindows}, + Val{"\\%{e}\\\\\"", "\\\\\"h\"\\^\"\"i\"\\\\\"", OsTypeWindows}, + Val{"\\%{f}\\\\\"", "\\\\\"\"\\^\"\"hi\"\\\\\"", OsTypeWindows}, + Val{"\\%{g}\\\\\"", "\\\\\"hi\"\\^\"\"\"\\\\\"", OsTypeWindows}, + Val{"\\%{h}\\\\\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\"", OsTypeWindows}, + Val{"\\%{i}\\\\\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\"", OsTypeWindows}, + Val{"\\%{j}\\\\\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\"", OsTypeWindows}, + Val{"\\%{x}\\\\\"", "\\\\\\\\\\\\\"", OsTypeWindows}, + Val{"\\%{y}\\\\\"", "\\\\\"\"\\^\"\"\"\\\\\"", OsTypeWindows}, + Val{"\\%{z}\\\\\"", "\\\\\\\\\"", OsTypeWindows}, + + Val{"multi-var", 0, OsTypeWindows}, + Val{"%{x}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, + Val{"%{x}%{z}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, + Val{"%{x}%{z}%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, + Val{"%{x}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows}, + Val{"%{x}%{z}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows}, + Val{"%{x}%{z}\\^\"", "\\\\\\^\"", OsTypeWindows}, + Val{"%{x}\\%{z}", "\\\\", OsTypeWindows}, + Val{"%{x}%{z}\\%{z}", "\\\\", OsTypeWindows}, + Val{"%{x}%{z}\\", "\\\\", OsTypeWindows}, + Val{"%{aa}%{a}", "\"hi hohi\"", OsTypeWindows}, + Val{"%{aa}%{aa}", "\"hi hohi ho\"", OsTypeWindows}, + Val{"%{aa}:%{aa}", "\"hi ho\":\"hi ho\"", OsTypeWindows}, + Val{"hallo ^|%{aa}^|", "hallo ^|\"hi ho\"^|", OsTypeWindows}, + + Val{"quoted multi-var", 0, OsTypeWindows}, + Val{"\"%{x}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{x}%{z}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{x}%{z}%{y}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, + Val{"\"%{x}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, + Val{"\"%{x}%{z}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, + Val{"\"%{x}%{z}\"^\"\"\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, + Val{"\"%{x}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows}, + Val{"\"%{x}%{z}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows}, + Val{"\"%{x}%{z}\\\\\"", "\"\\\\\\\\\"", OsTypeWindows}, + Val{"\"%{aa}%{a}\"", "\"hi hohi\"", OsTypeWindows}, + Val{"\"%{aa}%{aa}\"", "\"hi hohi ho\"", OsTypeWindows}, + Val{"\"%{aa}:%{aa}\"", "\"hi ho:hi ho\"", OsTypeWindows}, + + Val{"plain", 0, OsTypeLinux}, + Val{"%{a}", "hi", OsTypeLinux}, + Val{"%{b}", "'hi ho'", OsTypeLinux}, + Val{"%{c}", "'&special;'", OsTypeLinux}, + Val{"%{d}", "'h\\i'", OsTypeLinux}, + Val{"%{e}", "'h\"i'", OsTypeLinux}, + Val{"%{f}", "'h'\\''i'", OsTypeLinux}, + Val{"%{z}", "''", OsTypeLinux}, + Val{"\\%{z}%{z}", "\\%{z}%{z}", OsTypeLinux}, // stupid user check + + Val{"single-quoted", 0, OsTypeLinux}, + Val{"'%{a}'", "'hi'", OsTypeLinux}, + Val{"'%{b}'", "'hi ho'", OsTypeLinux}, + Val{"'%{c}'", "'&special;'", OsTypeLinux}, + Val{"'%{d}'", "'h\\i'", OsTypeLinux}, + Val{"'%{e}'", "'h\"i'", OsTypeLinux}, + Val{"'%{f}'", "'h'\\''i'", OsTypeLinux}, + Val{"'%{z}'", "''", OsTypeLinux}, + + Val{"double-quoted", 0, OsTypeLinux}, + Val{"\"%{a}\"", "\"hi\"", OsTypeLinux}, + Val{"\"%{b}\"", "\"hi ho\"", OsTypeLinux}, + Val{"\"%{c}\"", "\"&special;\"", OsTypeLinux}, + Val{"\"%{d}\"", "\"h\\\\i\"", OsTypeLinux}, + Val{"\"%{e}\"", "\"h\\\"i\"", OsTypeLinux}, + Val{"\"%{f}\"", "\"h'i\"", OsTypeLinux}, + Val{"\"%{z}\"", "\"\"", OsTypeLinux}, + + Val{"complex", 0, OsTypeLinux}, + Val{"echo \"$(echo %{a})\"", "echo \"$(echo hi)\"", OsTypeLinux}, + Val{"echo \"$(echo %{b})\"", "echo \"$(echo 'hi ho')\"", OsTypeLinux}, + Val{"echo \"$(echo \"%{a}\")\"", "echo \"$(echo \"hi\")\"", OsTypeLinux}, + // These make no sense shell-wise, but they test expando nesting + Val{"echo \"%{echo %{a}}\"", "echo \"%{echo hi}\"", OsTypeLinux}, + Val{"echo \"%{echo %{b}}\"", "echo \"%{echo hi ho}\"", OsTypeLinux}, + Val{"echo \"%{echo \"%{a}\"}\"", "echo \"%{echo \"hi\"}\"", OsTypeLinux}}; + + const char *title = 0; + for (const auto &val : vals) { + if (!val.out) { + title = val.in; + } else { + QString name + = QString("%1: %2 (%3)") + .arg(title, val.in, val.os == OsTypeWindows ? "windows" : "linux"); + QTest::newRow(name.toLatin1()) + << QString::fromLatin1(val.in) << QString::fromLatin1(val.out) << val.os; + QTest::newRow(("padded " + name).toLatin1()) + << QString(sp + QString::fromLatin1(val.in) + sp) + << QString(sp + QString::fromLatin1(val.out) + sp) << val.os; + } + } + } + + void expandProcessArgs() + { + QFETCH(QString, in); + QFETCH(QString, out); + QFETCH(OsType, os); + + MacroExpander expander; + + if (os == Utils::OsTypeWindows) { + expander.registerVariable("a", "", [] { return "hi"; }); + expander.registerVariable("aa", "", [] { return "hi ho"; }); + expander.registerVariable("b", "", [] { return "h\\i"; }); + expander.registerVariable("c", "", [] { return "\\hi"; }); + expander.registerVariable("d", "", [] { return "hi\\"; }); + expander.registerVariable("ba", "", [] { return "h\\i ho"; }); + expander.registerVariable("ca", "", [] { return "\\hi ho"; }); + expander.registerVariable("da", "", [] { return "hi ho\\"; }); + expander.registerVariable("e", "", [] { return "h\"i"; }); + expander.registerVariable("f", "", [] { return "\"hi"; }); + expander.registerVariable("g", "", [] { return "hi\""; }); + expander.registerVariable("h", "", [] { return "h\\\"i"; }); + expander.registerVariable("i", "", [] { return "\\\"hi"; }); + expander.registerVariable("j", "", [] { return "hi\\\""; }); + expander.registerVariable("k", "", [] { return "&special;"; }); + expander.registerVariable("x", "", [] { return "\\"; }); + expander.registerVariable("y", "", [] { return "\""; }); + expander.registerVariable("z", "", [] { return ""; }); + } else { + expander.registerVariable("a", "", [] { return "hi"; }); + expander.registerVariable("b", "", [] { return "hi ho"; }); + expander.registerVariable("c", "", [] { return "&special;"; }); + expander.registerVariable("d", "", [] { return "h\\i"; }); + expander.registerVariable("e", "", [] { return "h\"i"; }); + expander.registerVariable("f", "", [] { return "h'i"; }); + expander.registerVariable("z", "", [] { return ""; }); + } + + QCOMPARE(expander.expandProcessArgs(in, os), out); + } + + void testMacroExpander_data() + { + QTest::addColumn("in"); + QTest::addColumn("out"); + + struct Val + { + const char *in; + const char *out; + }; + + const std::array vals = { + Val{"text", "text"}, + Val{"%{a}", "hi"}, + Val{"%%{a}", "%hi"}, + Val{"%%%{a}", "%%hi"}, + Val{"%{b}", "%{b}"}, + Val{"pre%{a}", "prehi"}, + Val{"%{a}post", "hipost"}, + Val{"pre%{a}post", "prehipost"}, + Val{"%{a}%{a}", "hihi"}, + Val{"%{a}text%{a}", "hitexthi"}, + Val{"%{foo}%{a}text%{a}", "ahitexthi"}, + Val{"%{}{a}", "%{a}"}, + Val{"%{}", "%"}, + Val{"test%{}", "test%"}, + Val{"%{}test", "%test"}, + Val{"%{abc", "%{abc"}, + Val{"%{%{a}", "%{hi"}, + Val{"%{%{a}}", "ho"}, + Val{"%{%{a}}}post", "ho}post"}, + Val{"%{hi%{a}}", "bar"}, + Val{"%{hi%{%{foo}}}", "bar"}, + Val{"%{hihi/b/c}", "car"}, + Val{"%{hihi/a/}", "br"}, // empty replacement + Val{"%{hihi/b}", "bar"}, // incomplete substitution + Val{"%{hihi/./c}", "car"}, + Val{"%{hihi//./c}", "ccc"}, + Val{"%{hihi/(.)(.)r/\\2\\1c}", "abc"}, // no escape for capture groups + Val{"%{hihi/b/c/d}", "c/dar"}, + Val{"%{hihi/a/e{\\}e}", "be{}er"}, // escape closing brace + Val{"%{JS:with \\} inside}", "yay"}, // escape closing brace also in JS: + Val{"%{JS:literal%\\{}", "hurray"}, + Val{"%{slash/o\\/b/ol's c}", "fool's car"}, + Val{"%{sl\\/sh/(.)(a)(.)/\\2\\1\\3as}", "salsash"}, // escape in variable name + Val{"%{JS:foo/b/c}", "%{JS:foo/b/c}"}, // No replacement for JS (all considered varName) + Val{"%{%{a}%{a}/b/c}", "car"}, + Val{"%{nonsense:-sense}", "sense"}, + }; + + for (const auto &val : vals) + QTest::newRow(val.in) << QString::fromLatin1(val.in) << QString::fromLatin1(val.out); + } + + void testMacroExpander() + { + QFETCH(QString, in); + QFETCH(QString, out); + + MacroExpander expander; + expander.registerVariable("foo", "", [] { return "a"; }); + expander.registerVariable("a", "", [] { return "hi"; }); + expander.registerVariable("hi", "", [] { return "ho"; }); + expander.registerVariable("hihi", "", [] { return "bar"; }); + expander.registerVariable("slash", "", [] { return "foo/bar"; }); + expander.registerVariable("sl/sh", "", [] { return "slash"; }); + expander.registerVariable("JS:foor", "", [] { return "bar"; }); + expander.registerVariable("JS:with } inside", "", [] { return "yay"; }); + expander.registerVariable("JS:literal%{", "", [] { return "hurray"; }); + + QCOMPARE(expander.expand(in), out); } }; diff --git a/tests/auto/utils/process/tst_process.cpp b/tests/auto/utils/process/tst_process.cpp index d6e283e4f9a..01704d3d2ac 100644 --- a/tests/auto/utils/process/tst_process.cpp +++ b/tests/auto/utils/process/tst_process.cpp @@ -66,29 +66,6 @@ protected: int MessageHandler::s_destroyCount = 0; QtMessageHandler MessageHandler::s_oldMessageHandler = 0; -class MacroMapExpander : public AbstractMacroExpander { -public: - bool resolveMacro(const QString &name, QString *ret, QSet &seen) - override - { - // loop prevention - const int count = seen.count(); - seen.insert(this); - if (seen.count() == count) - return false; - - QHash::const_iterator it = m_map.constFind(name); - if (it != m_map.constEnd()) { - *ret = it.value(); - return true; - } - return false; - } - void insert(const QString &key, const QString &value) { m_map.insert(key, value); } -private: - QHash m_map; -}; - static constexpr char s_skipTerminateOnWindows[] = "Windows implementation of this test is lacking handling of WM_CLOSE message."; @@ -135,8 +112,6 @@ private slots: void prepareArgs(); void prepareArgsEnv_data(); void prepareArgsEnv(); - void expandMacros_data(); - void expandMacros(); void iterations_data(); void iterations(); void iteratorEditsWindows(); @@ -181,8 +156,6 @@ private: Environment envWindows; Environment envLinux; - MacroMapExpander mxWin; - MacroMapExpander mxUnix; QString homeStr; QString home; @@ -206,38 +179,6 @@ void tst_Process::initTestCase() env << "empty=" << "word=hi" << "words=hi ho" << "spacedwords= hi ho sucker "; envWindows = Environment(env, OsTypeWindows); envLinux = Environment(env, OsTypeLinux); - - mxWin.insert("a", "hi"); - mxWin.insert("aa", "hi ho"); - - mxWin.insert("b", "h\\i"); - mxWin.insert("c", "\\hi"); - mxWin.insert("d", "hi\\"); - mxWin.insert("ba", "h\\i ho"); - mxWin.insert("ca", "\\hi ho"); - mxWin.insert("da", "hi ho\\"); - - mxWin.insert("e", "h\"i"); - mxWin.insert("f", "\"hi"); - mxWin.insert("g", "hi\""); - - mxWin.insert("h", "h\\\"i"); - mxWin.insert("i", "\\\"hi"); - mxWin.insert("j", "hi\\\""); - - mxWin.insert("k", "&special;"); - - mxWin.insert("x", "\\"); - mxWin.insert("y", "\""); - mxWin.insert("z", ""); - - mxUnix.insert("a", "hi"); - mxUnix.insert("b", "hi ho"); - mxUnix.insert("c", "&special;"); - mxUnix.insert("d", "h\\i"); - mxUnix.insert("e", "h\"i"); - mxUnix.insert("f", "h'i"); - mxUnix.insert("z", ""); } void tst_Process::cleanupTestCase() @@ -550,252 +491,7 @@ void tst_Process::prepareArgsEnv() QCOMPARE(outstr, out); } -void tst_Process::expandMacros_data() -{ - QTest::addColumn("in"); - QTest::addColumn("out"); - QTest::addColumn("os"); - QChar sp(QLatin1Char(' ')); - - static const struct { - const char * const in; - const char * const out; - OsType os; - } vals[] = { - {"plain", 0, OsTypeWindows}, - {"%{a}", "hi", OsTypeWindows}, - {"%{aa}", "\"hi ho\"", OsTypeWindows}, - {"%{b}", "h\\i", OsTypeWindows}, - {"%{c}", "\\hi", OsTypeWindows}, - {"%{d}", "hi\\", OsTypeWindows}, - {"%{ba}", "\"h\\i ho\"", OsTypeWindows}, - {"%{ca}", "\"\\hi ho\"", OsTypeWindows}, - {"%{da}", "\"hi ho\\\\\"", OsTypeWindows}, // or "\"hi ho\"\\" - {"%{e}", "\"h\"\\^\"\"i\"", OsTypeWindows}, - {"%{f}", "\"\"\\^\"\"hi\"", OsTypeWindows}, - {"%{g}", "\"hi\"\\^\"\"\"", OsTypeWindows}, - {"%{h}", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, - {"%{i}", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, - {"%{j}", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, - {"%{k}", "\"&special;\"", OsTypeWindows}, - {"%{x}", "\\", OsTypeWindows}, - {"%{y}", "\"\"\\^\"\"\"", OsTypeWindows}, - {"%{z}", "\"\"", OsTypeWindows}, - {"^%{z}%{z}", "^%{z}%{z}", OsTypeWindows}, // stupid user check - - {"quoted", 0, OsTypeWindows}, - {"\"%{a}\"", "\"hi\"", OsTypeWindows}, - {"\"%{aa}\"", "\"hi ho\"", OsTypeWindows}, - {"\"%{b}\"", "\"h\\i\"", OsTypeWindows}, - {"\"%{c}\"", "\"\\hi\"", OsTypeWindows}, - {"\"%{d}\"", "\"hi\\\\\"", OsTypeWindows}, - {"\"%{ba}\"", "\"h\\i ho\"", OsTypeWindows}, - {"\"%{ca}\"", "\"\\hi ho\"", OsTypeWindows}, - {"\"%{da}\"", "\"hi ho\\\\\"", OsTypeWindows}, - {"\"%{e}\"", "\"h\"\\^\"\"i\"", OsTypeWindows}, - {"\"%{f}\"", "\"\"\\^\"\"hi\"", OsTypeWindows}, - {"\"%{g}\"", "\"hi\"\\^\"\"\"", OsTypeWindows}, - {"\"%{h}\"", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, - {"\"%{i}\"", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, - {"\"%{j}\"", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, - {"\"%{k}\"", "\"&special;\"", OsTypeWindows}, - {"\"%{x}\"", "\"\\\\\"", OsTypeWindows}, - {"\"%{y}\"", "\"\"\\^\"\"\"", OsTypeWindows}, - {"\"%{z}\"", "\"\"", OsTypeWindows}, - - {"leading bs", 0, OsTypeWindows}, - {"\\%{a}", "\\hi", OsTypeWindows}, - {"\\%{aa}", "\\\\\"hi ho\"", OsTypeWindows}, - {"\\%{b}", "\\h\\i", OsTypeWindows}, - {"\\%{c}", "\\\\hi", OsTypeWindows}, - {"\\%{d}", "\\hi\\", OsTypeWindows}, - {"\\%{ba}", "\\\\\"h\\i ho\"", OsTypeWindows}, - {"\\%{ca}", "\\\\\"\\hi ho\"", OsTypeWindows}, - {"\\%{da}", "\\\\\"hi ho\\\\\"", OsTypeWindows}, - {"\\%{e}", "\\\\\"h\"\\^\"\"i\"", OsTypeWindows}, - {"\\%{f}", "\\\\\"\"\\^\"\"hi\"", OsTypeWindows}, - {"\\%{g}", "\\\\\"hi\"\\^\"\"\"", OsTypeWindows}, - {"\\%{h}", "\\\\\"h\\\\\"\\^\"\"i\"", OsTypeWindows}, - {"\\%{i}", "\\\\\"\\\\\"\\^\"\"hi\"", OsTypeWindows}, - {"\\%{j}", "\\\\\"hi\\\\\"\\^\"\"\"", OsTypeWindows}, - {"\\%{x}", "\\\\", OsTypeWindows}, - {"\\%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, - {"\\%{z}", "\\", OsTypeWindows}, - - {"trailing bs", 0, OsTypeWindows}, - {"%{a}\\", "hi\\", OsTypeWindows}, - {"%{aa}\\", "\"hi ho\"\\", OsTypeWindows}, - {"%{b}\\", "h\\i\\", OsTypeWindows}, - {"%{c}\\", "\\hi\\", OsTypeWindows}, - {"%{d}\\", "hi\\\\", OsTypeWindows}, - {"%{ba}\\", "\"h\\i ho\"\\", OsTypeWindows}, - {"%{ca}\\", "\"\\hi ho\"\\", OsTypeWindows}, - {"%{da}\\", "\"hi ho\\\\\"\\", OsTypeWindows}, - {"%{e}\\", "\"h\"\\^\"\"i\"\\", OsTypeWindows}, - {"%{f}\\", "\"\"\\^\"\"hi\"\\", OsTypeWindows}, - {"%{g}\\", "\"hi\"\\^\"\"\"\\", OsTypeWindows}, - {"%{h}\\", "\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows}, - {"%{i}\\", "\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows}, - {"%{j}\\", "\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows}, - {"%{x}\\", "\\\\", OsTypeWindows}, - {"%{y}\\", "\"\"\\^\"\"\"\\", OsTypeWindows}, - {"%{z}\\", "\\", OsTypeWindows}, - - {"bs-enclosed", 0, OsTypeWindows}, - {"\\%{a}\\", "\\hi\\", OsTypeWindows}, - {"\\%{aa}\\", "\\\\\"hi ho\"\\", OsTypeWindows}, - {"\\%{b}\\", "\\h\\i\\", OsTypeWindows}, - {"\\%{c}\\", "\\\\hi\\", OsTypeWindows}, - {"\\%{d}\\", "\\hi\\\\", OsTypeWindows}, - {"\\%{ba}\\", "\\\\\"h\\i ho\"\\", OsTypeWindows}, - {"\\%{ca}\\", "\\\\\"\\hi ho\"\\", OsTypeWindows}, - {"\\%{da}\\", "\\\\\"hi ho\\\\\"\\", OsTypeWindows}, - {"\\%{e}\\", "\\\\\"h\"\\^\"\"i\"\\", OsTypeWindows}, - {"\\%{f}\\", "\\\\\"\"\\^\"\"hi\"\\", OsTypeWindows}, - {"\\%{g}\\", "\\\\\"hi\"\\^\"\"\"\\", OsTypeWindows}, - {"\\%{h}\\", "\\\\\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows}, - {"\\%{i}\\", "\\\\\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows}, - {"\\%{j}\\", "\\\\\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows}, - {"\\%{x}\\", "\\\\\\", OsTypeWindows}, - {"\\%{y}\\", "\\\\\"\"\\^\"\"\"\\", OsTypeWindows}, - {"\\%{z}\\", "\\\\", OsTypeWindows}, - - {"bs-enclosed and trailing literal quote", 0, OsTypeWindows}, - {"\\%{a}\\\\\\^\"", "\\hi\\\\\\^\"", OsTypeWindows}, - {"\\%{aa}\\\\\\^\"", "\\\\\"hi ho\"\\\\\\^\"", OsTypeWindows}, - {"\\%{b}\\\\\\^\"", "\\h\\i\\\\\\^\"", OsTypeWindows}, - {"\\%{c}\\\\\\^\"", "\\\\hi\\\\\\^\"", OsTypeWindows}, - {"\\%{d}\\\\\\^\"", "\\hi\\\\\\\\\\^\"", OsTypeWindows}, - {"\\%{ba}\\\\\\^\"", "\\\\\"h\\i ho\"\\\\\\^\"", OsTypeWindows}, - {"\\%{ca}\\\\\\^\"", "\\\\\"\\hi ho\"\\\\\\^\"", OsTypeWindows}, - {"\\%{da}\\\\\\^\"", "\\\\\"hi ho\\\\\"\\\\\\^\"", OsTypeWindows}, - {"\\%{e}\\\\\\^\"", "\\\\\"h\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows}, - {"\\%{f}\\\\\\^\"", "\\\\\"\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows}, - {"\\%{g}\\\\\\^\"", "\\\\\"hi\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, - {"\\%{h}\\\\\\^\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows}, - {"\\%{i}\\\\\\^\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows}, - {"\\%{j}\\\\\\^\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, - {"\\%{x}\\\\\\^\"", "\\\\\\\\\\\\\\^\"", OsTypeWindows}, - {"\\%{y}\\\\\\^\"", "\\\\\"\"\\^\"\"\"\\\\\\^\"", OsTypeWindows}, - {"\\%{z}\\\\\\^\"", "\\\\\\\\\\^\"", OsTypeWindows}, - - {"bs-enclosed and trailing unclosed quote", 0, OsTypeWindows}, - {"\\%{a}\\\\\"", "\\hi\\\\\"", OsTypeWindows}, - {"\\%{aa}\\\\\"", "\\\\\"hi ho\"\\\\\"", OsTypeWindows}, - {"\\%{b}\\\\\"", "\\h\\i\\\\\"", OsTypeWindows}, - {"\\%{c}\\\\\"", "\\\\hi\\\\\"", OsTypeWindows}, - {"\\%{d}\\\\\"", "\\hi\\\\\\\\\"", OsTypeWindows}, - {"\\%{ba}\\\\\"", "\\\\\"h\\i ho\"\\\\\"", OsTypeWindows}, - {"\\%{ca}\\\\\"", "\\\\\"\\hi ho\"\\\\\"", OsTypeWindows}, - {"\\%{da}\\\\\"", "\\\\\"hi ho\\\\\"\\\\\"", OsTypeWindows}, - {"\\%{e}\\\\\"", "\\\\\"h\"\\^\"\"i\"\\\\\"", OsTypeWindows}, - {"\\%{f}\\\\\"", "\\\\\"\"\\^\"\"hi\"\\\\\"", OsTypeWindows}, - {"\\%{g}\\\\\"", "\\\\\"hi\"\\^\"\"\"\\\\\"", OsTypeWindows}, - {"\\%{h}\\\\\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\"", OsTypeWindows}, - {"\\%{i}\\\\\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\"", OsTypeWindows}, - {"\\%{j}\\\\\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\"", OsTypeWindows}, - {"\\%{x}\\\\\"", "\\\\\\\\\\\\\"", OsTypeWindows}, - {"\\%{y}\\\\\"", "\\\\\"\"\\^\"\"\"\\\\\"", OsTypeWindows}, - {"\\%{z}\\\\\"", "\\\\\\\\\"", OsTypeWindows}, - - {"multi-var", 0, OsTypeWindows}, - {"%{x}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, - {"%{x}%{z}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, - {"%{x}%{z}%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows}, - {"%{x}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows}, - {"%{x}%{z}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows}, - {"%{x}%{z}\\^\"", "\\\\\\^\"", OsTypeWindows}, - {"%{x}\\%{z}", "\\\\", OsTypeWindows}, - {"%{x}%{z}\\%{z}", "\\\\", OsTypeWindows}, - {"%{x}%{z}\\", "\\\\", OsTypeWindows}, - {"%{aa}%{a}", "\"hi hohi\"", OsTypeWindows}, - {"%{aa}%{aa}", "\"hi hohi ho\"", OsTypeWindows}, - {"%{aa}:%{aa}", "\"hi ho\":\"hi ho\"", OsTypeWindows}, - {"hallo ^|%{aa}^|", "hallo ^|\"hi ho\"^|", OsTypeWindows}, - - {"quoted multi-var", 0, OsTypeWindows}, - {"\"%{x}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, - {"\"%{x}%{z}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, - {"\"%{x}%{z}%{y}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows}, - {"\"%{x}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, - {"\"%{x}%{z}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, - {"\"%{x}%{z}\"^\"\"\"", "\"\\\\\"^\"\"\"", OsTypeWindows}, - {"\"%{x}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows}, - {"\"%{x}%{z}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows}, - {"\"%{x}%{z}\\\\\"", "\"\\\\\\\\\"", OsTypeWindows}, - {"\"%{aa}%{a}\"", "\"hi hohi\"", OsTypeWindows}, - {"\"%{aa}%{aa}\"", "\"hi hohi ho\"", OsTypeWindows}, - {"\"%{aa}:%{aa}\"", "\"hi ho:hi ho\"", OsTypeWindows}, - - {"plain", 0, OsTypeLinux}, - {"%{a}", "hi", OsTypeLinux}, - {"%{b}", "'hi ho'", OsTypeLinux}, - {"%{c}", "'&special;'", OsTypeLinux}, - {"%{d}", "'h\\i'", OsTypeLinux}, - {"%{e}", "'h\"i'", OsTypeLinux}, - {"%{f}", "'h'\\''i'", OsTypeLinux}, - {"%{z}", "''", OsTypeLinux}, - {"\\%{z}%{z}", "\\%{z}%{z}", OsTypeLinux}, // stupid user check - - {"single-quoted", 0, OsTypeLinux}, - {"'%{a}'", "'hi'", OsTypeLinux}, - {"'%{b}'", "'hi ho'", OsTypeLinux}, - {"'%{c}'", "'&special;'", OsTypeLinux}, - {"'%{d}'", "'h\\i'", OsTypeLinux}, - {"'%{e}'", "'h\"i'", OsTypeLinux}, - {"'%{f}'", "'h'\\''i'", OsTypeLinux}, - {"'%{z}'", "''", OsTypeLinux}, - - {"double-quoted", 0, OsTypeLinux}, - {"\"%{a}\"", "\"hi\"", OsTypeLinux}, - {"\"%{b}\"", "\"hi ho\"", OsTypeLinux}, - {"\"%{c}\"", "\"&special;\"", OsTypeLinux}, - {"\"%{d}\"", "\"h\\\\i\"", OsTypeLinux}, - {"\"%{e}\"", "\"h\\\"i\"", OsTypeLinux}, - {"\"%{f}\"", "\"h'i\"", OsTypeLinux}, - {"\"%{z}\"", "\"\"", OsTypeLinux}, - - {"complex", 0, OsTypeLinux}, - {"echo \"$(echo %{a})\"", "echo \"$(echo hi)\"", OsTypeLinux}, - {"echo \"$(echo %{b})\"", "echo \"$(echo 'hi ho')\"", OsTypeLinux}, - {"echo \"$(echo \"%{a}\")\"", "echo \"$(echo \"hi\")\"", OsTypeLinux}, - // These make no sense shell-wise, but they test expando nesting - {"echo \"%{echo %{a}}\"", "echo \"%{echo hi}\"", OsTypeLinux}, - {"echo \"%{echo %{b}}\"", "echo \"%{echo hi ho}\"", OsTypeLinux}, - {"echo \"%{echo \"%{a}\"}\"", "echo \"%{echo \"hi\"}\"", OsTypeLinux }, - }; - - const char *title = 0; - for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) { - if (!vals[i].out) { - title = vals[i].in; - } else { - char buf[80]; - snprintf(buf, 80, "%s: %s", title, vals[i].in); - QTest::newRow(buf) << QString::fromLatin1(vals[i].in) - << QString::fromLatin1(vals[i].out) - << vals[i].os; - snprintf(buf, 80, "padded %s: %s", title, vals[i].in); - QTest::newRow(buf) << QString(sp + QString::fromLatin1(vals[i].in) + sp) - << QString(sp + QString::fromLatin1(vals[i].out) + sp) - << vals[i].os; - } - } -} - -void tst_Process::expandMacros() -{ - QFETCH(QString, in); - QFETCH(QString, out); - QFETCH(OsType, os); - - if (os == OsTypeWindows) - ProcessArgs::expandMacros(&in, &mxWin, os); - else - ProcessArgs::expandMacros(&in, &mxUnix, os); - QCOMPARE(in, out); -} void tst_Process::iterations_data() { diff --git a/tests/auto/utils/stringutils/tst_stringutils.cpp b/tests/auto/utils/stringutils/tst_stringutils.cpp index 0b5c5cd1527..4cfa1c7f35a 100644 --- a/tests/auto/utils/stringutils/tst_stringutils.cpp +++ b/tests/auto/utils/stringutils/tst_stringutils.cpp @@ -10,66 +10,12 @@ using namespace Utils; -class TestMacroExpander : public Utils::AbstractMacroExpander -{ -public: - bool resolveMacro(const QString &name, QString *ret, QSet &seen) - override - { - // loop prevention - const int count = seen.count(); - seen.insert(this); - if (seen.count() == count) - return false; - - if (name == QLatin1String("foo")) { - *ret = QLatin1String("a"); - return true; - } - if (name == QLatin1String("a")) { - *ret = QLatin1String("hi"); - return true; - } - if (name == QLatin1String("hi")) { - *ret = QLatin1String("ho"); - return true; - } - if (name == QLatin1String("hihi")) { - *ret = QLatin1String("bar"); - return true; - } - if (name == "slash") { - *ret = "foo/bar"; - return true; - } - if (name == "sl/sh") { - *ret = "slash"; - return true; - } - if (name == "JS:foo") { - *ret = "bar"; - return true; - } - if (name == "JS:with } inside") { - *ret = "yay"; - return true; - } - if (name == "JS:literal%{") { - *ret = "hurray"; - return true; - } - return false; - } -}; - class tst_StringUtils : public QObject { Q_OBJECT private slots: void testWithTildeHomePath(); - void testMacroExpander_data(); - void testMacroExpander(); void testStripAccelerator_data(); void testStripAccelerator(); void testParseUsedPortFromNetstatOutput_data(); @@ -84,9 +30,6 @@ private slots: void testSplitAtFirst(); void testAsciify_data(); void testAsciify(); - -private: - TestMacroExpander mx; }; void tst_StringUtils::testWithTildeHomePath() @@ -118,68 +61,6 @@ void tst_StringUtils::testWithTildeHomePath() #endif } -void tst_StringUtils::testMacroExpander_data() - -{ - QTest::addColumn("in"); - QTest::addColumn("out"); - - static const struct { - const char * const in; - const char * const out; - } vals[] = { - {"text", "text"}, - {"%{a}", "hi"}, - {"%%{a}", "%hi"}, - {"%%%{a}", "%%hi"}, - {"%{b}", "%{b}"}, - {"pre%{a}", "prehi"}, - {"%{a}post", "hipost"}, - {"pre%{a}post", "prehipost"}, - {"%{a}%{a}", "hihi"}, - {"%{a}text%{a}", "hitexthi"}, - {"%{foo}%{a}text%{a}", "ahitexthi"}, - {"%{}{a}", "%{a}"}, - {"%{}", "%"}, - {"test%{}", "test%"}, - {"%{}test", "%test"}, - {"%{abc", "%{abc"}, - {"%{%{a}", "%{hi"}, - {"%{%{a}}", "ho"}, - {"%{%{a}}}post", "ho}post"}, - {"%{hi%{a}}", "bar"}, - {"%{hi%{%{foo}}}", "bar"}, - {"%{hihi/b/c}", "car"}, - {"%{hihi/a/}", "br"}, // empty replacement - {"%{hihi/b}", "bar"}, // incomplete substitution - {"%{hihi/./c}", "car"}, - {"%{hihi//./c}", "ccc"}, - {"%{hihi/(.)(.)r/\\2\\1c}", "abc"}, // no escape for capture groups - {"%{hihi/b/c/d}", "c/dar"}, - {"%{hihi/a/e{\\}e}", "be{}er"}, // escape closing brace - {"%{JS:with \\} inside}", "yay"}, // escape closing brace also in JS: - {"%{JS:literal%\\{}", "hurray"}, - {"%{slash/o\\/b/ol's c}", "fool's car"}, - {"%{sl\\/sh/(.)(a)(.)/\\2\\1\\3as}", "salsash"}, // escape in variable name - {"%{JS:foo/b/c}", "%{JS:foo/b/c}"}, // No replacement for JS (all considered varName) - {"%{%{a}%{a}/b/c}", "car"}, - {"%{nonsense:-sense}", "sense"}, - }; - - for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) - QTest::newRow(vals[i].in) << QString::fromLatin1(vals[i].in) - << QString::fromLatin1(vals[i].out); -} - -void tst_StringUtils::testMacroExpander() -{ - QFETCH(QString, in); - QFETCH(QString, out); - - Utils::expandMacros(&in, &mx); - QCOMPARE(in, out); -} - void tst_StringUtils::testStripAccelerator_data() { QTest::addColumn("expected");