blob: 6a5a06536001e6cd740d376a0f67a5ae9566f32a [file] [log] [blame]
/*
* Copyright 2003-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing permissions and limitations under the
* License.
*/
package groovy.util
import org.apache.commons.cli.GnuParser
import org.apache.commons.cli.Option
import org.apache.commons.cli.OptionBuilder
import org.apache.commons.cli.PosixParser
import org.apache.commons.cli.BasicParser
/**
* Test class for the CliBuilder -- but then that is obvious from the name :-)
*
* <p>There appear to be issues when using the <code>PosixParser</code> &ndash; when an option with a
* parameter is passed using a long form and a single letter parameter of some sort has been declared (the
* problem does not occur if no single letter option has been declared) then the value "--" is returned
* instead of the option parameter value. This problem does not happen using the
* <code>GnuParser</code>.</p>
*
* <p>There appears to be an issue with <code>GnuParser</code> &ndash; if only a long option is defined
* then the usual Groovy syntax for accessing the option fails to work. It is fine if a short option of
* some sort is defined. This must be a <code>CliBuilder</code>/<code>OptionAccessor</code> problem. This
* problem does not happen with the <code>PosixParser</code>.</p>
*
* <p>Commons CLI 1.0 appears not to be able to access arguments using a long name, if that option has a
* short name -- in this case access is only using a short name. This means it is possible to work with
* long name option if and only if they have no short name.</p>
*
* <p>Commons CLI 1.1 whilst it has fixed most of the problems in 1.0, appears to have a broken
* getOptionValues -- it returns only the first value -- and so is worse than useless.</p>
*
* <p>1.0 PosixBuilder removes unrecognized single letter options silently. 1.1 version may also do this.
* GnuParser behaves according to the <code>stopAtNonOption</code> parameter -- throw
* <code>UnrecognizedOptionException</code> when <code>false</code>, terminate parse leaving everything
* following unprocessed if <code>true</code>.
*
* @author Dierk König
* @author Russel Winder
*/
//// Tests marked with 4 slashes work in Commons CLI 1.1 but not in 1.0. There are other differences, read
//// the comments.
class CliBuilderTest extends GroovyTestCase {
private StringWriter stringWriter
private PrintWriter printWriter
void setUp ( ) {
stringWriter = new StringWriter ( )
printWriter = new PrintWriter ( stringWriter )
}
private final expectedParameter = 'ASCII'
private final usageString = 'groovy [option]* filename'
private void runSample ( parser , optionList ) {
def cli = new CliBuilder ( usage : usageString , writer : printWriter , parser : parser )
cli.h ( longOpt : 'help', 'usage information' )
cli.c ( argName : 'charset' , args :1 , longOpt : 'encoding' , 'character encoding' )
cli.i ( argName : 'extension' , optionalArg : true, 'modify files in place, create backup if extension is given (e.g. \'.bak\')' )
def stringified = cli.options.toString ( )
assert stringified =~ /i=. option: i :: modify files in place, create backup if extension is given/
assert stringified =~ /c=. option: c encoding :: character encoding/
assert stringified =~ /h=. option: h help :: usage information/
assert stringified =~ /encoding=. option: c encoding :: character encoding/
assert stringified =~ /help=. option: h help :: usage information/
def options = cli.parse ( optionList )
assert options.hasOption ( 'h' )
////assert options.hasOption ( 'help' )
assert options.h
////assert options.help
if ( options.h ) { cli.usage ( ) }
def expectedUsage = """usage: $usageString
-c,--encoding <charset> character encoding
-h,--help usage information
-i modify files in place, create backup if
extension is given (e.g. '.bak')"""
assertEquals ( expectedUsage , stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
stringWriter = new StringWriter ( )
printWriter = new PrintWriter ( stringWriter )
cli.writer = printWriter
////if ( options.help ) { cli.usage ( ) }
////assertEquals ( expectedUsage , stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
assert options.hasOption ( 'c' )
assert options.c
////assert options.hasOption ( 'encoding' )
////assert options.encoding
assertEquals ( expectedParameter, options.getOptionValue ( 'c' ) )
assertEquals ( expectedParameter, options.c )
////assertEquals ( expectedParameter, options.getOptionValue ( 'encoding' ) )
////assertEquals ( expectedParameter, options.encoding )
assertEquals ( false, options.noSuchOptionGiven )
assertEquals ( false, options.hasOption ( 'noSuchOptionGiven' ) )
assertEquals ( false, options.x )
assertEquals ( false, options.hasOption ( 'x' ) )
}
void testSampleShort_BasicParser ( ) {
runSample ( new BasicParser ( ) , [ '-h' , '-c' , expectedParameter ] )
}
void testSampleShort_GnuParser ( ) {
runSample ( new GnuParser ( ) , [ '-h' , '-c' , expectedParameter ] )
}
void testSampleShort_PosixParser ( ) {
runSample ( new PosixParser ( ) , [ '-h' , '-c' , expectedParameter ] )
}
void testSampleLong_BasicParser ( ) {
runSample ( new GnuParser ( ) , [ '--help' , '--encoding' , expectedParameter ] )
}
void testSampleLong_GnuParser ( ) {
runSample ( new GnuParser ( ) , [ '--help' , '--encoding' , expectedParameter ] )
}
/*
* Cannot run this test because of the "--" instead of "ASCII" problem. See
* testLongAndShortOpts_PosixParser below. This is a 1.0 and a 1.1 problem.
*/
void XXX_testSampleLong_PosixParser ( ) {
runSample ( new PosixParser ( ) , [ '--help' , '--encoding' , expectedParameter ] )
}
void testMultipleArgs ( ) {
def cli = new CliBuilder ( )
cli.a ( longOpt : 'arg' , args : 2 , valueSeparator : ',' as char , 'arguments' )
def options = cli.parse ( [ '-a' , '1,2' ] )
assertEquals ( '1' , options.a )
assertEquals ( [ '1' , '2' ] , options.as )
////assertEquals ( '1' , options.arg )
////assertEquals ( [ '1' , '2' ] , options.args )
}
void testArgs ( ) {
def cli = new CliBuilder ( )
cli.a ( [:] , '' )
def options = cli.parse ( [ '-a' , '1' , '2' ] )
assertEquals ( [ '1' , '2' ] , options.arguments ( ) )
}
void testFailedParsePrintsUsage ( ) {
def cli = new CliBuilder ( writer : printWriter )
cli.x ( required : true , 'message' )
def options = cli.parse ( [ ] )
//
// FIXME: This test is very fragile and is bound to fail on different locales and versions of commons-cli... :-(
//
assertEquals ( '''error: -x
usage: groovy
-x message''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
}
void testLongOptsOnly_GnuParser ( ) {
def cli = new CliBuilder ( writer : printWriter , parser : new GnuParser ( ) )
def anOption = OptionBuilder.withLongOpt ( 'anOption' ).hasArg ( ).withDescription ( 'An option.' ).create ( )
cli.options.addOption ( anOption )
def options = cli.parse ( [ '-v' , '--anOption' , 'something' ] )
assert options.getOptionValue ( 'anOption' ) == null
assert false == options.anOption
}
void testLongOptsOnly_PosixParser ( ) {
def cli = new CliBuilder ( writer : printWriter , parser : new PosixParser ( ) )
def anOption = OptionBuilder.withLongOpt ( 'anOption' ).hasArg ( ).withDescription ( 'An option.' ).create ( )
cli.options.addOption ( anOption )
def options = cli.parse ( [ '-v' , '--anOption' , 'something' ] )
cli.usage ( )
assert 'something' == options.getOptionValue ( 'anOption' )
assert 'something' == options.anOption
assert ! options.v
}
private createOptionsWithLongAndShortOpts ( parser ) {
def cli = new CliBuilder ( writer : printWriter , parser : parser )
def anOption = OptionBuilder.withLongOpt ( 'anOption' ).hasArg ( ).withDescription ( 'An option.' ).create ( )
cli.options.addOption ( anOption )
cli.v ( longOpt : 'verbose' , 'verbose mode' )
def options = cli.parse ( [ '-v' , '--anOption' , 'something' ] )
cli.usage ( )
assert options.v
return options
}
void testLongAndShortOpts_BasicParser ( ) {
def options = createOptionsWithLongAndShortOpts ( new BasicParser ( ) )
assertEquals ( 'something' , options.getOptionValue ( 'anOption' ) )
assertEquals ( 'something' , options.anOption )
}
void testLongAndShortOpts_PosixParser ( ) {
def options = createOptionsWithLongAndShortOpts ( new PosixParser ( ) )
//
// This represents what currently happens, not what should happen. The problem needs investigating so
// that the expected results here can be set to 'something' as it should.
//
assertEquals ( '--' , options.getOptionValue ( 'anOption' ) )
assertEquals ( '--' , options.anOption )
}
void testLongAndShortOpts_GnuParser ( ) {
def options = createOptionsWithLongAndShortOpts ( new GnuParser ( ) )
assertEquals ( 'something' , options.getOptionValue ( 'anOption' ) )
assertEquals ( 'something' , options.anOption )
}
void unknownOptions ( parser ) {
def cli = new CliBuilder ( parser : parser )
cli.v ( longOpt : 'verbose' , 'verbose mode' )
def options = cli.parse ( [ '-x' , '-yyy' , '--zzz' , 'something' ] )
assertEquals ( [ '-x' , '-yyy' , '--zzz' , 'something' ] , options.arguments ( ) )
}
void testUnrecognizedOptions_BasicParser ( ) { unknownOptions ( new BasicParser ( ) ) }
void testUnrecognizedOptions_GnuParser ( ) { unknownOptions ( new GnuParser ( ) ) }
//
// The Posix Parser absorbs unrecognized options rather than passing them through. It is not clear if
// this is the correct or incorrect behaviour.
//
void testUnrecognizedOptions_PosixParser ( ) {
def cli = new CliBuilder ( parser : new PosixParser ( ) )
cli.v ( longOpt : 'verbose' , 'verbose mode' )
def options = cli.parse ( [ '-x' , '-yyy' , '--zzz' , 'something' ] )
assertEquals ( [ '-yyy' , '--zzz' , 'something' ] , options.arguments ( ) )
}
void bizarreProcessing ( parser ) {
def cli = new CliBuilder ( parser : parser )
def options = cli.parse ( [ '-xxxx' ] )
assertEquals ( [ '-xxxx' ] , options.arguments ( ) )
}
void testBizarreProcessing_BasicParser ( ) { bizarreProcessing ( new BasicParser ( ) ) }
void testBizarreProcessing_GnuParser ( ) { bizarreProcessing ( new GnuParser ( ) ) }
//
// This behaviour by the PosixParser is truly bizarre and so militates in favour of the switch from
// PosixParser to GnuParser as the CliBuilder default.
//
void testPosixBizarreness ( ) {
def cli = new CliBuilder ( parser : new PosixParser ( ) )
def options = cli.parse ( [ '-xxx' ] )
assertEquals ( [ 'xxx' , 'xx' , 'x' ] , options.arguments ( ) )
}
private void multipleOccurrencesSeparateSeparate ( parser ) {
def cli = new CliBuilder ( parser : parser )
cli.a ( longOpt : 'arg' , args : Option.UNLIMITED_VALUES , 'arguments' )
def options = cli.parse ( [ '-a' , '1' , '-a' , '2' , '-a' , '3' ] )
assertEquals ( '1' , options.a )
assertEquals ( [ '1' , '2' , '3' ] , options.as )
////assertEquals ( '1' , options.arg )
////assertEquals ( [ '1' , '2' , '3' ] , options.args )
assertEquals ( [ ] , options.arguments ( ) )
}
void testMultipleOccurrencesSeparateSeparate_BasicParser ( ) { multipleOccurrencesSeparateSeparate ( new BasicParser ( ) ) }
void testMultipleOccurrencesSeparateSeparate_GnuParser ( ) { multipleOccurrencesSeparateSeparate ( new GnuParser ( ) ) }
void testMultipleOccurrencesSeparateSeparate_PosixParser ( ) { multipleOccurrencesSeparateSeparate ( new PosixParser ( ) ) }
private void multipleOccurrencesSeparateJuxtaposed ( parser ) {
def cli = new CliBuilder ( parser : parser )
cli.a ( longOpt : 'arg' , args : Option.UNLIMITED_VALUES , 'arguments' )
def options = cli.parse ( [ '-a1' , '-a2' , '-a3' ] )
assertEquals ( '1' , options.a )
assertEquals ( [ '1' , '2' , '3' ] , options.as )
////assertEquals ( '1' , options.arg )
////assertEquals ( [ '1' , '2' , '3' ] , options.args )
assertEquals ( [ ] , options.arguments ( ) )
}
//
// BasicParser cannot handle this one.
//
//void testMultipleOccurrencesSeparateJuxtaposed_BasicParser ( ) { multipleOccurrencesSeparateJuxtaposed ( new BasicParser ( ) ) }
void testMultipleOccurrencesSeparateJuxtaposed_GnuParser ( ) { multipleOccurrencesSeparateJuxtaposed ( new GnuParser ( ) ) }
void testMultipleOccurrencesSeparateJuxtaposed_PosixParser ( ) { multipleOccurrencesSeparateJuxtaposed ( new PosixParser ( ) ) }
private void multipleOccurrencesTogetherSeparate ( parser ) {
def cli = new CliBuilder ( parser : parser )
cli.a ( longOpt : 'arg' , args : Option.UNLIMITED_VALUES , valueSeparator : ',' as char , 'arguments' )
def options = cli.parse ( [ '-a 1,2,3' ] )
assertEquals ( ' 1' , options.a )
assertEquals ( [ ' 1' , '2' , '3' ] , options.as )
////assertEquals ( ' 1' , options.arg )
////assertEquals ( [ ' 1' , '2' , '3' ] , options.args )
assertEquals ( [ ] , options.arguments ( ) )
}
//
// BasicParser cannot handle this one.
//
//void testMultipleOccurrencesTogetherSeparate_BasicParser ( ) { multipleOccurrencesTogetherSeparate ( new BasicParser ( ) ) }
void testMultipleOccurrencesTogetherSeparate_GnuParser ( ) { multipleOccurrencesTogetherSeparate ( new GnuParser ( ) ) }
void testMultipleOccurrencesTogetherSeparate_PosixParser ( ) { multipleOccurrencesTogetherSeparate ( new PosixParser ( ) ) }
private void multipleOccurrencesTogetherJuxtaposed ( parser ) {
def cli = new CliBuilder ( parser : parser )
cli.a ( longOpt : 'arg' , args : Option.UNLIMITED_VALUES , valueSeparator : ',' as char , 'arguments' )
def options = cli.parse ( [ '-a1,2,3' ] )
assertEquals ( '1' , options.a )
assertEquals ( [ '1' , '2' , '3' ] , options.as )
////assertEquals ( '1' , options.arg )
////assertEquals ( [ '1' , '2' , '3' ] , options.args )
assertEquals ( [ ] , options.arguments ( ) )
}
//
// BasicParser cannot handle this one.
//
//void testMultipleOccurrencesTogetherJuxtaposed_BasicParser ( ) { multipleOccurrencesTogetherJuxtaposed ( new BasicParser ( ) ) }
void testMultipleOccurrencesTogetherJuxtaposed_GnuParser ( ) { multipleOccurrencesTogetherJuxtaposed ( new GnuParser ( ) ) }
void testMultipleOccurrencesTogetherJuxtaposed_PosixParser ( ) { multipleOccurrencesTogetherJuxtaposed ( new PosixParser ( ) ) }
/*
* Investigate problems with unrecognized options. Should add the BasicParser here as well.
*/
void testUnrecognizedOptionSilentlyIgnored_GnuParser ( ) {
def cli = new CliBuilder ( usage : usageString , writer : printWriter , parser : new GnuParser ( ) )
def options = cli.parse ( [ '-v' ] )
/*
* The behaviour here depends on whether stopAtNonOption is true or false in the call to parser.parse
* in CliBuilder.parse. Currently this is true, so parse simply terminates. Any options after the
* unrecognized one are unprocessed.
*
assertEquals ( '''error: Unrecognized option: -v
usage: groovy [option]* filename''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
assertEquals ( null , options )
*/
assertEquals ( '''''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
assert !options.v
}
void testUnrecognizedOptionSilentlyIgnored_PosixParser ( ) {
//
// The PosixParser silently absorbs unrecognized options.
//
def cli = new CliBuilder ( usage : usageString , writer : printWriter , parser : new PosixParser ( ) )
def options = cli.parse ( [ '-v' ] )
assertEquals ( '''''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
assert ! options.v
}
void testUnrecognizedOptionTerminatesParse_GnuParser ( ) {
def cli = new CliBuilder ( usage : usageString , writer : printWriter , parser : new GnuParser ( ) )
cli.h ( longOpt : 'help', 'usage information' )
def options = cli.parse ( [ '-v' , '-h' ] )
/*
* The behaviour here depends on whether stopAtNonOption is true or false in the call to parser.parse
* in CliBuilder.parse. Currently this is true, so parse simply terminates. Any options after the
* unrecognized one are unprocessed.
*
assertEquals ( '''error: Unrecognized option: -v
usage: groovy [option]* filename''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
assertEquals ( null , options )
*/
assertEquals ( '''''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
assert !options.v
//
// Why is options.h not recognized here. This is weird.
//
//assert ! options.h
assertEquals ( [ '-v' , '-h' ] , options.arguments ( ) )
}
void testUnrecognizedOptionTerminatesParse_PosixParser ( ) {
//
// The PosixParser silently absorbs unrecognized options.
//
def cli = new CliBuilder ( usage : usageString , writer : printWriter , parser : new PosixParser ( ) )
cli.h ( longOpt : 'help', 'usage information' )
def options = cli.parse ( [ '-v' , '-h' ] )
assertEquals ( '''''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
assert ! options.v
//
// Why is options.h not recognized here. This is weird.
//
//assert ! options.h
assertEquals ( [ ] , options.arguments ( ) )
}
}