1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include <utils/environment.h>
27 #include <utils/hostosinfo.h>
28 #include <utils/porting.h>
29 #include <utils/qtcprocess.h>
30 #include <utils/stringutils.h>
31 
32 #include <QRegularExpression>
33 #include <QtTest>
34 
35 #include <iostream>
36 #include <fstream>
37 
38 #ifdef Q_OS_WIN
39   #include <io.h>
40   #include <fcntl.h>
41 #endif
42 
43 
44 using namespace Utils;
45 
46 const char kExitCodeSubProcessCode[] = "QTC_TST_QTCPROCESS_EXITCODE_CODE";
47 const char kRunBlockingStdOutSubProcessMagicWord[] = "42";
48 const char kRunBlockingStdOutSubProcessWithEndl[] = "QTC_TST_QTCPROCESS_RUNBLOCKINGSTDOUT_WITHENDL";
49 const char kLineCallback[] = "QTC_TST_QTCPROCESS_LINECALLBACK";
50 
51 // Expect ending lines detected at '|':
52 const char lineCallbackData[] =
53        "This is the first line\r\n|"
54        "Here comes the second one\r\n|"
55        "And a line without LF\n|"
56        "Rebasing (1/10)\r| <delay> Rebasing (2/10)\r| <delay> ...\r\n|"
57        "And no end";
58 
exitCodeSubProcessMain()59 static void exitCodeSubProcessMain()
60 {
61     const int exitCode = qEnvironmentVariableIntValue(kExitCodeSubProcessCode);
62     std::cout << "Exiting with code:" << exitCode << std::endl;
63     exit(exitCode);
64 }
65 
blockingStdOutSubProcessMain()66 static void blockingStdOutSubProcessMain()
67 {
68     std::cout << "Wait for the Answer to the Ultimate Question of Life, "
69                  "The Universe, and Everything..." << std::endl;
70     QThread::msleep(300);
71     std::cout << kRunBlockingStdOutSubProcessMagicWord << "...Now wait for the question...";
72     if (qEnvironmentVariable(kRunBlockingStdOutSubProcessWithEndl) == "true")
73         std::cout << std::endl;
74     QThread::msleep(5000);
75     exit(0);
76 }
77 
lineCallbackMain()78 static void lineCallbackMain()
79 {
80 #ifdef Q_OS_WIN
81     // Prevent \r\n -> \r\r\n translation.
82     setmode(fileno(stderr), O_BINARY);
83 #endif
84     fprintf(stderr, "%s", QByteArray(lineCallbackData).replace('|', "").data());
85     exit(0);
86 }
87 
88 class MacroMapExpander : public AbstractMacroExpander {
89 public:
resolveMacro(const QString & name,QString * ret,QSet<AbstractMacroExpander * > & seen)90     virtual bool resolveMacro(const QString &name, QString *ret, QSet<AbstractMacroExpander*> &seen)
91     {
92         // loop prevention
93         const int count = seen.count();
94         seen.insert(this);
95         if (seen.count() == count)
96             return false;
97 
98         QHash<QString, QString>::const_iterator it = m_map.constFind(name);
99         if (it != m_map.constEnd()) {
100             *ret = it.value();
101             return true;
102         }
103         return false;
104     }
insert(const QString & key,const QString & value)105     void insert(const QString &key, const QString &value) { m_map.insert(key, value); }
106 private:
107     QHash<QString, QString> m_map;
108 };
109 
110 class tst_QtcProcess : public QObject
111 {
112     Q_OBJECT
113 
114 private slots:
115     void initTestCase();
116 
117     void splitArgs_data();
118     void splitArgs();
119     void prepareArgs_data();
120     void prepareArgs();
121     void prepareArgsEnv_data();
122     void prepareArgsEnv();
123     void expandMacros_data();
124     void expandMacros();
125     void iterations_data();
126     void iterations();
127     void iteratorEditsWindows();
128     void iteratorEditsLinux();
129     void exitCode_data();
130     void exitCode();
131     void runBlockingStdOut_data();
132     void runBlockingStdOut();
133     void lineCallback();
134     void lineCallbackIntern();
135 
136 private:
137     void iteratorEditsHelper(OsType osType);
138 
139     Environment envWindows;
140     Environment envLinux;
141 
142     MacroMapExpander mxWin;
143     MacroMapExpander mxUnix;
144     QString homeStr;
145     QString home;
146 };
147 
initTestCase()148 void tst_QtcProcess::initTestCase()
149 {
150     if (qEnvironmentVariableIsSet(kExitCodeSubProcessCode))
151         exitCodeSubProcessMain();
152     if (qEnvironmentVariableIsSet(kRunBlockingStdOutSubProcessWithEndl))
153         blockingStdOutSubProcessMain();
154     if (qEnvironmentVariableIsSet(kLineCallback))
155         lineCallbackMain();
156 
157     homeStr = QLatin1String("@HOME@");
158     home = QDir::homePath();
159 
160     QStringList env;
161     env << "empty=" << "word=hi" << "words=hi ho" << "spacedwords= hi   ho sucker ";
162     envWindows = Environment(env, OsTypeWindows);
163     envLinux = Environment(env, OsTypeLinux);
164 
165     mxWin.insert("a", "hi");
166     mxWin.insert("aa", "hi ho");
167 
168     mxWin.insert("b", "h\\i");
169     mxWin.insert("c", "\\hi");
170     mxWin.insert("d", "hi\\");
171     mxWin.insert("ba", "h\\i ho");
172     mxWin.insert("ca", "\\hi ho");
173     mxWin.insert("da", "hi ho\\");
174 
175     mxWin.insert("e", "h\"i");
176     mxWin.insert("f", "\"hi");
177     mxWin.insert("g", "hi\"");
178 
179     mxWin.insert("h", "h\\\"i");
180     mxWin.insert("i", "\\\"hi");
181     mxWin.insert("j", "hi\\\"");
182 
183     mxWin.insert("k", "&special;");
184 
185     mxWin.insert("x", "\\");
186     mxWin.insert("y", "\"");
187     mxWin.insert("z", "");
188 
189     mxUnix.insert("a", "hi");
190     mxUnix.insert("b", "hi ho");
191     mxUnix.insert("c", "&special;");
192     mxUnix.insert("d", "h\\i");
193     mxUnix.insert("e", "h\"i");
194     mxUnix.insert("f", "h'i");
195     mxUnix.insert("z", "");
196 }
197 
198 
199 Q_DECLARE_METATYPE(ProcessArgs::SplitError)
Q_DECLARE_METATYPE(Utils::OsType)200 Q_DECLARE_METATYPE(Utils::OsType)
201 
202 void tst_QtcProcess::splitArgs_data()
203 {
204     QTest::addColumn<QString>("in");
205     QTest::addColumn<QString>("out");
206     QTest::addColumn<ProcessArgs::SplitError>("err");
207     QTest::addColumn<Utils::OsType>("os");
208 
209     static const struct {
210         const char * const in;
211         const char * const out;
212         const ProcessArgs::SplitError err;
213         const OsType os;
214     } vals[] = {
215         {"", "", ProcessArgs::SplitOk, OsTypeWindows},
216         {" ", "", ProcessArgs::SplitOk, OsTypeWindows},
217         {"hi", "hi", ProcessArgs::SplitOk, OsTypeWindows},
218         {"hi ho", "hi ho", ProcessArgs::SplitOk, OsTypeWindows},
219         {" hi ho ", "hi ho", ProcessArgs::SplitOk, OsTypeWindows},
220         {"\"hi ho\" \"hi\" ho  ", "\"hi ho\" hi ho", ProcessArgs::SplitOk, OsTypeWindows},
221         {"\\", "\\", ProcessArgs::SplitOk, OsTypeWindows},
222         {"\\\"", "\"\"\\^\"\"\"", ProcessArgs::SplitOk, OsTypeWindows},
223         {"\"hi\"\"\"ho\"", "\"hi\"\\^\"\"ho\"", ProcessArgs::SplitOk, OsTypeWindows},
224         {"\\\\\\\"", "\"\"\\\\\\^\"\"\"", ProcessArgs::SplitOk, OsTypeWindows},
225         {" ^^ ", "\"^^\"", ProcessArgs::SplitOk, OsTypeWindows},
226         {"hi\"", "", ProcessArgs::BadQuoting, OsTypeWindows},
227         {"hi\"dood", "", ProcessArgs::BadQuoting, OsTypeWindows},
228         {"%var%", "%var%", ProcessArgs::SplitOk, OsTypeWindows},
229 
230         {"", "", ProcessArgs::SplitOk, OsTypeLinux},
231         {" ", "", ProcessArgs::SplitOk, OsTypeLinux},
232         {"hi", "hi", ProcessArgs::SplitOk, OsTypeLinux},
233         {"hi ho", "hi ho", ProcessArgs::SplitOk, OsTypeLinux},
234         {" hi ho ", "hi ho", ProcessArgs::SplitOk, OsTypeLinux},
235         {"'hi ho' \"hi\" ho  ", "'hi ho' hi ho", ProcessArgs::SplitOk, OsTypeLinux},
236         {" \\ ", "' '", ProcessArgs::SplitOk, OsTypeLinux},
237         {" \\\" ", "'\"'", ProcessArgs::SplitOk, OsTypeLinux},
238         {" '\"' ", "'\"'", ProcessArgs::SplitOk, OsTypeLinux},
239         {" \"\\\"\" ", "'\"'", ProcessArgs::SplitOk, OsTypeLinux},
240         {"hi'", "", ProcessArgs::BadQuoting, OsTypeLinux},
241         {"hi\"dood", "", ProcessArgs::BadQuoting, OsTypeLinux},
242         {"$var", "'$var'", ProcessArgs::SplitOk, OsTypeLinux},
243         {"~", "@HOME@", ProcessArgs::SplitOk, OsTypeLinux},
244         {"~ foo", "@HOME@ foo", ProcessArgs::SplitOk, OsTypeLinux},
245         {"foo ~", "foo @HOME@", ProcessArgs::SplitOk, OsTypeLinux},
246         {"~/foo", "@HOME@/foo", ProcessArgs::SplitOk, OsTypeLinux},
247         {"~foo", "'~foo'", ProcessArgs::SplitOk, OsTypeLinux}
248     };
249 
250     for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {
251         QString out = QString::fromLatin1(vals[i].out);
252         if (vals[i].os == OsTypeLinux)
253             out.replace(homeStr, home);
254         QTest::newRow(vals[i].in) << QString::fromLatin1(vals[i].in)
255                                   << out << vals[i].err << vals[i].os;
256     }
257 }
258 
splitArgs()259 void tst_QtcProcess::splitArgs()
260 {
261     QFETCH(QString, in);
262     QFETCH(QString, out);
263     QFETCH(ProcessArgs::SplitError, err);
264     QFETCH(Utils::OsType, os);
265 
266     ProcessArgs::SplitError outerr;
267     QString outstr = ProcessArgs::joinArgs(ProcessArgs::splitArgs(in, os, false, &outerr), os);
268     QCOMPARE(outerr, err);
269     if (err == ProcessArgs::SplitOk)
270         QCOMPARE(outstr, out);
271 }
272 
prepareArgs_data()273 void tst_QtcProcess::prepareArgs_data()
274 {
275     QTest::addColumn<QString>("in");
276     QTest::addColumn<QString>("out");
277     QTest::addColumn<ProcessArgs::SplitError>("err");
278     QTest::addColumn<OsType>("os");
279 
280     static const struct {
281         const char * const in;
282         const char * const out;
283         const ProcessArgs::SplitError err;
284         const OsType os;
285     } vals[] = {
286         {" ", " ", ProcessArgs::SplitOk, OsTypeWindows},
287         {"", "", ProcessArgs::SplitOk, OsTypeWindows},
288         {"hi", "hi", ProcessArgs::SplitOk, OsTypeWindows},
289         {"hi ho", "hi ho", ProcessArgs::SplitOk, OsTypeWindows},
290         {" hi ho ", " hi ho ", ProcessArgs::SplitOk, OsTypeWindows},
291         {"\"hi ho\" \"hi\" ho  ", "\"hi ho\" \"hi\" ho  ", ProcessArgs::SplitOk, OsTypeWindows},
292         {"\\", "\\", ProcessArgs::SplitOk, OsTypeWindows},
293         {"\\\"", "\\\"", ProcessArgs::SplitOk, OsTypeWindows},
294         {"\"hi\"\"ho\"", "\"hi\"\"ho\"", ProcessArgs::SplitOk, OsTypeWindows},
295         {"\\\\\\\"", "\\\\\\\"", ProcessArgs::SplitOk, OsTypeWindows},
296         {"^^", "^", ProcessArgs::SplitOk, OsTypeWindows},
297         {"hi\"", "hi\"", ProcessArgs::SplitOk, OsTypeWindows},
298         {"hi\"dood", "hi\"dood", ProcessArgs::SplitOk, OsTypeWindows},
299         {"%var%", "", ProcessArgs::FoundMeta, OsTypeWindows},
300         {"echo hi > file", "", ProcessArgs::FoundMeta, OsTypeWindows},
301 
302         {"", "", ProcessArgs::SplitOk, OsTypeLinux},
303         {" ", "", ProcessArgs::SplitOk, OsTypeLinux},
304         {"hi", "hi", ProcessArgs::SplitOk, OsTypeLinux},
305         {"hi ho", "hi ho", ProcessArgs::SplitOk, OsTypeLinux},
306         {" hi ho ", "hi ho", ProcessArgs::SplitOk, OsTypeLinux},
307         {"'hi ho' \"hi\" ho  ", "'hi ho' hi ho", ProcessArgs::SplitOk, OsTypeLinux},
308         {" \\ ", "' '", ProcessArgs::SplitOk, OsTypeLinux},
309         {"hi'", "", ProcessArgs::BadQuoting, OsTypeLinux},
310         {"hi\"dood", "", ProcessArgs::BadQuoting, OsTypeLinux},
311         {"$var", "", ProcessArgs::FoundMeta, OsTypeLinux},
312         {"~", "@HOME@", ProcessArgs::SplitOk, OsTypeLinux},
313         {"~ foo", "@HOME@ foo", ProcessArgs::SplitOk, OsTypeLinux},
314         {"~/foo", "@HOME@/foo", ProcessArgs::SplitOk, OsTypeLinux},
315         {"~foo", "", ProcessArgs::FoundMeta, OsTypeLinux}
316     };
317 
318     for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {
319         QString out = QString::fromLatin1(vals[i].out);
320         if (vals[i].os == OsTypeLinux)
321             out.replace(homeStr, home);
322         QTest::newRow(vals[i].in) << QString::fromLatin1(vals[i].in)
323                                   << out << vals[i].err << vals[i].os;
324     }
325 }
326 
prepareArgs()327 void tst_QtcProcess::prepareArgs()
328 {
329     QFETCH(QString, in);
330     QFETCH(QString, out);
331     QFETCH(ProcessArgs::SplitError, err);
332     QFETCH(OsType, os);
333 
334     ProcessArgs::SplitError outerr;
335     ProcessArgs args = ProcessArgs::prepareArgs(in, &outerr, os);
336     QString outstr = args.toString();
337 
338     QCOMPARE(outerr, err);
339     if (err == ProcessArgs::SplitOk)
340         QCOMPARE(outstr, out);
341 }
342 
prepareArgsEnv_data()343 void tst_QtcProcess::prepareArgsEnv_data()
344 {
345     QTest::addColumn<QString>("in");
346     QTest::addColumn<QString>("out");
347     QTest::addColumn<ProcessArgs::SplitError>("err");
348     QTest::addColumn<OsType>("os");
349 
350     static const struct {
351         const char * const in;
352         const char * const out;
353         const ProcessArgs::SplitError err;
354         const OsType os;
355     } vals[] = {
356         {" ", " ", ProcessArgs::SplitOk, OsTypeWindows},
357         {"", "", ProcessArgs::SplitOk, OsTypeWindows},
358         {"hi", "hi", ProcessArgs::SplitOk, OsTypeWindows},
359         {"hi ho", "hi ho", ProcessArgs::SplitOk, OsTypeWindows},
360         {" hi ho ", " hi ho ", ProcessArgs::SplitOk, OsTypeWindows},
361         {"\"hi ho\" \"hi\" ho  ", "\"hi ho\" \"hi\" ho  ", ProcessArgs::SplitOk, OsTypeWindows},
362         {"\\", "\\", ProcessArgs::SplitOk, OsTypeWindows},
363         {"\\\"", "\\\"", ProcessArgs::SplitOk, OsTypeWindows},
364         {"\"hi\"\"ho\"", "\"hi\"\"ho\"", ProcessArgs::SplitOk, OsTypeWindows},
365         {"\\\\\\\"", "\\\\\\\"", ProcessArgs::SplitOk, OsTypeWindows},
366         {"^^", "^", ProcessArgs::SplitOk, OsTypeWindows},
367         {"hi\"", "hi\"", ProcessArgs::SplitOk, OsTypeWindows},
368         {"hi\"dood", "hi\"dood", ProcessArgs::SplitOk, OsTypeWindows},
369         {"%empty%", "%empty%", ProcessArgs::SplitOk, OsTypeWindows}, // Yep, no empty variables on Windows.
370         {"%word%", "hi", ProcessArgs::SplitOk, OsTypeWindows},
371         {" %word% ", " hi ", ProcessArgs::SplitOk, OsTypeWindows},
372         {"%words%", "hi ho", ProcessArgs::SplitOk, OsTypeWindows},
373         {"%nonsense%words%", "%nonsensehi ho", ProcessArgs::SplitOk, OsTypeWindows},
374         {"fail%nonsense%words%", "fail%nonsensehi ho", ProcessArgs::SplitOk, OsTypeWindows},
375         {"%words%words%", "hi howords%", ProcessArgs::SplitOk, OsTypeWindows},
376         {"%words%%words%", "hi hohi ho", ProcessArgs::SplitOk, OsTypeWindows},
377         {"echo hi > file", "", ProcessArgs::FoundMeta, OsTypeWindows},
378 
379         {"", "", ProcessArgs::SplitOk, OsTypeLinux},
380         {" ", "", ProcessArgs::SplitOk, OsTypeLinux},
381         {"hi", "hi", ProcessArgs::SplitOk, OsTypeLinux},
382         {"hi ho", "hi ho", ProcessArgs::SplitOk, OsTypeLinux},
383         {" hi ho ", "hi ho", ProcessArgs::SplitOk, OsTypeLinux},
384         {"'hi ho' \"hi\" ho  ", "'hi ho' hi ho", ProcessArgs::SplitOk, OsTypeLinux},
385         {" \\ ", "' '", ProcessArgs::SplitOk, OsTypeLinux},
386         {"hi'", "", ProcessArgs::BadQuoting, OsTypeLinux},
387         {"hi\"dood", "", ProcessArgs::BadQuoting, OsTypeLinux},
388         {"$empty", "", ProcessArgs::SplitOk, OsTypeLinux},
389         {"$word", "hi", ProcessArgs::SplitOk, OsTypeLinux},
390         {" $word ", "hi", ProcessArgs::SplitOk, OsTypeLinux},
391         {"${word}", "hi", ProcessArgs::SplitOk, OsTypeLinux},
392         {" ${word} ", "hi", ProcessArgs::SplitOk, OsTypeLinux},
393         {"$words", "hi ho", ProcessArgs::SplitOk, OsTypeLinux},
394         {"$spacedwords", "hi ho sucker", ProcessArgs::SplitOk, OsTypeLinux},
395         {"hi${empty}ho", "hiho", ProcessArgs::SplitOk, OsTypeLinux},
396         {"hi${words}ho", "hihi hoho", ProcessArgs::SplitOk, OsTypeLinux},
397         {"hi${spacedwords}ho", "hi hi ho sucker ho", ProcessArgs::SplitOk, OsTypeLinux},
398         {"${", "", ProcessArgs::BadQuoting, OsTypeLinux},
399         {"${var", "", ProcessArgs::BadQuoting, OsTypeLinux},
400         {"${var ", "", ProcessArgs::FoundMeta, OsTypeLinux},
401         {"\"hi${words}ho\"", "'hihi hoho'", ProcessArgs::SplitOk, OsTypeLinux},
402         {"\"hi${spacedwords}ho\"", "'hi hi   ho sucker ho'", ProcessArgs::SplitOk, OsTypeLinux},
403         {"\"${", "", ProcessArgs::BadQuoting, OsTypeLinux},
404         {"\"${var", "", ProcessArgs::BadQuoting, OsTypeLinux},
405         {"\"${var ", "", ProcessArgs::FoundMeta, OsTypeLinux},
406     };
407 
408     for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {
409         QString out = QString::fromLatin1(vals[i].out);
410         if (vals[i].os == OsTypeLinux)
411             out.replace(homeStr, home);
412         QTest::newRow(vals[i].in) << QString::fromLatin1(vals[i].in)
413                                   << out << vals[i].err << vals[i].os;
414     }
415 }
416 
prepareArgsEnv()417 void tst_QtcProcess::prepareArgsEnv()
418 {
419     QFETCH(QString, in);
420     QFETCH(QString, out);
421     QFETCH(ProcessArgs::SplitError, err);
422     QFETCH(OsType, os);
423 
424     ProcessArgs::SplitError outerr;
425     ProcessArgs args = ProcessArgs::prepareArgs(in, &outerr, os, os == OsTypeLinux ? &envLinux : &envWindows);
426     QString outstr = args.toString();
427 
428     QCOMPARE(outerr, err);
429     if (err == ProcessArgs::SplitOk)
430         QCOMPARE(outstr, out);
431 }
432 
expandMacros_data()433 void tst_QtcProcess::expandMacros_data()
434 
435 {
436     QTest::addColumn<QString>("in");
437     QTest::addColumn<QString>("out");
438     QTest::addColumn<OsType>("os");
439     QChar sp(QLatin1Char(' '));
440 
441     static const struct {
442         const char * const in;
443         const char * const out;
444         OsType os;
445     } vals[] = {
446         {"plain", 0, OsTypeWindows},
447         {"%{a}", "hi", OsTypeWindows},
448         {"%{aa}", "\"hi ho\"", OsTypeWindows},
449         {"%{b}", "h\\i", OsTypeWindows},
450         {"%{c}", "\\hi", OsTypeWindows},
451         {"%{d}", "hi\\", OsTypeWindows},
452         {"%{ba}", "\"h\\i ho\"", OsTypeWindows},
453         {"%{ca}", "\"\\hi ho\"", OsTypeWindows},
454         {"%{da}", "\"hi ho\\\\\"", OsTypeWindows}, // or "\"hi ho\"\\"
455         {"%{e}", "\"h\"\\^\"\"i\"", OsTypeWindows},
456         {"%{f}", "\"\"\\^\"\"hi\"", OsTypeWindows},
457         {"%{g}", "\"hi\"\\^\"\"\"", OsTypeWindows},
458         {"%{h}", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows},
459         {"%{i}", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows},
460         {"%{j}", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows},
461         {"%{k}", "\"&special;\"", OsTypeWindows},
462         {"%{x}", "\\", OsTypeWindows},
463         {"%{y}", "\"\"\\^\"\"\"", OsTypeWindows},
464         {"%{z}", "\"\"", OsTypeWindows},
465         {"^%{z}%{z}", "^%{z}%{z}", OsTypeWindows}, // stupid user check
466 
467         {"quoted", 0, OsTypeWindows},
468         {"\"%{a}\"", "\"hi\"", OsTypeWindows},
469         {"\"%{aa}\"", "\"hi ho\"", OsTypeWindows},
470         {"\"%{b}\"", "\"h\\i\"", OsTypeWindows},
471         {"\"%{c}\"", "\"\\hi\"", OsTypeWindows},
472         {"\"%{d}\"", "\"hi\\\\\"", OsTypeWindows},
473         {"\"%{ba}\"", "\"h\\i ho\"", OsTypeWindows},
474         {"\"%{ca}\"", "\"\\hi ho\"", OsTypeWindows},
475         {"\"%{da}\"", "\"hi ho\\\\\"", OsTypeWindows},
476         {"\"%{e}\"", "\"h\"\\^\"\"i\"", OsTypeWindows},
477         {"\"%{f}\"", "\"\"\\^\"\"hi\"", OsTypeWindows},
478         {"\"%{g}\"", "\"hi\"\\^\"\"\"", OsTypeWindows},
479         {"\"%{h}\"", "\"h\\\\\"\\^\"\"i\"", OsTypeWindows},
480         {"\"%{i}\"", "\"\\\\\"\\^\"\"hi\"", OsTypeWindows},
481         {"\"%{j}\"", "\"hi\\\\\"\\^\"\"\"", OsTypeWindows},
482         {"\"%{k}\"", "\"&special;\"", OsTypeWindows},
483         {"\"%{x}\"", "\"\\\\\"", OsTypeWindows},
484         {"\"%{y}\"", "\"\"\\^\"\"\"", OsTypeWindows},
485         {"\"%{z}\"", "\"\"", OsTypeWindows},
486 
487         {"leading bs", 0, OsTypeWindows},
488         {"\\%{a}", "\\hi", OsTypeWindows},
489         {"\\%{aa}", "\\\\\"hi ho\"", OsTypeWindows},
490         {"\\%{b}", "\\h\\i", OsTypeWindows},
491         {"\\%{c}", "\\\\hi", OsTypeWindows},
492         {"\\%{d}", "\\hi\\", OsTypeWindows},
493         {"\\%{ba}", "\\\\\"h\\i ho\"", OsTypeWindows},
494         {"\\%{ca}", "\\\\\"\\hi ho\"", OsTypeWindows},
495         {"\\%{da}", "\\\\\"hi ho\\\\\"", OsTypeWindows},
496         {"\\%{e}", "\\\\\"h\"\\^\"\"i\"", OsTypeWindows},
497         {"\\%{f}", "\\\\\"\"\\^\"\"hi\"", OsTypeWindows},
498         {"\\%{g}", "\\\\\"hi\"\\^\"\"\"", OsTypeWindows},
499         {"\\%{h}", "\\\\\"h\\\\\"\\^\"\"i\"", OsTypeWindows},
500         {"\\%{i}", "\\\\\"\\\\\"\\^\"\"hi\"", OsTypeWindows},
501         {"\\%{j}", "\\\\\"hi\\\\\"\\^\"\"\"", OsTypeWindows},
502         {"\\%{x}", "\\\\", OsTypeWindows},
503         {"\\%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows},
504         {"\\%{z}", "\\", OsTypeWindows},
505 
506         {"trailing bs", 0, OsTypeWindows},
507         {"%{a}\\", "hi\\", OsTypeWindows},
508         {"%{aa}\\", "\"hi ho\"\\", OsTypeWindows},
509         {"%{b}\\", "h\\i\\", OsTypeWindows},
510         {"%{c}\\", "\\hi\\", OsTypeWindows},
511         {"%{d}\\", "hi\\\\", OsTypeWindows},
512         {"%{ba}\\", "\"h\\i ho\"\\", OsTypeWindows},
513         {"%{ca}\\", "\"\\hi ho\"\\", OsTypeWindows},
514         {"%{da}\\", "\"hi ho\\\\\"\\", OsTypeWindows},
515         {"%{e}\\", "\"h\"\\^\"\"i\"\\", OsTypeWindows},
516         {"%{f}\\", "\"\"\\^\"\"hi\"\\", OsTypeWindows},
517         {"%{g}\\", "\"hi\"\\^\"\"\"\\", OsTypeWindows},
518         {"%{h}\\", "\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows},
519         {"%{i}\\", "\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows},
520         {"%{j}\\", "\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows},
521         {"%{x}\\", "\\\\", OsTypeWindows},
522         {"%{y}\\", "\"\"\\^\"\"\"\\", OsTypeWindows},
523         {"%{z}\\", "\\", OsTypeWindows},
524 
525         {"bs-enclosed", 0, OsTypeWindows},
526         {"\\%{a}\\", "\\hi\\", OsTypeWindows},
527         {"\\%{aa}\\", "\\\\\"hi ho\"\\", OsTypeWindows},
528         {"\\%{b}\\", "\\h\\i\\", OsTypeWindows},
529         {"\\%{c}\\", "\\\\hi\\", OsTypeWindows},
530         {"\\%{d}\\", "\\hi\\\\", OsTypeWindows},
531         {"\\%{ba}\\", "\\\\\"h\\i ho\"\\", OsTypeWindows},
532         {"\\%{ca}\\", "\\\\\"\\hi ho\"\\", OsTypeWindows},
533         {"\\%{da}\\", "\\\\\"hi ho\\\\\"\\", OsTypeWindows},
534         {"\\%{e}\\", "\\\\\"h\"\\^\"\"i\"\\", OsTypeWindows},
535         {"\\%{f}\\", "\\\\\"\"\\^\"\"hi\"\\", OsTypeWindows},
536         {"\\%{g}\\", "\\\\\"hi\"\\^\"\"\"\\", OsTypeWindows},
537         {"\\%{h}\\", "\\\\\"h\\\\\"\\^\"\"i\"\\", OsTypeWindows},
538         {"\\%{i}\\", "\\\\\"\\\\\"\\^\"\"hi\"\\", OsTypeWindows},
539         {"\\%{j}\\", "\\\\\"hi\\\\\"\\^\"\"\"\\", OsTypeWindows},
540         {"\\%{x}\\", "\\\\\\", OsTypeWindows},
541         {"\\%{y}\\", "\\\\\"\"\\^\"\"\"\\", OsTypeWindows},
542         {"\\%{z}\\", "\\\\", OsTypeWindows},
543 
544         {"bs-enclosed and trailing literal quote", 0, OsTypeWindows},
545         {"\\%{a}\\\\\\^\"", "\\hi\\\\\\^\"", OsTypeWindows},
546         {"\\%{aa}\\\\\\^\"", "\\\\\"hi ho\"\\\\\\^\"", OsTypeWindows},
547         {"\\%{b}\\\\\\^\"", "\\h\\i\\\\\\^\"", OsTypeWindows},
548         {"\\%{c}\\\\\\^\"", "\\\\hi\\\\\\^\"", OsTypeWindows},
549         {"\\%{d}\\\\\\^\"", "\\hi\\\\\\\\\\^\"", OsTypeWindows},
550         {"\\%{ba}\\\\\\^\"", "\\\\\"h\\i ho\"\\\\\\^\"", OsTypeWindows},
551         {"\\%{ca}\\\\\\^\"", "\\\\\"\\hi ho\"\\\\\\^\"", OsTypeWindows},
552         {"\\%{da}\\\\\\^\"", "\\\\\"hi ho\\\\\"\\\\\\^\"", OsTypeWindows},
553         {"\\%{e}\\\\\\^\"", "\\\\\"h\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows},
554         {"\\%{f}\\\\\\^\"", "\\\\\"\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows},
555         {"\\%{g}\\\\\\^\"", "\\\\\"hi\"\\^\"\"\"\\\\\\^\"", OsTypeWindows},
556         {"\\%{h}\\\\\\^\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\\^\"", OsTypeWindows},
557         {"\\%{i}\\\\\\^\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\\^\"", OsTypeWindows},
558         {"\\%{j}\\\\\\^\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\\^\"", OsTypeWindows},
559         {"\\%{x}\\\\\\^\"", "\\\\\\\\\\\\\\^\"", OsTypeWindows},
560         {"\\%{y}\\\\\\^\"", "\\\\\"\"\\^\"\"\"\\\\\\^\"", OsTypeWindows},
561         {"\\%{z}\\\\\\^\"", "\\\\\\\\\\^\"", OsTypeWindows},
562 
563         {"bs-enclosed and trailing unclosed quote", 0, OsTypeWindows},
564         {"\\%{a}\\\\\"", "\\hi\\\\\"", OsTypeWindows},
565         {"\\%{aa}\\\\\"", "\\\\\"hi ho\"\\\\\"", OsTypeWindows},
566         {"\\%{b}\\\\\"", "\\h\\i\\\\\"", OsTypeWindows},
567         {"\\%{c}\\\\\"", "\\\\hi\\\\\"", OsTypeWindows},
568         {"\\%{d}\\\\\"", "\\hi\\\\\\\\\"", OsTypeWindows},
569         {"\\%{ba}\\\\\"", "\\\\\"h\\i ho\"\\\\\"", OsTypeWindows},
570         {"\\%{ca}\\\\\"", "\\\\\"\\hi ho\"\\\\\"", OsTypeWindows},
571         {"\\%{da}\\\\\"", "\\\\\"hi ho\\\\\"\\\\\"", OsTypeWindows},
572         {"\\%{e}\\\\\"", "\\\\\"h\"\\^\"\"i\"\\\\\"", OsTypeWindows},
573         {"\\%{f}\\\\\"", "\\\\\"\"\\^\"\"hi\"\\\\\"", OsTypeWindows},
574         {"\\%{g}\\\\\"", "\\\\\"hi\"\\^\"\"\"\\\\\"", OsTypeWindows},
575         {"\\%{h}\\\\\"", "\\\\\"h\\\\\"\\^\"\"i\"\\\\\"", OsTypeWindows},
576         {"\\%{i}\\\\\"", "\\\\\"\\\\\"\\^\"\"hi\"\\\\\"", OsTypeWindows},
577         {"\\%{j}\\\\\"", "\\\\\"hi\\\\\"\\^\"\"\"\\\\\"", OsTypeWindows},
578         {"\\%{x}\\\\\"", "\\\\\\\\\\\\\"", OsTypeWindows},
579         {"\\%{y}\\\\\"", "\\\\\"\"\\^\"\"\"\\\\\"", OsTypeWindows},
580         {"\\%{z}\\\\\"", "\\\\\\\\\"", OsTypeWindows},
581 
582         {"multi-var", 0, OsTypeWindows},
583         {"%{x}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows},
584         {"%{x}%{z}%{y}%{z}", "\\\\\"\"\\^\"\"\"", OsTypeWindows},
585         {"%{x}%{z}%{y}", "\\\\\"\"\\^\"\"\"", OsTypeWindows},
586         {"%{x}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows},
587         {"%{x}%{z}\\^\"%{z}", "\\\\\\^\"", OsTypeWindows},
588         {"%{x}%{z}\\^\"", "\\\\\\^\"", OsTypeWindows},
589         {"%{x}\\%{z}", "\\\\", OsTypeWindows},
590         {"%{x}%{z}\\%{z}", "\\\\", OsTypeWindows},
591         {"%{x}%{z}\\", "\\\\", OsTypeWindows},
592         {"%{aa}%{a}", "\"hi hohi\"", OsTypeWindows},
593         {"%{aa}%{aa}", "\"hi hohi ho\"", OsTypeWindows},
594         {"%{aa}:%{aa}", "\"hi ho\":\"hi ho\"", OsTypeWindows},
595         {"hallo ^|%{aa}^|", "hallo ^|\"hi ho\"^|", OsTypeWindows},
596 
597         {"quoted multi-var", 0, OsTypeWindows},
598         {"\"%{x}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows},
599         {"\"%{x}%{z}%{y}%{z}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows},
600         {"\"%{x}%{z}%{y}\"", "\"\\\\\"\\^\"\"\"", OsTypeWindows},
601         {"\"%{x}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows},
602         {"\"%{x}%{z}\"^\"\"%{z}\"", "\"\\\\\"^\"\"\"", OsTypeWindows},
603         {"\"%{x}%{z}\"^\"\"\"", "\"\\\\\"^\"\"\"", OsTypeWindows},
604         {"\"%{x}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows},
605         {"\"%{x}%{z}\\%{z}\"", "\"\\\\\\\\\"", OsTypeWindows},
606         {"\"%{x}%{z}\\\\\"", "\"\\\\\\\\\"", OsTypeWindows},
607         {"\"%{aa}%{a}\"", "\"hi hohi\"", OsTypeWindows},
608         {"\"%{aa}%{aa}\"", "\"hi hohi ho\"", OsTypeWindows},
609         {"\"%{aa}:%{aa}\"", "\"hi ho:hi ho\"", OsTypeWindows},
610 
611         {"plain", 0, OsTypeLinux},
612         {"%{a}", "hi", OsTypeLinux},
613         {"%{b}", "'hi ho'", OsTypeLinux},
614         {"%{c}", "'&special;'", OsTypeLinux},
615         {"%{d}", "'h\\i'", OsTypeLinux},
616         {"%{e}", "'h\"i'", OsTypeLinux},
617         {"%{f}", "'h'\\''i'", OsTypeLinux},
618         {"%{z}", "''", OsTypeLinux},
619         {"\\%{z}%{z}", "\\%{z}%{z}", OsTypeLinux}, // stupid user check
620 
621         {"single-quoted", 0, OsTypeLinux},
622         {"'%{a}'", "'hi'", OsTypeLinux},
623         {"'%{b}'", "'hi ho'", OsTypeLinux},
624         {"'%{c}'", "'&special;'", OsTypeLinux},
625         {"'%{d}'", "'h\\i'", OsTypeLinux},
626         {"'%{e}'", "'h\"i'", OsTypeLinux},
627         {"'%{f}'", "'h'\\''i'", OsTypeLinux},
628         {"'%{z}'", "''", OsTypeLinux},
629 
630         {"double-quoted", 0, OsTypeLinux},
631         {"\"%{a}\"", "\"hi\"", OsTypeLinux},
632         {"\"%{b}\"", "\"hi ho\"", OsTypeLinux},
633         {"\"%{c}\"", "\"&special;\"", OsTypeLinux},
634         {"\"%{d}\"", "\"h\\\\i\"", OsTypeLinux},
635         {"\"%{e}\"", "\"h\\\"i\"", OsTypeLinux},
636         {"\"%{f}\"", "\"h'i\"", OsTypeLinux},
637         {"\"%{z}\"", "\"\"", OsTypeLinux},
638 
639         {"complex", 0, OsTypeLinux},
640         {"echo \"$(echo %{a})\"", "echo \"$(echo hi)\"", OsTypeLinux},
641         {"echo \"$(echo %{b})\"", "echo \"$(echo 'hi ho')\"", OsTypeLinux},
642         {"echo \"$(echo \"%{a}\")\"", "echo \"$(echo \"hi\")\"", OsTypeLinux},
643         // These make no sense shell-wise, but they test expando nesting
644         {"echo \"%{echo %{a}}\"", "echo \"%{echo hi}\"", OsTypeLinux},
645         {"echo \"%{echo %{b}}\"", "echo \"%{echo hi ho}\"", OsTypeLinux},
646         {"echo \"%{echo \"%{a}\"}\"", "echo \"%{echo \"hi\"}\"", OsTypeLinux },
647     };
648 
649     const char *title = 0;
650     for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {
651         if (!vals[i].out) {
652             title = vals[i].in;
653         } else {
654             char buf[80];
655             sprintf(buf, "%s: %s", title, vals[i].in);
656             QTest::newRow(buf) << QString::fromLatin1(vals[i].in)
657                                << QString::fromLatin1(vals[i].out)
658                                << vals[i].os;
659             sprintf(buf, "padded %s: %s", title, vals[i].in);
660             QTest::newRow(buf) << QString(sp + QString::fromLatin1(vals[i].in) + sp)
661                                << QString(sp + QString::fromLatin1(vals[i].out) + sp)
662                                << vals[i].os;
663         }
664     }
665 }
666 
expandMacros()667 void tst_QtcProcess::expandMacros()
668 {
669     QFETCH(QString, in);
670     QFETCH(QString, out);
671     QFETCH(OsType, os);
672 
673     if (os == OsTypeWindows)
674         ProcessArgs::expandMacros(&in, &mxWin, os);
675     else
676         ProcessArgs::expandMacros(&in, &mxUnix, os);
677     QCOMPARE(in, out);
678 }
679 
iterations_data()680 void tst_QtcProcess::iterations_data()
681 {
682     QTest::addColumn<QString>("in");
683     QTest::addColumn<QString>("out");
684     QTest::addColumn<OsType>("os");
685 
686     static const struct {
687         const char * const in;
688         const char * const out;
689         OsType os;
690     } vals[] = {
691         {"", "", OsTypeWindows},
692         {"hi", "hi", OsTypeWindows},
693         {"  hi ", "hi", OsTypeWindows},
694         {"hi ho", "hi ho", OsTypeWindows},
695         {"\"hi ho\" sucker", "\"hi ho\" sucker", OsTypeWindows},
696         {"\"hi\"^\"\"ho\" sucker", "\"hi\"\\^\"\"ho\" sucker", OsTypeWindows},
697         {"\"hi\"\\^\"\"ho\" sucker", "\"hi\"\\^\"\"ho\" sucker", OsTypeWindows},
698         {"hi^|ho", "\"hi|ho\"", OsTypeWindows},
699         {"c:\\", "c:\\", OsTypeWindows},
700         {"\"c:\\\\\"", "c:\\", OsTypeWindows},
701         {"\\hi\\ho", "\\hi\\ho", OsTypeWindows},
702         {"hi null%", "hi null%", OsTypeWindows},
703         {"hi null% ho", "hi null% ho", OsTypeWindows},
704         {"hi null%here ho", "hi null%here ho", OsTypeWindows},
705         {"hi null%here%too ho", "hi {} ho", OsTypeWindows},
706         {"echo hello | more", "echo hello", OsTypeWindows},
707         {"echo hello| more", "echo hello", OsTypeWindows},
708 
709         {"", "", OsTypeLinux},
710         {" ", "", OsTypeLinux},
711         {"hi", "hi", OsTypeLinux},
712         {"  hi ", "hi", OsTypeLinux},
713         {"'hi'", "hi", OsTypeLinux},
714         {"hi ho", "hi ho", OsTypeLinux},
715         {"\"hi ho\" sucker", "'hi ho' sucker", OsTypeLinux},
716         {"\"hi\\\"ho\" sucker", "'hi\"ho' sucker", OsTypeLinux},
717         {"\"hi'ho\" sucker", "'hi'\\''ho' sucker", OsTypeLinux},
718         {"'hi ho' sucker", "'hi ho' sucker", OsTypeLinux},
719         {"\\\\", "'\\'", OsTypeLinux},
720         {"'\\'", "'\\'", OsTypeLinux},
721         {"hi 'null${here}too' ho", "hi 'null${here}too' ho", OsTypeLinux},
722         {"hi null${here}too ho", "hi {} ho", OsTypeLinux},
723         {"hi $(echo $dollar cent) ho", "hi {} ho", OsTypeLinux},
724         {"hi `echo $dollar \\`echo cent\\` | cat` ho", "hi {} ho", OsTypeLinux},
725         {"echo hello | more", "echo hello", OsTypeLinux},
726         {"echo hello| more", "echo hello", OsTypeLinux},
727     };
728 
729     for (unsigned i = 0; i < sizeof(vals)/sizeof(vals[0]); i++)
730         QTest::newRow(vals[i].in) << QString::fromLatin1(vals[i].in)
731                                   << QString::fromLatin1(vals[i].out)
732                                   << vals[i].os;
733 }
734 
iterations()735 void tst_QtcProcess::iterations()
736 {
737     QFETCH(QString, in);
738     QFETCH(QString, out);
739     QFETCH(OsType, os);
740 
741     QString outstr;
742     for (ProcessArgs::ArgIterator ait(&in, os); ait.next(); ) {
743         if (ait.isSimple())
744             ProcessArgs::addArg(&outstr, ait.value(), os);
745         else
746             ProcessArgs::addArgs(&outstr, "{}");
747     }
748     QCOMPARE(outstr, out);
749 }
750 
iteratorEditsHelper(OsType osType)751 void tst_QtcProcess::iteratorEditsHelper(OsType osType)
752 {
753     QString in1 = "one two three", in2 = in1, in3 = in1, in4 = in1, in5 = in1;
754 
755     ProcessArgs::ArgIterator ait1(&in1, osType);
756     QVERIFY(ait1.next());
757     ait1.deleteArg();
758     QVERIFY(ait1.next());
759     QVERIFY(ait1.next());
760     QVERIFY(!ait1.next());
761     QCOMPARE(in1, QString::fromLatin1("two three"));
762     ait1.appendArg("four");
763     QCOMPARE(in1, QString::fromLatin1("two three four"));
764 
765     ProcessArgs::ArgIterator ait2(&in2, osType);
766     QVERIFY(ait2.next());
767     QVERIFY(ait2.next());
768     ait2.deleteArg();
769     QVERIFY(ait2.next());
770     ait2.appendArg("four");
771     QVERIFY(!ait2.next());
772     QCOMPARE(in2, QString::fromLatin1("one three four"));
773 
774     ProcessArgs::ArgIterator ait3(&in3, osType);
775     QVERIFY(ait3.next());
776     ait3.appendArg("one-b");
777     QVERIFY(ait3.next());
778     QVERIFY(ait3.next());
779     ait3.deleteArg();
780     QVERIFY(!ait3.next());
781     QCOMPARE(in3, QString::fromLatin1("one one-b two"));
782 
783     ProcessArgs::ArgIterator ait4(&in4, osType);
784     ait4.appendArg("pre-one");
785     QVERIFY(ait4.next());
786     QVERIFY(ait4.next());
787     QVERIFY(ait4.next());
788     ait4.deleteArg();
789     QVERIFY(!ait4.next());
790     QCOMPARE(in4, QString::fromLatin1("pre-one one two"));
791 
792     ProcessArgs::ArgIterator ait5(&in5, osType);
793     QVERIFY(ait5.next());
794     QVERIFY(ait5.next());
795     QVERIFY(ait5.next());
796     QVERIFY(!ait5.next());
797     ait5.deleteArg();
798     QVERIFY(!ait5.next());
799     QCOMPARE(in5, QString::fromLatin1("one two"));
800 }
801 
iteratorEditsWindows()802 void tst_QtcProcess::iteratorEditsWindows()
803 {
804     iteratorEditsHelper(OsTypeWindows);
805 }
806 
iteratorEditsLinux()807 void tst_QtcProcess::iteratorEditsLinux()
808 {
809     iteratorEditsHelper(OsTypeLinux);
810 }
811 
exitCode_data()812 void tst_QtcProcess::exitCode_data()
813 {
814     QTest::addColumn<int>("exitCode");
815 
816     static const auto exitCodes = {
817 #ifdef Q_OS_WIN
818         "99999999", "-255", "-1",
819 #endif // Q_OS_WIN
820         "0", "1", "255"
821     };
822     for (auto exitCode : exitCodes)
823         QTest::newRow(exitCode) << QString::fromLatin1(exitCode).toInt();
824 }
825 
exitCode()826 void tst_QtcProcess::exitCode()
827 {
828     QFETCH(int, exitCode);
829 
830     Environment env = Environment::systemEnvironment();
831     env.set(kExitCodeSubProcessCode, QString::number(exitCode));
832     QStringList args = QCoreApplication::arguments();
833     const QString binary = args.takeFirst();
834     const CommandLine command(binary, args);
835 
836     {
837         QtcProcess qtcP;
838         qtcP.setCommand(command);
839         qtcP.setEnvironment(env);
840         qtcP.start();
841         const bool finished = qtcP.waitForFinished();
842 
843         QVERIFY(finished);
844         QCOMPARE(qtcP.exitCode(), exitCode);
845         QCOMPARE(qtcP.exitCode() == 0, qtcP.result() == QtcProcess::FinishedWithSuccess);
846     }
847     {
848         QtcProcess sP;
849         sP.setCommand(command);
850         sP.setEnvironment(env);
851         sP.runBlocking();
852 
853         QCOMPARE(sP.exitCode(), exitCode);
854         QCOMPARE(sP.exitCode() == 0, sP.result() == QtcProcess::FinishedWithSuccess);
855     }
856 }
857 
runBlockingStdOut_data()858 void tst_QtcProcess::runBlockingStdOut_data()
859 {
860     QTest::addColumn<bool>("withEndl");
861     QTest::addColumn<int>("timeOutS");
862 
863     QTest::newRow("Terminated stdout delivered instantly")
864             << true
865             << 2;
866     QTest::newRow("Unterminated stdout lost: early timeout")
867             << false
868             << 2;
869     QTest::newRow("Unterminated stdout lost: hanging")
870             << false
871             << 20;
872 }
873 
runBlockingStdOut()874 void tst_QtcProcess::runBlockingStdOut()
875 {
876     QFETCH(bool, withEndl);
877     QFETCH(int, timeOutS);
878 
879     QtcProcess sp;
880     QStringList args = QCoreApplication::arguments();
881     const QString binary = args.takeFirst();
882     sp.setCommand(CommandLine(binary, args));
883     Environment env = Environment::systemEnvironment();
884     env.set(kRunBlockingStdOutSubProcessWithEndl, withEndl ? "true" : "false");
885     sp.setEnvironment(env);
886     sp.setTimeoutS(timeOutS);
887     bool readLastLine = false;
888     sp.setStdOutCallback([&readLastLine, &sp](const QString &out) {
889         if (out.startsWith(kRunBlockingStdOutSubProcessMagicWord)) {
890             readLastLine = true;
891             sp.kill();
892         }
893     });
894     sp.runBlocking();
895 
896     // See also QTCREATORBUG-25667 for why it is a bad idea to use QtcProcess::runBlocking
897     // with interactive cli tools.
898     QEXPECT_FAIL("Unterminated stdout lost: early timeout", "", Continue);
899     QVERIFY2(sp.result() != QtcProcess::Hang, "Process run did not time out.");
900     QEXPECT_FAIL("Unterminated stdout lost: early timeout", "", Continue);
901     QVERIFY2(readLastLine, "Last line was read.");
902 }
903 
lineCallback()904 void tst_QtcProcess::lineCallback()
905 {
906     QtcProcess process;
907     QStringList args = QCoreApplication::arguments();
908     const QString binary = args.takeFirst();
909     process.setCommand(CommandLine(binary, args));
910     Environment env = Environment::systemEnvironment();
911     env.set(kLineCallback, "Yes");
912     process.setEnvironment(env);
913     QStringList lines = QString(lineCallbackData).split('|');
914     int lineNumber = 0;
915     process.setStdErrLineCallback([lines, &lineNumber](const QString &actual) {
916         QString expected = lines.at(lineNumber++);
917         expected.replace("\r\n", "\n");
918         QCOMPARE(actual, expected);
919     });
920     process.start();
921     process.waitForFinished();
922     QCOMPARE(lineNumber, lines.size());
923 }
924 
lineCallbackIntern()925 void tst_QtcProcess::lineCallbackIntern()
926 {
927     QtcProcess process;
928     QStringList lines = QString(lineCallbackData).split('|');
929     int lineNumber = 0;
930     process.setStdOutLineCallback([lines, &lineNumber](const QString &actual) {
931         QString expected = lines.at(lineNumber++);
932         expected.replace("\r\n", "\n");
933         QCOMPARE(actual, expected);
934     });
935     process.beginFeed();
936     process.feedStdOut(QByteArray(lineCallbackData).replace('|', ""));
937     process.endFeed();
938     QCOMPARE(lineNumber, lines.size());
939 }
940 
941 QTEST_MAIN(tst_QtcProcess)
942 
943 #include "tst_qtcprocess.moc"
944