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 | |
32 | Q_DECLARE_METATYPE(char**) |
33 | Q_DECLARE_METATYPE(QCommandLineParser::OptionsAfterPositionalArgumentsMode) |
34 | |
35 | class tst_QCommandLineParser : public QObject |
36 | { |
37 | Q_OBJECT |
38 | |
39 | public slots: |
40 | void initTestCase(); |
41 | |
42 | private 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 | |
84 | static char *empty_argv[] = { 0 }; |
85 | static int empty_argc = 1; |
86 | |
87 | void tst_QCommandLineParser::initTestCase() |
88 | { |
89 | Q_ASSERT(!empty_argv[0]); |
90 | empty_argv[0] = const_cast<char*>(QTest::currentAppName()); |
91 | } |
92 | |
93 | Q_DECLARE_METATYPE(QCommandLineParser::SingleDashWordOptionMode) |
94 | |
95 | void 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 | |
103 | void 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 | |
111 | void 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 | |
120 | void 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 | |
128 | void 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 | |
138 | void 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 | |
156 | void 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 | |
171 | void 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 | |
190 | void 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 | |
200 | void 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 | |
216 | void 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 | |
228 | void 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 | |
256 | void 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 | |
274 | void 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 | |
305 | void 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 | |
326 | void 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 | |
342 | void tst_QCommandLineParser::testDoubleDash_data() |
343 | { |
344 | parsingModes_data(); |
345 | } |
346 | |
347 | void 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 | |
365 | void 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 | |
376 | void 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 | |
387 | void 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 | |
395 | void 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 | |
405 | void tst_QCommandLineParser::testStdinArgument_data() |
406 | { |
407 | parsingModes_data(); |
408 | } |
409 | |
410 | void 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 | |
437 | void 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 | |
476 | void 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 | |
503 | void 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 | |
524 | void 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 | |
546 | static 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 | |
567 | void 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 | |
592 | void 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 | |
640 | void 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 | |
666 | void 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 | |
687 | void 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 | |
716 | void 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 | |
741 | void 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 | |
773 | QTEST_APPLESS_MAIN(tst_QCommandLineParser) |
774 | #include "tst_qcommandlineparser.moc" |
775 | |
776 | |