2016-06-12 01:56:57 +02:00
# include "./testutils.h"
2017-01-27 18:51:54 +01:00
# include "../conversion/stringbuilder.h"
2016-06-12 01:56:57 +02:00
# include "../application/argumentparser.h"
2016-12-23 09:55:12 +01:00
# include "../application/argumentparserprivate.h"
2016-06-12 01:56:57 +02:00
# include "../application/failure.h"
# include "../application/fakeqtconfigarguments.h"
2016-07-03 22:36:48 +02:00
# include "../io/path.h"
2016-06-12 01:56:57 +02:00
# include "resources/config.h"
# include <cppunit/extensions/HelperMacros.h>
# include <cppunit/TestFixture.h>
# include <cstring>
2016-07-17 01:26:34 +02:00
# include <cstdlib>
2016-06-12 01:56:57 +02:00
using namespace std ;
using namespace ApplicationUtilities ;
2017-01-27 18:51:54 +01:00
using namespace ConversionUtilities ;
2016-06-12 01:56:57 +02:00
using namespace CPPUNIT_NS ;
/*!
* \ brief The ArgumentParserTests class tests the ArgumentParser and Argument classes .
*/
class ArgumentParserTests : public TestFixture
{
CPPUNIT_TEST_SUITE ( ArgumentParserTests ) ;
CPPUNIT_TEST ( testArgument ) ;
CPPUNIT_TEST ( testParsing ) ;
CPPUNIT_TEST ( testCallbacks ) ;
2016-07-03 22:36:48 +02:00
CPPUNIT_TEST ( testBashCompletion ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_TEST_SUITE_END ( ) ;
public :
void setUp ( ) ;
void tearDown ( ) ;
void testArgument ( ) ;
void testParsing ( ) ;
void testCallbacks ( ) ;
2016-07-03 22:36:48 +02:00
void testBashCompletion ( ) ;
2016-06-12 01:56:57 +02:00
private :
void callback ( ) ;
} ;
CPPUNIT_TEST_SUITE_REGISTRATION ( ArgumentParserTests ) ;
void ArgumentParserTests : : setUp ( )
{ }
void ArgumentParserTests : : tearDown ( )
{ }
/*!
* \ brief Tests the behaviour of the argument class .
*/
void ArgumentParserTests : : testArgument ( )
{
Argument argument ( " test " , ' t ' , " some description " ) ;
CPPUNIT_ASSERT_EQUAL ( argument . isRequired ( ) , false ) ;
argument . setConstraints ( 1 , 10 ) ;
CPPUNIT_ASSERT_EQUAL ( argument . isRequired ( ) , true ) ;
Argument subArg ( " sub " , ' s ' , " sub arg " ) ;
argument . addSubArgument ( & subArg ) ;
CPPUNIT_ASSERT_EQUAL ( subArg . parents ( ) . at ( 0 ) , & argument ) ;
CPPUNIT_ASSERT ( ! subArg . conflictsWithArgument ( ) ) ;
2016-07-17 01:26:34 +02:00
CPPUNIT_ASSERT ( ! argument . firstValue ( ) ) ;
argument . setEnvironmentVariable ( " PATH " ) ;
if ( getenv ( " PATH " ) ) {
CPPUNIT_ASSERT ( argument . firstValue ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( argument . firstValue ( ) , getenv ( " PATH " ) ) ) ;
} else {
CPPUNIT_ASSERT ( ! 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
2016-06-12 01:56:57 +02:00
ArgumentParser parser ;
SET_APPLICATION_INFO ;
QT_CONFIG_ARGUMENTS qtConfigArgs ;
HelpArgument helpArg ( parser ) ;
Argument verboseArg ( " verbose " , ' v ' , " be verbose " ) ;
verboseArg . setCombinable ( true ) ;
Argument fileArg ( " file " , ' f ' , " specifies the path of the file to be opened " ) ;
fileArg . setValueNames ( { " path " } ) ;
fileArg . setRequiredValueCount ( 1 ) ;
2016-07-17 01:26:34 +02:00
fileArg . setEnvironmentVariable ( " PATH " ) ;
2016-06-12 01:56:57 +02:00
Argument filesArg ( " files " , ' f ' , " specifies the path of the file(s) to be opened " ) ;
filesArg . setValueNames ( { " path 1 " , " path 2 " } ) ;
filesArg . setRequiredValueCount ( - 1 ) ;
Argument outputFileArg ( " output-file " , ' o ' , " specifies the path of the output file " ) ;
outputFileArg . setValueNames ( { " path " } ) ;
outputFileArg . setRequiredValueCount ( 1 ) ;
outputFileArg . setRequired ( true ) ;
outputFileArg . setCombinable ( true ) ;
Argument printFieldNamesArg ( " print-field-names " , ' \0 ' , " prints available field names " ) ;
Argument displayFileInfoArg ( " display-file-info " , ' i ' , " displays general file information " ) ;
2016-11-15 22:02:40 +01:00
Argument notAlbumArg ( " album " , ' a ' , " should not be confused with album value " ) ;
2016-06-12 01:56:57 +02:00
displayFileInfoArg . setDenotesOperation ( true ) ;
2016-11-15 22:02:40 +01:00
displayFileInfoArg . setSubArguments ( { & fileArg , & verboseArg , & notAlbumArg } ) ;
2016-06-12 01:56:57 +02:00
Argument fieldsArg ( " fields " , ' \0 ' , " specifies the fields " ) ;
fieldsArg . setRequiredValueCount ( - 1 ) ;
fieldsArg . setValueNames ( { " title " , " album " , " artist " , " trackpos " } ) ;
2016-06-14 00:43:32 +02:00
fieldsArg . setImplicit ( true ) ;
2016-06-12 01:56:57 +02:00
Argument displayTagInfoArg ( " get " , ' p ' , " displays the values of all specified tag fields (displays all fields if none specified) " ) ;
displayTagInfoArg . setDenotesOperation ( true ) ;
2016-11-14 22:38:21 +01:00
displayTagInfoArg . setSubArguments ( { & fieldsArg , & filesArg , & verboseArg , & notAlbumArg } ) ;
2016-11-15 22:02:40 +01:00
parser . setMainArguments ( { & qtConfigArgs . qtWidgetsGuiArg ( ) , & printFieldNamesArg , & displayTagInfoArg , & displayFileInfoArg , & helpArg } ) ;
2016-06-12 01:56:57 +02:00
2016-12-07 21:06:21 +01:00
// error about uncombinable arguments
2016-06-12 01:56:57 +02:00
const char * argv [ ] = { " tageditor " , " get " , " album " , " title " , " diskpos " , " -f " , " somefile " } ;
// try to parse, this should fail
try {
parser . parseArgs ( 7 , argv ) ;
CPPUNIT_FAIL ( " Exception expected. " ) ;
} catch ( const Failure & e ) {
CPPUNIT_ASSERT ( ! strcmp ( e . what ( ) , " The argument \" files \" can not be combined with \" fields \" . " ) ) ;
}
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 ( ) ;
2016-06-12 01:56:57 +02:00
parser . parseArgs ( 7 , argv ) ;
// 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 ( ) ) ;
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! strcmp ( parser . executable ( ) , " tageditor " ) ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT ( ! verboseArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fieldsArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 0 ) , " album " ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 1 ) , " title " ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 2 ) , " diskpos " ) ) ;
CPPUNIT_ASSERT_THROW ( displayTagInfoArg . values ( ) . at ( 3 ) , out_of_range ) ;
2016-12-07 21:06:21 +01:00
// skip empty args
2016-07-03 22:36:48 +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 ( ) ;
2016-06-23 22:06:59 +02:00
parser . parseArgs ( 9 , argv2 ) ;
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 ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 0 ) , " album " ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 1 ) , " title " ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 2 ) , " diskpos " ) ) ;
2016-06-23 22:06:59 +02:00
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 3 ) , " " ) ) ;
CPPUNIT_ASSERT_THROW ( fieldsArg . values ( ) . at ( 4 ) , out_of_range ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT ( filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( filesArg . values ( ) . at ( 0 ) , " somefile " ) ) ;
2016-12-07 21:06:21 +01:00
// error about unknown argument: forget get/-p
2016-07-03 22:36:48 +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 ( ) ;
2016-06-12 01:56:57 +02:00
parser . parseArgs ( 6 , argv3 ) ;
CPPUNIT_FAIL ( " Exception expected. " ) ;
} catch ( const Failure & e ) {
2017-04-05 23:45:23 +02:00
CPPUNIT_ASSERT_EQUAL ( " The specified argument \" album \" is unknown. " s , string ( e . what ( ) ) ) ;
2016-06-12 01:56:57 +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 ) ;
2016-06-14 00:43:32 +02:00
// redirect stderr to check whether warnings are printed correctly
stringstream buffer ;
streambuf * regularCerrBuffer = cerr . rdbuf ( buffer . rdbuf ( ) ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2016-06-14 00:43:32 +02:00
try {
parser . parseArgs ( 6 , argv3 ) ;
} catch ( . . . ) {
cerr . rdbuf ( regularCerrBuffer ) ;
throw ;
}
2016-12-07 21:06:21 +01:00
cerr . rdbuf ( regularCerrBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " The specified argument \" album \" is unknown and will be ignored. \n " s
" The specified argument \" title \" is unknown and will be ignored. \n " s
" The specified argument \" diskpos \" is unknown and will be ignored. \n " s
" The specified argument \" --files \" is unknown and will be ignored. \n " s
" The specified argument \" somefile \" is unknown and will be ignored. \n " s ,
buffer . str ( ) ) ;
2016-06-12 01:56:57 +02:00
// none of the arguments should be present now
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 ( ! displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! fieldsArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! filesArg . isPresent ( ) ) ;
2016-12-07 21:06:21 +01:00
// combined abbreviations like "-vf"
2016-06-12 01:56:57 +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 ( ) ;
2016-06-12 01:56:57 +02:00
parser . parseArgs ( 4 , argv4 ) ;
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 ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fileArg . values ( ) . at ( 0 ) , " test " ) ) ;
CPPUNIT_ASSERT_THROW ( fileArg . values ( ) . at ( 1 ) , out_of_range ) ;
2016-12-07 21:06:21 +01:00
// constraint checking: no multiple occurrences (not resetting verboseArg on purpose)
2016-06-12 01:56:57 +02:00
displayFileInfoArg . reset ( ) , fileArg . reset ( ) ;
try {
parser . parseArgs ( 4 , argv4 ) ;
CPPUNIT_FAIL ( " Exception expected. " ) ;
} catch ( const Failure & e ) {
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT ( ! strcmp ( e . what ( ) , " The argument \" verbose \" mustn't be specified more than 1 time. " ) ) ;
}
2016-12-07 21:06:21 +01:00
// constraint checking: no contraint (not resetting verboseArg on purpose)
displayFileInfoArg . reset ( ) , fileArg . reset ( ) ;
2016-06-12 01:56:57 +02:00
verboseArg . setConstraints ( 0 , - 1 ) ;
parser . parseArgs ( 4 , argv4 ) ;
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 ( ) ;
2016-06-12 01:56:57 +02:00
parser . parseArgs ( 4 , argv4 ) ;
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
// contraint checking: error about missing mandatory argument
2016-06-12 01:56:57 +02:00
const char * argv5 [ ] = { " tageditor " , " -i " , " -f " , " test " } ;
displayFileInfoArg . reset ( ) , fileArg . reset ( ) , verboseArg . reset ( ) ;
try {
parser . parseArgs ( 4 , argv5 ) ;
CPPUNIT_FAIL ( " Exception expected. " ) ;
} catch ( const Failure & e ) {
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT ( ! strcmp ( e . what ( ) , " The argument \" verbose \" must be specified at least 1 time. " ) ) ;
}
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"
2016-10-29 23:54:30 +02:00
const char * argv10 [ ] = { " tageditor " , " -pf " , " test " } ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2016-10-29 23:54:30 +02:00
parser . parseArgs ( 3 , argv10 ) ;
CPPUNIT_ASSERT ( displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! displayFileInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! fileArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT_EQUAL ( filesArg . values ( 0 ) . size ( ) , static_cast < vector < const char * > : : size_type > ( 1 ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( filesArg . values ( 0 ) . front ( ) , " test " ) ) ;
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2016-12-07 21:06:21 +01:00
// constraint checking: no complains about missing -i
2016-06-12 01:56:57 +02:00
const char * argv6 [ ] = { " tageditor " , " -g " } ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2016-06-12 01:56:57 +02:00
parser . parseArgs ( 2 , argv6 ) ;
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: dependend arguments (-f requires -i or -p)
2016-06-12 01:56:57 +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 {
parser . parseArgs ( 3 , argv7 ) ;
CPPUNIT_FAIL ( " Exception expected. " ) ;
} catch ( const Failure & e ) {
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
2017-04-05 23:45:23 +02:00
CPPUNIT_ASSERT_EQUAL ( " The specified argument \" -f \" is unknown. " s , string ( 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
2016-10-29 23:54:30 +02:00
const char * argv11 [ ] = { " tageditor " , " -if=test " } ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2016-10-29 23:54:30 +02:00
parser . parseArgs ( 2 , argv11 ) ;
CPPUNIT_ASSERT ( ! filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fileArg . isPresent ( ) ) ;
CPPUNIT_ASSERT_EQUAL ( fileArg . values ( 0 ) . size ( ) , static_cast < vector < const char * > : : size_type > ( 1 ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fileArg . values ( 0 ) . front ( ) , " test " ) ) ;
2016-12-07 21:06:21 +01:00
// specifying value directly after abbreviation
2016-10-29 23:54:30 +02:00
const char * argv12 [ ] = { " tageditor " , " -iftest " } ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2016-10-29 23:54:30 +02:00
parser . parseArgs ( 2 , argv12 ) ;
CPPUNIT_ASSERT ( ! filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fileArg . isPresent ( ) ) ;
CPPUNIT_ASSERT_EQUAL ( fileArg . values ( 0 ) . size ( ) , static_cast < vector < const char * > : : size_type > ( 1 ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fileArg . values ( 0 ) . front ( ) , " test " ) ) ;
2016-12-07 21:06:21 +01:00
// default argument
2016-06-14 00:43:32 +02:00
const char * argv8 [ ] = { " tageditor " } ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2016-06-14 00:43:32 +02:00
parser . parseArgs ( 1 , argv8 ) ;
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 ( ) ) ;
2016-07-17 01:26:34 +02:00
if ( getenv ( " PATH " ) ) {
CPPUNIT_ASSERT ( fileArg . firstValue ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fileArg . firstValue ( ) , getenv ( " PATH " ) ) ) ;
} 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
2016-11-14 22:38:21 +01: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 ( ) ;
2016-11-14 22:38:21 +01:00
parser . parseArgs ( 8 , argv13 ) ;
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 ( ) ) ;
2016-11-14 22:38:21 +01:00
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 0 ) , " album=test " ) ) ;
2016-06-14 00:43:32 +02:00
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 1 ) , " title " ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 2 ) , " diskpos " ) ) ;
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 ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( filesArg . values ( ) . at ( 0 ) , " somefile " ) ) ;
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
2016-06-14 00:43:32 +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 {
parser . parseArgs ( 5 , argv9 ) ;
CPPUNIT_FAIL ( " Exception expected. " ) ;
} catch ( const Failure & e ) {
CPPUNIT_ASSERT ( ! qtConfigArgs . qtWidgetsGuiArg ( ) . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( e . what ( ) , " Not all parameter for argument \" fields \" provided. You have to provide the following parameter: title album artist trackpos " ) ) ;
}
2017-04-06 00:01:06 +02:00
// nested operations
const char * argv14 [ ] = { " tageditor " , " get " , " fields " , " album=test " , " -f " , " somefile " } ;
parser . resetArgs ( ) ;
fieldsArg . setRequiredValueCount ( - 1 ) ;
fieldsArg . setDenotesOperation ( true ) ;
parser . parseArgs ( 6 , argv14 ) ;
CPPUNIT_ASSERT ( displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fieldsArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 0 ) , " album=test " ) ) ;
// implicit flag still works when argument doesn't denote operation
parser . resetArgs ( ) ;
fieldsArg . setDenotesOperation ( false ) ;
parser . parseArgs ( 6 , argv14 ) ;
CPPUNIT_ASSERT ( displayTagInfoArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( fieldsArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 0 ) , " fields " ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( fieldsArg . values ( ) . at ( 1 ) , " album=test " ) ) ;
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 ;
Argument callbackArg ( " with-callback " , ' t ' , " callback test " ) ;
callbackArg . setRequiredValueCount ( 2 ) ;
2016-07-31 23:20:31 +02:00
callbackArg . setCallback ( [ ] ( const ArgumentOccurrence & occurrence ) {
CPPUNIT_ASSERT_EQUAL ( static_cast < size_t > ( 0 ) , occurrence . index ) ;
CPPUNIT_ASSERT ( occurrence . path . empty ( ) ) ;
CPPUNIT_ASSERT_EQUAL ( static_cast < size_t > ( 2 ) , occurrence . values . size ( ) ) ;
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 ) ;
parser . setMainArguments ( { & callbackArg , & noCallbackArg } ) ;
// test whether callback is invoked when argument with callback is specified
const char * argv [ ] = { " test " , " -t " , " val1 " , " val2 " } ;
try {
parser . parseArgs ( 4 , argv ) ;
} catch ( int i ) {
CPPUNIT_ASSERT_EQUAL ( i , 42 ) ;
}
// test whether callback is not invoked when argument with callback is not specified
callbackArg . reset ( ) ;
const char * argv2 [ ] = { " test " , " -l " , " val1 " , " val2 " } ;
parser . parseArgs ( 4 , argv2 ) ;
}
2016-07-03 22:36:48 +02:00
/*!
* \ brief Tests bash completion .
* \ remarks This tests makes assumptions about the order and the exact output format
* which should be improved .
*/
void ArgumentParserTests : : testBashCompletion ( )
{
ArgumentParser parser ;
HelpArgument helpArg ( parser ) ;
Argument verboseArg ( " verbose " , ' v ' , " be verbose " ) ;
verboseArg . setCombinable ( true ) ;
Argument filesArg ( " files " , ' f ' , " specifies the path of the file(s) to be opened " ) ;
filesArg . setRequiredValueCount ( - 1 ) ;
filesArg . setCombinable ( true ) ;
Argument nestedSubArg ( " nested-sub " , ' \0 ' , " nested sub arg " ) ;
Argument subArg ( " sub " , ' \0 ' , " sub arg " ) ;
subArg . setSubArguments ( { & nestedSubArg } ) ;
Argument displayFileInfoArg ( " display-file-info " , ' i ' , " displays general file information " ) ;
displayFileInfoArg . setDenotesOperation ( true ) ;
displayFileInfoArg . setSubArguments ( { & filesArg , & verboseArg , & subArg } ) ;
Argument fieldsArg ( " fields " , ' \0 ' , " specifies the fields " ) ;
fieldsArg . setRequiredValueCount ( - 1 ) ;
fieldsArg . setPreDefinedCompletionValues ( " title album artist trackpos " ) ;
fieldsArg . setImplicit ( true ) ;
Argument valuesArg ( " values " , ' \0 ' , " specifies the fields " ) ;
valuesArg . setRequiredValueCount ( - 1 ) ;
valuesArg . setPreDefinedCompletionValues ( " title album artist trackpos " ) ;
valuesArg . setImplicit ( true ) ;
valuesArg . setValueCompletionBehavior ( ValueCompletionBehavior : : PreDefinedValues | ValueCompletionBehavior : : AppendEquationSign ) ;
Argument getArg ( " get " , ' g ' , " gets tag values " ) ;
getArg . setSubArguments ( { & fieldsArg , & filesArg } ) ;
Argument setArg ( " set " , ' s ' , " sets tag values " ) ;
setArg . setSubArguments ( { & valuesArg , & filesArg } ) ;
2016-11-26 00:14:45 +01:00
2016-07-03 22:36:48 +02:00
parser . setMainArguments ( { & helpArg , & displayFileInfoArg , & getArg , & setArg } ) ;
// redirect cout to custom buffer
stringstream buffer ;
streambuf * regularCoutBuffer = cout . rdbuf ( buffer . rdbuf ( ) ) ;
try {
2016-12-07 21:06:21 +01:00
// fail due to operation flags not set
2016-07-03 22:36:48 +02:00
const char * const argv1 [ ] = { " se " } ;
2016-12-23 09:55:12 +01:00
ArgumentReader reader ( parser , argv1 , argv1 + 1 , true ) ;
reader . read ( ) ;
2016-12-23 22:41:06 +01:00
parser . printBashCompletion ( 1 , argv1 , 0 , reader ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( regularCoutBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=() \n " s , buffer . str ( ) ) ;
2016-07-03 22:36:48 +02:00
2016-12-07 21:06:21 +01:00
// correct operation arg flags
2016-12-23 09:55:12 +01:00
buffer . str ( string ( ) ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( buffer . rdbuf ( ) ) ;
getArg . setDenotesOperation ( true ) , setArg . setDenotesOperation ( true ) ;
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 ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( regularCoutBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=('set' ) \n " s , buffer . str ( ) ) ;
2016-07-03 22:36:48 +02:00
2016-12-07 21:06:21 +01:00
// argument at current cursor position already specified -> the completion should just return the argument
2016-07-03 22:36:48 +02:00
const char * const argv2 [ ] = { " set " } ;
2016-12-23 09:55:12 +01:00
buffer . str ( string ( ) ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( buffer . rdbuf ( ) ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
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 ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( regularCoutBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=('set' ) \n " s , buffer . str ( ) ) ;
2016-07-03 22:36:48 +02:00
// advance the cursor position -> the completion should propose the next argument
2016-12-23 09:55:12 +01:00
buffer . str ( string ( ) ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( buffer . rdbuf ( ) ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
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 ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( regularCoutBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=('--files' '--values' ) \n " s , buffer . str ( ) ) ;
2016-07-03 22:36:48 +02:00
2017-04-06 00:01:06 +02:00
// nested operations should be proposed as operations
buffer . str ( string ( ) ) ;
cout . rdbuf ( buffer . rdbuf ( ) ) ;
parser . resetArgs ( ) ;
filesArg . setDenotesOperation ( true ) ;
reader . reset ( argv2 , argv2 + 1 ) . read ( ) ;
parser . printBashCompletion ( 1 , argv2 , 1 , reader ) ;
cout . rdbuf ( regularCoutBuffer ) ;
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=('files' '--values' ) \n " s , buffer . str ( ) ) ;
2016-07-03 22:36:48 +02:00
// specifying no args should propose all main arguments
2016-12-23 09:55:12 +01:00
buffer . str ( string ( ) ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( buffer . rdbuf ( ) ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
2017-04-06 00:01:06 +02:00
filesArg . setDenotesOperation ( false ) ;
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 ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( regularCoutBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=('display-file-info' 'get' 'set' '--help' ) \n " s , buffer . str ( ) ) ;
2016-07-03 22:36:48 +02:00
2016-11-26 00:14:45 +01:00
// pre-defined values
2016-07-03 22:36:48 +02:00
const char * const argv3 [ ] = { " get " , " --fields " } ;
2016-12-23 09:55:12 +01:00
buffer . str ( string ( ) ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( buffer . rdbuf ( ) ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
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 ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( regularCoutBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=('title' 'album' 'artist' 'trackpos' '--files' ) \n " s , buffer . str ( ) ) ;
2016-07-03 22:36:48 +02:00
2016-11-26 00:14:45 +01:00
// pre-defined values with equation sign, one letter already present
2016-07-03 22:36:48 +02:00
const char * const argv4 [ ] = { " set " , " --values " , " a " } ;
2016-12-23 09:55:12 +01:00
buffer . str ( string ( ) ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( buffer . rdbuf ( ) ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
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 ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( regularCoutBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=('album=' 'artist=' ); compopt -o nospace \n " s , buffer . str ( ) ) ;
2016-07-03 22:36:48 +02:00
// file names
string iniFilePath = TestUtilities : : testFilePath ( " test.ini " ) ;
2016-10-22 19:32:16 +02:00
iniFilePath . resize ( iniFilePath . size ( ) - 4 ) ;
string mkvFilePath = TestUtilities : : testFilePath ( " test 'with quote'.mkv " ) ;
mkvFilePath . resize ( mkvFilePath . size ( ) - 17 ) ;
TestUtilities : : testFilePath ( " t.aac " ) ;
2016-07-03 22:36:48 +02:00
const char * const argv5 [ ] = { " get " , " --files " , iniFilePath . c_str ( ) } ;
2016-12-23 09:55:12 +01:00
buffer . str ( string ( ) ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( buffer . rdbuf ( ) ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
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 ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( regularCoutBuffer ) ;
2016-10-29 00:50:17 +02:00
// order for file names is not specified
const string res ( buffer . str ( ) ) ;
if ( res . find ( " .mkv " ) < res . find ( " .ini " ) ) {
2017-01-27 18:51:54 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=(' " % mkvFilePath % " ' \" ' \" 'with quote' \" ' \" '.mkv' ' " % iniFilePath + " .ini' ); compopt -o filenames \n " , buffer . str ( ) ) ;
2016-10-29 00:50:17 +02:00
} else {
2017-01-27 18:51:54 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=(' " % iniFilePath % " .ini' ' " % mkvFilePath + " ' \" ' \" 'with quote' \" ' \" '.mkv' ); compopt -o filenames \n " , buffer . str ( ) ) ;
2016-10-29 00:50:17 +02:00
}
2016-07-03 22:36:48 +02:00
// sub arguments
const char * const argv6 [ ] = { " set " , " -- " } ;
2016-12-23 09:55:12 +01:00
buffer . str ( string ( ) ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( buffer . rdbuf ( ) ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
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 ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( regularCoutBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=('--files' '--values' ) \n " s , buffer . str ( ) ) ;
2016-07-03 22:36:48 +02:00
// nested sub arguments
const char * const argv7 [ ] = { " -i " , " --sub " , " -- " } ;
2016-12-23 09:55:12 +01:00
buffer . str ( string ( ) ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( buffer . rdbuf ( ) ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
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 ) ;
2016-07-03 22:36:48 +02:00
cout . rdbuf ( regularCoutBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=('--files' '--nested-sub' '--verbose' ) \n " s , buffer . str ( ) ) ;
2016-07-03 22:36:48 +02:00
2016-11-26 00:14:45 +01:00
// started pre-defined values with equation sign, one letter already present, last value matches
const char * const argv8 [ ] = { " set " , " --values " , " t " } ;
2016-12-23 09:55:12 +01:00
buffer . str ( string ( ) ) ;
2016-11-26 00:14:45 +01:00
cout . rdbuf ( buffer . rdbuf ( ) ) ;
2016-12-07 21:06:21 +01:00
parser . resetArgs ( ) ;
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 ) ;
2016-11-26 00:14:45 +01:00
cout . rdbuf ( regularCoutBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=('title=' 'trackpos=' ); compopt -o nospace \n " s , buffer . str ( ) ) ;
2016-11-26 00:14:45 +01:00
2016-12-23 22:41:06 +01:00
// combined abbreviations
const char * const argv9 [ ] = { " -gf " } ;
buffer . str ( string ( ) ) ;
cout . rdbuf ( buffer . rdbuf ( ) ) ;
parser . resetArgs ( ) ;
reader . reset ( argv9 , argv9 + 1 ) . read ( ) ;
parser . printBashCompletion ( 1 , argv9 , 0 , reader ) ;
cout . rdbuf ( regularCoutBuffer ) ;
2017-02-06 18:23:59 +01:00
CPPUNIT_ASSERT_EQUAL ( " COMPREPLY=('-gf' ) \n " s , buffer . str ( ) ) ;
2016-12-23 22:41:06 +01:00
buffer . str ( string ( ) ) ;
cout . rdbuf ( buffer . rdbuf ( ) ) ;
parser . resetArgs ( ) ;
reader . reset ( argv9 , argv9 + 1 ) . read ( ) ;
parser . printBashCompletion ( 1 , argv9 , 1 , reader ) ;
cout . rdbuf ( regularCoutBuffer ) ;
CPPUNIT_ASSERT_EQUAL ( static_cast < string : : size_type > ( 0 ) , buffer . str ( ) . find ( " COMPREPLY=('--fields' " ) ) ;
2016-07-03 22:36:48 +02:00
} catch ( . . . ) {
cout . rdbuf ( regularCoutBuffer ) ;
throw ;
}
}