1/****************************************************************************
2**
3** Copyright (C) 2013 David Faure <faure@kde.org>
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <QtCore/QCommandLineParser>
31
32Q_DECLARE_METATYPE(char**)
33Q_DECLARE_METATYPE(QCommandLineParser::OptionsAfterPositionalArgumentsMode)
34
35class tst_QCommandLineParser : public QObject
36{
37 Q_OBJECT
38
39public slots:
40 void initTestCase();
41
42private slots:
43 void parsingModes_data();
44
45 // In-process tests
46 void testInvalidOptions();
47 void testDuplicateOption();
48 void testPositionalArguments();
49 void testBooleanOption_data();
50 void testBooleanOption();
51 void testOptionsAndPositional_data();
52 void testOptionsAndPositional();
53 void testMultipleNames_data();
54 void testMultipleNames();
55 void testSingleValueOption_data();
56 void testSingleValueOption();
57 void testValueNotSet();
58 void testMultipleValuesOption();
59 void testUnknownOptionErrorHandling_data();
60 void testUnknownOptionErrorHandling();
61 void testDoubleDash_data();
62 void testDoubleDash();
63 void testDefaultValue();
64 void testProcessNotCalled();
65 void testEmptyArgsList();
66 void testMissingOptionValue();
67 void testStdinArgument_data();
68 void testStdinArgument();
69 void testSingleDashWordOptionModes_data();
70 void testSingleDashWordOptionModes();
71 void testCpp11StyleInitialization();
72
73 // QProcess-based tests using qcommandlineparser_test_helper
74 void testVersionOption();
75 void testHelpOption_data();
76 void testHelpOption();
77 void testQuoteEscaping();
78 void testUnknownOption();
79 void testHelpAll_data();
80 void testHelpAll();
81 void testVeryLongOptionNames();
82};
83
84static char *empty_argv[] = { 0 };
85static int empty_argc = 1;
86
87void tst_QCommandLineParser::initTestCase()
88{
89 Q_ASSERT(!empty_argv[0]);
90 empty_argv[0] = const_cast<char*>(QTest::currentAppName());
91}
92
93Q_DECLARE_METATYPE(QCommandLineParser::SingleDashWordOptionMode)
94
95void tst_QCommandLineParser::parsingModes_data()
96{
97 QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>(name: "parsingMode");
98
99 QTest::newRow(dataTag: "collapsed") << QCommandLineParser::ParseAsCompactedShortOptions;
100 QTest::newRow(dataTag: "implicitlylong") << QCommandLineParser::ParseAsLongOptions;
101}
102
103void tst_QCommandLineParser::testInvalidOptions()
104{
105 QCoreApplication app(empty_argc, empty_argv);
106 QCommandLineParser parser;
107 QTest::ignoreMessage(type: QtWarningMsg, message: "QCommandLineOption: Option names cannot start with a '-'");
108 QVERIFY(!parser.addOption(QCommandLineOption(QStringLiteral("-v"), QStringLiteral("Displays version information."))));
109}
110
111void tst_QCommandLineParser::testDuplicateOption()
112{
113 QCoreApplication app(empty_argc, empty_argv);
114 QCommandLineParser parser;
115 QVERIFY(parser.addOption(QCommandLineOption(QStringLiteral("h"), QStringLiteral("Hostname."), QStringLiteral("hostname"))));
116 QTest::ignoreMessage(type: QtWarningMsg, message: "QCommandLineParser: already having an option named \"h\"");
117 parser.addHelpOption();
118}
119
120void tst_QCommandLineParser::testPositionalArguments()
121{
122 QCoreApplication app(empty_argc, empty_argv);
123 QCommandLineParser parser;
124 QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "file.txt"));
125 QCOMPARE(parser.positionalArguments(), QStringList() << QStringLiteral("file.txt"));
126}
127
128void tst_QCommandLineParser::testBooleanOption_data()
129{
130 QTest::addColumn<QStringList>(name: "args");
131 QTest::addColumn<QStringList>(name: "expectedOptionNames");
132 QTest::addColumn<bool>(name: "expectedIsSet");
133
134 QTest::newRow(dataTag: "set") << (QStringList() << "tst_qcommandlineparser" << "-b") << (QStringList() << "b") << true;
135 QTest::newRow(dataTag: "unset") << (QStringList() << "tst_qcommandlineparser") << QStringList() << false;
136}
137
138void tst_QCommandLineParser::testBooleanOption()
139{
140 QFETCH(QStringList, args);
141 QFETCH(QStringList, expectedOptionNames);
142 QFETCH(bool, expectedIsSet);
143 QCoreApplication app(empty_argc, empty_argv);
144 QCommandLineParser parser;
145 QVERIFY(parser.addOption(QCommandLineOption(QStringLiteral("b"))));
146 QVERIFY(parser.parse(args));
147 QCOMPARE(parser.optionNames(), expectedOptionNames);
148 QCOMPARE(parser.isSet("b"), expectedIsSet);
149 QCOMPARE(parser.values("b"), QStringList());
150 QCOMPARE(parser.positionalArguments(), QStringList());
151 // Should warn on typos
152 QTest::ignoreMessage(type: QtWarningMsg, message: "QCommandLineParser: option not defined: \"c\"");
153 QVERIFY(!parser.isSet("c"));
154}
155
156void tst_QCommandLineParser::testOptionsAndPositional_data()
157{
158 QTest::addColumn<QStringList>(name: "args");
159 QTest::addColumn<QStringList>(name: "expectedOptionNames");
160 QTest::addColumn<bool>(name: "expectedIsSet");
161 QTest::addColumn<QStringList>(name: "expectedPositionalArguments");
162 QTest::addColumn<QCommandLineParser::OptionsAfterPositionalArgumentsMode>(name: "parsingMode");
163
164 const QStringList arg = QStringList() << "arg";
165 QTest::newRow(dataTag: "before_positional_default") << (QStringList() << "tst_qcommandlineparser" << "-b" << "arg") << (QStringList() << "b") << true << arg << QCommandLineParser::ParseAsOptions;
166 QTest::newRow(dataTag: "after_positional_default") << (QStringList() << "tst_qcommandlineparser" << "arg" << "-b") << (QStringList() << "b") << true << arg << QCommandLineParser::ParseAsOptions;
167 QTest::newRow(dataTag: "before_positional_parseAsArg") << (QStringList() << "tst_qcommandlineparser" << "-b" << "arg") << (QStringList() << "b") << true << arg << QCommandLineParser::ParseAsPositionalArguments;
168 QTest::newRow(dataTag: "after_positional_parseAsArg") << (QStringList() << "tst_qcommandlineparser" << "arg" << "-b") << (QStringList()) << false << (QStringList() << "arg" << "-b") << QCommandLineParser::ParseAsPositionalArguments;
169}
170
171void tst_QCommandLineParser::testOptionsAndPositional()
172{
173 QFETCH(QStringList, args);
174 QFETCH(QStringList, expectedOptionNames);
175 QFETCH(bool, expectedIsSet);
176 QFETCH(QStringList, expectedPositionalArguments);
177 QFETCH(QCommandLineParser::OptionsAfterPositionalArgumentsMode, parsingMode);
178
179 QCoreApplication app(empty_argc, empty_argv);
180 QCommandLineParser parser;
181 parser.setOptionsAfterPositionalArgumentsMode(parsingMode);
182 QVERIFY(parser.addOption(QCommandLineOption(QStringLiteral("b"), QStringLiteral("a boolean option"))));
183 QVERIFY(parser.parse(args));
184 QCOMPARE(parser.optionNames(), expectedOptionNames);
185 QCOMPARE(parser.isSet("b"), expectedIsSet);
186 QCOMPARE(parser.values("b"), QStringList());
187 QCOMPARE(parser.positionalArguments(), expectedPositionalArguments);
188}
189
190void tst_QCommandLineParser::testMultipleNames_data()
191{
192 QTest::addColumn<QStringList>(name: "args");
193 QTest::addColumn<QStringList>(name: "expectedOptionNames");
194
195 QTest::newRow(dataTag: "short") << (QStringList() << "tst_qcommandlineparser" << "-v") << (QStringList() << "v");
196 QTest::newRow(dataTag: "long") << (QStringList() << "tst_qcommandlineparser" << "--version") << (QStringList() << "version");
197 QTest::newRow(dataTag: "not_set") << (QStringList() << "tst_qcommandlineparser") << QStringList();
198}
199
200void tst_QCommandLineParser::testMultipleNames()
201{
202 QFETCH(QStringList, args);
203 QFETCH(QStringList, expectedOptionNames);
204 QCoreApplication app(empty_argc, empty_argv);
205 QCommandLineOption option(QStringList() << "v" << "version", QStringLiteral("Show version information"));
206 QCOMPARE(option.names(), QStringList() << "v" << "version");
207 QCommandLineParser parser;
208 QVERIFY(parser.addOption(option));
209 QVERIFY(parser.parse(args));
210 QCOMPARE(parser.optionNames(), expectedOptionNames);
211 const bool expectedIsSet = !expectedOptionNames.isEmpty();
212 QCOMPARE(parser.isSet("v"), expectedIsSet);
213 QCOMPARE(parser.isSet("version"), expectedIsSet);
214}
215
216void tst_QCommandLineParser::testSingleValueOption_data()
217{
218 QTest::addColumn<QStringList>(name: "args");
219 QTest::addColumn<QStringList>(name: "defaults");
220 QTest::addColumn<bool>(name: "expectedIsSet");
221
222 QTest::newRow(dataTag: "short") << (QStringList() << "tst" << "-s" << "oxygen") << QStringList() << true;
223 QTest::newRow(dataTag: "long") << (QStringList() << "tst" << "--style" << "oxygen") << QStringList() << true;
224 QTest::newRow(dataTag: "longequal") << (QStringList() << "tst" << "--style=oxygen") << QStringList() << true;
225 QTest::newRow(dataTag: "default") << (QStringList() << "tst") << (QStringList() << "oxygen") << false;
226}
227
228void tst_QCommandLineParser::testSingleValueOption()
229{
230 QFETCH(QStringList, args);
231 QFETCH(QStringList, defaults);
232 QFETCH(bool, expectedIsSet);
233 QCoreApplication app(empty_argc, empty_argv);
234 QCommandLineParser parser;
235 QCommandLineOption option(QStringList() << "s" << "style", QStringLiteral("style name"), "styleName");
236 option.setDefaultValues(defaults);
237 QVERIFY(parser.addOption(option));
238 for (int mode = 0; mode < 2; ++mode) {
239 parser.setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode(mode));
240 QVERIFY(parser.parse(args));
241 QCOMPARE(parser.isSet("s"), expectedIsSet);
242 QCOMPARE(parser.isSet("style"), expectedIsSet);
243 QCOMPARE(parser.isSet(option), expectedIsSet);
244 QCOMPARE(parser.value("s"), QString("oxygen"));
245 QCOMPARE(parser.value("style"), QString("oxygen"));
246 QCOMPARE(parser.values("s"), QStringList() << "oxygen");
247 QCOMPARE(parser.values("style"), QStringList() << "oxygen");
248 QCOMPARE(parser.values(option), QStringList() << "oxygen");
249 QCOMPARE(parser.positionalArguments(), QStringList());
250 }
251 // Should warn on typos
252 QTest::ignoreMessage(type: QtWarningMsg, message: "QCommandLineParser: option not defined: \"c\"");
253 QVERIFY(parser.values("c").isEmpty());
254}
255
256void tst_QCommandLineParser::testValueNotSet()
257{
258 QCoreApplication app(empty_argc, empty_argv);
259 // Not set, no default value
260 QCommandLineParser parser;
261 QCommandLineOption option(QStringList() << "s" << "style", QStringLiteral("style name"));
262 option.setValueName("styleName");
263 QVERIFY(parser.addOption(option));
264 QVERIFY(parser.parse(QStringList() << "tst"));
265 QCOMPARE(parser.optionNames(), QStringList());
266 QVERIFY(!parser.isSet("s"));
267 QVERIFY(!parser.isSet("style"));
268 QCOMPARE(parser.value("s"), QString());
269 QCOMPARE(parser.value("style"), QString());
270 QCOMPARE(parser.values("s"), QStringList());
271 QCOMPARE(parser.values("style"), QStringList());
272}
273
274void tst_QCommandLineParser::testMultipleValuesOption()
275{
276 QCoreApplication app(empty_argc, empty_argv);
277 QCommandLineOption option(QStringLiteral("param"), QStringLiteral("Pass parameter to the backend."));
278 option.setValueName("key=value");
279 QCommandLineParser parser;
280 QVERIFY(parser.addOption(option));
281 {
282 QVERIFY(parser.parse(QStringList() << "tst" << "--param" << "key1=value1"));
283 QVERIFY(parser.isSet("param"));
284 QCOMPARE(parser.values("param"), QStringList() << "key1=value1");
285 QCOMPARE(parser.value("param"), QString("key1=value1"));
286 }
287 {
288 QVERIFY(parser.parse(QStringList() << "tst" << "--param" << "key1=value1" << "--param" << "key2=value2"));
289 QVERIFY(parser.isSet("param"));
290 QCOMPARE(parser.values("param"), QStringList() << "key1=value1" << "key2=value2");
291 QCOMPARE(parser.value("param"), QString("key2=value2"));
292 }
293
294 QString expected =
295 "Usage: tst_qcommandlineparser [options]\n"
296 "\n"
297 "Options:\n"
298 " --param <key=value> Pass parameter to the backend.\n";
299
300 const QString exeName = QCoreApplication::instance()->arguments().first(); // e.g. debug\tst_qcommandlineparser.exe on Windows
301 expected.replace(QStringLiteral("tst_qcommandlineparser"), after: exeName);
302 QCOMPARE(parser.helpText(), expected);
303}
304
305void tst_QCommandLineParser::testUnknownOptionErrorHandling_data()
306{
307 QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>(name: "parsingMode");
308 QTest::addColumn<QStringList>(name: "args");
309 QTest::addColumn<QStringList>(name: "expectedUnknownOptionNames");
310 QTest::addColumn<QString>(name: "expectedErrorText");
311
312 const QStringList args_hello = QStringList() << "tst_qcommandlineparser" << "--hello";
313 const QString error_hello("Unknown option 'hello'.");
314 QTest::newRow(dataTag: "unknown_name_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_hello << QStringList("hello") << error_hello;
315 QTest::newRow(dataTag: "unknown_name_long") << QCommandLineParser::ParseAsLongOptions << args_hello << QStringList("hello") << error_hello;
316
317 const QStringList args_value = QStringList() << "tst_qcommandlineparser" << "-b=1";
318 QTest::newRow(dataTag: "bool_with_value_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_value << QStringList() << QString("Unexpected value after '-b'.");
319 QTest::newRow(dataTag: "bool_with_value_long") << QCommandLineParser::ParseAsLongOptions << args_value << QStringList() << QString("Unexpected value after '-b'.");
320
321 const QStringList args_dash_long = QStringList() << "tst_qcommandlineparser" << "-bool";
322 const QString error_bool("Unknown options: o, o, l.");
323 QTest::newRow(dataTag: "unknown_name_long_collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << args_dash_long << (QStringList() << "o" << "o" << "l") << error_bool;
324}
325
326void tst_QCommandLineParser::testUnknownOptionErrorHandling()
327{
328 QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
329 QFETCH(QStringList, args);
330 QFETCH(QStringList, expectedUnknownOptionNames);
331 QFETCH(QString, expectedErrorText);
332
333 QCoreApplication app(empty_argc, empty_argv);
334 QCommandLineParser parser;
335 parser.setSingleDashWordOptionMode(parsingMode);
336 QVERIFY(parser.addOption(QCommandLineOption(QStringList() << "b" << "bool", QStringLiteral("a boolean option"))));
337 QCOMPARE(parser.parse(args), expectedErrorText.isEmpty());
338 QCOMPARE(parser.unknownOptionNames(), expectedUnknownOptionNames);
339 QCOMPARE(parser.errorText(), expectedErrorText);
340}
341
342void tst_QCommandLineParser::testDoubleDash_data()
343{
344 parsingModes_data();
345}
346
347void tst_QCommandLineParser::testDoubleDash()
348{
349 QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
350
351 QCoreApplication app(empty_argc, empty_argv);
352 QCommandLineParser parser;
353 QVERIFY(parser.addOption(QCommandLineOption(QStringList() << "o" << "output", QStringLiteral("Output file"), QStringLiteral("filename"))));
354 parser.setSingleDashWordOptionMode(parsingMode);
355 QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--output" << "foo"));
356 QCOMPARE(parser.value("output"), QString("foo"));
357 QCOMPARE(parser.positionalArguments(), QStringList());
358 QCOMPARE(parser.unknownOptionNames(), QStringList());
359 QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--" << "--output" << "bar" << "-b" << "bleh"));
360 QCOMPARE(parser.value("output"), QString());
361 QCOMPARE(parser.positionalArguments(), QStringList() << "--output" << "bar" << "-b" << "bleh");
362 QCOMPARE(parser.unknownOptionNames(), QStringList());
363}
364
365void tst_QCommandLineParser::testDefaultValue()
366{
367 QCommandLineOption opt(QStringLiteral("name"), QStringLiteral("desc"),
368 QStringLiteral("valueName"), QStringLiteral("default"));
369 QCOMPARE(opt.defaultValues(), QStringList(QStringLiteral("default")));
370 opt.setDefaultValue(QStringLiteral(""));
371 QCOMPARE(opt.defaultValues(), QStringList());
372 opt.setDefaultValue(QStringLiteral("default"));
373 QCOMPARE(opt.defaultValues(), QStringList(QStringLiteral("default")));
374}
375
376void tst_QCommandLineParser::testProcessNotCalled()
377{
378 QCoreApplication app(empty_argc, empty_argv);
379 QCommandLineParser parser;
380 QVERIFY(parser.addOption(QCommandLineOption(QStringLiteral("b"), QStringLiteral("a boolean option"))));
381 QTest::ignoreMessage(type: QtWarningMsg, message: "QCommandLineParser: call process() or parse() before isSet");
382 QVERIFY(!parser.isSet("b"));
383 QTest::ignoreMessage(type: QtWarningMsg, message: "QCommandLineParser: call process() or parse() before values");
384 QCOMPARE(parser.values("b"), QStringList());
385}
386
387void tst_QCommandLineParser::testEmptyArgsList()
388{
389 QCoreApplication app(empty_argc, empty_argv);
390 QCommandLineParser parser;
391 QTest::ignoreMessage(type: QtWarningMsg, message: "QCommandLineParser: argument list cannot be empty, it should contain at least the executable name");
392 QVERIFY(!parser.parse(QStringList())); // invalid call, argv[0] is missing
393}
394
395void tst_QCommandLineParser::testMissingOptionValue()
396{
397 QCoreApplication app(empty_argc, empty_argv);
398 QCommandLineParser parser;
399 QVERIFY(parser.addOption(QCommandLineOption(QStringLiteral("option"), QStringLiteral("An option"), "value")));
400 QVERIFY(!parser.parse(QStringList() << "argv0" << "--option")); // the user forgot to pass a value for --option
401 QCOMPARE(parser.value("option"), QString());
402 QCOMPARE(parser.errorText(), QString("Missing value after '--option'."));
403}
404
405void tst_QCommandLineParser::testStdinArgument_data()
406{
407 parsingModes_data();
408}
409
410void tst_QCommandLineParser::testStdinArgument()
411{
412 QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
413
414 QCoreApplication app(empty_argc, empty_argv);
415 QCommandLineParser parser;
416 parser.setSingleDashWordOptionMode(parsingMode);
417 QVERIFY(parser.addOption(QCommandLineOption(QStringList() << "i" << "input", QStringLiteral("Input file."), QStringLiteral("filename"))));
418 QVERIFY(parser.addOption(QCommandLineOption("b", QStringLiteral("Boolean option."))));
419 QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--input" << "-"));
420 QCOMPARE(parser.value("input"), QString("-"));
421 QCOMPARE(parser.positionalArguments(), QStringList());
422 QCOMPARE(parser.unknownOptionNames(), QStringList());
423
424 QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "--input" << "-" << "-b" << "arg"));
425 QCOMPARE(parser.value("input"), QString("-"));
426 QVERIFY(parser.isSet("b"));
427 QCOMPARE(parser.positionalArguments(), QStringList() << "arg");
428 QCOMPARE(parser.unknownOptionNames(), QStringList());
429
430 QVERIFY(parser.parse(QStringList() << "tst_qcommandlineparser" << "-"));
431 QCOMPARE(parser.value("input"), QString());
432 QVERIFY(!parser.isSet("b"));
433 QCOMPARE(parser.positionalArguments(), QStringList() << "-");
434 QCOMPARE(parser.unknownOptionNames(), QStringList());
435}
436
437void tst_QCommandLineParser::testSingleDashWordOptionModes_data()
438{
439 QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>(name: "parsingMode");
440 QTest::addColumn<QStringList>(name: "commandLine");
441 QTest::addColumn<QStringList>(name: "expectedOptionNames");
442 QTest::addColumn<QStringList>(name: "expectedOptionValues");
443
444 QTest::newRow(dataTag: "collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-abc" << "val")
445 << (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val");
446 QTest::newRow(dataTag: "collapsed_with_equalsign_value") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-abc=val")
447 << (QStringList() << "a" << "b" << "c") << (QStringList() << QString() << QString() << "val");
448 QTest::newRow(dataTag: "collapsed_explicit_longoption") << QCommandLineParser::ParseAsCompactedShortOptions << QStringList("--nn")
449 << QStringList("nn") << QStringList();
450 QTest::newRow(dataTag: "collapsed_longoption_value") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "--abc" << "val")
451 << QStringList("abc") << QStringList("val");
452 QTest::newRow(dataTag: "compiler") << QCommandLineParser::ParseAsCompactedShortOptions << QStringList("-cab")
453 << QStringList("c") << QStringList("ab");
454 QTest::newRow(dataTag: "compiler_with_space") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-c" << "val")
455 << QStringList("c") << QStringList("val");
456
457 QTest::newRow(dataTag: "implicitlylong") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-abc" << "val")
458 << QStringList("abc") << QStringList("val");
459 QTest::newRow(dataTag: "implicitlylong_equal") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-abc=val")
460 << QStringList("abc") << QStringList("val");
461 QTest::newRow(dataTag: "implicitlylong_longoption") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "--nn")
462 << QStringList("nn") << QStringList();
463 QTest::newRow(dataTag: "implicitlylong_longoption_value") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "--abc" << "val")
464 << QStringList("abc") << QStringList("val");
465 QTest::newRow(dataTag: "implicitlylong_with_space") << QCommandLineParser::ParseAsCompactedShortOptions << (QStringList() << "-c" << "val")
466 << QStringList("c") << QStringList("val");
467
468 QTest::newRow(dataTag: "forceshort_detached") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-I" << "45")
469 << QStringList("I") << QStringList("45");
470 QTest::newRow(dataTag: "forceshort_attached") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-I46")
471 << QStringList("I") << QStringList("46");
472 QTest::newRow(dataTag: "forceshort_mixed") << QCommandLineParser::ParseAsLongOptions << (QStringList() << "-I45" << "-nn")
473 << (QStringList() << "I" << "nn") << QStringList("45");
474}
475
476void tst_QCommandLineParser::testSingleDashWordOptionModes()
477{
478 QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
479 QFETCH(QStringList, commandLine);
480 QFETCH(QStringList, expectedOptionNames);
481 QFETCH(QStringList, expectedOptionValues);
482
483 commandLine.prepend(t: "tst_QCommandLineParser");
484
485 QCoreApplication app(empty_argc, empty_argv);
486 QCommandLineParser parser;
487 parser.setSingleDashWordOptionMode(parsingMode);
488 QVERIFY(parser.addOption(QCommandLineOption("a", QStringLiteral("a option."))));
489 QVERIFY(parser.addOption(QCommandLineOption("b", QStringLiteral("b option."))));
490 QVERIFY(parser.addOption(QCommandLineOption(QStringList() << "c" << "abc", QStringLiteral("c option."), QStringLiteral("value"))));
491 QVERIFY(parser.addOption(QCommandLineOption("nn", QStringLiteral("nn option."))));
492 QCommandLineOption forceShort(QStringLiteral("I"), QStringLiteral("always short option"),
493 QStringLiteral("path"), QStringLiteral("default"));
494 forceShort.setFlags(QCommandLineOption::ShortOptionStyle);
495 QVERIFY(parser.addOption(forceShort));
496 QVERIFY(parser.parse(commandLine));
497 QCOMPARE(parser.optionNames(), expectedOptionNames);
498 for (int i = 0; i < expectedOptionValues.count(); ++i)
499 QCOMPARE(parser.value(parser.optionNames().at(i)), expectedOptionValues.at(i));
500 QCOMPARE(parser.unknownOptionNames(), QStringList());
501}
502
503void tst_QCommandLineParser::testCpp11StyleInitialization()
504{
505#if defined(Q_COMPILER_UNIFORM_INIT)
506 QCoreApplication app(empty_argc, empty_argv);
507
508 QCommandLineParser parser;
509 // primarily check that this compiles:
510 QVERIFY(parser.addOptions({
511 { "a", "The A option." },
512 { { "v", "verbose" }, "The verbose option." },
513 { { "i", "infile" }, "The input file.", "value" },
514 }));
515 // but do a very basic functionality test, too:
516 QVERIFY(parser.parse({"tst_QCommandLineParser", "-a", "-vvv", "--infile=in.txt"}));
517 QCOMPARE(parser.optionNames(), (QStringList{"a", "v", "v", "v", "infile"}));
518 QCOMPARE(parser.value("infile"), QString("in.txt"));
519#else
520 QSKIP("This test requires C++11 uniform initialization support in the compiler.");
521#endif
522}
523
524void tst_QCommandLineParser::testVersionOption()
525{
526#if !QT_CONFIG(process)
527 QSKIP("This test requires QProcess support");
528#else
529#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
530 QSKIP("Deploying executable applications to file system on Android not supported.");
531#endif
532
533 QCoreApplication app(empty_argc, empty_argv);
534 QProcess process;
535 process.start(program: "testhelper/qcommandlineparser_test_helper", arguments: QStringList() << "0" << "--version");
536 QVERIFY(process.waitForFinished(5000));
537 QCOMPARE(process.exitStatus(), QProcess::NormalExit);
538 QString output = process.readAll();
539#ifdef Q_OS_WIN
540 output.replace(QStringLiteral("\r\n"), QStringLiteral("\n"));
541#endif
542 QCOMPARE(output, QString("qcommandlineparser_test_helper 1.0\n"));
543#endif // QT_CONFIG(process)
544}
545
546static const char expectedOptionsHelp[] =
547 "Options:\n"
548 " -h, --help Displays help on commandline options.\n"
549 " --help-all Displays help including Qt specific options.\n"
550 " -v, --version Displays version information.\n"
551 " --load <url> Load file from URL.\n"
552 " -o, --output <file> Set output file.\n"
553 " -D <key=value> Define macro.\n"
554 " --long-option\n"
555 " -n, --no-implicit-includes Disable magic generation of implicit\n"
556 " #include-directives.\n"
557 " --newline This is an option with a rather long\n"
558 " description using explicit newline characters (but\n"
559 " testing automatic wrapping too). In addition,\n"
560 " here, we test breaking after a comma. Testing\n"
561 " -option. Long URL:\n"
562 " http://qt-project.org/wiki/How_to_create_a_library\n"
563 " _with_Qt_and_use_it_in_an_application\n"
564 " abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx\n"
565 " yzabcdefghijklmnopqrstuvwxyz\n";
566
567void tst_QCommandLineParser::testHelpOption_data()
568{
569 QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>(name: "parsingMode");
570 QTest::addColumn<QString>(name: "expectedHelpOutput");
571
572 QString expectedOutput = QString::fromLatin1(
573 str: "Usage: testhelper/qcommandlineparser_test_helper [options] parsingMode command\n"
574 "Test helper\n"
575 "\n")
576 + QString::fromLatin1(str: expectedOptionsHelp) +
577 QString::fromLatin1(
578 str: "\n"
579 "Arguments:\n"
580 " parsingMode The parsing mode to test.\n"
581 " command The command to execute.\n");
582#ifdef Q_OS_WIN
583 expectedOutput.replace(" -h, --help Displays help on commandline options.\n",
584 " -?, -h, --help Displays help on commandline options.\n");
585 expectedOutput.replace("testhelper/", "testhelper\\");
586#endif
587
588 QTest::newRow(dataTag: "collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << expectedOutput;
589 QTest::newRow(dataTag: "long") << QCommandLineParser::ParseAsLongOptions << expectedOutput;
590}
591
592void tst_QCommandLineParser::testHelpOption()
593{
594#if !QT_CONFIG(process)
595 QSKIP("This test requires QProcess support");
596#else
597#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
598 QSKIP("Deploying executable applications to file system on Android not supported.");
599#endif
600
601 QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
602 QFETCH(QString, expectedHelpOutput);
603 QCoreApplication app(empty_argc, empty_argv);
604 QProcess process;
605 process.start(program: "testhelper/qcommandlineparser_test_helper", arguments: QStringList() << QString::number(parsingMode) << "--help");
606 QVERIFY(process.waitForFinished(5000));
607 QCOMPARE(process.exitStatus(), QProcess::NormalExit);
608 QString output = process.readAll();
609#ifdef Q_OS_WIN
610 output.replace(QStringLiteral("\r\n"), QStringLiteral("\n"));
611#endif
612 QCOMPARE(output.split('\n'), expectedHelpOutput.split('\n')); // easier to debug than the next line, on failure
613 QCOMPARE(output, expectedHelpOutput);
614
615 process.start(program: "testhelper/qcommandlineparser_test_helper", arguments: QStringList() << "0" << "resize" << "--help");
616 QVERIFY(process.waitForFinished(5000));
617 QCOMPARE(process.exitStatus(), QProcess::NormalExit);
618 output = process.readAll();
619#ifdef Q_OS_WIN
620 output.replace(QStringLiteral("\r\n"), QStringLiteral("\n"));
621#endif
622 QByteArray expectedResizeHelp = QByteArrayLiteral(
623 "Usage: testhelper/qcommandlineparser_test_helper [options] resize [resize_options]\n"
624 "Test helper\n"
625 "\n")
626 + expectedOptionsHelp +
627 " --size <size> New size.\n"
628 "\n"
629 "Arguments:\n"
630 " resize Resize the object to a new size.\n";
631#ifdef Q_OS_WIN
632 expectedResizeHelp.replace(" -h, --help Displays help on commandline options.\n",
633 " -?, -h, --help Displays help on commandline options.\n");
634 expectedResizeHelp.replace("testhelper/", "testhelper\\");
635#endif
636 QCOMPARE(output, QString(expectedResizeHelp));
637#endif // QT_CONFIG(process)
638}
639
640void tst_QCommandLineParser::testQuoteEscaping()
641{
642#if !QT_CONFIG(process)
643 QSKIP("This test requires QProcess support");
644#elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
645 QSKIP("Deploying executable applications to file system on Android not supported.");
646#else
647 QCoreApplication app(empty_argc, empty_argv);
648 QProcess process;
649 process.start(program: "testhelper/qcommandlineparser_test_helper", arguments: QStringList() <<
650 QString::number(QCommandLineParser::ParseAsCompactedShortOptions) <<
651 "\\\\server\\path" <<
652 "-DKEY1=\"VALUE1\""
653 "-DQTBUG-15379=C:\\path\\'file.ext" <<
654 "-DQTBUG-30628=C:\\temp\\'file'.ext");
655 QVERIFY(process.waitForFinished(5000));
656 QCOMPARE(process.exitStatus(), QProcess::NormalExit);
657 QString output = process.readAll();
658 QVERIFY2(!output.contains("ERROR"), qPrintable(output));
659 QVERIFY2(output.contains("\\\\server\\path"), qPrintable(output));
660 QVERIFY2(output.contains("KEY1=\"VALUE1\""), qPrintable(output));
661 QVERIFY2(output.contains("QTBUG-15379=C:\\path\\'file.ext"), qPrintable(output));
662 QVERIFY2(output.contains("QTBUG-30628=C:\\temp\\'file'.ext"), qPrintable(output));
663#endif // QT_CONFIG(process)
664}
665
666void tst_QCommandLineParser::testUnknownOption()
667{
668#if !QT_CONFIG(process)
669 QSKIP("This test requires QProcess support");
670#elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
671 QSKIP("Deploying executable applications to file system on Android not supported.");
672#else
673 QCoreApplication app(empty_argc, empty_argv);
674 QProcess process;
675 process.start(program: "testhelper/qcommandlineparser_test_helper", arguments: QStringList() <<
676 QString::number(QCommandLineParser::ParseAsLongOptions) <<
677 "-unknown-option");
678 QVERIFY(process.waitForFinished(5000));
679 QCOMPARE(process.exitStatus(), QProcess::NormalExit);
680 process.setReadChannel(QProcess::StandardError);
681 QString output = process.readAll();
682 QVERIFY2(output.contains("qcommandlineparser_test_helper"), qPrintable(output)); // separate in case of .exe extension
683 QVERIFY2(output.contains(": Unknown option 'unknown-option'"), qPrintable(output));
684#endif // QT_CONFIG(process)
685}
686
687void tst_QCommandLineParser::testHelpAll_data()
688{
689 QTest::addColumn<QCommandLineParser::SingleDashWordOptionMode>(name: "parsingMode");
690 QTest::addColumn<QString>(name: "expectedHelpOutput");
691
692 QString expectedOutput = QString::fromLatin1(
693 str: "Usage: testhelper/qcommandlineparser_test_helper [options] parsingMode command\n"
694 "Test helper\n"
695 "\n")
696 + QString::fromLatin1(str: expectedOptionsHelp) +
697 QString::fromLatin1(
698 str: " --qmljsdebugger <value> Activates the QML/JS debugger with a specified\n"
699 " port. The value must be of format\n"
700 " port:1234[,block]. \"block\" makes the application\n"
701 " wait for a connection.\n"
702 "\n"
703 "Arguments:\n"
704 " parsingMode The parsing mode to test.\n"
705 " command The command to execute.\n");
706#ifdef Q_OS_WIN
707 expectedOutput.replace(" -h, --help Displays help on commandline options.\n",
708 " -?, -h, --help Displays help on commandline options.\n");
709 expectedOutput.replace("testhelper/", "testhelper\\");
710#endif
711
712 QTest::newRow(dataTag: "collapsed") << QCommandLineParser::ParseAsCompactedShortOptions << expectedOutput;
713 QTest::newRow(dataTag: "long") << QCommandLineParser::ParseAsLongOptions << expectedOutput;
714}
715
716void tst_QCommandLineParser::testHelpAll()
717{
718#if !QT_CONFIG(process)
719 QSKIP("This test requires QProcess support");
720#else
721#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
722 QSKIP("Deploying executable applications to file system on Android not supported.");
723#endif
724
725 QFETCH(QCommandLineParser::SingleDashWordOptionMode, parsingMode);
726 QFETCH(QString, expectedHelpOutput);
727 QCoreApplication app(empty_argc, empty_argv);
728 QProcess process;
729 process.start(program: "testhelper/qcommandlineparser_test_helper", arguments: QStringList() << QString::number(parsingMode) << "--help-all");
730 QVERIFY(process.waitForFinished(5000));
731 QCOMPARE(process.exitStatus(), QProcess::NormalExit);
732 QString output = process.readAll();
733#ifdef Q_OS_WIN
734 output.replace(QStringLiteral("\r\n"), QStringLiteral("\n"));
735#endif
736 QCOMPARE(output.split('\n'), expectedHelpOutput.split('\n')); // easier to debug than the next line, on failure
737 QCOMPARE(output, expectedHelpOutput);
738#endif // QT_CONFIG(process)
739}
740
741void tst_QCommandLineParser::testVeryLongOptionNames()
742{
743#if !QT_CONFIG(process)
744 QSKIP("This test requires QProcess support");
745#else
746#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
747 QSKIP("Deploying executable applications to file system on Android not supported.");
748#endif
749
750 QCoreApplication app(empty_argc, empty_argv);
751 QProcess process;
752 process.start(program: "testhelper/qcommandlineparser_test_helper", arguments: QStringList() << "0" << "long" << "--help");
753 QVERIFY(process.waitForFinished(5000));
754 QCOMPARE(process.exitStatus(), QProcess::NormalExit);
755 QString output = process.readAll();
756#ifdef Q_OS_WIN
757 output.replace(QStringLiteral("\r\n"), QStringLiteral("\n"));
758#endif
759 const QStringList lines = output.split(sep: '\n');
760 const int last = lines.count() - 1;
761 // Let's not compare everything, just the final parts.
762 QCOMPARE(lines.at(last - 7), " cdefghijklmnopqrstuvwxyz");
763 QCOMPARE(lines.at(last - 6), " --looooooooooooong-option, --looooong-opt-alias <l Short description");
764 QCOMPARE(lines.at(last - 5), " ooooooooooooong-value-name>");
765 QCOMPARE(lines.at(last - 4), "");
766 QCOMPARE(lines.at(last - 3), "Arguments:");
767 QCOMPARE(lines.at(last - 2), " parsingMode The parsing mode to test.");
768 QCOMPARE(lines.at(last - 1), " command The command to execute.");
769
770#endif // QT_CONFIG(process)
771}
772
773QTEST_APPLESS_MAIN(tst_QCommandLineParser)
774#include "tst_qcommandlineparser.moc"
775
776

source code of qtbase/tests/auto/corelib/tools/qcommandlineparser/tst_qcommandlineparser.cpp