2017-09-20 17:09:34 +02:00
# include "./outputcheck.h"
2016-06-12 01:56:57 +02:00
# include "./testutils.h"
2017-01-27 18:51:54 +01:00
# include "../conversion/stringbuilder.h"
2018-09-22 17:04:14 +02:00
# include "../conversion/stringconversion.h"
2017-01-27 18:51:54 +01:00
2016-06-12 01:56:57 +02:00
# include "../application/argumentparser.h"
2016-12-23 09:55:12 +01:00
# include "../application/argumentparserprivate.h"
2017-06-25 15:12:38 +02:00
# include "../application/commandlineutils.h"
2016-06-12 01:56:57 +02:00
# include "../application/fakeqtconfigarguments.h"
2017-10-19 00:48:05 +02:00
# include "../io/ansiescapecodes.h"
2016-07-03 22:36:48 +02:00
# include "../io/path.h"
2019-06-10 16:03:27 +02:00
# include "../misc/parseerror.h"
2016-06-12 01:56:57 +02:00
# include "resources/config.h"
# include <cppunit/TestFixture.h>
2017-05-04 22:44:00 +02:00
# include <cppunit/extensions/HelperMacros.h>
2016-06-12 01:56:57 +02:00
2016-07-17 01:26:34 +02:00
# include <cstdlib>
2017-05-04 22:44:00 +02:00
# include <cstring>
2023-02-28 00:07:27 +01:00
# include <regex>
2016-06-12 01:56:57 +02:00
2018-10-03 22:15:08 +02:00
# ifdef PLATFORM_WINDOWS
# include <windows.h>
# endif
2016-06-12 01:56:57 +02:00
using namespace std ;
2019-06-10 21:56:46 +02:00
using namespace CppUtilities ;
using namespace CppUtilities : : Literals ;
2016-06-12 01:56:57 +02:00
using namespace CPPUNIT_NS ;
/*!
* \ brief The ArgumentParserTests class tests the ArgumentParser and Argument classes .
*/
2017-05-04 22:44:00 +02:00
class ArgumentParserTests : public TestFixture {
2016-06-12 01:56:57 +02:00
CPPUNIT_TEST_SUITE ( ArgumentParserTests ) ;
CPPUNIT_TEST ( testArgument ) ;
CPPUNIT_TEST ( testParsing ) ;
CPPUNIT_TEST ( testCallbacks ) ;
2018-10-03 22:15:08 +02:00
CPPUNIT_TEST ( testSetMainArguments ) ;
CPPUNIT_TEST ( testValueConversion ) ;
# ifndef PLATFORM_WINDOWS
2016-07-03 22:36:48 +02:00
CPPUNIT_TEST ( testBashCompletion ) ;
2017-06-25 01:19:21 +02:00
CPPUNIT_TEST ( testHelp ) ;
2018-01-29 16:23:10 +01:00
CPPUNIT_TEST ( testNoColorArgument ) ;
2018-10-03 22:15:08 +02:00
# endif
2016-06-12 01:56:57 +02:00
CPPUNIT_TEST_SUITE_END ( ) ;
public :
2019-10-13 18:27:41 +02:00
void setUp ( ) override ;
void tearDown ( ) override ;
2016-06-12 01:56:57 +02:00
void testArgument ( ) ;
void testParsing ( ) ;
void testCallbacks ( ) ;
2018-10-03 22:15:08 +02:00
void testSetMainArguments ( ) ;
void testValueConversion ( ) ;
# ifndef PLATFORM_WINDOWS
2016-07-03 22:36:48 +02:00
void testBashCompletion ( ) ;
2017-06-25 01:19:21 +02:00
void testHelp ( ) ;
2018-01-29 16:23:10 +01:00
void testNoColorArgument ( ) ;
2018-10-03 22:15:08 +02:00
# endif
2016-06-12 01:56:57 +02:00
private :
void callback ( ) ;
2019-03-24 21:52:10 +01:00
[[noreturn]] void failOnExit ( int code ) ;
2016-06-12 01:56:57 +02:00
} ;
CPPUNIT_TEST_SUITE_REGISTRATION ( ArgumentParserTests ) ;
void ArgumentParserTests : : setUp ( )
2017-05-04 22:44:00 +02:00
{
2018-10-03 22:15:08 +02:00
# ifndef PLATFORM_WINDOWS
setenv ( " ENABLE_ESCAPE_CODES " , " 0 " , 1 ) ;
# endif
EscapeCodes : : enabled = false ;
2017-05-04 22:44:00 +02:00
}
2016-06-12 01:56:57 +02:00
void ArgumentParserTests : : tearDown ( )
2017-05-04 22:44:00 +02:00
{
}
2016-06-12 01:56:57 +02:00
2019-03-24 21:52:10 +01:00
[[noreturn]] void ArgumentParserTests : : failOnExit ( int code )
{
CPPUNIT_FAIL ( argsToString ( " Exited unexpectedly with code " , code ) ) ;
}
2016-06-12 01:56:57 +02:00
/*!
* \ brief Tests the behaviour of the argument class .
*/
void ArgumentParserTests : : testArgument ( )
{
Argument argument ( " test " , ' t ' , " some description " ) ;
2018-10-03 22:15:08 +02:00
CPPUNIT_ASSERT_EQUAL ( false , argument . isRequired ( ) ) ;
2016-06-12 01:56:57 +02:00
argument . setConstraints ( 1 , 10 ) ;
2018-10-03 22:15:08 +02:00
CPPUNIT_ASSERT_EQUAL ( true , argument . isRequired ( ) ) ;
2016-06-12 01:56:57 +02:00
Argument subArg ( " sub " , ' s ' , " sub arg " ) ;
argument . addSubArgument ( & subArg ) ;
2018-10-03 22:15:08 +02:00
CPPUNIT_ASSERT_EQUAL ( & argument , subArg . parents ( ) . at ( 0 ) ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT ( ! subArg . conflictsWithArgument ( ) ) ;
2016-07-17 01:26:34 +02:00
CPPUNIT_ASSERT ( ! argument . firstValue ( ) ) ;
2017-06-25 15:12:38 +02:00
argument . setEnvironmentVariable ( " FOO_ENV_VAR " ) ;
2018-10-03 22:15:08 +02:00
# ifndef PLATFORM_WINDOWS // disabled under Windows for same reason as testNoColorArgument()
setenv ( " FOO_ENV_VAR " , " foo " , 1 ) ;
CPPUNIT_ASSERT_EQUAL ( " foo " s , string ( argument . firstValue ( ) ) ) ;
# endif
2017-06-25 15:12:38 +02:00
ArgumentOccurrence occurrence ( 0 , vector < Argument * > ( ) , nullptr ) ;
occurrence . values . emplace_back ( " bar " ) ;
2023-01-28 20:20:30 +01:00
argument . m_occurrences . emplace_back ( std : : move ( occurrence ) ) ;
2018-10-03 22:15:08 +02:00
CPPUNIT_ASSERT_EQUAL ( " bar " s , string ( argument . firstValue ( ) ) ) ;
2016-06-12 01:56:57 +02:00
}
/*!
* \ brief Tests parsing command line arguments .
*/
void ArgumentParserTests : : testParsing ( )
{
2016-06-14 00:43:32 +02:00
// setup parser with some test argument definitions
2022-06-26 11:18:36 +02:00
auto parser = ArgumentParser ( ) ;
2019-03-24 21:52:10 +01:00
parser . setExitFunction ( std : : bind ( & ArgumentParserTests : : failOnExit , this , std : : placeholders : : _1 ) ) ;
2016-06-12 01:56:57 +02:00
SET_APPLICATION_INFO ;
2022-06-26 11:18:36 +02:00
auto qtConfigArgs = QT_CONFIG_ARGUMENTS ( ) ;
auto verboseArg = Argument ( " verbose " , ' v ' , " be verbose " ) ;
2016-06-12 01:56:57 +02:00
verboseArg . setCombinable ( true ) ;
2022-06-26 11:18:36 +02:00
auto fileArg = Argument ( " file " , ' f ' , " specifies the path of the file to be opened " ) ;
2017-05-04 22:44:00 +02:00
fileArg . setValueNames ( { " path " } ) ;
2016-06-12 01:56:57 +02:00
fileArg . setRequiredValueCount ( 1 ) ;
2016-07-17 01:26:34 +02:00
fileArg . setEnvironmentVariable ( " PATH " ) ;
2022-06-26 11:18:36 +02:00
auto filesArg = Argument ( " files " , ' f ' , " specifies the path of the file(s) to be opened " ) ;
2017-05-04 22:44:00 +02:00
filesArg . setValueNames ( { " path 1 " , " path 2 " } ) ;
2017-09-20 17:07:06 +02:00
filesArg . setRequiredValueCount ( Argument : : varValueCount ) ;
2022-06-26 11:18:36 +02:00
auto outputFileArg = Argument ( " output-file " , ' o ' , " specifies the path of the output file " ) ;
2017-05-04 22:44:00 +02:00
outputFileArg . setValueNames ( { " path " } ) ;
2016-06-12 01:56:57 +02:00
outputFileArg . setRequiredValueCount ( 1 ) ;
outputFileArg . setRequired ( true ) ;
outputFileArg . setCombinable ( true ) ;
2022-06-26 11:18:36 +02:00
auto printFieldNamesArg = Argument ( " print-field-names " , ' \0 ' , " prints available field names " ) ;
auto displayFileInfoArg = Argument ( " display-file-info " , ' i ' , " displays general file information " ) ;
auto notAlbumArg = Argument ( " album " , ' a ' , " should not be confused with album value " ) ;
2016-06-12 01:56:57 +02:00
displayFileInfoArg . setDenotesOperation ( true ) ;
2017-05-04 22:44:00 +02:00
displayFileInfoArg . setSubArguments ( { & fileArg , & verboseArg , & notAlbumArg } ) ;
2022-06-26 11:18:36 +02:00
auto fieldsArg = Argument ( " fields " , ' \0 ' , " specifies the fields " ) ;
2017-09-20 17:07:06 +02:00
fieldsArg . setRequiredValueCount ( Argument : : varValueCount ) ;
2017-05-04 22:44:00 +02:00
fieldsArg . setValueNames ( { " title " , " album " , " artist " , " trackpos " } ) ;
2016-06-14 00:43:32 +02:00
fieldsArg . setImplicit ( true ) ;
2022-06-26 11:18:36 +02:00
auto displayTagInfoArg = Argument ( " get " , ' p ' , " displays the values of all specified tag fields (displays all fields if none specified) " ) ;
2016-06-12 01:56:57 +02:00
displayTagInfoArg . setDenotesOperation ( true ) ;
2017-05-04 22:44:00 +02:00
displayTagInfoArg . setSubArguments ( { & fieldsArg , & filesArg , & verboseArg , & notAlbumArg } ) ;
2019-03-24 21:52:45 +01:00
parser . setMainArguments (
{ & qtConfigArgs . qtWidgetsGuiArg ( ) , & printFieldNamesArg , & displayTagInfoArg , & displayFileInfoArg , & parser . noColorArg ( ) , & parser . helpArg ( ) } ) ;
2016-06-12 01:56:57 +02:00
2018-01-29 16:23:10 +01:00
// no args present
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 0 , nullptr , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2018-01-29 16:23:10 +01:00
CPPUNIT_ASSERT ( ! parser . executable ( ) ) ;
CPPUNIT_ASSERT ( ! parser . specifiedOperation ( ) ) ;
CPPUNIT_ASSERT_EQUAL ( 0u , parser . actualArgumentCount ( ) ) ;
2016-12-07 21:06:21 +01:00
// error about uncombinable arguments
2017-05-04 22:44:00 +02:00
const char * argv [ ] = { " tageditor " , " get " , " album " , " title " , " diskpos " , " -f " , " somefile " } ;
2016-06-12 01:56:57 +02:00
// try to parse, this should fail
try {
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 7 , argv , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_FAIL ( " Exception expected. " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & e ) {
2018-01-29 16:23:10 +01:00
CPPUNIT_ASSERT_EQUAL ( " The argument \" files \" can not be combined with \" fields \" . " s , string ( e . what ( ) ) ) ;
// test printing btw
2022-06-26 11:18:36 +02:00
auto ss = std : : stringstream ( ) ;
2018-01-29 16:23:10 +01:00
ss < < e ;
CPPUNIT_ASSERT_EQUAL (
2022-06-26 11:18:36 +02:00
" Error: Unable to parse arguments: The argument \" files \" can not be combined with \" fields \" . \n See --help for available commands. \n " sv ,
std : : string_view ( ss . str ( ) ) ) ;
2016-06-12 01:56:57 +02:00
}
2018-01-29 16:23:10 +01:00
CPPUNIT_ASSERT ( parser . isUncombinableMainArgPresent ( ) ) ;
2016-06-12 01:56:57 +02:00
2016-12-07 21:06:21 +01:00
// arguments read correctly after successful parse
2016-06-12 01:56:57 +02:00
filesArg . setCombinable ( true ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 7 , argv , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-12 01:56:57 +02:00
// check results
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT ( ! displayFileInfoArg . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " tageditor " sv , std : : string_view ( parser . executable ( ) ) ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT ( ! verboseArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fieldsArg . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " album " sv , std : : string_view ( fieldsArg . values ( ) . at ( 0 ) ) ) ;
CPPUNIT_ASSERT_EQUAL ( " title " sv , std : : string_view ( fieldsArg . values ( ) . at ( 1 ) ) ) ;
CPPUNIT_ASSERT_EQUAL ( " diskpos " sv , std : : string_view ( fieldsArg . values ( ) . at ( 2 ) ) ) ;
CPPUNIT_ASSERT_THROW ( displayTagInfoArg . values ( ) . at ( 3 ) , std : : out_of_range ) ;
2018-01-29 16:23:10 +01:00
CPPUNIT_ASSERT_EQUAL ( & displayTagInfoArg , parser . specifiedOperation ( ) ) ;
2016-06-12 01:56:57 +02:00
2016-12-07 21:06:21 +01:00
// skip empty args
2017-05-04 22:44:00 +02:00
const char * argv2 [ ] = { " tageditor " , " " , " -p " , " album " , " title " , " diskpos " , " " , " --files " , " somefile " } ;
2016-06-12 01:56:57 +02:00
// reparse the args
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 9 , argv2 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-12 01:56:57 +02:00
// check results again
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT ( ! displayFileInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! verboseArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fieldsArg . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " album " sv , std : : string_view ( fieldsArg . values ( ) . at ( 0 ) ) ) ;
CPPUNIT_ASSERT_EQUAL ( " title " sv , std : : string_view ( fieldsArg . values ( ) . at ( 1 ) ) ) ;
CPPUNIT_ASSERT_EQUAL ( " diskpos " sv , std : : string_view ( fieldsArg . values ( ) . at ( 2 ) ) ) ;
CPPUNIT_ASSERT_EQUAL ( " " sv , std : : string_view ( fieldsArg . values ( ) . at ( 3 ) ) ) ;
CPPUNIT_ASSERT_THROW ( fieldsArg . values ( ) . at ( 4 ) , std : : out_of_range ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT ( filesArg . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " somefile " sv , std : : string_view ( filesArg . values ( ) . at ( 0 ) ) ) ;
2016-06-12 01:56:57 +02:00
2016-12-07 21:06:21 +01:00
// error about unknown argument: forget get/-p
2017-05-04 22:44:00 +02:00
const char * argv3 [ ] = { " tageditor " , " album " , " title " , " diskpos " , " --files " , " somefile " } ;
2016-06-12 01:56:57 +02:00
try {
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 6 , argv3 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_FAIL ( " Exception expected. " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & e ) {
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " The specified argument \" album \" is unknown. \n Did you mean get or --help? " sv , std : : string_view ( e . what ( ) ) ) ;
2016-06-12 01:56:57 +02:00
}
2018-05-11 15:51:30 +02:00
// error about unknown argument: mistake in final argument
const char * argv18 [ ] = { " tageditor " , " get " , " album " , " title " , " diskpos " , " --verbose " , " --fi " } ;
try {
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 7 , argv18 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2018-05-11 15:51:30 +02:00
CPPUNIT_FAIL ( " Exception expected. " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & e ) {
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " The specified argument \" --fi \" is unknown. \n Did you mean --files or --no-color? " sv , std : : string_view ( e . what ( ) ) ) ;
2018-05-11 15:51:30 +02:00
}
2016-12-07 21:06:21 +01:00
// warning about unknown argument
2016-07-03 22:36:48 +02:00
parser . setUnknownArgumentBehavior ( UnknownArgumentBehavior : : Warn ) ;
2018-10-03 22:15:08 +02:00
{
# ifndef PLATFORM_WINDOWS
const OutputCheck outputCheck ( " Warning: The specified argument \" album \" is unknown and will be ignored. \n " s
" Warning: The specified argument \" title \" is unknown and will be ignored. \n " s
" Warning: The specified argument \" diskpos \" is unknown and will be ignored. \n " s
" Warning: The specified argument \" --files \" is unknown and will be ignored. \n " s
" Warning: The specified argument \" somefile \" is unknown and will be ignored. \n " s ,
cerr ) ;
# endif
parser . resetArgs ( ) ;
EscapeCodes : : enabled = false ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 6 , argv3 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2018-10-03 22:15:08 +02:00
// none of the arguments should be present now
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! displayFileInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! fieldsArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! filesArg . isPresent ( ) ) ;
}
2016-06-12 01:56:57 +02:00
2016-12-07 21:06:21 +01:00
// combined abbreviations like "-vf"
2017-05-04 22:44:00 +02:00
const char * argv4 [ ] = { " tageditor " , " -i " , " -vf " , " test " } ;
2016-07-03 22:36:48 +02:00
parser . setUnknownArgumentBehavior ( UnknownArgumentBehavior : : Fail ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 4 , argv4 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT ( displayFileInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( verboseArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fileArg . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " test " sv , std : : string_view ( fileArg . values ( ) . at ( 0 ) ) ) ;
CPPUNIT_ASSERT_THROW ( fileArg . values ( ) . at ( 1 ) , std : : out_of_range ) ;
2016-06-12 01:56:57 +02:00
2016-12-07 21:06:21 +01:00
// constraint checking: no multiple occurrences (not resetting verboseArg on purpose)
2019-05-04 21:38:23 +02:00
displayFileInfoArg . reset ( ) ;
fileArg . reset ( ) ;
2016-06-12 01:56:57 +02:00
try {
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 4 , argv4 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_FAIL ( " Exception expected. " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & e ) {
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " The argument \" verbose \" mustn't be specified more than 1 time. " sv , std : : string_view ( e . what ( ) ) ) ;
2016-06-12 01:56:57 +02:00
}
2021-07-03 19:07:49 +02:00
// constraint checking: no constraint (not resetting verboseArg on purpose)
2019-05-04 21:38:23 +02:00
displayFileInfoArg . reset ( ) ;
fileArg . reset ( ) ;
2017-09-20 17:07:06 +02:00
verboseArg . setConstraints ( 0 , Argument : : varValueCount ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 4 , argv4 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2016-06-12 01:56:57 +02:00
2016-12-07 21:06:21 +01:00
// constraint checking: mandatory argument
2016-06-12 01:56:57 +02:00
verboseArg . setRequired ( true ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 4 , argv4 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2016-06-12 01:56:57 +02:00
2021-07-03 19:07:49 +02:00
// constraint checking: error about missing mandatory argument
2017-05-04 22:44:00 +02:00
const char * argv5 [ ] = { " tageditor " , " -i " , " -f " , " test " } ;
2019-05-04 21:38:23 +02:00
displayFileInfoArg . reset ( ) ;
fileArg . reset ( ) ;
verboseArg . reset ( ) ;
2016-06-12 01:56:57 +02:00
try {
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 4 , argv5 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_FAIL ( " Exception expected. " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & e ) {
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " The argument \" verbose \" must be specified at least 1 time. " sv , std : : string_view ( e . what ( ) ) ) ;
2016-06-12 01:56:57 +02:00
}
2016-12-07 21:06:21 +01:00
verboseArg . setRequired ( false ) ;
2016-06-12 01:56:57 +02:00
2016-12-07 21:06:21 +01:00
// combined abbreviation with nesting "-pf"
2017-05-04 22:44:00 +02:00
const char * argv10 [ ] = { " tageditor " , " -pf " , " test " } ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 3 , argv10 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-10-29 23:54:30 +02:00
CPPUNIT_ASSERT ( displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! displayFileInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! fileArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( filesArg . isPresent ( ) ) ;
2017-11-29 20:29:51 +01:00
CPPUNIT_ASSERT_EQUAL ( 1 _st , filesArg . values ( 0 ) . size ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " test " sv , std : : string_view ( filesArg . values ( 0 ) . at ( 0 ) ) ) ;
2016-10-29 23:54:30 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2016-12-07 21:06:21 +01:00
// constraint checking: no complains about missing -i
2017-05-04 22:44:00 +02:00
const char * argv6 [ ] = { " tageditor " , " -g " } ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 2 , argv6 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2016-06-12 01:56:57 +02:00
2022-06-26 11:48:44 +02:00
// constraint checking: dependent arguments (-f requires -i or -p)
2017-05-04 22:44:00 +02:00
const char * argv7 [ ] = { " tageditor " , " -f " , " test " } ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2016-06-12 01:56:57 +02:00
try {
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 3 , argv7 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_FAIL ( " Exception expected. " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & e ) {
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " The specified argument \" -f \" is unknown. \n Did you mean get or --help? " sv , std : : string_view ( e . what ( ) ) ) ;
2016-06-12 01:56:57 +02:00
}
2016-06-14 00:43:32 +02:00
2016-12-07 21:06:21 +01:00
// equation sign syntax
2017-11-29 20:29:51 +01:00
const char * argv11 [ ] = { " tageditor " , " -if=test-v " } ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 2 , argv11 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-10-29 23:54:30 +02:00
CPPUNIT_ASSERT ( ! filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fileArg . isPresent ( ) ) ;
2017-11-29 20:29:51 +01:00
CPPUNIT_ASSERT ( ! verboseArg . isPresent ( ) ) ;
CPPUNIT_ASSERT_EQUAL ( 1 _st , fileArg . values ( 0 ) . size ( ) ) ;
CPPUNIT_ASSERT_EQUAL ( " test-v " s , string ( fileArg . values ( 0 ) . front ( ) ) ) ;
const char * argv15 [ ] = { " tageditor " , " -i " , " --file=test " , " -v " } ;
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 4 , argv15 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2017-11-29 20:29:51 +01:00
CPPUNIT_ASSERT ( ! filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fileArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( verboseArg . isPresent ( ) ) ;
CPPUNIT_ASSERT_EQUAL ( 1 _st , fileArg . values ( 0 ) . size ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " test " sv , std : : string_view ( fileArg . values ( 0 ) . front ( ) ) ) ;
2016-10-29 23:54:30 +02:00
2016-12-07 21:06:21 +01:00
// specifying value directly after abbreviation
2017-05-04 22:44:00 +02:00
const char * argv12 [ ] = { " tageditor " , " -iftest " } ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 2 , argv12 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-10-29 23:54:30 +02:00
CPPUNIT_ASSERT ( ! filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fileArg . isPresent ( ) ) ;
2017-11-29 20:29:51 +01:00
CPPUNIT_ASSERT_EQUAL ( 1 _st , fileArg . values ( 0 ) . size ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " test " sv , std : : string_view ( fileArg . values ( ) . at ( 0 ) ) ) ;
2016-10-29 23:54:30 +02:00
2017-11-29 22:52:50 +01:00
// specifying top-level argument after abbreviation
const char * argv17 [ ] = { " tageditor " , " -if=test-v " , " --no-color " } ;
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 3 , argv17 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2017-11-29 22:52:50 +01:00
CPPUNIT_ASSERT ( ! filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fileArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! verboseArg . isPresent ( ) ) ;
2017-10-19 00:51:16 +02:00
CPPUNIT_ASSERT ( parser . noColorArg ( ) . isPresent ( ) ) ;
2017-11-29 22:52:50 +01:00
CPPUNIT_ASSERT_EQUAL ( 1 _st , fileArg . values ( 0 ) . size ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " test-v " sv , std : : string_view ( fileArg . values ( 0 ) . front ( ) ) ) ;
2017-11-29 22:52:50 +01:00
2016-12-07 21:06:21 +01:00
// default argument
2017-05-04 22:44:00 +02:00
const char * argv8 [ ] = { " tageditor " } ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 1 , argv8 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! displayFileInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! verboseArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! fileArg . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
if ( const auto path = std : : getenv ( " PATH " ) ) {
2016-07-17 01:26:34 +02:00
CPPUNIT_ASSERT ( fileArg . firstValue ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( std : : string_view ( path ) , std : : string_view ( fileArg . firstValue ( ) ) ) ;
2016-07-17 01:26:34 +02:00
} else {
CPPUNIT_ASSERT ( ! fileArg . firstValue ( ) ) ;
}
2016-06-14 00:43:32 +02:00
2016-12-07 21:06:21 +01:00
// constraint checking: required value count with sufficient number of provided parameters
2017-05-04 22:44:00 +02:00
const char * argv13 [ ] = { " tageditor " , " get " , " --fields " , " album=test " , " title " , " diskpos " , " --files " , " somefile " } ;
2016-06-14 00:43:32 +02:00
verboseArg . setRequired ( false ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 8 , argv13 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-14 00:43:32 +02:00
// this should still work without complaints
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! displayFileInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! verboseArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fieldsArg . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " album=test " sv , std : : string_view ( fieldsArg . values ( ) . at ( 0 ) ) ) ;
CPPUNIT_ASSERT_EQUAL ( " title " sv , std : : string_view ( fieldsArg . values ( ) . at ( 1 ) ) ) ;
CPPUNIT_ASSERT_EQUAL ( " diskpos " sv , std : : string_view ( fieldsArg . values ( ) . at ( 2 ) ) ) ;
2016-06-23 22:06:59 +02:00
CPPUNIT_ASSERT_THROW ( fieldsArg . values ( ) . at ( 3 ) , out_of_range ) ;
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( filesArg . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " somefile " sv , std : : string_view ( filesArg . values ( ) . at ( 0 ) ) ) ;
2016-11-14 22:38:21 +01:00
CPPUNIT_ASSERT ( ! notAlbumArg . isPresent ( ) ) ;
2016-06-14 00:43:32 +02:00
2016-12-07 21:06:21 +01:00
// constraint checking: required value count with insufficient number of provided parameters
2017-05-04 22:44:00 +02:00
const char * argv9 [ ] = { " tageditor " , " -p " , " album " , " title " , " diskpos " } ;
2016-12-07 21:06:21 +01:00
fieldsArg . setRequiredValueCount ( 4 ) ;
parser . resetArgs ( ) ;
2016-06-14 00:43:32 +02:00
try {
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 5 , argv9 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-14 00:43:32 +02:00
CPPUNIT_FAIL ( " Exception expected. " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & e ) {
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2017-11-29 20:29:51 +01:00
CPPUNIT_ASSERT_EQUAL (
2022-06-26 11:18:36 +02:00
" Not all parameters for argument \" fields \" provided. You have to provide the following parameters: title album artist trackpos " sv ,
std : : string_view ( e . what ( ) ) ) ;
2017-11-29 20:29:51 +01:00
}
// constraint checking: truncated argument not wrongly detected
const char * argv16 [ ] = { " tageditor " , " --hel " , " -p " , " album " , " title " , " diskpos " } ;
fieldsArg . setRequiredValueCount ( Argument : : varValueCount ) ;
parser . resetArgs ( ) ;
try {
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 6 , argv16 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2017-11-29 20:29:51 +01:00
CPPUNIT_FAIL ( " Exception expected. " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & e ) {
2017-11-29 20:29:51 +01:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " The specified argument \" --hel \" is unknown. \n Did you mean --help or get? " sv , std : : string_view ( e . what ( ) ) ) ;
2016-06-14 00:43:32 +02:00
}
2017-04-06 00:01:06 +02:00
// nested operations
2017-05-04 22:44:00 +02:00
const char * argv14 [ ] = { " tageditor " , " get " , " fields " , " album=test " , " -f " , " somefile " } ;
2017-04-06 00:01:06 +02:00
parser . resetArgs ( ) ;
fieldsArg . setDenotesOperation ( true ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 6 , argv14 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2017-04-06 00:01:06 +02:00
CPPUNIT_ASSERT ( displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fieldsArg . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " album=test " sv , std : : string_view ( fieldsArg . values ( ) . at ( 0 ) ) ) ;
2017-04-06 00:01:06 +02:00
// implicit flag still works when argument doesn't denote operation
parser . resetArgs ( ) ;
fieldsArg . setDenotesOperation ( false ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 6 , argv14 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2017-04-06 00:01:06 +02:00
CPPUNIT_ASSERT ( displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fieldsArg . isPresent ( ) ) ;
2022-06-26 11:18:36 +02:00
CPPUNIT_ASSERT_EQUAL ( " fields " sv , std : : string_view ( fieldsArg . values ( ) . at ( 0 ) ) ) ;
CPPUNIT_ASSERT_EQUAL ( " album=test " sv , std : : string_view ( fieldsArg . values ( ) . at ( 1 ) ) ) ;
2022-06-26 11:20:24 +02:00
// greedy-flag
const char * argv19 [ ] = { " tageditor " , " get " , " --fields " , " foo " , " bar " , " --help " } ;
parser . resetArgs ( ) ;
try {
parser . parseArgs ( 6 , argv19 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
CPPUNIT_FAIL ( " Exception expected. " ) ;
} catch ( const ParseError & e ) {
CPPUNIT_ASSERT_EQUAL_MESSAGE ( " --help assumed to be an argument without greedy-flag (leading to error) " ,
" The argument \" help \" can not be combined with \" get \" . " sv , std : : string_view ( e . what ( ) ) ) ;
}
parser . resetArgs ( ) ;
fieldsArg . setFlags ( Argument : : Flags : : Greedy , true ) ;
parser . parseArgs ( 6 , argv19 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
CPPUNIT_ASSERT ( displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fieldsArg . isPresent ( ) ) ;
CPPUNIT_ASSERT_MESSAGE ( " --help not considered an argument with greedy-flag " , ! parser . helpArg ( ) . isPresent ( ) ) ;
CPPUNIT_ASSERT_EQUAL ( " foo " sv , std : : string_view ( fieldsArg . values ( ) . at ( 0 ) ) ) ;
CPPUNIT_ASSERT_EQUAL ( " bar " sv , std : : string_view ( fieldsArg . values ( ) . at ( 1 ) ) ) ;
CPPUNIT_ASSERT_EQUAL ( " --help " sv , std : : string_view ( fieldsArg . values ( ) . at ( 2 ) ) ) ;
CPPUNIT_ASSERT_THROW ( fieldsArg . values ( ) . at ( 3 ) , std : : out_of_range ) ;
2016-06-12 01:56:57 +02:00
}
2016-07-03 22:36:48 +02:00
/*!
* \ brief Tests whether callbacks are called correctly .
*/
2016-06-12 01:56:57 +02:00
void ArgumentParserTests : : testCallbacks ( )
{
ArgumentParser parser ;
2019-03-24 21:52:10 +01:00
parser . setExitFunction ( std : : bind ( & ArgumentParserTests : : failOnExit , this , std : : placeholders : : _1 ) ) ;
2016-06-12 01:56:57 +02:00
Argument callbackArg ( " with-callback " , ' t ' , " callback test " ) ;
callbackArg . setRequiredValueCount ( 2 ) ;
2017-05-04 22:44:00 +02:00
callbackArg . setCallback ( [ ] ( const ArgumentOccurrence & occurrence ) {
2017-11-29 20:29:51 +01:00
CPPUNIT_ASSERT_EQUAL ( 0 _st , occurrence . index ) ;
2016-07-31 23:20:31 +02:00
CPPUNIT_ASSERT ( occurrence . path . empty ( ) ) ;
2017-11-29 20:29:51 +01:00
CPPUNIT_ASSERT_EQUAL ( 2 _st , occurrence . values . size ( ) ) ;
2016-07-31 23:20:31 +02:00
CPPUNIT_ASSERT ( ! strcmp ( occurrence . values [ 0 ] , " val1 " ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( occurrence . values [ 1 ] , " val2 " ) ) ;
2016-06-12 01:56:57 +02:00
throw 42 ;
} ) ;
Argument noCallbackArg ( " no-callback " , ' l ' , " callback test " ) ;
noCallbackArg . setRequiredValueCount ( 2 ) ;
2017-05-04 22:44:00 +02:00
parser . setMainArguments ( { & callbackArg , & noCallbackArg } ) ;
2016-06-12 01:56:57 +02:00
// test whether callback is invoked when argument with callback is specified
2017-05-04 22:44:00 +02:00
const char * argv [ ] = { " test " , " -t " , " val1 " , " val2 " } ;
2016-06-12 01:56:57 +02:00
try {
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 4 , argv , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2017-05-04 22:44:00 +02:00
} catch ( int i ) {
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT_EQUAL ( i , 42 ) ;
}
// test whether callback is not invoked when argument with callback is not specified
callbackArg . reset ( ) ;
2017-05-04 22:44:00 +02:00
const char * argv2 [ ] = { " test " , " -l " , " val1 " , " val2 " } ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 4 , argv2 , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2016-06-12 01:56:57 +02:00
}
2016-07-03 22:36:48 +02:00
2018-10-03 22:15:08 +02:00
# ifndef PLATFORM_WINDOWS
2017-07-28 19:27:56 +02:00
/*!
* \ brief Used to check whether the exit ( ) function is called when printing bash completion .
*/
static bool exitCalled = false ;
2016-07-03 22:36:48 +02:00
/*!
* \ brief Tests bash completion .
2018-10-03 22:15:08 +02:00
* \ remarks
* - Disabled under Windows because OutputCheck isn ' t working .
* - This tests makes assumptions about the order and the exact output format .
2016-07-03 22:36:48 +02:00
*/
void ArgumentParserTests : : testBashCompletion ( )
{
ArgumentParser parser ;
2019-03-24 21:52:10 +01:00
parser . setExitFunction ( std : : bind ( & ArgumentParserTests : : failOnExit , this , std : : placeholders : : _1 ) ) ;
2016-07-03 22:36:48 +02:00
Argument verboseArg ( " verbose " , ' v ' , " be verbose " ) ;
verboseArg . setCombinable ( true ) ;
Argument filesArg ( " files " , ' f ' , " specifies the path of the file(s) to be opened " ) ;
2017-07-28 19:27:56 +02:00
filesArg . setRequiredValueCount ( Argument : : varValueCount ) ;
2016-07-03 22:36:48 +02:00
filesArg . setCombinable ( true ) ;
Argument nestedSubArg ( " nested-sub " , ' \0 ' , " nested sub arg " ) ;
Argument subArg ( " sub " , ' \0 ' , " sub arg " ) ;
2017-05-04 22:44:00 +02:00
subArg . setSubArguments ( { & nestedSubArg } ) ;
2016-07-03 22:36:48 +02:00
Argument displayFileInfoArg ( " display-file-info " , ' i ' , " displays general file information " ) ;
displayFileInfoArg . setDenotesOperation ( true ) ;
2017-05-04 22:44:00 +02:00
displayFileInfoArg . setSubArguments ( { & filesArg , & verboseArg , & subArg } ) ;
2016-07-03 22:36:48 +02:00
Argument fieldsArg ( " fields " , ' \0 ' , " specifies the fields " ) ;
2017-07-28 19:27:56 +02:00
fieldsArg . setRequiredValueCount ( Argument : : varValueCount ) ;
2016-07-03 22:36:48 +02:00
fieldsArg . setPreDefinedCompletionValues ( " title album artist trackpos " ) ;
fieldsArg . setImplicit ( true ) ;
Argument valuesArg ( " values " , ' \0 ' , " specifies the fields " ) ;
2017-07-28 19:27:56 +02:00
valuesArg . setRequiredValueCount ( Argument : : varValueCount ) ;
2016-07-03 22:36:48 +02:00
valuesArg . setPreDefinedCompletionValues ( " title album artist trackpos " ) ;
2017-07-28 17:32:07 +02:00
valuesArg . setImplicit ( false ) ;
2016-07-03 22:36:48 +02:00
valuesArg . setValueCompletionBehavior ( ValueCompletionBehavior : : PreDefinedValues | ValueCompletionBehavior : : AppendEquationSign ) ;
2018-01-29 16:23:10 +01:00
Argument selectorsArg ( " selectors " , ' \0 ' , " has some more pre-defined values " ) ;
selectorsArg . setRequiredValueCount ( Argument : : varValueCount ) ;
selectorsArg . setPreDefinedCompletionValues ( " tag=id3v1 tag=id3v2 tag=matroska target=file target=track " ) ;
selectorsArg . setImplicit ( false ) ;
selectorsArg . setValueCompletionBehavior ( ValueCompletionBehavior : : PreDefinedValues ) ;
selectorsArg . setCallback (
[ & selectorsArg ] ( const ArgumentOccurrence & ) { selectorsArg . setPreDefinedCompletionValues ( " tag=matroska tag=mp4 tag=vorbis " ) ; } ) ;
2016-07-03 22:36:48 +02:00
Argument getArg ( " get " , ' g ' , " gets tag values " ) ;
2017-05-04 22:44:00 +02:00
getArg . setSubArguments ( { & fieldsArg , & filesArg } ) ;
2016-07-03 22:36:48 +02:00
Argument setArg ( " set " , ' s ' , " sets tag values " ) ;
2018-01-29 16:23:10 +01:00
setArg . setSubArguments ( { & valuesArg , & filesArg , & selectorsArg } ) ;
2016-11-26 00:14:45 +01:00
2019-03-24 21:52:45 +01:00
parser . setMainArguments ( { & displayFileInfoArg , & getArg , & setArg , & parser . noColorArg ( ) , & parser . helpArg ( ) } ) ;
2016-07-03 22:36:48 +02:00
2017-07-28 19:27:56 +02:00
// fail due to operation flags not set
const char * const argv1 [ ] = { " se " } ;
ArgumentReader reader ( parser , argv1 , argv1 + 1 , true ) ;
{
2017-09-20 17:09:34 +02:00
const OutputCheck c ( " COMPREPLY=() \ n " ) ;
2018-05-07 20:04:30 +02:00
CPPUNIT_ASSERT ( reader . read ( ) ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 1 , argv1 , 0 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2016-07-03 22:36:48 +02:00
2017-07-28 19:27:56 +02:00
// correct operation arg flags
getArg . setDenotesOperation ( true ) ;
setArg . setDenotesOperation ( true ) ;
{
2017-09-20 17:09:34 +02:00
const OutputCheck c ( " COMPREPLY=('set' ) \ n " ) ;
2016-12-23 09:55:12 +01:00
reader . reset ( argv1 , argv1 + 1 ) . read ( ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 1 , argv1 , 0 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2016-07-03 22:36:48 +02:00
2017-07-28 19:27:56 +02:00
// argument at current cursor position already specified -> the completion should just return the argument
const char * const argv2 [ ] = { " set " } ;
parser . resetArgs ( ) ;
{
2017-09-20 17:09:34 +02:00
const OutputCheck c ( " COMPREPLY=('set' ) \ n " ) ;
2016-12-23 09:55:12 +01:00
reader . reset ( argv2 , argv2 + 1 ) . read ( ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 1 , argv2 , 0 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2016-07-03 22:36:48 +02:00
2017-07-28 19:27:56 +02:00
// advance the cursor position -> the completion should propose the next argument
parser . resetArgs ( ) ;
{
2019-03-24 21:52:45 +01:00
const OutputCheck c ( " COMPREPLY=('--files' '--no-color' '--selectors' '--values' ) \ n " ) ;
2016-12-23 09:55:12 +01:00
reader . reset ( argv2 , argv2 + 1 ) . read ( ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 1 , argv2 , 1 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2016-07-03 22:36:48 +02:00
2017-07-28 19:27:56 +02:00
// nested operations should be proposed as operations
parser . resetArgs ( ) ;
filesArg . setDenotesOperation ( true ) ;
{
2019-03-24 21:52:45 +01:00
const OutputCheck c ( " COMPREPLY=('files' '--no-color' '--selectors' '--values' ) \ n " ) ;
2017-04-06 00:01:06 +02:00
reader . reset ( argv2 , argv2 + 1 ) . read ( ) ;
parser . printBashCompletion ( 1 , argv2 , 1 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2017-04-06 00:01:06 +02:00
2017-07-28 19:27:56 +02:00
// specifying no args should propose all main arguments
parser . resetArgs ( ) ;
filesArg . setDenotesOperation ( false ) ;
{
2017-10-19 00:51:16 +02:00
const OutputCheck c ( " COMPREPLY=('display-file-info' 'get' 'set' '--help' '--no-color' ) \ n " ) ;
2016-12-23 09:55:12 +01:00
reader . reset ( nullptr , nullptr ) . read ( ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 0 , nullptr , 0 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2016-07-03 22:36:48 +02:00
2017-07-28 19:27:56 +02:00
// pre-defined values
const char * const argv3 [ ] = { " get " , " --fields " } ;
parser . resetArgs ( ) ;
{
2017-10-19 00:51:16 +02:00
const OutputCheck c ( " COMPREPLY=('title' 'album' 'artist' 'trackpos' '--files' '--no-color' ) \ n " ) ;
2016-12-23 09:55:12 +01:00
reader . reset ( argv3 , argv3 + 2 ) . read ( ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 2 , argv3 , 2 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2016-07-03 22:36:48 +02:00
2017-07-28 19:27:56 +02:00
// pre-defined values with equation sign, one letter already present
const char * const argv4 [ ] = { " set " , " --values " , " a " } ;
parser . resetArgs ( ) ;
{
2017-09-20 17:09:34 +02:00
const OutputCheck c ( " COMPREPLY=('album=' 'artist=' ) ; compopt - o nospace \ n " );
2016-12-23 09:55:12 +01:00
reader . reset ( argv4 , argv4 + 3 ) . read ( ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 3 , argv4 , 2 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2016-07-03 22:36:48 +02:00
2018-01-29 16:23:10 +01:00
// pre-defined values containing equation sign, equation sign already present
const char * const argv12 [ ] = { " set " , " --selectors " , " tag=id3 " } ;
parser . resetArgs ( ) ;
{
const OutputCheck c ( " COMPREPLY=('tag=id3v1' 'tag=id3v2' ) \ n " ) ;
reader . reset ( argv12 , argv12 + 3 ) . read ( ) ;
parser . printBashCompletion ( 3 , argv12 , 2 , reader ) ;
}
// recombining pre-defined values containing equation sign, equation sign already present
const char * const argv13 [ ] = { " set " , " --selectors " , " tag " , " = " , " id3 " } ;
parser . resetArgs ( ) ;
{
const OutputCheck c ( " COMPREPLY=('id3v1' 'id3v2' ) \ n " ) ;
reader . reset ( argv13 , argv13 + 5 ) . read ( ) ;
parser . printBashCompletion ( 5 , argv13 , 4 , reader ) ;
}
parser . resetArgs ( ) ;
{
const OutputCheck c ( " COMPREPLY=('id3v1' 'id3v2' 'matroska' ) \ n " ) ;
reader . reset ( argv13 , argv13 + 5 ) . read ( ) ;
parser . printBashCompletion ( 5 , argv13 , 3 , reader ) ;
}
// computing pre-defined values just in time using callback
selectorsArg . setValueCompletionBehavior ( selectorsArg . valueCompletionBehaviour ( ) | ValueCompletionBehavior : : InvokeCallback ) ;
parser . resetArgs ( ) ;
{
const OutputCheck c ( " COMPREPLY=('matroska' 'mp4' 'vorbis' ) \ n " ) ;
reader . reset ( argv13 , argv13 + 5 ) . read ( ) ;
parser . printBashCompletion ( 5 , argv13 , 3 , reader ) ;
}
2017-07-28 19:27:56 +02:00
// pre-defined values for implicit argument
parser . resetArgs ( ) ;
{
2017-10-19 00:51:16 +02:00
const OutputCheck c ( " COMPREPLY=('title' 'album' 'artist' 'trackpos' '--fields' '--files' '--no-color' ) \ n " ) ;
2017-07-28 17:32:07 +02:00
reader . reset ( argv3 , argv3 + 1 ) . read ( ) ;
parser . printBashCompletion ( 1 , argv3 , 2 , reader ) ;
2017-07-28 19:27:56 +02:00
}
// file names
2019-06-10 21:56:46 +02:00
string iniFilePath = CppUtilities : : testFilePath ( " test.ini " ) ;
2017-07-28 19:27:56 +02:00
iniFilePath . resize ( iniFilePath . size ( ) - 4 ) ;
2019-06-10 21:56:46 +02:00
string mkvFilePath = CppUtilities : : testFilePath ( " test 'with quote'.mkv " ) ;
2017-07-28 19:27:56 +02:00
mkvFilePath . resize ( mkvFilePath . size ( ) - 17 ) ;
parser . resetArgs ( ) ;
const char * const argv5 [ ] = { " get " , " --files " , iniFilePath . c_str ( ) } ;
{
2019-07-02 18:40:25 +02:00
# ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
2017-07-28 19:27:56 +02:00
// order for file names is not specified
2017-09-20 17:09:34 +02:00
const OutputCheck c ( " COMPREPLY=(' " % mkvFilePath % " ' \" ' \" 'with quote' \" ' \" '.mkv' ' " % iniFilePath + " .ini' ) ; compopt - o filenames \ n " ,
2017-07-28 19:27:56 +02:00
" COMPREPLY=(' " % iniFilePath % " .ini' ' " % mkvFilePath + " ' \" ' \" 'with quote' \" ' \" '.mkv' ); compopt -o filenames \n " ) ;
2019-07-02 18:40:25 +02:00
# else
const OutputCheck c ( " COMPREPLY=() \ n " ) ;
# endif
2016-12-23 09:55:12 +01:00
reader . reset ( argv5 , argv5 + 3 ) . read ( ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 3 , argv5 , 2 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2019-06-05 23:49:53 +02:00
// directory names
2020-06-16 01:03:39 +02:00
string directoryPath = CppUtilities : : testFilePath ( " subdir/foo/bar " ) ;
directoryPath . resize ( directoryPath . size ( ) - 4 ) ;
2019-06-05 23:49:53 +02:00
filesArg . setValueCompletionBehavior ( ValueCompletionBehavior : : Directories ) ;
parser . resetArgs ( ) ;
const char * const argv14 [ ] = { " get " , " --files " , directoryPath . c_str ( ) } ;
{
2019-07-02 18:40:25 +02:00
# ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
2020-06-16 01:03:39 +02:00
const OutputCheck c ( " COMPREPLY=(' " % directoryPath + " ' ) ; compopt - o filenames \ n " );
2019-07-02 18:40:25 +02:00
# else
const OutputCheck c ( " COMPREPLY=() \ n " ) ;
# endif
2019-06-05 23:49:53 +02:00
reader . reset ( argv14 , argv14 + 3 ) . read ( ) ;
parser . printBashCompletion ( 3 , argv14 , 2 , reader ) ;
}
2017-07-28 19:27:56 +02:00
// sub arguments
const char * const argv6 [ ] = { " set " , " -- " } ;
parser . resetArgs ( ) ;
{
2019-03-24 21:52:45 +01:00
const OutputCheck c ( " COMPREPLY=('--files' '--no-color' '--selectors' '--values' ) \ n " ) ;
2016-12-23 09:55:12 +01:00
reader . reset ( argv6 , argv6 + 2 ) . read ( ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 2 , argv6 , 1 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2016-07-03 22:36:48 +02:00
2017-07-28 19:27:56 +02:00
// nested sub arguments
const char * const argv7 [ ] = { " -i " , " --sub " , " -- " } ;
parser . resetArgs ( ) ;
{
2017-10-19 00:51:16 +02:00
const OutputCheck c ( " COMPREPLY=('--files' '--nested-sub' '--no-color' '--verbose' ) \ n " ) ;
2016-12-23 09:55:12 +01:00
reader . reset ( argv7 , argv7 + 3 ) . read ( ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 3 , argv7 , 2 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2016-07-03 22:36:48 +02:00
2017-07-28 19:27:56 +02:00
// started pre-defined values with equation sign, one letter already present, last value matches
const char * const argv8 [ ] = { " set " , " --values " , " t " } ;
parser . resetArgs ( ) ;
{
2017-09-20 17:09:34 +02:00
const OutputCheck c ( " COMPREPLY=('title=' 'trackpos=' ) ; compopt - o nospace \ n " );
2016-12-23 09:55:12 +01:00
reader . reset ( argv8 , argv8 + 3 ) . read ( ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 3 , argv8 , 2 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2016-11-26 00:14:45 +01:00
2017-07-28 19:27:56 +02:00
// combined abbreviations
const char * const argv9 [ ] = { " -gf " } ;
parser . resetArgs ( ) ;
{
2017-09-20 17:09:34 +02:00
const OutputCheck c ( " COMPREPLY=('-gf' ) \ n " ) ;
2016-12-23 22:41:06 +01:00
reader . reset ( argv9 , argv9 + 1 ) . read ( ) ;
parser . printBashCompletion ( 1 , argv9 , 0 , reader ) ;
2017-07-28 19:27:56 +02:00
}
parser . resetArgs ( ) ;
{
2017-09-20 17:09:34 +02:00
const OutputCheck c ( [ ] ( const string & actualOutput ) { CPPUNIT_ASSERT_EQUAL ( 0 _st , actualOutput . find ( " COMPREPLY=('--fields' " ) ) ; } ) ;
2016-12-23 22:41:06 +01:00
reader . reset ( argv9 , argv9 + 1 ) . read ( ) ;
parser . printBashCompletion ( 1 , argv9 , 1 , reader ) ;
2017-07-28 19:27:56 +02:00
}
2016-12-23 22:41:06 +01:00
2017-07-28 19:27:56 +02:00
// override exit function to prevent readArgs() from terminating the test run
2017-10-19 00:51:16 +02:00
parser . setExitFunction ( [ ] ( int ) { exitCalled = true ; } ) ;
2017-07-28 18:24:52 +02:00
2017-07-28 19:27:56 +02:00
// call completion via readArgs() with current word index
const char * const argv10 [ ] = { " /some/path/tageditor " , " --bash-completion-for " , " 0 " } ;
parser . resetArgs ( ) ;
{
2017-10-19 00:51:16 +02:00
const OutputCheck c ( " COMPREPLY=('display-file-info' 'get' 'set' '--help' '--no-color' ) \ n " ) ;
2017-07-28 18:24:52 +02:00
parser . readArgs ( 3 , argv10 ) ;
2017-07-28 19:27:56 +02:00
}
CPPUNIT_ASSERT ( ! strcmp ( " /some/path/tageditor " , parser . executable ( ) ) ) ;
CPPUNIT_ASSERT ( exitCalled ) ;
2017-07-28 18:24:52 +02:00
2017-07-28 19:27:56 +02:00
// call completion via readArgs() without current word index
const char * const argv11 [ ] = { " /some/path/tageditor " , " --bash-completion-for " , " ge " } ;
parser . resetArgs ( ) ;
{
2017-09-20 17:09:34 +02:00
const OutputCheck c ( " COMPREPLY=('get' ) \ n " ) ;
2017-07-28 19:27:56 +02:00
parser . readArgs ( 3 , argv11 ) ;
2016-07-03 22:36:48 +02:00
}
}
2018-10-03 22:15:08 +02:00
# endif
2017-06-25 01:19:21 +02:00
2018-10-03 22:15:08 +02:00
# ifndef PLATFORM_WINDOWS
2017-06-25 01:19:21 +02:00
/*!
* \ brief Tests - - help output .
2018-10-03 22:15:08 +02:00
* \ remarks Disabled under Windows because OutputCheck isn ' t working .
2017-06-25 01:19:21 +02:00
*/
void ArgumentParserTests : : testHelp ( )
{
2021-07-03 19:07:49 +02:00
// indentation
2017-06-25 15:12:38 +02:00
Indentation indent ;
indent = indent + 3 ;
CPPUNIT_ASSERT_EQUAL ( static_cast < unsigned char > ( 4 + 3 ) , indent . level ) ;
2017-06-25 01:19:21 +02:00
// setup parser
ArgumentParser parser ;
2019-03-24 21:52:45 +01:00
parser . setExitFunction ( std : : bind ( & ArgumentParserTests : : failOnExit , this , std : : placeholders : : _1 ) ) ;
2017-06-25 01:19:21 +02:00
OperationArgument verboseArg ( " verbose " , ' v ' , " be verbose " , " actually not an operation " ) ;
verboseArg . setCombinable ( true ) ;
ConfigValueArgument nestedSubArg ( " nested-sub " , ' \0 ' , " nested sub arg " , { " value1 " , " value2 " } ) ;
2017-09-20 17:07:06 +02:00
nestedSubArg . setRequiredValueCount ( Argument : : varValueCount ) ;
2018-01-29 16:23:10 +01:00
Argument subArg ( " foo " , ' f ' , " dummy " ) ;
subArg . setName ( " sub " ) ;
subArg . setAbbreviation ( ' \0 ' ) ;
subArg . setDescription ( " sub arg " ) ;
subArg . setExample ( " sub arg example " ) ;
2017-06-25 01:19:21 +02:00
subArg . setRequired ( true ) ;
subArg . addSubArgument ( & nestedSubArg ) ;
Argument filesArg ( " files " , ' f ' , " specifies the path of the file(s) to be opened " ) ;
filesArg . setCombinable ( true ) ;
filesArg . addSubArgument ( & subArg ) ;
2017-06-25 15:12:38 +02:00
filesArg . setSubArguments ( { & subArg } ) ; // test re-assignment btw
2017-06-25 01:19:21 +02:00
Argument envArg ( " env " , ' \0 ' , " env " ) ;
envArg . setEnvironmentVariable ( " FILES " ) ;
envArg . setRequiredValueCount ( 2 ) ;
2017-06-25 15:12:38 +02:00
envArg . appendValueName ( " file " ) ;
2019-05-04 23:14:43 +02:00
Argument deprecatedArg ( " deprecated " ) ;
deprecatedArg . markAsDeprecated ( & filesArg ) ;
2017-10-19 00:51:16 +02:00
parser . helpArg ( ) . setRequired ( true ) ;
2019-05-04 23:14:43 +02:00
parser . setMainArguments ( { & verboseArg , & filesArg , & envArg , & deprecatedArg , & parser . noColorArg ( ) , & parser . helpArg ( ) } ) ;
2019-05-04 22:49:57 +02:00
applicationInfo . dependencyVersions = { " somelib " , " some other lib " } ;
2017-06-25 01:19:21 +02:00
2017-07-28 19:27:56 +02:00
// parse args and assert output
2017-06-25 01:19:21 +02:00
const char * const argv [ ] = { " app " , " -h " } ;
2017-07-28 19:27:56 +02:00
{
2017-09-20 17:09:34 +02:00
const OutputCheck c ( " \ e[1m " APP_NAME " , version " APP_VERSION " \n "
2019-07-22 18:17:39 +02:00
" \n "
" \ e[0m " APP_DESCRIPTION " \n "
" \n "
2018-01-28 00:38:05 +01:00
" Available operations: \n "
2017-09-20 17:09:34 +02:00
" \ e[1mverbose, -v \ e[0m \n "
" be verbose \n "
" example: actually not an operation \n "
" \n "
2018-01-28 00:38:05 +01:00
" Available top-level options: \n "
2017-09-20 17:09:34 +02:00
" \ e[1m--files, -f \ e[0m \n "
" specifies the path of the file(s) to be opened \n "
" \ e[1m--sub \ e[0m \n "
" sub arg \n "
" particularities: mandatory if parent argument is present \n "
" \ e[1m--nested-sub \ e[0m [value1] [value2] ... \n "
" nested sub arg \n "
2018-01-29 16:23:10 +01:00
" example: sub arg example \n "
2017-09-20 17:09:34 +02:00
" \n "
" \ e[1m--env \ e[0m [file] [value 2] \n "
" env \n "
" default environment variable: FILES \n "
" \n "
2017-10-19 00:51:16 +02:00
" \ e[1m--no-color \ e[0m \n "
" disables formatted/colorized output \n "
" default environment variable: ENABLE_ESCAPE_CODES \n "
" \n "
2019-07-22 18:17:39 +02:00
" Linked against: somelib, some other lib \n "
" \n "
2017-09-20 17:09:34 +02:00
" Project website: " APP_URL " \n " ) ;
2017-10-19 00:48:05 +02:00
EscapeCodes : : enabled = true ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 2 , argv , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2017-07-28 19:27:56 +02:00
}
2018-01-29 16:23:10 +01:00
verboseArg . setDenotesOperation ( false ) ;
2019-03-24 21:52:45 +01:00
parser . setMainArguments ( { & verboseArg , & filesArg , & envArg , & parser . helpArg ( ) } ) ;
2018-01-29 16:23:10 +01:00
{
const OutputCheck c ( APP_NAME " , version " APP_VERSION " \n "
2019-07-26 22:01:25 +02:00
" \n " APP_DESCRIPTION " \n "
2018-01-29 16:23:10 +01:00
" \n "
" Available arguments: \n "
" --verbose, -v \n "
" be verbose \n "
" example: actually not an operation \n "
" \n "
" --files, -f \n "
" specifies the path of the file(s) to be opened \n "
" --sub \n "
" sub arg \n "
" particularities: mandatory if parent argument is present \n "
" --nested-sub [value1] [value2] ... \n "
" nested sub arg \n "
" example: sub arg example \n "
" \n "
" --env [file] [value 2] \n "
" env \n "
" default environment variable: FILES \n "
" \n "
2019-07-22 18:17:39 +02:00
" Linked against: somelib, some other lib \n "
" \n "
2018-01-29 16:23:10 +01:00
" Project website: " APP_URL " \n " ) ;
EscapeCodes : : enabled = false ;
parser . resetArgs ( ) ;
2019-06-10 16:03:27 +02:00
parser . parseArgs ( 2 , argv , ParseArgumentBehavior : : CheckConstraints | ParseArgumentBehavior : : InvokeCallbacks ) ;
2018-01-29 16:23:10 +01:00
}
2017-06-25 01:19:21 +02:00
}
2018-10-03 22:15:08 +02:00
# endif
2017-06-25 15:12:38 +02:00
/*!
* \ brief Tests some corner cases in setMainArguments ( ) which are not already checked in the other tests .
*/
void ArgumentParserTests : : testSetMainArguments ( )
{
ArgumentParser parser ;
2019-03-24 21:52:10 +01:00
parser . setExitFunction ( std : : bind ( & ArgumentParserTests : : failOnExit , this , std : : placeholders : : _1 ) ) ;
2019-03-24 21:52:45 +01:00
HelpArgument & helpArg ( parser . helpArg ( ) ) ;
2017-06-25 15:12:38 +02:00
Argument subArg ( " sub-arg " , ' s ' , " mandatory sub arg " ) ;
subArg . setRequired ( true ) ;
helpArg . addSubArgument ( & subArg ) ;
parser . addMainArgument ( & helpArg ) ;
parser . setMainArguments ( { } ) ;
CPPUNIT_ASSERT_MESSAGE ( " clear main args " , parser . mainArguments ( ) . empty ( ) ) ;
parser . setMainArguments ( { & helpArg } ) ;
CPPUNIT_ASSERT_MESSAGE ( " no default due to required sub arg " , ! parser . defaultArgument ( ) ) ;
subArg . setConstraints ( 0 , 20 ) ;
parser . setMainArguments ( { & helpArg } ) ;
CPPUNIT_ASSERT_MESSAGE ( " default if no required sub arg " , & helpArg = = parser . defaultArgument ( ) ) ;
}
2018-01-29 16:23:10 +01:00
2018-10-03 22:15:08 +02:00
# ifndef PLATFORM_WINDOWS
2018-01-29 16:23:10 +01:00
/*!
* \ brief Tests whether NocolorArgument toggles escape codes correctly .
2018-10-03 22:15:08 +02:00
* \ remarks
* Disabled under Windows . Under that platform we could alter the environment using
* SetEnvironmentVariableW . However , that doesn ' t seem to have an effect on further
* getenv ( ) or _wgetenv ( ) calls .
2018-01-29 16:23:10 +01:00
*/
void ArgumentParserTests : : testNoColorArgument ( )
{
// assume escape codes are enabled by default
EscapeCodes : : enabled = true ;
{
unsetenv ( " ENABLE_ESCAPE_CODES " ) ;
NoColorArgument noColorArg ;
noColorArg . apply ( ) ;
CPPUNIT_ASSERT_MESSAGE ( " default used if not present " , EscapeCodes : : enabled ) ;
noColorArg . m_occurrences . emplace_back ( 0 ) ;
noColorArg . apply ( ) ;
CPPUNIT_ASSERT_MESSAGE ( " default negated if present " , ! EscapeCodes : : enabled ) ;
}
{
setenv ( " ENABLE_ESCAPE_CODES " , " 0 " , 1 ) ;
const NoColorArgument noColorArg ;
CPPUNIT_ASSERT ( ! EscapeCodes : : enabled ) ;
}
{
setenv ( " ENABLE_ESCAPE_CODES " , " 1 " , 1 ) ;
const NoColorArgument noColorArg ;
CPPUNIT_ASSERT ( EscapeCodes : : enabled ) ;
}
}
2018-10-03 22:15:08 +02:00
# endif
2018-09-22 17:04:14 +02:00
template < typename ValueTuple > void checkConvertedValues ( const std : : string & message , const ValueTuple & values )
{
CPPUNIT_ASSERT_EQUAL_MESSAGE ( message , " foo " s , get < 0 > ( values ) ) ;
CPPUNIT_ASSERT_EQUAL_MESSAGE ( message , 42u , get < 1 > ( values ) ) ;
CPPUNIT_ASSERT_EQUAL_MESSAGE ( message , 7.5 , get < 2 > ( values ) ) ;
CPPUNIT_ASSERT_EQUAL_MESSAGE ( message , - 42 , get < 3 > ( values ) ) ;
}
/*!
* \ brief Tests value conversion provided by Argument and ArgumentOccurrence .
*/
void ArgumentParserTests : : testValueConversion ( )
{
// convert values directly from ArgumentOccurrence
ArgumentOccurrence occurrence ( 0 ) ;
occurrence . values = { " foo " , " 42 " , " 7.5 " , " -42 " } ;
checkConvertedValues ( " values from ArgumentOccurrence::convertValues " , occurrence . convertValues < string , unsigned int , double , int > ( ) ) ;
static_assert ( std : : is_same < std : : tuple < > , decltype ( occurrence . convertValues < > ( ) ) > : : value , " specifying no types yields empty tuple " ) ;
// convert values via Argument's API
Argument arg ( " test " , ' \0 ' ) ;
arg . m_occurrences = { occurrence , occurrence } ;
checkConvertedValues ( " values from Argument::convertValues " , arg . valuesAs < string , unsigned int , double , int > ( ) ) ;
checkConvertedValues ( " values from Argument::convertValues(1) " , arg . valuesAs < string , unsigned int , double , int > ( 1 ) ) ;
const auto allValues = arg . allValuesAs < string , unsigned int , double , int > ( ) ;
CPPUNIT_ASSERT_EQUAL ( 2 _st , allValues . size ( ) ) ;
for ( const auto & values : allValues ) {
checkConvertedValues ( " values from Argument::convertAllValues " , values ) ;
}
static_assert ( std : : is_same < std : : tuple < > , decltype ( arg . valuesAs < > ( ) ) > : : value , " specifying no types yields empty tuple " ) ;
// error handling
try {
occurrence . convertValues < string , unsigned int , double , int , int > ( ) ;
CPPUNIT_FAIL ( " Expected exception " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & failure ) {
2018-09-22 17:04:14 +02:00
CPPUNIT_ASSERT_EQUAL ( " Expected 5 top-level values to be present but only 4 have been specified. " s , string ( failure . what ( ) ) ) ;
}
try {
occurrence . convertValues < int > ( ) ;
CPPUNIT_FAIL ( " Expected exception " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & failure ) {
2023-02-28 00:07:27 +01:00
TESTUTILS_ASSERT_LIKE_FLAGS ( " conversion error of top-level value " , " Conversion of top-level value \" foo \" to type \" .* \" failed: The character \" f \" is no valid digit. " s , std : : regex : : extended , std : : string ( failure . what ( ) ) ) ;
2018-09-22 17:04:14 +02:00
}
occurrence . path = { & arg } ;
try {
occurrence . convertValues < string , unsigned int , double , int , int > ( ) ;
CPPUNIT_FAIL ( " Expected exception " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & failure ) {
2018-09-22 17:04:14 +02:00
CPPUNIT_ASSERT_EQUAL ( " Expected 5 values for argument --test to be present but only 4 have been specified. " s , string ( failure . what ( ) ) ) ;
}
try {
occurrence . convertValues < int > ( ) ;
CPPUNIT_FAIL ( " Expected exception " ) ;
2019-06-10 16:03:27 +02:00
} catch ( const ParseError & failure ) {
2023-02-28 00:07:27 +01:00
TESTUTILS_ASSERT_LIKE_FLAGS ( " conversion error of argument value " , " Conversion of value \" foo \" \\ (for argument --test \\ ) to type \" .* \" failed: The character \" f \" is no valid digit. " s , std : : regex : : extended , std : : string ( failure . what ( ) ) ) ;
2018-09-22 17:04:14 +02:00
}
}