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