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 |
Definitions
- tst_QCommandLineParser
- empty_argv
- empty_argc
- initTestCase
- parsingModes_data
- testInvalidOptions
- testDuplicateOption
- testPositionalArguments
- testBooleanOption_data
- testBooleanOption
- testOptionsAndPositional_data
- testOptionsAndPositional
- testMultipleNames_data
- testMultipleNames
- testSingleValueOption_data
- testSingleValueOption
- testValueNotSet
- testMultipleValuesOption
- testUnknownOptionErrorHandling_data
- testUnknownOptionErrorHandling
- testDoubleDash_data
- testDoubleDash
- testDefaultValue
- testProcessNotCalled
- testEmptyArgsList
- testMissingOptionValue
- testStdinArgument_data
- testStdinArgument
- testSingleDashWordOptionModes_data
- testSingleDashWordOptionModes
- testCpp11StyleInitialization
- testVersionOption
- expectedOptionsHelp
- testHelpOption_data
- testHelpOption
- testQuoteEscaping
- testUnknownOption
- testHelpAll_data
- testHelpAll
Start learning QML with our Intro Training
Find out more