// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include #include #include #include using namespace Utils; class tst_expander : public QObject { Q_OBJECT private slots: void expandFilePath_data() { QTest::addColumn("input"); QTest::addColumn("expected"); QTest::addColumn>("setup"); std::function empty; std::function home = [](MacroExpander &expander) { expander.registerVariable("Home", "", [] { return QDir::homePath(); }); }; std::function remotehome = [](MacroExpander &expander) { expander.registerVariable("Home", "", [] { return "ssh://127.0.0.1/home"; }); }; std::function buildfolderwithspecialchars = [](MacroExpander &expander) { expander.registerVariable("BuildFolder", "", [] { return "C:/Test/Some.Dots.In.File"; }); }; QTest::newRow("empty") << FilePath() << FilePath() << empty; QTest::newRow("no expansion") << FilePath("foo") << FilePath("foo") << empty; QTest::newRow("no expansion with slash") << FilePath("foo/bar") << FilePath("foo/bar") << empty; QTest::newRow("home") << FilePath("%{Home}/foo") << FilePath::fromString(QDir::homePath() + "/foo") << home; QTest::newRow("remote-home") << FilePath("%{Home}/foo") << FilePath::fromParts(QString("ssh"), QString("127.0.0.1"), QString("/home/foo")) << remotehome; QTest::newRow("colon") << FilePath("foo:bar") << FilePath::fromParts({}, {}, QString("foo:bar")) << empty; QTest::newRow("win-dots") << FilePath("%{BuildFolder}/Debug/my.file.with.dots.elf") << FilePath::fromParts( {}, {}, QString("C:/Test/Some.Dots.In.File/Debug/my.file.with.dots.elf")) << buildfolderwithspecialchars; } void expandFilePath() { QFETCH(FilePath, input); QFETCH(FilePath, expected); QFETCH(std::function, setup); MacroExpander expander; if (setup) setup(expander); QCOMPARE(expander.expand(input), expected); } void expandString_data() { QTest::addColumn("input"); QTest::addColumn("expected"); QTest::addColumn>("setup"); std::function empty; std::function user = [](MacroExpander &expander) { expander.registerVariable("UserName", "", [] { return "JohnDoe"; }); expander.registerVariable("FirstName", "", [] { return "John"; }); expander.registerVariable("LastName", "", [] { return "Doe"; }); expander.registerVariable("Email", "", [] { return "john.doe@example.com"; }); }; QTest::newRow("empty") << QString() << QString() << empty; QTest::newRow("no expansion") << QString("foo") << QString("foo") << empty; QTest::newRow("simple-expansion") << QString("My name is: %{FirstName}") << QString("My name is: John") << user; QTest::newRow("multiple-expansions") << QString("My name is: %{FirstName} %{LastName} (%{UserName})") << QString("My name is: John Doe (JohnDoe)") << user; QTest::newRow("email") << QString("My email is: %{Email}") << QString("My email is: john.doe@example.com") << user; QTest::newRow("default-expansion") << QString("My name is: %{VarDoesNotExist:-Johanna}") << QString("My name is: Johanna") << empty; } void expandString() { QFETCH(QString, input); QFETCH(QString, expected); QFETCH(std::function, setup); MacroExpander expander; if (setup) setup(expander); QCOMPARE(expander.expand(input), expected); } void subProvider() { MacroExpander expander; expander.registerVariable("MainVar", "", [] { return "MainValue"; }); expander.registerSubProvider({this, [] { static MacroExpander *sub = new MacroExpander; sub->registerVariable("SubVar", "", [] { return "SubValue"; }); return sub; }}); QCOMPARE(expander.expand(QString("%{MainVar} %{SubVar}")), QString("MainValue SubValue")); QString resolved; expander.resolveMacro("MainVar", &resolved); QCOMPARE(resolved, QString("MainValue")); expander.resolveMacro("SubVar", &resolved); QCOMPARE(resolved, QString("SubValue")); } void expandByteArray_data() { QTest::addColumn("input"); QTest::addColumn("expected"); QTest::addColumn>("setup"); std::function empty; std::function user = [](MacroExpander &expander) { expander.registerVariable("UserName", "", [] { return "JohnDoe"; }); expander.registerVariable("FirstName", "", [] { return "John"; }); expander.registerVariable("LastName", "", [] { return "Doe"; }); }; QTest::newRow("empty") << QByteArray() << QByteArray() << empty; QTest::newRow("no expansion") << QByteArray("\0\x10\xff", 3) << QByteArray("\0\x10\xff", 3) << empty; QTest::newRow("simple-expansion") << QByteArray("My name is: %{FirstName}") << QByteArray("My name is: John") << user; QTest::newRow("with-zeroes") << QByteArray("My name is: \0%{FirstName}", 25) << QByteArray("My name is: \0John", 17) << user; } void expandByteArray() { QFETCH(QByteArray, input); QFETCH(QByteArray, expected); QFETCH(std::function, setup); MacroExpander expander; if (setup) setup(expander); QCOMPARE(expander.expand(input), expected); } void expandCommandArgs_data() { QTest::addColumn("input"); QTest::addColumn("expected"); QTest::addColumn("os"); QTest::addColumn>("setup"); std::function empty; std::function file = [](MacroExpander &expander) { expander.registerVariable("file", "", [] { return "foo.txt"; }); }; std::function withspace = [](MacroExpander &expander) { expander.registerVariable("WithSpace", "", [] { return "This has spaces"; }); }; 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") << Utils::OsTypeLinux << file; QTest::newRow("with-ticks") << 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\"'") << Utils::OsTypeLinux << file; QTest::newRow("with-spaces") << QString("echo %{WithSpace}") << QString("echo 'This has spaces'") << Utils::OsTypeLinux << 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("sh -c 'echo %{WithSpace}'") << QString("sh -c 'echo This has spaces'") << Utils::OsTypeLinux << 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; // Windows tests QTest::newRow("with-spaces") << QString("echo %{WithSpace}") << QString("echo \"This has spaces\"") << Utils::OsTypeWindows << 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; if (setup) setup(expander); 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{"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{"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("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 "\""; }); } else { 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("a", "", [] { return "hi"; }); expander.registerVariable("z", "", [] { return ""; }); QCOMPARE(expander.expandProcessArgs(in, os), out); } void testProcessArgsFails() { MacroExpander expander; expander.registerVariable("z", "", [] { return ""; }); expander.registerVariable("file", "", [] { return "foo.txt"; }); QVERIFY(!expander.expandProcessArgs("\\%{z}%{z}", OsTypeLinux)); QVERIFY(!expander.expandProcessArgs("echo \\%{file}", OsTypeLinux)); QVERIFY(!expander.expandProcessArgs("^%{z}%{z}", OsTypeWindows)); } 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); } }; QTEST_GUILESS_MAIN(tst_expander) #include "tst_expander.moc"