blob: b1adab8070256d27c0db575767d16e0fd965273f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.cli.picocli
import groovy.cli.Option
import groovy.cli.Unparsed
import groovy.test.GroovyTestCase
import groovy.transform.ToString
import groovy.transform.TypeChecked
import picocli.CommandLine.DuplicateOptionAnnotationsException
import java.math.RoundingMode
import java.text.SimpleDateFormat
import static picocli.CommandLine.Model.OptionSpec.builder
/**
* Test class for the picocli version of CliBuilder.
* <p>
* Ensures compatibility with the commons-cli version of CliBuilder and tests
* picocli-specific functionality.
*/
class CliBuilderTest extends GroovyTestCase {
/** Commons-cli constant that specifies the number of argument values is infinite */
private static final int COMMONS_CLI_UNLIMITED_VALUES = -2;
private StringWriter stringWriter
private PrintWriter printWriter
void setUp() {
resetPrintWriter()
}
private final expectedParameter = 'ASCII'
private final usageString = 'groovy [option]* filename'
private void runSample(optionList) {
resetPrintWriter()
def cli = new CliBuilder(usage: usageString, writer: printWriter)
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 specified (e.g. \'.bak\')')
def stringified = cli.commandSpec.toString()
// assert stringified =~ /i=\[ option: i :: modify files in place, create backup if extension is given/
// assert stringified =~ /c=\[ option: c encoding \[ARG] :: character encoding/
// assert stringified =~ /h=\[ option: h help :: usage information/
// assert stringified =~ /encoding=\[ option: c encoding \[ARG] :: 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=[<extension>] modify files in place, create backup if extension
is specified (e.g. '.bak')"""
assertEquals(expectedUsage, stringWriter.toString().tokenize('\r\n').join('\n'))
resetPrintWriter()
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'))
}
private void resetPrintWriter() {
stringWriter = new StringWriter()
printWriter = new PrintWriter(stringWriter)
}
void testSampleShort() {
runSample(['-h', '-c', expectedParameter])
}
void testSampleLong() {
runSample( ['--help', '--encoding', expectedParameter])
}
void testSimpleArg() {
def cli = new CliBuilder()
cli.a([:], '')
def options = cli.parse(['-a', '1', '2'])
assertEquals(['1', '2'], options.arguments())
}
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 testPosixNullValueHandledCorrectly_inConstructor() {
def cli = new CliBuilder()
assert cli.posix == true
assert cli.parser.posixClusteredShortOptionsAllowed()
cli = new CliBuilder(posix: false)
assert cli.posix == false
assert !cli.parser.posixClusteredShortOptionsAllowed()
cli = new CliBuilder(posix: null)
assert cli.posix == null
assert !cli.parser.posixClusteredShortOptionsAllowed()
}
void testPosixNullValueHandledCorrectly_inSetter() {
def cli = new CliBuilder()
assert cli.posix == true
assert cli.parser.posixClusteredShortOptionsAllowed()
cli.posix = false
assert cli.posix == false
assert !cli.parser.posixClusteredShortOptionsAllowed()
cli = new CliBuilder()
assert cli.posix == true
assert cli.parser.posixClusteredShortOptionsAllowed()
cli.posix = null
assert cli.posix == null
assert !cli.parser.posixClusteredShortOptionsAllowed()
}
void testFailedParsePrintsUsage() {
def cli = new CliBuilder(writer: printWriter)
cli.x(required: true, 'message')
cli.parse([])
// NB: This test is very fragile and is bound to fail on different locales and versions of commons-cli... :-(
assert stringWriter.toString() == String.format(
"error: Missing required option '-x'%n" +\
"Usage: groovy -x%n" +\
" -x message%n")
}
void testLongOptsOnly_nonOptionShouldStopArgProcessing() {
def cli = new CliBuilder()
def anOption = builder('anOption').arity("1").description('An option.')
.build()
cli.commandSpec.addOption(anOption)
def options = cli.parse(['-v', '--anOption', 'something'])
// no options should be found
assert options.getOptionValue('anOption') == null
assert !options.anOption
assert !options.v
// arguments should be still sitting there
assert options.arguments() == ['-v', '--anOption', 'something']
}
void testLongAndShortOpts_allOptionsValid() {
def cli = new CliBuilder()
def anOption = builder('--anOption').arity("1").description('An option.').build()
cli.commandSpec.addOption(anOption)
cli.v(longOpt: 'verbose', 'verbose mode')
def options = cli.parse(['-v', '--anOption', 'something'])
assert options.v
assert options.getOptionValue('anOption') == 'something'
assert options.anOption == 'something'
assert !options.arguments()
}
void testUnrecognizedOptions() {
def cli = new CliBuilder()
cli.v(longOpt: 'verbose', 'verbose mode')
def options = cli.parse(['-x', '-yyy', '--zzz', 'something'])
assertEquals(['-x', '-yyy', '--zzz', 'something'], options.arguments())
}
void testMultipleOccurrencesSeparateSeparate() {
def cli = new CliBuilder()
cli.a(longOpt: 'arg', args: COMMONS_CLI_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 testMandatoryParametersDoNotConsumeOtherOptions() {
def cli = new CliBuilder()
cli.a(args: 2, 'arguments')
cli.b(args: 2, valueSeparator: ',', 'arguments')
cli.c(args: '+', valueSeparator: ',', 'arguments')
def options = cli.parse(['-a', '1', '-a', '2'])
assertNull(options)
options = cli.parse(['-a', '1', '-a', '2', '-a', '3'])
assertNull(options)
}
void testMultipleOccurrencesSeparateSeparate3() {
def cli = new CliBuilder()
// cli.a(longOpt: 'arg', args: COMMONS_CLI_UNLIMITED_VALUES, 'arguments')
cli.a(args: 2, 'arguments')
cli.b(args: 2, valueSeparator: ',', 'arguments')
cli.c(args: '+', valueSeparator: ',', 'arguments')
def options = cli.parse(['-a', '1'])
assertNull(options)
options = cli.parse(['-a1'])
assertEquals('1', options.a)
assertEquals(['1'], options.as)
// options = cli.parse(['-a', '1', '-a', '2']) // TODO
// assertNull(options)
options = cli.parse(['-a1', '-a2'])
assertEquals('1', options.a)
assertEquals(['1', '2'], options.as)
options = cli.parse(['-a1', '-a2', '-a3'])
assertEquals('1', options.a)
assertEquals(['1', '2', '3'], options.as)
// options = cli.parse(['-a', '1', '-a', '2', '-a', '3'])
// assertNull(options)
options = cli.parse(['-a', '1', '2'])
assertEquals('1', options.a)
assertEquals(['1', '2'], options.as)
options = cli.parse(['-a1', '2'])
assertEquals('1', options.a)
assert options.arguments() == ['2']
assertEquals(['1'], options.as)
options = cli.parse(['-a', '1', '2', '-a', '3', '4'])
assertEquals('1', options.a)
assertEquals(['1', '2', '3', '4'], options.as)
options = cli.parse(['-a', '1', '2', '-a3', '-a4', '-a5'])
assertEquals('1', options.a)
assertEquals(['1', '2', '3', '4', '5'], options.as)
options = cli.parse(['-a', '1', '2', '-a3', '-a', '4', '5' ])
assertEquals('1', options.a)
assertEquals(['1', '2', '3', '4', '5'], options.as)
options = cli.parse(['-a1', '2', '-a3', '4'])
assertEquals('1', options.a)
assert options.arguments() == ['2', '-a3', '4']
//assertEquals(['1', '2', '3', '4'], options.as)
options = cli.parse(['-b1,2'])
assert options.bs == ['1', '2']
options = cli.parse(['-b1,2,3'])
assert options.bs == ['1', '2,3']
options = cli.parse(['-b', '1,2', '3,4'])
assert options.bs == ['1', '2']
assert options.arguments() == ['3,4']
options = cli.parse(['-b', '1,2', '-b', '3,4'])
assert options.bs == ['1', '2', '3', '4']
// assert options.arguments() == []
options = cli.parse(['-b', '1', '2', '-b', '3', '4'])
assert options.bs == ['1', '2', '3', '4']
}
void testMultipleOccurrencesSeparateJuxtaposed() {
def cli = new CliBuilder()
// cli.a ( longOpt : 'arg' , args : COMMONS_CLI_UNLIMITED_VALUES , 'arguments' )
cli.a(longOpt: 'arg', args: 1, '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())
}
void testMultipleOccurrencesTogetherSeparate() {
def cli = new CliBuilder()
cli.a(longOpt: 'arg', args: COMMONS_CLI_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())
}
void testMultipleOccurrencesTogetherJuxtaposed() {
def cli1 = new CliBuilder()
cli1.a(longOpt: 'arg', args: COMMONS_CLI_UNLIMITED_VALUES, valueSeparator: ',' as char, 'arguments')
def options = cli1.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()) }
/*
* Behaviour with unrecognized options.
*/
void testUnrecognizedOptionSilentlyIgnored_GnuParser() {
def cli = new CliBuilder(usage: usageString, writer: printWriter)
def options = cli.parse(['-v'])
assertEquals('''''', stringWriter.toString().tokenize('\r\n').join('\n'))
assert !options.v
}
private void checkNoOutput() {
assert stringWriter.toString().tokenize('\r\n').join('\n') == ''''''
}
void testUnrecognizedOptionSilentlyIgnored_DefaultParser() {
def cli = new CliBuilder(usage: usageString, writer: printWriter/*, parser: new DefaultParser()*/)
def options = cli.parse(['-v'])
checkNoOutput()
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'])
checkNoOutput()
assert !options.v
assert !options.h
assertEquals(['-v', '-h'], options.arguments())
}
void testUnrecognizedOptionTerminatesParse_DefaultParser() {
def cli = new CliBuilder(usage: usageString, writer: printWriter/*, parser: new DefaultParser()*/)
cli.h(longOpt: 'help', 'usage information')
def options = cli.parse(['-v', '-h'])
checkNoOutput()
assert !options.v
assert !options.h
assertEquals(['-v', '-h'], options.arguments())
}
void testMultiCharShortOpt() {
def cli = new CliBuilder(writer: printWriter)
cli.abc('abc option')
cli.def(longOpt: 'defdef', 'def option')
def options = cli.parse(['-abc', '--defdef', 'ghi'])
assert options
assert options.arguments() == ['ghi']
assert options.abc && options.def && options.defdef
checkNoOutput()
}
void testArgumentBursting_DefaultParserOnly() {
def cli = new CliBuilder(writer: printWriter)
// must not have longOpt 'abc' and also no args for a or b
cli.a('a')
cli.b('b')
cli.c('c')
def options = cli.parse(['-abc', '-d'])
assert options
assert options.arguments() == ['-d']
assert options.a && options.b && options.c && !options.d
checkNoOutput()
}
void testLongOptEndingWithS() {
def cli = new CliBuilder()
cli.s(longOpt: 'number_of_seconds', 'a long arg that ends with an "s"')
def options = cli.parse(['-s'])
assert options.hasOption('s')
assert options.hasOption('number_of_seconds')
assert options.s
assert options.number_of_seconds
}
void testArgumentFileExpansion() {
def cli = new CliBuilder(usage: 'test usage')
cli.h(longOpt: 'help', 'usage information')
cli.d(longOpt: 'debug', 'turn on debug info')
def args = ['-h', '@temp.args', 'foo', '@@baz']
def temp = new File('temp.args')
temp.deleteOnExit()
temp.text = '-d bar'
def options = cli.parse(args)
assert options.h
assert options.d
assert options.arguments() == ['bar', 'foo', '@baz']
}
void testArgumentFileExpansionArgOrdering() {
def cli = new CliBuilder(usage: 'test usage')
def args = ['one', '@temp1.args', 'potato', '@temp2.args', 'four']
def temp1 = new File('temp1.args')
temp1.deleteOnExit()
temp1.text = 'potato two'
def temp2 = new File('temp2.args')
temp2.deleteOnExit()
temp2.text = 'three potato'
def options = cli.parse(args)
assert options.arguments() == 'one potato two potato three potato four'.split()
}
void testArgumentFileExpansionTurnedOff() {
def cli = new CliBuilder(usage: 'test usage', expandArgumentFiles:false)
cli.h(longOpt: 'help', 'usage information')
cli.d(longOpt: 'debug', 'turn on debug info')
def args = ['-h', '@temp.args', 'foo', '@@baz']
def temp = new File('temp.args')
temp.deleteOnExit()
temp.text = '-d bar'
def options = cli.parse(args)
assert options.h
assert !options.d
assert options.arguments() == ['@temp.args', 'foo', '@@baz']
}
void testGStringSpecification_Groovy4621() {
def user = 'scott'
def pass = 'tiger'
def ignore = false
def longOptName = 'user'
def cli = new CliBuilder(usage: 'blah')
cli.dbusername(longOpt:"$longOptName", args: 1, "Database username [default $user]")
cli.dbpassword(args: 1, "Database password [default $pass]")
cli.i("ignore case [default $ignore]")
def args = ['-dbpassword', 'foo', '--user', 'bar', '-i']
def options = cli.parse(args)
assert options.user == 'bar'
assert options.dbusername == 'bar'
assert options.dbpassword == 'foo'
assert options.i
}
void testNoExpandArgsWithEmptyArg() {
def cli = new CliBuilder(expandArgumentFiles: false)
cli.parse(['something', ''])
}
void testExpandArgsWithEmptyArg() {
def cli = new CliBuilder(expandArgumentFiles: true)
cli.parse(['something', ''])
}
void testDoubleHyphenShortOptions() {
def cli = new CliBuilder()
cli.a([:], '')
cli.b([:], '')
def options = cli.parse(['-a', '--', '-b', 'foo'])
assert options.arguments() == ['-b', 'foo']
}
void testDoubleHyphenLongOptions() {
def cli = new CliBuilder()
cli._([longOpt:'alpha'], '')
cli._([longOpt:'beta'], '')
def options = cli.parse(['--alpha', '--', '--beta', 'foo'])
assert options.alpha
assert options.arguments() == ['--beta', 'foo']
}
void testMixedShortAndLongOptions() {
def cli = new CliBuilder()
cli.a([longOpt:'alpha', args:1], '')
cli.b([:], '')
def options = cli.parse(['-b', '--alpha', 'param', 'foo'])
assert options.a == 'param'
assert options.arguments() == ['foo']
}
void testMixedBurstingAndLongOptions() {
def cli = new CliBuilder()
cli.a([:], '')
cli.b([:], '')
cli.c([:], '')
cli.d([longOpt:'abacus'], '')
def options = cli.parse(['-abc', 'foo'])
assert options.a
assert options.b
assert options.c
assert options.arguments() == ['foo']
options = cli.parse(['--abacus', 'foo'])
assert !options.a
assert !options.b
assert !options.c
assert options.d
assert options.arguments() == ['foo']
//this passed in previous version of CliBuilder:
// longOpt may have 1 or 2 hyphens
resetPrintWriter()
cli.writer = printWriter
options = cli.parse(['-abacus', 'foo'])
assert options == null
assertTrue(stringWriter.toString().startsWith('error: Unmatched argument'))
assertTrue(stringWriter.toString().contains('-us'))
}
void testMixedBurstingAndLongOptions_singleHyphen() {
def cli = new CliBuilder()
cli.acceptLongOptionsWithSingleHyphen = true
cli.a([:], '')
cli.b([:], '')
cli.c([:], '')
cli.d([longOpt:'abacus'], '')
def options = cli.parse(['-abc', 'foo'])
assert options.a
assert options.b
assert options.c
assert options.arguments() == ['foo']
options = cli.parse(['--abacus', 'foo'])
assert !options.a
assert !options.b
assert !options.c
assert options.d
assert options.arguments() == ['foo']
//this passed in previous version of CliBuilder:
// longOpt may have 1 or 2 hyphens
options = cli.parse(['-abacus', 'foo'])
assert !options.a
assert !options.b
assert !options.c
assert options.d
assert options.arguments() == ['foo']
}
interface PersonI {
@Option String first()
@Option String last()
@Option boolean flag1()
@Option Boolean flag2()
@Option(longName = 'specialFlag') Boolean flag3()
@Option flag4()
@Option int age()
@Option Integer born()
@Option float discount()
@Option BigDecimal pi()
@Option File biography()
@Option RoundingMode roundingMode()
@Unparsed List remaining()
}
def argz = "--first John --last Smith --flag1 --flag2 --specialFlag --age 21 --born 1980 --discount 3.5 --pi 3.14159 --biography cv.txt --roundingMode DOWN and some more".split()
void testParseFromSpec() {
def builder1 = new CliBuilder()
def p1 = builder1.parseFromSpec(PersonI, argz)
assert p1.first() == 'John'
assert p1.last() == 'Smith'
assert p1.flag1()
assert p1.flag2()
assert p1.flag3()
assert !p1.flag4()
assert p1.born() == 1980
assert p1.age() == 21
assert p1.discount() == 3.5f
assert p1.pi() == 3.14159
assert p1.biography() == new File('cv.txt')
assert p1.roundingMode() == RoundingMode.DOWN
assert p1.remaining() == ['and', 'some', 'more']
}
@ToString(includeFields=true, includePackage=false)
class PersonC {
@Option String first
private String last
@Option boolean flag1
private Boolean flag2
private Boolean flag3
private Boolean flag4
private int age
private Integer born
private float discount
private BigDecimal pi
private File biography
private RoundingMode roundingMode
private List remaining
@Option void setLast(String last) {
this.last = last
}
@Option void setFlag2(boolean flag2) {
this.flag2 = flag2
}
@Option(longName = 'specialFlag') void setFlag3(boolean flag3) {
this.flag3 = flag3
}
@Option void setFlag4(boolean flag4) {
this.flag4 = flag4
}
@Option void setAge(int age) {
this.age = age
}
@Option void setBorn(Integer born) {
this.born = born
}
@Option void setDiscount(float discount) {
this.discount = discount
}
@Option void setPi(BigDecimal pi) {
this.pi = pi
}
@Option void setBiography(File biography) {
this.biography = biography
}
@Option void setRoundingMode(RoundingMode roundingMode) {
this.roundingMode = roundingMode
}
@Unparsed void setRemaining(List remaining) {
this.remaining = remaining
}
}
class DefaultValueC {
@Option(shortName='f', defaultValue='one') String from
@Option(shortName='t', defaultValue='35') int to
@Option(shortName='b') int by = 1
}
void testDefaultValueClass() {
def cli = new CliBuilder()
def options = new DefaultValueC()
cli.parseFromInstance(options, '-f two'.split())
assert options.from == 'two'
assert options.to == 35
assert options.by == 1
options = new DefaultValueC()
cli.parseFromInstance(options, '-t 45 --by 2'.split())
assert options.from == 'one'
assert options.to == 45
assert options.by == 2
}
class ValSepC {
@Option(numberOfArguments=2) String[] a
@Option(numberOfArgumentsString='2', valueSeparator=',') String[] b
@Option(numberOfArgumentsString='+', valueSeparator=',') String[] c
@Unparsed remaining
}
void testValSepClass() {
def cli = new CliBuilder()
def options = new ValSepC()
cli.parseFromInstance(options, '-a 1 2 3 4'.split())
assert options.a == ['1', '2']
assert options.remaining == ['3', '4']
options = new ValSepC()
cli.parseFromInstance(options, '-a 1 2 -a 3 4'.split())
assert options.a == ['1', '2', '3', '4']
// assert options.remaining == []
options = new ValSepC()
cli.parseFromInstance(options, '-a1 -a2 3'.split())
assert options.a == ['1', '2']
assert options.remaining == ['3']
options = new ValSepC()
cli.parseFromInstance(options, ['-b1,2'] as String[])
assert options.b == ['1', '2']
options = new ValSepC()
cli.parseFromInstance(options, ['-b1,2,3'] as String[])
assert options.b == ['1', '2,3']
options = new ValSepC()
cli.parseFromInstance(options, ['-c', '1'] as String[])
assert options.c == ['1']
options = new ValSepC()
cli.parseFromInstance(options, ['-c1'] as String[])
assert options.c == ['1']
options = new ValSepC()
cli.parseFromInstance(options, ['-c1,2,3'] as String[])
assert options.c == ['1', '2', '3']
}
class WithConvertC {
@Option(convert={ it.toLowerCase() }) String a
@Option(convert={ it.toUpperCase() }) String b
@Option(convert={ new SimpleDateFormat("yyyy-MM-dd").parse(it) }) Date d
@Unparsed List remaining
}
void testConvertClass() {
Date newYears = new SimpleDateFormat("yyyy-MM-dd").parse("2016-01-01")
def argz = '''-a John -b Mary -d 2016-01-01 and some more'''.split()
def cli = new CliBuilder()
def options = new WithConvertC()
cli.parseFromInstance(options, argz)
assert options.a == 'john'
assert options.b == 'MARY'
assert options.d == newYears
assert options.remaining == ['and', 'some', 'more']
}
class TypeCheckedC {
@Option String name
@Option int age
@Unparsed List remaining
}
@TypeChecked
void testTypeCheckedClass() {
def argz = "--name John --age 21 and some more".split()
def cli = new CliBuilder()
def options = new TypeCheckedC()
cli.parseFromInstance(options, argz)
String n = options.name
int a = options.age
assert n == 'John' && a == 21
assert options.remaining == ['and', 'some', 'more']
}
void testParseFromInstance() {
def p2 = new PersonC()
def builder2 = new CliBuilder()
builder2.parseFromInstance(p2, argz)
// properties show first in toString()
assert p2.toString() == 'CliBuilderTest$PersonC(John, true, Smith, true, true, false, 21, 1980, 3.5, 3.14159,' +
' cv.txt, DOWN, [and, some, more])'
}
interface RetTypeI {
@Unparsed Integer[] nums()
}
// this feature is incubating
void testTypedUnparsedFromSpec() {
def argz = '12 34 56'.split()
def cli = new CliBuilder()
def options = cli.parseFromSpec(RetTypeI, argz)
assert options.nums() == [12, 34, 56]
}
class RetTypeC {
@Unparsed Integer[] nums
}
// this feature is incubating
void testTypedUnparsedFromInstance() {
def argz = '12 34 56'.split()
def cli = new CliBuilder()
def options = new RetTypeC()
cli.parseFromInstance(options, argz)
assert options.nums == [12, 34, 56]
}
interface FlagEdgeCasesI {
@Option boolean abc()
@Option(numberOfArgumentsString='1') boolean efg()
@Option(numberOfArguments=1) ijk()
@Option(numberOfArguments=0) lmn()
@Unparsed List remaining()
}
void testParseFromInstanceFlagEdgeCases_singleHyphen() {
def cli = new CliBuilder(acceptLongOptionsWithSingleHyphen: true)
def options = cli.parseFromSpec(FlagEdgeCasesI, '-abc -efg true -ijk foo -lmn bar baz'.split())
assert options.abc() && options.efg()
assert options.ijk() == 'foo'
assert options.lmn() == true
assert options.remaining() == ['bar', 'baz']
options = cli.parseFromSpec(FlagEdgeCasesI, '-abc -ijk cat -efg false bar baz'.split())
assert options.abc()
assert options.ijk() == 'cat'
assert !options.efg()
assert options.lmn() == false
assert options.remaining() == ['bar', 'baz']
}
void testParseFromInstanceFlagEdgeCases_doubleHyphen() {
def cli = new CliBuilder()
def options = cli.parseFromSpec(FlagEdgeCasesI, '--abc --efg true --ijk foo --lmn bar baz'.split())
assert options.abc() && options.efg()
assert options.ijk() == 'foo'
assert options.lmn() == true
assert options.remaining() == ['bar', 'baz']
options = cli.parseFromSpec(FlagEdgeCasesI, '--abc --ijk cat --efg false bar baz'.split())
assert options.abc()
assert options.ijk() == 'cat'
assert !options.efg()
assert options.lmn() == false
assert options.remaining() == ['bar', 'baz']
}
void testParseScript() {
new GroovyShell().run('''
import groovy.cli.OptionField
import groovy.cli.UnparsedField
import groovy.cli.picocli.CliBuilder
import java.math.RoundingMode
@OptionField String first
@OptionField String last
@OptionField boolean flag1
@OptionField Boolean flag2
@OptionField(longName = 'specialFlag') Boolean flag3
@OptionField Boolean flag4
@OptionField int age
@OptionField Integer born
@OptionField float discount
@OptionField BigDecimal pi
@OptionField File biography
@OptionField RoundingMode roundingMode
@UnparsedField List remaining
new CliBuilder().parseFromInstance(this, args)
assert first == 'John'
assert last == 'Smith'
assert flag1
assert flag2
assert flag3
assert !flag4
assert born == 1980
assert age == 21
assert discount == 3.5f
assert pi == 3.14159
assert biography == new File('cv.txt')
assert roundingMode == RoundingMode.DOWN
assert remaining == ['and', 'some', 'more']
''', 'CliBuilderTestScript.groovy', argz)
}
void testOptionProperties() {
CliBuilder cli = new CliBuilder(usage: 'groovyConsole [options] [filename]', stopAtNonOption: false)
cli.with {
D(longOpt: 'define', args: 2, argName: 'name=value', valueSeparator: '=', 'description')
}
OptionAccessor options = cli.parse('-Dk=v -Dk2=v2'.split())
assert options.hasOption('D')
Properties props = options.getOptionProperties('D')
assert 'v' == props.getProperty('k')
assert 'v2' == props.getProperty('k2')
}
void testAcceptLongOptionsWithSingleHyphen_defaultFalse() {
assert !new CliBuilder().acceptLongOptionsWithSingleHyphen
}
void testAcceptLongOptionsWithSingleHyphen_DuplicateOptionAnnotationsException() {
CliBuilder cli = new CliBuilder(acceptLongOptionsWithSingleHyphen: true)
try {
cli.with {
classpath('description')
cp(longOpt: 'classpath', 'description')
}
} catch (DuplicateOptionAnnotationsException expected) {
assert expected.message == 'Option name \'-classpath\' is used by both option --classpath and option -classpath'
}
}
void testLongOptionsRequireDoubleHyphenByDefault() {
CliBuilder cli = new CliBuilder()
cli.with {
classpath('description')
cp(longOpt: 'classpath', 'cli.option.cp.description')
h(longOpt: 'help', 'cli.option.help.description')
V(longOpt: 'version', 'cli.option.version.description')
pa(longOpt: 'parameters', 'cli.option.parameters.description')
pr(longOpt: 'enable-preview', 'cli.option.preview.description')
D(longOpt: 'define', args: 2, argName: 'name=value', valueSeparator: '=', 'cli.option.define.description')
_(longOpt: 'configscript', args: 1, 'cli.option.configscript.description')
}
assert cli.parse(['--classpath']).cp
assert cli.parse(['-cp']).cp
assert cli.parse(['-classpath']).classpath
assert cli.parse(['--parameters']).parameters
assert cli.parse(['--parameters']).pa
def options = cli.parse(['-parameters'])
assert !options.parameters
assert !options.pa
assert options.arguments() == ['-parameters']
assert cli.parse(['--enable-preview']).'enable-preview'
assert cli.parse(['--enable-preview']).pr
resetPrintWriter()
assert cli.parse(['--help']).help
assert cli.parse(['--help']).h
resetPrintWriter()
cli.writer = printWriter
assert cli.parse(['-help']) == null
assertTrue(stringWriter.toString().startsWith('error: Unmatched argument'))
assertTrue(stringWriter.toString().contains('-elp'))
assert cli.parse(['--version']).version
assert cli.parse(['--version']).V
options = cli.parse(['-version'])
assert !options.version
assert !options.V
assert options.arguments() == ['-version']
assert cli.parse('--configscript abc'.split()).configscript == 'abc'
options = cli.parse('-configscript abc'.split())
assert !options.configscript
assert options.arguments() == ['-configscript', 'abc']
}
void testAcceptLongOptionsWithSingleHyphen_registersLongOptionsTwice() {
CliBuilder cli = new CliBuilder(acceptLongOptionsWithSingleHyphen: true)
cli.with {
cp(longOpt: 'classpath', 'cli.option.cp.description')
h(longOpt: 'help', 'cli.option.help.description')
V(longOpt: 'version', 'cli.option.version.description')
pa(longOpt: 'parameters', 'cli.option.parameters.description')
pr(longOpt: 'enable-preview', 'cli.option.preview.description')
D(longOpt: 'define', args: 2, argName: 'name=value', valueSeparator: '=', 'cli.option.define.description')
_(longOpt: 'configscript', args: 1, 'cli.option.configscript.description')
}
assert cli.parse(['--classpath']).cp
assert cli.parse(['-classpath']).cp
assert cli.parse(['-cp']).classpath
assert cli.parse(['--parameters']).pa
assert cli.parse(['-parameters']).pa
assert cli.parse(['-pa']).parameters
assert cli.parse(['--enable-preview']).'enable-preview'
assert cli.parse(['-enable-preview']).'enable-preview'
assert cli.parse(['-pr']).pr
assert cli.parse(['--help']).h
assert cli.parse(['-help']).h
assert cli.parse(['-h']).help
assert cli.parse(['--version']).V
assert cli.parse(['-version']).V
assert cli.parse(['-V']).version
assert cli.parse('--configscript abc'.split()).configscript == 'abc'
assert cli.parse( '-configscript abc'.split()).configscript == 'abc'
}
// GROOVY-8607
void testOptIgnoredWhenSupplyingMapOfArgs() {
def builder = new CliBuilder()
def helpOpt = [opt:'h', longOpt: 'help']
builder."$helpOpt.opt"(helpOpt, 'help option').with {
assert opt == 'h'
assert longOpt == 'help'
}
}
interface StringIntArray {
@Option(shortName='u') String user()
@Unparsed Integer[] nums()
}
// GROOVY-8975
void testTypedCaseWithRemainingArray() {
def cli = new CliBuilder()
def argz = '--user abc 12 34'.split()
StringIntArray hello = cli.parseFromSpec(StringIntArray, argz)
assert hello.user() == 'abc'
assert hello.nums() == [12, 34]
}
void testAcceptLongOptionsWithSingleHyphen_usage() {
resetPrintWriter()
CliBuilder cli = new CliBuilder(acceptLongOptionsWithSingleHyphen: true, writer: printWriter)
cli.with {
cp(longOpt: 'classpath', 'cli.option.cp.description')
h(longOpt: 'help', 'cli.option.help.description')
V(longOpt: 'version', 'cli.option.version.description')
pa(longOpt: 'parameters', 'cli.option.parameters.description')
pr(longOpt: 'enable-preview', 'cli.option.preview.description')
D(longOpt: 'define', args: 2, argName: 'String', valueSeparator: '=', 'cli.option.define.description')
_(longOpt: 'configscript', args: 1, 'cli.option.configscript.description')
}
cli.usage()
def expectedUsage = """\
Usage: groovy [-hV] [-cp] [-pa] [-pr] [-configscript=PARAM]
[-D=<String>=<String>]...
-configscript, --configscript=PARAM
cli.option.configscript.description
-cp, -classpath, --classpath
cli.option.cp.description
-D, -define, --define=<String>=<String>
cli.option.define.description
-h, -help, --help cli.option.help.description
-pa, -parameters, --parameters
cli.option.parameters.description
-pr, -enable-preview, --enable-preview
cli.option.preview.description
-V, -version, --version cli.option.version.description"""
assertEquals(expectedUsage, stringWriter.toString().tokenize('\r\n').join('\n'))
resetPrintWriter()
cli = new CliBuilder(acceptLongOptionsWithSingleHyphen: false, writer: printWriter)
cli.with {
cp(longOpt: 'classpath', 'cli.option.cp.description')
h(longOpt: 'help', 'cli.option.help.description')
V(longOpt: 'version', 'cli.option.version.description')
pa(longOpt: 'parameters', 'cli.option.parameters.description')
pr(longOpt: 'enable-preview', 'cli.option.preview.description')
D(longOpt: 'define', args: 2, argName: 'String', valueSeparator: '=', 'cli.option.define.description')
_(longOpt: 'configscript', args: 1, 'cli.option.configscript.description')
}
cli.usage()
expectedUsage = """\
Usage: groovy [-hV] [-cp] [-pa] [-pr] [--configscript=PARAM]
[-D=<String>=<String>]...
--configscript=PARAM cli.option.configscript.description
-cp, --classpath cli.option.cp.description
-D, --define=<String>=<String>
cli.option.define.description
-h, --help cli.option.help.description
-pa, --parameters cli.option.parameters.description
-pr, --enable-preview cli.option.preview.description
-V, --version cli.option.version.description"""
assertEquals(expectedUsage, stringWriter.toString().tokenize('\r\n').join('\n'))
}
void testNonOption() {
CliBuilder cli = new CliBuilder(stopAtNonOption: false)
def optionAccessor = cli.parse(['-x'])
assertNull(optionAccessor)
}
// GROOVY-9519
void testIntOptionWithDefaultZeroShouldNotConvertToBooleanFalse() {
def cli = new CliBuilder()
cli.i(type: Integer, longOpt: 'intTest', required: false, args: 1, defaultValue: '0', 'Testing integer with default value 0')
def opts = cli.parse([]) // no args, so defaults are applied
assert opts
assert Integer == opts.i.getClass()
assert opts.i == 0
}
}