2016-06-12 01:56:57 +02:00
# include "./testutils.h"
# include "../application/argumentparser.h"
# include "../application/failure.h"
# include "../application/fakeqtconfigarguments.h"
# include "resources/config.h"
# include <cppunit/extensions/HelperMacros.h>
# include <cppunit/TestFixture.h>
# include <cstring>
using namespace std ;
using namespace ApplicationUtilities ;
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 ) ;
CPPUNIT_TEST_SUITE_END ( ) ;
public :
void setUp ( ) ;
void tearDown ( ) ;
void testArgument ( ) ;
void testParsing ( ) ;
void testCallbacks ( ) ;
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 ( ) ) ;
}
/*!
* \ 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 ) ;
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 " ) ;
displayFileInfoArg . setDenotesOperation ( true ) ;
displayFileInfoArg . setSubArguments ( { & fileArg , & verboseArg } ) ;
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 ) ;
displayTagInfoArg . setSubArguments ( { & fieldsArg , & filesArg , & verboseArg } ) ;
parser . setMainArguments ( { & qtConfigArgs . qtWidgetsGuiArg ( ) , & printFieldNamesArg , & displayTagInfoArg , & displayFileInfoArg , & helpArg } ) ;
// define some argument values
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 \" . " ) ) ;
}
// try to parse again, but adjust the configuration for a successful parse
displayTagInfoArg . reset ( ) , fieldsArg . reset ( ) , filesArg . reset ( ) ;
filesArg . setCombinable ( true ) ;
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 ) ;
// define the same arguments in a different way
const char * argv2 [ ] = { " tageditor " , " -p " , " album " , " title " , " diskpos " , " --file " , " somefile " } ;
// reparse the args
displayTagInfoArg . reset ( ) , fieldsArg . reset ( ) , filesArg . reset ( ) ;
parser . parseArgs ( 7 , argv2 ) ;
// 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 " ) ) ;
CPPUNIT_ASSERT_THROW ( displayTagInfoArg . values ( ) . at ( 3 ) , out_of_range ) ;
CPPUNIT_ASSERT ( filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( filesArg . values ( ) . at ( 0 ) , " somefile " ) ) ;
// forget "get"/"-p"
const char * argv3 [ ] = { " tageditor " , " album " , " title " , " diskpos " , " --file " , " somefile " } ;
displayTagInfoArg . reset ( ) , fieldsArg . reset ( ) , filesArg . reset ( ) ;
// a parsing error should occur because the argument "album" is not defined
try {
parser . parseArgs ( 6 , argv3 ) ;
CPPUNIT_FAIL ( " Exception expected. " ) ;
} catch ( const Failure & e ) {
CPPUNIT_ASSERT ( ! strcmp ( e . what ( ) , " The specified argument \" album \" is unknown and will be ignored. " ) ) ;
}
// repeat the test, but this time just ignore the undefined argument
displayTagInfoArg . reset ( ) , fieldsArg . reset ( ) , filesArg . reset ( ) ;
parser . setIgnoreUnknownArguments ( true ) ;
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 ( ) ) ;
try {
parser . parseArgs ( 6 , argv3 ) ;
cerr . rdbuf ( regularCerrBuffer ) ;
} catch ( . . . ) {
cerr . rdbuf ( regularCerrBuffer ) ;
throw ;
}
CPPUNIT_ASSERT ( ! strcmp ( buffer . str ( ) . data ( ) , " The specified argument \" album \" is unknown and will be ignored. \n "
" The specified argument \" title \" is unknown and will be ignored. \n "
" The specified argument \" diskpos \" is unknown and will be ignored. \n "
" The specified argument \" --file \" is unknown and will be ignored. \n "
" The specified argument \" somefile \" is unknown and will be ignored. \n " ) ) ;
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 ( ) ) ;
// test abbreviations like "-vf"
const char * argv4 [ ] = { " tageditor " , " -i " , " -vf " , " test " } ;
displayTagInfoArg . reset ( ) , fieldsArg . reset ( ) , filesArg . reset ( ) ;
parser . setIgnoreUnknownArguments ( false ) ;
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 ) ;
// don't reset verbose argument to test constraint checking
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. " ) ) ;
}
// relax constraint
displayFileInfoArg . reset ( ) , fileArg . reset ( ) , verboseArg . reset ( ) ;
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
// make verbose mandatory
verboseArg . setRequired ( true ) ;
displayFileInfoArg . reset ( ) , fileArg . reset ( ) , verboseArg . reset ( ) ;
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
// make it complain about missing argument
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. " ) ) ;
}
// it should not complain if -i is not present
const char * argv6 [ ] = { " tageditor " , " -g " } ;
displayFileInfoArg . reset ( ) , fileArg . reset ( ) , verboseArg . reset ( ) ;
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
// it should not be possible to specify -f without -i or -p
const char * argv7 [ ] = { " tageditor " , " -f " , " test " } ;
2016-06-14 00:43:32 +02:00
displayFileInfoArg . reset ( ) , fileArg . reset ( ) , verboseArg . reset ( ) , qtConfigArgs . qtWidgetsGuiArg ( ) . reset ( ) ;
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 ( ) ) ;
2016-06-12 01:56:57 +02:00
CPPUNIT_ASSERT ( ! strcmp ( e . what ( ) , " The specified argument \" -f \" is unknown and will be ignored. " ) ) ;
}
2016-06-14 00:43:32 +02:00
// test default argument
const char * argv8 [ ] = { " tageditor " } ;
displayFileInfoArg . reset ( ) , fileArg . reset ( ) , verboseArg . reset ( ) ;
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 ( ) ) ;
// test required value count constraint with sufficient number of provided parameters
qtConfigArgs . qtWidgetsGuiArg ( ) . reset ( ) ;
fieldsArg . setRequiredValueCount ( 3 ) ;
verboseArg . setRequired ( false ) ;
parser . parseArgs ( 7 , argv2 ) ;
// 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 ( ) ) ;
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 ) ;
CPPUNIT_ASSERT ( filesArg . isPresent ( ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( filesArg . values ( ) . at ( 0 ) , " somefile " ) ) ;
// test required value count constraint with insufficient number of provided parameters
displayTagInfoArg . reset ( ) , fieldsArg . reset ( ) , filesArg . reset ( ) ;
fieldsArg . setRequiredValueCount ( 4 ) ;
const char * argv9 [ ] = { " tageditor " , " -p " , " album " , " title " , " diskpos " } ;
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 " ) ) ;
}
2016-06-12 01:56:57 +02:00
}
void ArgumentParserTests : : testCallbacks ( )
{
ArgumentParser parser ;
Argument callbackArg ( " with-callback " , ' t ' , " callback test " ) ;
callbackArg . setRequiredValueCount ( 2 ) ;
callbackArg . setCallback ( [ ] ( const vector < const char * > & values ) {
CPPUNIT_ASSERT ( values . size ( ) = = 2 ) ;
CPPUNIT_ASSERT ( ! strcmp ( values [ 0 ] , " val1 " ) ) ;
CPPUNIT_ASSERT ( ! strcmp ( values [ 1 ] , " val2 " ) ) ;
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 ) ;
}