| /* |
| * 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> – 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> – 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 ( ) ) |
| } |
| |
| } |