You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2018/05/19 14:02:07 UTC

[1/3] groovy git commit: GROOVY-8556 consolidate tests/doco a bit more

Repository: groovy
Updated Branches:
  refs/heads/GROOVY_2_5_X 997938a93 -> 3fbd0b6f6


GROOVY-8556 consolidate tests/doco a bit more


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/926b24a4
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/926b24a4
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/926b24a4

Branch: refs/heads/GROOVY_2_5_X
Commit: 926b24a431689e91727b43d9aba3e362b02d4276
Parents: 997938a
Author: Paul King <pa...@asert.com.au>
Authored: Sat May 19 23:07:55 2018 +1000
Committer: Paul King <pa...@asert.com.au>
Committed: Sun May 20 00:01:52 2018 +1000

----------------------------------------------------------------------
 .../doc/core-domain-specific-languages.adoc     | 117 +++---
 src/spec/test/cli/CliBuilderTestCase.groovy     | 380 +++++++++++++++++++
 src/spec/test/cli/GreeterC.groovy               |  39 ++
 src/spec/test/cli/GreeterI.groovy               |  30 ++
 src/spec/test/cli/TypeCheckedI.groovy           |  30 ++
 src/spec/test/cli/ValSepI.groovy                |  31 ++
 src/spec/test/cli/WithArgsI.groovy              |  31 ++
 src/spec/test/cli/WithConvertI.groovy           |  31 ++
 src/spec/test/cli/WithDefaultValueI.groovy      |  28 ++
 subprojects/groovy-cli-commons/build.gradle     |   1 +
 .../src/spec/test/builder/CliBuilderTest.groovy | 344 +----------------
 .../spec/test/groovy/util/CliBuilderTest.groovy |  28 ++
 subprojects/groovy-cli-picocli/build.gradle     |   1 +
 .../src/spec/test/builder/CliBuilderTest.groovy | 370 ++----------------
 14 files changed, 716 insertions(+), 745 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/src/spec/doc/core-domain-specific-languages.adoc
----------------------------------------------------------------------
diff --git a/src/spec/doc/core-domain-specific-languages.adoc b/src/spec/doc/core-domain-specific-languages.adoc
index 6db531f..06f9e8d 100644
--- a/src/spec/doc/core-domain-specific-languages.adoc
+++ b/src/spec/doc/core-domain-specific-languages.adoc
@@ -1103,22 +1103,27 @@ Here is a simple example `Greeter.groovy` script illustrating usage:
 
 [source,groovy]
 ---------------------------
+// import of CliBuilder not shown                          <1>
 // specify parameters
-def cli = new CliBuilder(usage: 'groovy Greeter [option]') <1>
-cli.a(longOpt: 'audience', args: 1, 'greeting audience')   <2>
-cli.h(longOpt: 'help', 'display usage')                    <3>
+def cli = new CliBuilder(usage: 'groovy Greeter [option]') <2>
+cli.a(longOpt: 'audience', args: 1, 'greeting audience')   <3>
+cli.h(longOpt: 'help', 'display usage')                    <4>
 
 // parse and process parameters
-def options = cli.parse(args)                              <4>
-if (options.h) cli.usage()                                 <5>
-else println "Hello ${options.a ? options.a : 'World'}"    <6>
+def options = cli.parse(args)                              <5>
+if (options.h) cli.usage()                                 <6>
+else println "Hello ${options.a ? options.a : 'World'}"    <7>
 ---------------------------
-<1> define a new `CliBuilder` instance specifying an optional usage string
-<2> specify a `-a` option taking a single argument with an optional long variant `--audience`
-<3> specify a `-h` option taking no arguments with an optional long variant `--help`
-<4> parse the commandline parameters supplied to the script
-<5> if the `h` option is found display a usage message
-<6> display a standard greeting or, if the `a` option is found, a customized greeting
+<1> Earlier versions of Groovy had a CliBuilder in the groovy.util package and no import was necessary.
+While still supported, this approach is now deprecated and you should instead choose the groovy.cli.picocli
+or groovy.cli.commons version. The groovy.util version points to the commons-cli version for backwards compatibility
+but will be removed in a future version of Groovy.
+<2> define a new `CliBuilder` instance specifying an optional usage string
+<3> specify a `-a` option taking a single argument with an optional long variant `--audience`
+<4> specify a `-h` option taking no arguments with an optional long variant `--help`
+<5> parse the commandline parameters supplied to the script
+<6> if the `h` option is found display a usage message
+<7> display a standard greeting or, if the `a` option is found, a customized greeting
 
 Running this script with no commandline parameters, i.e.:
 
@@ -1178,13 +1183,13 @@ properties are supported when specifying an allowed commandline option:
 | Name           | Description | Type
 | argName        | the name of the argument for this option used in output | `String`
 | longOpt        | the long representation or long name of the option | `String`
-| args           | the number of argument values | `int` or `String`<1>
+| args           | the number of argument values | `int` or `String` <1>
 | optionalArg    | whether the argument value is optional | `boolean`
 | required       | whether the option is mandatory | `boolean`
 | type           | the type of this option | `Class`
-| valueSeparator | the character that is the value separator | `char`<2>
+| valueSeparator | the character that is the value separator | `char` <2>
 | defaultValue   | a default value | `String`
-| convert        | converts the incoming String to the required type | `Closure`<1>
+| convert        | converts the incoming String to the required type | `Closure` <1>
 |======================
 <1> More details later
 <2> Single character Strings are coerced to chars in special cases in Groovy
@@ -1206,7 +1211,7 @@ Here is how such a specification can be defined:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=annotationInterfaceSpec,indent=0]
+include::{projectdir}/src/spec/test/cli/GreeterI.groovy[tags=annotationInterfaceSpec,indent=0]
 ----
 <1> Specify a Boolean option set using `-h` or `--help`
 <2> Specify a String option set using `-a` or `--audience`
@@ -1221,7 +1226,7 @@ Here is how you could use the interface specification:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=annotationInterface,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=annotationInterface,indent=0]
 ----
 <1> Create a `CliBuilder` instance as before with optional properties
 <2> Parse parameters using the interface specification
@@ -1243,7 +1248,7 @@ Here is how such a specification can be defined:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=annotationClassSpec,indent=0]
+include::{projectdir}/src/spec/test/cli/GreeterC.groovy[tags=annotationClassSpec,indent=0]
 ----
 <1> Indicate that a Boolean property is an option
 <2> Indicate that a String property (with explicit setter) is an option
@@ -1253,7 +1258,7 @@ And here is how you could use the specification:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=annotationClass,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=annotationClass,indent=0]
 ----
 <1> Create a `CliBuilder` instance as before with optional parameters
 <2> Create an instance for `CliBuilder` to populate
@@ -1277,7 +1282,7 @@ with the same arguments as shown for the instance example earlier:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=annotationScript,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=annotationScript,indent=0]
 ----
 
 ===== Options with arguments
@@ -1289,7 +1294,7 @@ Here is an example involving those cases:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withArgument,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=withArgument,indent=0]
 ----
 <1> An option that is simply a flag - the default; setting args to 0 is allowed but not needed.
 <2> An option with exactly one argument
@@ -1306,14 +1311,14 @@ illustrating such a definition:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withArgumentInterfaceSpec,indent=0]
+include::{projectdir}/src/spec/test/cli/WithArgsI.groovy[tags=withArgumentInterfaceSpec,indent=0]
 ----
 
 And here is how it is used:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withArgumentInterface,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=withArgumentInterface,indent=0]
 ----
 
 This example makes use of an array-typed option specification. We cover this in more detail shortly when we discuss
@@ -1334,7 +1339,7 @@ Here is an example using types with the dynamic api argument definition style:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withType,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=withType,indent=0]
 ----
 
 Primitives, numeric types, files, enums and arrays thereof, are supported (they are converted using
@@ -1347,7 +1352,7 @@ for you. Here is a sample using the dynamic api style:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withConvert,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=withConvert,indent=0]
 ----
 
 Alternatively, you can use the annotation style by supplying the conversion closure as an annotation parameter.
@@ -1355,14 +1360,14 @@ Here is an example specification:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withConvertInterfaceSpec,indent=0]
+include::{projectdir}/src/spec/test/cli/WithConvertI.groovy[tags=withConvertInterfaceSpec,indent=0]
 ----
 
 And an example using that specification:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withConvertInterface,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=withConvertInterface,indent=0]
 ----
 
 ===== Options with multiple arguments
@@ -1388,7 +1393,7 @@ Here is an excerpt highlighting the use of multiple arguments:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=multipleArgs,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=multipleArgs,indent=0]
 ----
 <1> Args value supplied as a String and comma value separator specified
 <2> One or more arguments are allowed
@@ -1408,14 +1413,14 @@ array type for the annotated class member (method or property) as this example s
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=multipleArgsInterfaceSpec,indent=0]
+include::{projectdir}/src/spec/test/cli/ValSepI.groovy[tags=multipleArgsInterfaceSpec,indent=0]
 ----
 
 And used as follows:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=multipleArgsInterface,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=multipleArgsInterface,indent=0]
 ----
 
 ===== Types and multiple arguments
@@ -1424,7 +1429,7 @@ Here is an example using types and multiple arguments with the dynamic api argum
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withTypeMultiple,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=withTypeMultiple,indent=0]
 ----
 <1> For an array type, the trailing 's' can be used but isn't needed
 
@@ -1439,7 +1444,7 @@ Here is how you could use it using the dynamic api style:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withDefaultValue,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=withDefaultValue,indent=0]
 ----
 
 Similarly, you might want such a specification using the annotation style. Here is an example using an interface
@@ -1447,14 +1452,14 @@ specification:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withDefaultValueInterfaceSpec,indent=0]
+include::{projectdir}/src/spec/test/cli/WithDefaultValueI.groovy[tags=withDefaultValueInterfaceSpec,indent=0]
 ----
 
 Which would be used like this:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withDefaultValueInterface,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=withDefaultValueInterface,indent=0]
 ----
 
 You can also use the `defaultValue` annotation attribute when using annotations with an instance,
@@ -1468,14 +1473,14 @@ annotation style, for example, here is an interface option specification:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withTypeCheckedInterfaceSpec,indent=0]
+include::{projectdir}/src/spec/test/cli/TypeCheckedI.groovy[tags=withTypeCheckedInterfaceSpec,indent=0]
 ----
 
-And it can be used  in combination with `@TypeChecked` as shown here:
+And it can be used in combination with `@TypeChecked` as shown here:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withTypeCheckedInterface,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=withTypeCheckedInterface,indent=0]
 ----
 
 Secondly, there is a feature of the dynamic api style which offers some support. The definition statements
@@ -1508,7 +1513,7 @@ the following example:
 
 [source,groovy]
 ----
-include::{projectdir}/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy[tags=withTypeChecked,indent=0]
+include::{projectdir}/src/spec/test/cli/CliBuilderTestCase.groovy[tags=withTypeChecked,indent=0]
 ----
 
 ===== Advanced CLI Usage
@@ -1549,13 +1554,14 @@ assert !cli.parse('-d -o'.split()) // <1>
 <1> The parse will fail since only one option from a group can be used at a time.
 
 ====== Picocli
+
 Below are some features available in the picocli version of `CliBuilder`.
 
 *New property: errorWriter*
 
 When users of your application give invalid command line arguments,
 CliBuilder writes an error message and the usage help message to the `stderr` output stream.
-It doesn’t use the `stdout` stream to prevent the error message from being parsed when your program's
+It doesn't use the `stdout` stream to prevent the error message from being parsed when your program's
 output is used as input for another process.
 You can customize the destination by setting the `errorWriter` to a different value.
 
@@ -1588,28 +1594,12 @@ which gives fine-grained control over various sections of the usage help message
 
 [source,groovy]
 ----
-def cli = new CliBuilder()
-cli.name = "myapp"
-cli.usageMessage.with {
-    headerHeading("@|bold,underline Header heading:|@%n")
-    header("Header 1", "Header 2")                     // before the synopsis
-    synopsisHeading("%n@|bold,underline Usage:|@ ")
-    descriptionHeading("%n@|bold,underline Description heading:|@%n")
-    description("Description 1", "Description 2")      // after the synopsis
-    optionListHeading("%n@|bold,underline Options heading:|@%n")
-    footerHeading("%n@|bold,underline Footer heading:|@%n")
-    footer("Footer 1", "Footer 2")
-}
-cli.a('option a description')
-cli.b('option b description')
-cli.c(args: '*', 'option c description')
-cli.usage()
+include::{projectdir}/subprojects/groovy-cli-picocli/src/spec/test/builder/CliBuilderTest.groovy[tags=withUsageMessageSpec,indent=0]
 ----
 Gives this output:
 
 image::assets/img/usageMessageSpec.png[]
 
-
 *New property: parser*
 
 The `parser` property gives access to the picocli `ParserSpec` object that can be used to customize the parser behavior.
@@ -1621,19 +1611,9 @@ Finally, if your application has options that are key-value pairs, you may be in
 
 [source,groovy]
 ----
-import java.util.concurrent.TimeUnit
-import static java.util.concurrent.TimeUnit.DAYS
-import static java.util.concurrent.TimeUnit.HOURS
+include::{projectdir}/subprojects/groovy-cli-picocli/src/spec/test/builder/CliBuilderTest.groovy[tags=mapOptionImports,indent=0]
 
-def cli = new CliBuilder()
-cli.D(args: 2,   valueSeparator: '=', 'the old way')                          // <1>
-cli.X(type: Map, 'the new way')                                               // <2>
-cli.Z(type: Map, auxiliaryTypes: [TimeUnit, Integer].toArray(), 'typed map')  // <3>
-
-def options = cli.parse('-Da=b -Dc=d -Xx=y -Xi=j -ZDAYS=2 -ZHOURS=23'.split())// <4>
-assert options.Ds == ['a', 'b', 'c', 'd']                                     // <5>
-assert options.Xs == [ 'x':'y', 'i':'j' ]                                     // <6>
-assert options.Zs == [ (DAYS as TimeUnit):2, (HOURS as TimeUnit):23 ]         // <7>
+include::{projectdir}/subprojects/groovy-cli-picocli/src/spec/test/builder/CliBuilderTest.groovy[tags=mapOption,indent=0]
 ----
 <1> Previously, `key=value` pairs were split up into parts and added to a list
 <2> Picocli map support: simply specify `Map` as the type of the option
@@ -1644,7 +1624,6 @@ assert options.Zs == [ (DAYS as TimeUnit):2, (HOURS as TimeUnit):23 ]         //
 <7> Both keys and values of the map can be strongly typed
 
 
-
 ==== ObjectGraphBuilder
 
 `ObjectGraphBuilder` is a builder for an arbitrary graph of beans that

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/src/spec/test/cli/CliBuilderTestCase.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/cli/CliBuilderTestCase.groovy b/src/spec/test/cli/CliBuilderTestCase.groovy
new file mode 100644
index 0000000..1b6c9cc
--- /dev/null
+++ b/src/spec/test/cli/CliBuilderTestCase.groovy
@@ -0,0 +1,380 @@
+/*
+ *  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 cli
+
+abstract class CliBuilderTestCase extends GroovyTestCase {
+
+    abstract String getImportCliBuilder()
+
+    void testAnnotationsInterface() {
+        assertScript """
+        import cli.GreeterI
+        $importCliBuilder
+        // tag::annotationInterface[]
+        // import CliBuilder not shown
+        def cli = new CliBuilder(usage: 'groovy Greeter')  // <1>
+        def argz = '--audience Groovologist'.split()
+        def options = cli.parseFromSpec(GreeterI, argz)             // <2>
+        assert options.audience() == 'Groovologist'                 // <3>
+
+        argz = '-h Some Other Args'.split()
+        options = cli.parseFromSpec(GreeterI, argz)                 // <4>
+        assert options.help()
+        assert options.remaining() == ['Some', 'Other', 'Args']     // <5>
+        // end::annotationInterface[]
+        """
+    }
+
+    void testAnnotationsClass() {
+        assertScript """
+        import cli.GreeterC
+        $importCliBuilder
+        // tag::annotationClass[]
+        // import CliBuilder not shown
+        def cli = new CliBuilder(usage: 'groovy Greeter [option]') // <1>
+        def options = new GreeterC()                               // <2>
+        def argz = '--audience Groovologist foo'.split()
+        cli.parseFromInstance(options, argz)                       // <3>
+        assert options.audience == 'Groovologist'                  // <4>
+        assert options.remaining == ['foo']                        // <5>
+        // end::annotationClass[]
+        """
+    }
+
+    // performs toString comparison of usage help - this isn't because we guarantee any specific output
+    // (hence expected is provided as a parameter) but we want to know when it changes significantly and
+    // in particular we want to ensure that no information that we expect to be there just disappears
+    protected void doTestAnnotationsInterfaceToString(String key, String expected) {
+        assertScript """
+        import cli.GreeterI
+        $importCliBuilder
+        def cli = new CliBuilder($key: 'groovy Greeter')
+
+        def options = cli.parseFromSpec(GreeterI, ['-h', 'Some', 'Other', 'Args'] as String[])
+        assert options.help()
+        assert options.remaining() == ['Some', 'Other', 'Args']
+        StringWriter sw = new StringWriter()
+        cli.writer = new PrintWriter(sw)
+        cli.usage()
+
+        assert '$expected'.normalize() == sw.toString().normalize()
+        """
+    }
+
+    void testParseScript() {
+        def argz = '--audience Groovologist foo'.split()
+        new GroovyShell().run("""
+            $importCliBuilder
+            // tag::annotationScript[]
+            // import CliBuilder not shown
+            import groovy.cli.OptionField
+            import groovy.cli.UnparsedField
+
+            @OptionField String audience
+            @OptionField Boolean help
+            @UnparsedField List remaining
+            new CliBuilder().parseFromInstance(this, args)
+            assert audience == 'Groovologist'
+            assert remaining == ['foo']
+            // end::annotationScript[]
+        """, 'TestScript.groovy', argz)
+    }
+
+    void testWithArgument() {
+        assertScript """
+        $importCliBuilder
+        // tag::withArgument[]
+        // import CliBuilder not shown
+        def cli = new CliBuilder()
+        cli.a(args: 0, 'a arg') // <1>
+        cli.b(args: 1, 'b arg') // <2>
+        cli.c(args: 1, optionalArg: true, 'c arg') // <3>
+        def options = cli.parse('-a -b foo -c bar baz'.split()) // <4>
+
+        assert options.a == true
+        assert options.b == 'foo'
+        assert options.c == 'bar'
+        assert options.arguments() == ['baz']
+
+        options = cli.parse('-a -c -b foo bar baz'.split()) // <5>
+
+        assert options.a == true
+        assert options.c == true
+        assert options.b == 'foo'
+        assert options.arguments() == ['bar', 'baz']
+        // end::withArgument[]
+        """
+    }
+
+    void testMultipleArgsAndOptionalValueSeparator() {
+        assertScript """
+        $importCliBuilder
+        // tag::multipleArgs[]
+        // import CliBuilder not shown
+        def cli = new CliBuilder()
+        cli.a(args: 2, 'a-arg')
+        cli.b(args: '2', valueSeparator: ',', 'b-arg') // <1>
+        cli.c(args: '+', valueSeparator: ',', 'c-arg') // <2>
+
+        def options = cli.parse('-a 1 2 3 4'.split()) // <3>
+        assert options.a == '1' // <4>
+        assert options.as == ['1', '2'] // <5>
+        assert options.arguments() == ['3', '4']
+
+        options = cli.parse('-a1 -a2 3'.split()) // <6>
+        assert options.as == ['1', '2']
+        assert options.arguments() == ['3']
+
+        options = cli.parse(['-b1,2']) // <7>
+        assert options.bs == ['1', '2']
+
+        options = cli.parse(['-c', '1'])
+        assert options.cs == ['1']
+
+        options = cli.parse(['-c1'])
+        assert options.cs == ['1']
+
+        options = cli.parse(['-c1,2,3'])
+        assert options.cs == ['1', '2', '3']
+        // end::multipleArgs[]
+        """
+    }
+
+    void testWithArgumentInterface() {
+        assertScript """
+        import cli.WithArgsI
+        $importCliBuilder
+        // tag::withArgumentInterface[]
+        def cli = new CliBuilder()
+        def options = cli.parseFromSpec(WithArgsI, '-a -b foo -c bar baz'.split())
+        assert options.a()
+        assert options.b() == 'foo'
+        assert options.c() == ['bar']
+        assert options.remaining() == ['baz']
+
+        options = cli.parseFromSpec(WithArgsI, '-a -c -b foo bar baz'.split())
+        assert options.a()
+        assert options.c() == []
+        assert options.b() == 'foo'
+        assert options.remaining() == ['bar', 'baz']
+        // end::withArgumentInterface[]
+        """
+    }
+
+    void testMultipleArgsAndOptionalValueSeparatorInterface() {
+        assertScript """
+        import cli.ValSepI
+        $importCliBuilder
+        // tag::multipleArgsInterface[]
+        def cli = new CliBuilder()
+
+        def options = cli.parseFromSpec(ValSepI, '-a 1 2 3 4'.split())
+        assert options.a() == ['1', '2']
+        assert options.remaining() == ['3', '4']
+
+        options = cli.parseFromSpec(ValSepI, '-a1 -a2 3'.split())
+        assert options.a() == ['1', '2']
+        assert options.remaining() == ['3']
+
+        options = cli.parseFromSpec(ValSepI, ['-b1,2'] as String[])
+        assert options.b() == ['1', '2']
+
+        options = cli.parseFromSpec(ValSepI, ['-c', '1'] as String[])
+        assert options.c() == ['1']
+
+        options = cli.parseFromSpec(ValSepI, ['-c1'] as String[])
+        assert options.c() == ['1']
+
+        options = cli.parseFromSpec(ValSepI, ['-c1,2,3'] as String[])
+        assert options.c() == ['1', '2', '3']
+        // end::multipleArgsInterface[]
+        """
+    }
+
+    void testType() {
+        assertScript """
+        import java.math.RoundingMode
+        $importCliBuilder
+        // tag::withType[]
+        def argz = '''-a John -b -d 21 -e 1980 -f 3.5 -g 3.14159
+            -h cv.txt -i DOWN and some more'''.split()
+        def cli = new CliBuilder()
+        cli.a(type: String, 'a-arg')
+        cli.b(type: boolean, 'b-arg')
+        cli.c(type: Boolean, 'c-arg')
+        cli.d(type: int, 'd-arg')
+        cli.e(type: Long, 'e-arg')
+        cli.f(type: Float, 'f-arg')
+        cli.g(type: BigDecimal, 'g-arg')
+        cli.h(type: File, 'h-arg')
+        cli.i(type: RoundingMode, 'i-arg')
+        def options = cli.parse(argz)
+        assert options.a == 'John'
+        assert options.b
+        assert !options.c
+        assert options.d == 21
+        assert options.e == 1980L
+        assert options.f == 3.5f
+        assert options.g == 3.14159
+        assert options.h == new File('cv.txt')
+        assert options.i == RoundingMode.DOWN
+        assert options.arguments() == ['and', 'some', 'more']
+        // end::withType[]
+        """
+    }
+
+    void testTypeMultiple() {
+        assertScript """
+        $importCliBuilder
+        // tag::withTypeMultiple[]
+        def argz = '''-j 3 4 5 -k1.5,2.5,3.5 and some more'''.split()
+        def cli = new CliBuilder()
+        cli.j(args: 3, type: int[], 'j-arg')
+        cli.k(args: '+', valueSeparator: ',', type: BigDecimal[], 'k-arg')
+        def options = cli.parse(argz)
+        assert options.js == [3, 4, 5] // <1>
+        assert options.j == [3, 4, 5]  // <1>
+        assert options.k == [1.5, 2.5, 3.5]
+        assert options.arguments() == ['and', 'some', 'more']
+        // end::withTypeMultiple[]
+        """
+    }
+
+    void testConvert() {
+        assertScript """
+        $importCliBuilder
+        // tag::withConvert[]
+        def argz = '''-a John -b Mary -d 2016-01-01 and some more'''.split()
+        def cli = new CliBuilder()
+        def lower = { it.toLowerCase() }
+        cli.a(convert: lower, 'a-arg')
+        cli.b(convert: { it.toUpperCase() }, 'b-arg')
+        cli.d(convert: { Date.parse('yyyy-MM-dd', it) }, 'd-arg')
+        def options = cli.parse(argz)
+        assert options.a == 'john'
+        assert options.b == 'MARY'
+        assert options.d.format('dd-MMM-yyyy') == '01-Jan-2016'
+        assert options.arguments() == ['and', 'some', 'more']
+        // end::withConvert[]
+        """
+    }
+
+    void testConvertInterface() {
+        assertScript """
+        import cli.WithConvertI
+        $importCliBuilder
+        // tag::withConvertInterface[]
+        Date newYears = Date.parse("yyyy-MM-dd", "2016-01-01")
+        def argz = '''-a John -b Mary -d 2016-01-01 and some more'''.split()
+        def cli = new CliBuilder()
+        def options = cli.parseFromSpec(WithConvertI, argz)
+        assert options.a() == 'john'
+        assert options.b() == 'MARY'
+        assert options.d() == newYears
+        assert options.remaining() == ['and', 'some', 'more']
+        // end::withConvertInterface[]
+        """
+    }
+
+    void testDefaultValue() {
+        assertScript """
+        $importCliBuilder
+        // tag::withDefaultValue[]
+        def cli = new CliBuilder()
+        cli.f longOpt: 'from', type: String, args: 1, defaultValue: 'one', 'f option'
+        cli.t longOpt: 'to', type: int, defaultValue: '35', 't option'
+
+        def options = cli.parse('-f two'.split())
+        assert options.hasOption('f')
+        assert options.f == 'two'
+        assert !options.hasOption('t')
+        assert options.t == 35
+
+        options = cli.parse('-t 45'.split())
+        assert !options.hasOption('from')
+        assert options.from == 'one'
+        assert options.hasOption('to')
+        assert options.to == 45
+        // end::withDefaultValue[]
+        """
+    }
+
+    void testDefaultValueInterface() {
+        assertScript """
+        $importCliBuilder
+        import cli.WithDefaultValueI
+        // tag::withDefaultValueInterface[]
+        def cli = new CliBuilder()
+
+        def options = cli.parseFromSpec(WithDefaultValueI, '-f two'.split())
+        assert options.from() == 'two'
+        assert options.to() == 35
+
+        options = cli.parseFromSpec(WithDefaultValueI, '-t 45'.split())
+        assert options.from() == 'one'
+        assert options.to() == 45
+        // end::withDefaultValueInterface[]
+        """
+    }
+
+    void testTypeCheckedInterfaceRunner() {
+        assertScript """
+        $importCliBuilder
+        import cli.TypeCheckedI
+        import groovy.transform.TypeChecked
+        // tag::withTypeCheckedInterface[]
+        @TypeChecked
+        void testTypeCheckedInterface() {
+            def argz = "--name John --age 21 and some more".split()
+            def cli = new CliBuilder()
+            def options = cli.parseFromSpec(TypeCheckedI, argz)
+            String n = options.name()
+            int a = options.age()
+            assert n == 'John' && a == 21
+            assert options.remaining() == ['and', 'some', 'more']
+        }
+        // end::withTypeCheckedInterface[]
+        testTypeCheckedInterface()
+        """
+    }
+
+    void testTypeCheckedRunner() {
+        assertScript """
+        $importCliBuilder
+        // tag::withTypeChecked[]
+        import groovy.cli.TypedOption
+        import groovy.transform.TypeChecked
+
+        @TypeChecked
+        void testTypeChecked() {
+            def cli = new CliBuilder()
+            TypedOption<String> name = cli.option(String, opt: 'n', longOpt: 'name', 'name option')
+            TypedOption<Integer> age = cli.option(Integer, longOpt: 'age', 'age option')
+            def argz = "--name John --age 21 and some more".split()
+            def options = cli.parse(argz)
+            String n = options[name]
+            int a = options[age]
+            assert n == 'John' && a == 21
+            assert options.arguments() == ['and', 'some', 'more']
+        }
+        // end::withTypeChecked[]
+        testTypeChecked()
+        """
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/src/spec/test/cli/GreeterC.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/cli/GreeterC.groovy b/src/spec/test/cli/GreeterC.groovy
new file mode 100644
index 0000000..500819a
--- /dev/null
+++ b/src/spec/test/cli/GreeterC.groovy
@@ -0,0 +1,39 @@
+/*
+ *  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 cli
+
+import groovy.cli.Option
+import groovy.cli.Unparsed
+
+// tag::annotationClassSpec[]
+class GreeterC {
+    @Option(shortName='h', description='display usage')
+    Boolean help                        // <1>
+
+    private String audience
+    @Option(shortName='a', description='greeting audience')
+    void setAudience(String audience) { // <2>
+        this.audience = audience
+    }
+    String getAudience() { audience }
+
+    @Unparsed(description = "positional parameters")
+    List remaining                      // <3>
+}
+// end::annotationClassSpec[]

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/src/spec/test/cli/GreeterI.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/cli/GreeterI.groovy b/src/spec/test/cli/GreeterI.groovy
new file mode 100644
index 0000000..bf9ca3d
--- /dev/null
+++ b/src/spec/test/cli/GreeterI.groovy
@@ -0,0 +1,30 @@
+/*
+ *  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 cli
+
+import groovy.cli.Option
+import groovy.cli.Unparsed
+
+// tag::annotationInterfaceSpec[]
+interface GreeterI {
+    @Option(shortName='h', description='display usage') Boolean help()        // <1>
+    @Option(shortName='a', description='greeting audience') String audience() // <2>
+    @Unparsed(description = "positional parameters") List remaining()         // <3>
+}
+// end::annotationInterfaceSpec[]

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/src/spec/test/cli/TypeCheckedI.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/cli/TypeCheckedI.groovy b/src/spec/test/cli/TypeCheckedI.groovy
new file mode 100644
index 0000000..9569364
--- /dev/null
+++ b/src/spec/test/cli/TypeCheckedI.groovy
@@ -0,0 +1,30 @@
+/*
+ *  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 cli
+
+import groovy.cli.Option
+import groovy.cli.Unparsed
+
+// tag::withTypeCheckedInterfaceSpec[]
+interface TypeCheckedI{
+    @Option String name()
+    @Option int age()
+    @Unparsed List remaining()
+}
+// end::withTypeCheckedInterfaceSpec[]

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/src/spec/test/cli/ValSepI.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/cli/ValSepI.groovy b/src/spec/test/cli/ValSepI.groovy
new file mode 100644
index 0000000..3dc4a2d
--- /dev/null
+++ b/src/spec/test/cli/ValSepI.groovy
@@ -0,0 +1,31 @@
+/*
+ *  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 cli
+
+import groovy.cli.Option
+import groovy.cli.Unparsed
+
+// tag::multipleArgsInterfaceSpec[]
+interface ValSepI {
+    @Option(numberOfArguments=2) String[] a()
+    @Option(numberOfArgumentsString='2', valueSeparator=',') String[] b()
+    @Option(numberOfArgumentsString='+', valueSeparator=',') String[] c()
+    @Unparsed remaining()
+}
+// end::multipleArgsInterfaceSpec[]

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/src/spec/test/cli/WithArgsI.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/cli/WithArgsI.groovy b/src/spec/test/cli/WithArgsI.groovy
new file mode 100644
index 0000000..aff71c0
--- /dev/null
+++ b/src/spec/test/cli/WithArgsI.groovy
@@ -0,0 +1,31 @@
+/*
+ *  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 cli
+
+import groovy.cli.Option
+import groovy.cli.Unparsed
+
+// tag::withArgumentInterfaceSpec[]
+interface WithArgsI {
+    @Option boolean a()
+    @Option String b()
+    @Option(optionalArg=true) String[] c()
+    @Unparsed List remaining()
+}
+// end::withArgumentInterfaceSpec[]

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/src/spec/test/cli/WithConvertI.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/cli/WithConvertI.groovy b/src/spec/test/cli/WithConvertI.groovy
new file mode 100644
index 0000000..1f3d97b
--- /dev/null
+++ b/src/spec/test/cli/WithConvertI.groovy
@@ -0,0 +1,31 @@
+/*
+ *  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 cli
+
+import groovy.cli.Option
+import groovy.cli.Unparsed
+
+// tag::withConvertInterfaceSpec[]
+interface WithConvertI {
+    @Option(convert={ it.toLowerCase() }) String a()
+    @Option(convert={ it.toUpperCase() }) String b()
+    @Option(convert={ Date.parse("yyyy-MM-dd", it) }) Date d()
+    @Unparsed List remaining()
+}
+// end::withConvertInterfaceSpec[]

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/src/spec/test/cli/WithDefaultValueI.groovy
----------------------------------------------------------------------
diff --git a/src/spec/test/cli/WithDefaultValueI.groovy b/src/spec/test/cli/WithDefaultValueI.groovy
new file mode 100644
index 0000000..2d27379
--- /dev/null
+++ b/src/spec/test/cli/WithDefaultValueI.groovy
@@ -0,0 +1,28 @@
+/*
+ *  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 cli
+
+import groovy.cli.Option
+
+// tag::withDefaultValueInterfaceSpec[]
+interface WithDefaultValueI {
+    @Option(shortName='f', defaultValue='one') String from()
+    @Option(shortName='t', defaultValue='35') int to()
+}
+// end::withDefaultValueInterfaceSpec[]

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/subprojects/groovy-cli-commons/build.gradle
----------------------------------------------------------------------
diff --git a/subprojects/groovy-cli-commons/build.gradle b/subprojects/groovy-cli-commons/build.gradle
index c77cf10..ccb91ab 100644
--- a/subprojects/groovy-cli-commons/build.gradle
+++ b/subprojects/groovy-cli-commons/build.gradle
@@ -19,6 +19,7 @@
 dependencies {
     compile rootProject
     compile "commons-cli:commons-cli:$commonsCliVersion"
+    testCompile rootProject.sourceSets.test.output
     testCompile project(':groovy-test')
     testCompile project(':groovy-dateutil')
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy b/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy
index c0b760a..7985159 100644
--- a/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy
+++ b/subprojects/groovy-cli-commons/src/spec/test/builder/CliBuilderTest.groovy
@@ -18,351 +18,35 @@
  */
 package builder
 
-import groovy.cli.Option
+import cli.CliBuilderTestCase
 import groovy.cli.TypedOption
-import groovy.cli.Unparsed
+import groovy.cli.commons.CliBuilder
 import groovy.transform.TypeChecked
 
-import java.math.RoundingMode
+class CliBuilderTest extends CliBuilderTestCase {
 
-class CliBuilderTest extends GroovyTestCase {
-//    void tearDown() {
-//    }
+    String getImportCliBuilder() { 'import groovy.cli.commons.CliBuilder\n' }
 
-    // tag::annotationInterfaceSpec[]
-    interface GreeterI {
-        @Option(shortName='h', description='display usage') Boolean help()        // <1>
-        @Option(shortName='a', description='greeting audience') String audience() // <2>
-        @Unparsed List remaining()                                                // <3>
+    // don't expect toString values to be the same, so test per CliBuilder implementation
+    void testAnnotationsInterfaceToString() {
+        doTestAnnotationsInterfaceToString('usage', '''\
+usage: groovy Greeter
+ -a,--audience <arg>   greeting audience
+ -h,--help             display usage
+''')
     }
-    // end::annotationInterfaceSpec[]
 
-    // tag::annotationClassSpec[]
-    class GreeterC {
-        @Option(shortName='h', description='display usage')
-        Boolean help                        // <1>
-
-        private String audience
-        @Option(shortName='a', description='greeting audience')
-        void setAudience(String audience) { // <2>
-            this.audience = audience
-        }
-        String getAudience() { audience }
-
-        @Unparsed
-        List remaining                      // <3>
-    }
-    // end::annotationClassSpec[]
-
-    void testAnnotationsInterface() {
-        // tag::annotationInterface[]
-        def cli = new CliBuilder(usage: 'groovy Greeter [option]')  // <1>
-        def argz = '--audience Groovologist'.split()
-        def options = cli.parseFromSpec(GreeterI, argz)             // <2>
-        assert options.audience() == 'Groovologist'                 // <3>
-
-        argz = '-h Some Other Args'.split()
-        options = cli.parseFromSpec(GreeterI, argz)                 // <4>
-        assert options.help()
-        assert options.remaining() == ['Some', 'Other', 'Args']     // <5>
-        // end::annotationInterface[]
-    }
-
-    void testAnnotationsClass() {
-        // tag::annotationClass[]
-        def cli = new CliBuilder(usage: 'groovy Greeter [option]') // <1>
-        def options = new GreeterC()                               // <2>
-        def argz = '--audience Groovologist foo'.split()
-        cli.parseFromInstance(options, argz)                       // <3>
-        assert options.audience == 'Groovologist'                  // <4>
-        assert options.remaining == ['foo']                        // <5>
-        // end::annotationClass[]
-    }
-
-    void testParseScript() {
-        def argz = '--audience Groovologist foo'.split()
-        new GroovyShell().run('''
-            // tag::annotationScript[]
-            import groovy.cli.OptionField
-            import groovy.cli.UnparsedField
-
-            @OptionField String audience
-            @OptionField Boolean help
-            @UnparsedField List remaining
-            new CliBuilder().parseFromInstance(this, args)
-            assert audience == 'Groovologist'
-            assert remaining == ['foo']
-            // end::annotationScript[]
-        ''', 'TestScript.groovy', argz)
-    }
-
-    void testWithArgument() {
-        // tag::withArgument[]
-        def cli = new CliBuilder()
-        cli.a(args: 0, 'a arg') // <1>
-        cli.b(args: 1, 'b arg') // <2>
-        cli.c(args: 1, optionalArg: true, 'c arg') // <3>
-        def options = cli.parse('-a -b foo -c bar baz'.split()) // <4>
-
-        assert options.a == true
-        assert options.b == 'foo'
-        assert options.c == 'bar'
-        assert options.arguments() == ['baz']
-
-        options = cli.parse('-a -c -b foo bar baz'.split()) // <5>
-
-        assert options.a == true
-        assert options.c == true
-        assert options.b == 'foo'
-        assert options.arguments() == ['bar', 'baz']
-        // end::withArgument[]
-    }
-
-    // tag::withArgumentInterfaceSpec[]
-    interface WithArgsI {
-        @Option boolean a()
-        @Option String b()
-        @Option(optionalArg=true) String[] c()
-        @Unparsed List remaining()
-    }
-    // end::withArgumentInterfaceSpec[]
-
-    void testWithArgumentInterface() {
-        // tag::withArgumentInterface[]
-        def cli = new CliBuilder()
-        def options = cli.parseFromSpec(WithArgsI, '-a -b foo -c bar baz'.split())
-        assert options.a()
-        assert options.b() == 'foo'
-        assert options.c() == ['bar']
-        assert options.remaining() == ['baz']
-
-        options = cli.parseFromSpec(WithArgsI, '-a -c -b foo bar baz'.split())
-        assert options.a()
-        assert options.c() == []
-        assert options.b() == 'foo'
-        assert options.remaining() == ['bar', 'baz']
-        // end::withArgumentInterface[]
-    }
-
-    void testMultipleArgsAndOptionalValueSeparator() {
-        // tag::multipleArgs[]
-        def cli = new CliBuilder()
-        cli.a(args: 2, 'a-arg')
-        cli.b(args: '2', valueSeparator: ',', 'b-arg') // <1>
-        cli.c(args: '+', valueSeparator: ',', 'c-arg') // <2>
-
-        def options = cli.parse('-a 1 2 3 4'.split()) // <3>
-        assert options.a == '1' // <4>
-        assert options.as == ['1', '2'] // <5>
-        assert options.arguments() == ['3', '4']
-
-        options = cli.parse('-a1 -a2 3'.split()) // <6>
-        assert options.as == ['1', '2']
-        assert options.arguments() == ['3']
-
-        options = cli.parse(['-b1,2']) // <7>
-        assert options.bs == ['1', '2']
-
-        options = cli.parse(['-c', '1'])
-        assert options.cs == ['1']
-
-        options = cli.parse(['-c1'])
-        assert options.cs == ['1']
-
-        options = cli.parse(['-c1,2,3'])
-        assert options.cs == ['1', '2', '3']
-        // end::multipleArgs[]
-    }
-
-    // tag::multipleArgsInterfaceSpec[]
-    interface ValSepI {
-        @Option(numberOfArguments=2) String[] a()
-        @Option(numberOfArgumentsString='2', valueSeparator=',') String[] b()
-        @Option(numberOfArgumentsString='+', valueSeparator=',') String[] c()
-        @Unparsed remaining()
-    }
-    // end::multipleArgsInterfaceSpec[]
-
-    void testMultipleArgsAndOptionalValueSeparatorInterface() {
-        // tag::multipleArgsInterface[]
-        def cli = new CliBuilder()
-
-        def options = cli.parseFromSpec(ValSepI, '-a 1 2 3 4'.split())
-        assert options.a() == ['1', '2']
-        assert options.remaining() == ['3', '4']
-
-        options = cli.parseFromSpec(ValSepI, '-a1 -a2 3'.split())
-        assert options.a() == ['1', '2']
-        assert options.remaining() == ['3']
-
-        options = cli.parseFromSpec(ValSepI, ['-b1,2'] as String[])
-        assert options.b() == ['1', '2']
-
-        options = cli.parseFromSpec(ValSepI, ['-c', '1'] as String[])
-        assert options.c() == ['1']
-
-        options = cli.parseFromSpec(ValSepI, ['-c1'] as String[])
-        assert options.c() == ['1']
-
-        options = cli.parseFromSpec(ValSepI, ['-c1,2,3'] as String[])
-        assert options.c() == ['1', '2', '3']
-        // end::multipleArgsInterface[]
-    }
-
-    void testType() {
-        // tag::withType[]
-        def argz = '''-a John -b -d 21 -e 1980 -f 3.5 -g 3.14159
-            -h cv.txt -i DOWN and some more'''.split()
-        def cli = new CliBuilder()
-        cli.a(type: String, 'a-arg')
-        cli.b(type: boolean, 'b-arg')
-        cli.c(type: Boolean, 'c-arg')
-        cli.d(type: int, 'd-arg')
-        cli.e(type: Long, 'e-arg')
-        cli.f(type: Float, 'f-arg')
-        cli.g(type: BigDecimal, 'g-arg')
-        cli.h(type: File, 'h-arg')
-        cli.i(type: RoundingMode, 'i-arg')
-        def options = cli.parse(argz)
-        assert options.a == 'John'
-        assert options.b
-        assert !options.c
-        assert options.d == 21
-        assert options.e == 1980L
-        assert options.f == 3.5f
-        assert options.g == 3.14159
-        assert options.h == new File('cv.txt')
-        assert options.i == RoundingMode.DOWN
-        assert options.arguments() == ['and', 'some', 'more']
-        // end::withType[]
-    }
-
-    void testTypeMultiple() {
-        // tag::withTypeMultiple[]
-        def argz = '''-j 3 4 5 -k1.5,2.5,3.5 and some more'''.split()
-        def cli = new CliBuilder()
-        cli.j(args: 3, type: int[], 'j-arg')
-        cli.k(args: '+', valueSeparator: ',', type: BigDecimal[], 'k-arg')
-        def options = cli.parse(argz)
-        assert options.js == [3, 4, 5] // <1>
-        assert options.j == [3, 4, 5]  // <1>
-        assert options.k == [1.5, 2.5, 3.5]
-        assert options.arguments() == ['and', 'some', 'more']
-        // end::withTypeMultiple[]
-    }
-
-    void testConvert() {
-        // tag::withConvert[]
-        def argz = '''-a John -b Mary -d 2016-01-01 and some more'''.split()
-        def cli = new CliBuilder()
-        def lower = { it.toLowerCase() }
-        cli.a(convert: lower, 'a-arg')
-        cli.b(convert: { it.toUpperCase() }, 'b-arg')
-        cli.d(convert: { Date.parse('yyyy-MM-dd', it) }, 'd-arg')
-        def options = cli.parse(argz)
-        assert options.a == 'john'
-        assert options.b == 'MARY'
-        assert options.d.format('dd-MMM-yyyy') == '01-Jan-2016'
-        assert options.arguments() == ['and', 'some', 'more']
-        // end::withConvert[]
-    }
-
-    // tag::withConvertInterfaceSpec[]
-    interface WithConvertI {
-        @Option(convert={ it.toLowerCase() }) String a()
-        @Option(convert={ it.toUpperCase() }) String b()
-        @Option(convert={ Date.parse("yyyy-MM-dd", it) }) Date d()
-        @Unparsed List remaining()
-    }
-    // end::withConvertInterfaceSpec[]
-
-    void testConvertInterface() {
-        // tag::withConvertInterface[]
-        Date newYears = Date.parse("yyyy-MM-dd", "2016-01-01")
-        def argz = '''-a John -b Mary -d 2016-01-01 and some more'''.split()
-        def cli = new CliBuilder()
-        def options = cli.parseFromSpec(WithConvertI, argz)
-        assert options.a() == 'john'
-        assert options.b() == 'MARY'
-        assert options.d() == newYears
-        assert options.remaining() == ['and', 'some', 'more']
-        // end::withConvertInterface[]
-    }
-
-    void testDefaultValue() {
-        // tag::withDefaultValue[]
-        def cli = new CliBuilder()
-        cli.f longOpt: 'from', type: String, args: 1, defaultValue: 'one', 'f option'
-        cli.t longOpt: 'to', type: int, defaultValue: '35', 't option'
-
-        def options = cli.parse('-f two'.split())
-        assert options.hasOption('f')
-        assert options.f == 'two'
-        assert !options.hasOption('t')
-        assert options.t == 35
-
-        options = cli.parse('-t 45'.split())
-        assert !options.hasOption('from')
-        assert options.from == 'one'
-        assert options.hasOption('to')
-        assert options.to == 45
-        // end::withDefaultValue[]
-    }
-
-    // tag::withDefaultValueInterfaceSpec[]
-    interface WithDefaultValueI {
-        @Option(shortName='f', defaultValue='one') String from()
-        @Option(shortName='t', defaultValue='35') int to()
-    }
-    // end::withDefaultValueInterfaceSpec[]
-
-    void testDefaultValueInterface() {
-        // tag::withDefaultValueInterface[]
-        def cli = new CliBuilder()
-
-        def options = cli.parseFromSpec(WithDefaultValueI, '-f two'.split())
-        assert options.from() == 'two'
-        assert options.to() == 35
-
-        options = cli.parseFromSpec(WithDefaultValueI, '-t 45'.split())
-        assert options.from() == 'one'
-        assert options.to() == 45
-        // end::withDefaultValueInterface[]
-    }
-
-    // tag::withTypeCheckedInterfaceSpec[]
-    interface TypeCheckedI{
-        @Option String name()
-        @Option int age()
-        @Unparsed List remaining()
-    }
-    // end::withTypeCheckedInterfaceSpec[]
-
-    // tag::withTypeCheckedInterface[]
-    @TypeChecked
-    void testTypeCheckedInterface() {
-        def argz = "--name John --age 21 and some more".split()
-        def cli = new CliBuilder()
-        def options = cli.parseFromSpec(TypeCheckedI, argz)
-        String n = options.name()
-        int a = options.age()
-        assert n == 'John' && a == 21
-        assert options.remaining() == ['and', 'some', 'more']
-    }
-    // end::withTypeCheckedInterface[]
-
-    // tag::withTypeChecked[]
     @TypeChecked
-    void testTypeChecked() {
+    void testTypeChecked_showingSingleHyphenForLongOptSupport() {
         def cli = new CliBuilder()
         TypedOption<String> name = cli.option(String, opt: 'n', longOpt: 'name', 'name option')
         TypedOption<Integer> age = cli.option(Integer, longOpt: 'age', 'age option')
-        def argz = "--name John --age 21 and some more".split()
+        def argz = "--name John -age 21 and some more".split()
         def options = cli.parse(argz)
         String n = options[name]
         int a = options[age]
         assert n == 'John' && a == 21
         assert options.arguments() == ['and', 'some', 'more']
     }
-    // end::withTypeChecked[]
+
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/subprojects/groovy-cli-commons/src/spec/test/groovy/util/CliBuilderTest.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-cli-commons/src/spec/test/groovy/util/CliBuilderTest.groovy b/subprojects/groovy-cli-commons/src/spec/test/groovy/util/CliBuilderTest.groovy
new file mode 100644
index 0000000..a0d513c
--- /dev/null
+++ b/subprojects/groovy-cli-commons/src/spec/test/groovy/util/CliBuilderTest.groovy
@@ -0,0 +1,28 @@
+/*
+ *  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.util
+
+import cli.CliBuilderTestCase
+
+// test groovy.util legacy delegate
+class CliBuilderTest extends CliBuilderTestCase {
+
+    String getImportCliBuilder() { '' }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/subprojects/groovy-cli-picocli/build.gradle
----------------------------------------------------------------------
diff --git a/subprojects/groovy-cli-picocli/build.gradle b/subprojects/groovy-cli-picocli/build.gradle
index dececa0..d83ddfd 100644
--- a/subprojects/groovy-cli-picocli/build.gradle
+++ b/subprojects/groovy-cli-picocli/build.gradle
@@ -19,6 +19,7 @@
 dependencies {
     compile rootProject
     compile "info.picocli:picocli:$picocliVersion"
+    testCompile rootProject.sourceSets.test.output
     testCompile project(':groovy-test')
     testCompile project(':groovy-dateutil')
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/926b24a4/subprojects/groovy-cli-picocli/src/spec/test/builder/CliBuilderTest.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-cli-picocli/src/spec/test/builder/CliBuilderTest.groovy b/subprojects/groovy-cli-picocli/src/spec/test/builder/CliBuilderTest.groovy
index 195ac0d..62e00fd 100644
--- a/subprojects/groovy-cli-picocli/src/spec/test/builder/CliBuilderTest.groovy
+++ b/subprojects/groovy-cli-picocli/src/spec/test/builder/CliBuilderTest.groovy
@@ -18,366 +18,45 @@
  */
 package builder
 
+import cli.CliBuilderTestCase
 import groovy.cli.picocli.CliBuilder
-import groovy.cli.Option
 import groovy.cli.TypedOption
-import groovy.cli.Unparsed
 import groovy.transform.TypeChecked
 
-import java.math.RoundingMode
-import java.text.SimpleDateFormat
+// tag::mapOptionImports[]
 import java.util.concurrent.TimeUnit
 import static java.util.concurrent.TimeUnit.DAYS
 import static java.util.concurrent.TimeUnit.HOURS
+// end::mapOptionImports[]
 
-//import java.math.RoundingMode
+// Core functionality we expect to remain the same for all implementations is tested in the base test case
+// here we also add any functionality specific to this implementation that we value highly
+class CliBuilderTest extends CliBuilderTestCase {
 
-class CliBuilderTest extends GroovyTestCase {
-//    void tearDown() {
-//    }
+    String getImportCliBuilder() { 'import groovy.cli.picocli.CliBuilder\n' }
 
-    // tag::annotationInterfaceSpec[]
-    interface GreeterI {
-        @Option(shortName='h', description='display usage') Boolean help()        // <1>
-        @Option(shortName='a', description='greeting audience') String audience() // <2>
-        @Unparsed(description = "positional parameters") List remaining()         // <3>
-    }
-    // end::annotationInterfaceSpec[]
-
-    // tag::annotationClassSpec[]
-    class GreeterC {
-        @Option(shortName='h', description='display usage')
-        Boolean help                        // <1>
-
-        private String audience
-        @Option(shortName='a', description='greeting audience')
-        void setAudience(String audience) { // <2>
-            this.audience = audience
-        }
-        String getAudience() { audience }
-
-        @Unparsed(description = "positional parameters")
-        List remaining                      // <3>
+    void testAnnotationsInterfaceToStringWithUsage() {
+        doTestAnnotationsInterfaceToString('usage', '''\
+Usage: groovy Greeter
+      [<remaining>...]   positional parameters
+  -a, --audience=<audience>
+                         greeting audience
+  -h, --help             display usage
+''')
     }
-    // end::annotationClassSpec[]
-
-    void testAnnotationsInterface() {
-        // tag::annotationInterface[]
-        def cli = new CliBuilder(name: 'groovy Greeter')  // <1>
-        def argz = '--audience Groovologist'.split()
-        def options = cli.parseFromSpec(GreeterI, argz)             // <2>
-        assert options.audience() == 'Groovologist'                 // <3>
-
-        argz = '-h Some Other Args'.split()
-        options = cli.parseFromSpec(GreeterI, argz)                 // <4>
-        assert options.help()
-        assert options.remaining() == ['Some', 'Other', 'Args']     // <5>
-        // end::annotationInterface[]
-
-        options = cli.parseFromSpec(GreeterI, ['-h', 'Some', 'Other', 'Args'] as String[])
-        assert options.help()
-        assert options.remaining() == ['Some', 'Other', 'Args']
-        StringWriter sw = new StringWriter()
-        cli.writer = new PrintWriter(sw)
-        cli.usage()
 
-        String expected = '''\
+    void testAnnotationsInterfaceToStringWithName() {
+        doTestAnnotationsInterfaceToString('name', '''\
 Usage: groovy Greeter [-h] [-a=<audience>] [<remaining>...]
       [<remaining>...]   positional parameters
   -a, --audience=<audience>
                          greeting audience
   -h, --help             display usage
-'''
-        assert expected.normalize() == sw.toString().normalize()
-    }
-
-    void testAnnotationsClass() {
-        // tag::annotationClass[]
-        def cli = new CliBuilder(usage: 'groovy Greeter [option]') // <1>
-        def options = new GreeterC()                               // <2>
-        def argz = '--audience Groovologist foo'.split()
-        cli.parseFromInstance(options, argz)                       // <3>
-        assert options.audience == 'Groovologist'                  // <4>
-        assert options.remaining == ['foo']                        // <5>
-        // end::annotationClass[]
-    }
-
-    void testParseScript() {
-        def argz = '--audience Groovologist foo'.split()
-        new GroovyShell().run('''
-            // tag::annotationScript[]
-            import groovy.cli.picocli.CliBuilder
-            import groovy.cli.OptionField
-            import groovy.cli.UnparsedField
-
-            @OptionField String audience
-            @OptionField Boolean help
-            @UnparsedField List remaining
-            new CliBuilder().parseFromInstance(this, args)
-            assert audience == 'Groovologist'
-            assert remaining == ['foo']
-            // end::annotationScript[]
-        ''', 'TestScript.groovy', argz)
-    }
-
-    void testWithArgument() {
-        // tag::withArgument[]
-        def cli = new CliBuilder()
-        cli.a(args: 0, 'a arg') // <1>
-        cli.b(args: 1, 'b arg') // <2>
-        cli.c(args: 1, optionalArg: true, 'c arg') // <3>
-        def options = cli.parse('-a -b foo -c bar baz'.split()) // <4>
-
-        assert options.a == true
-        assert options.b == 'foo'
-        assert options.c == 'bar'
-        assert options.arguments() == ['baz']
-
-        options = cli.parse('-a -c -b foo bar baz'.split()) // <5>
-
-        assert options.a == true
-        assert options.c == true
-        assert options.b == 'foo'
-        assert options.arguments() == ['bar', 'baz']
-        // end::withArgument[]
-    }
-
-    // tag::withArgumentInterfaceSpec[]
-    interface WithArgsI {
-        @Option boolean a()
-        @Option String b()
-        @Option(optionalArg=true) String[] c()
-        @Unparsed List remaining()
-    }
-    // end::withArgumentInterfaceSpec[]
-
-    void testWithArgumentInterface() {
-        // tag::withArgumentInterface[]
-        def cli = new CliBuilder()
-        def options = cli.parseFromSpec(WithArgsI, '-a -b foo -c bar baz'.split())
-        assert options.a()
-        assert options.b() == 'foo'
-        assert options.c() == ['bar']
-        assert options.remaining() == ['baz']
-
-        options = cli.parseFromSpec(WithArgsI, '-a -c -b foo bar baz'.split())
-        assert options.a()
-        assert options.c() == []
-        assert options.b() == 'foo'
-        assert options.remaining() == ['bar', 'baz']
-        // end::withArgumentInterface[]
-    }
-
-    void testMultipleArgsAndOptionalValueSeparator() {
-        // tag::multipleArgs[]
-        def cli = new CliBuilder()
-        cli.a(args: 2, 'a-arg')
-        cli.b(args: '2', valueSeparator: ',', 'b-arg') // <1>
-        cli.c(args: '+', valueSeparator: ',', 'c-arg') // <2>
-
-        def options = cli.parse('-a 1 2 3 4'.split()) // <3>
-        assert options.a == '1' // <4>
-        assert options.as == ['1', '2'] // <5>
-        assert options.arguments() == ['3', '4']
-
-        options = cli.parse('-a1 -a2 3'.split()) // <6>
-        assert options.as == ['1', '2']
-        assert options.arguments() == ['3']
-
-        options = cli.parse(['-b1,2']) // <7>
-        assert options.bs == ['1', '2']
-
-        options = cli.parse(['-c', '1'])
-        assert options.cs == ['1']
-
-        options = cli.parse(['-c1'])
-        assert options.cs == ['1']
-
-        options = cli.parse(['-c1,2,3'])
-        assert options.cs == ['1', '2', '3']
-        // end::multipleArgs[]
-    }
-
-    // tag::multipleArgsInterfaceSpec[]
-    interface ValSepI {
-        @Option(numberOfArguments=2) String[] a()
-        @Option(numberOfArgumentsString='2', valueSeparator=',') String[] b()
-        @Option(numberOfArgumentsString='+', valueSeparator=',') String[] c()
-        @Unparsed remaining()
-    }
-    // end::multipleArgsInterfaceSpec[]
-
-    void testMultipleArgsAndOptionalValueSeparatorInterface() {
-        // tag::multipleArgsInterface[]
-        def cli = new CliBuilder()
-
-        def options = cli.parseFromSpec(ValSepI, '-a 1 2 3 4'.split())
-        assert options.a() == ['1', '2']
-        assert options.remaining() == ['3', '4']
-
-        options = cli.parseFromSpec(ValSepI, '-a1 -a2 3'.split())
-        assert options.a() == ['1', '2']
-        assert options.remaining() == ['3']
-
-        options = cli.parseFromSpec(ValSepI, ['-b1,2'] as String[])
-        assert options.b() == ['1', '2']
-
-        options = cli.parseFromSpec(ValSepI, ['-c', '1'] as String[])
-        assert options.c() == ['1']
-
-        options = cli.parseFromSpec(ValSepI, ['-c1'] as String[])
-        assert options.c() == ['1']
-
-        options = cli.parseFromSpec(ValSepI, ['-c1,2,3'] as String[])
-        assert options.c() == ['1', '2', '3']
-        // end::multipleArgsInterface[]
-    }
-
-    void testType() {
-        // tag::withType[]
-        def argz = '''-a John -b -d 21 -e 1980 -f 3.5 -g 3.14159
-            -h cv.txt -i DOWN and some more'''.split()
-        def cli = new CliBuilder()
-        cli.a(type: String, 'a-arg')
-        cli.b(type: boolean, 'b-arg')
-        cli.c(type: Boolean, 'c-arg')
-        cli.d(type: int, 'd-arg')
-        cli.e(type: Long, 'e-arg')
-        cli.f(type: Float, 'f-arg')
-        cli.g(type: BigDecimal, 'g-arg')
-        cli.h(type: File, 'h-arg')
-        cli.i(type: RoundingMode, 'i-arg')
-        def options = cli.parse(argz)
-        assert options.a == 'John'
-        assert options.b
-        assert !options.c
-        assert options.d == 21
-        assert options.e == 1980L
-        assert options.f == 3.5f
-        assert options.g == 3.14159
-        assert options.h == new File('cv.txt')
-        assert options.i == RoundingMode.DOWN
-        assert options.arguments() == ['and', 'some', 'more']
-        // end::withType[]
-    }
-
-    void testTypeMultiple() {
-        // tag::withTypeMultiple[]
-        def argz = '''-j 3 4 5 -k1.5,2.5,3.5 and some more'''.split()
-        def cli = new CliBuilder()
-        cli.j(args: 3, type: int[], 'j-arg')
-        cli.k(args: '+', valueSeparator: ',', type: BigDecimal[], 'k-arg')
-        def options = cli.parse(argz)
-        assert options.js == [3, 4, 5] // <1>
-        assert options.j == [3, 4, 5]  // <1>
-        assert options.k == [1.5, 2.5, 3.5]
-        assert options.arguments() == ['and', 'some', 'more']
-        // end::withTypeMultiple[]
-    }
-
-    void testConvert() {
-        // tag::withConvert[]
-        def argz = '''-a John -b Mary -d 2016-01-01 and some more'''.split()
-        def cli = new CliBuilder()
-        def lower = { it.toLowerCase() }
-        cli.a(convert: lower, 'a-arg')
-        cli.b(convert: { it.toUpperCase() }, 'b-arg')
-        cli.d(convert: { new SimpleDateFormat("yyyy-MM-dd").parse(it) }, 'd-arg')
-        def options = cli.parse(argz)
-        assert options.a == 'john'
-        assert options.b == 'MARY'
-        assert new SimpleDateFormat("dd-MMM-yyyy").format(options.d) == '01-Jan-2016'
-        assert options.arguments() == ['and', 'some', 'more']
-        // end::withConvert[]
-    }
-
-    // tag::withConvertInterfaceSpec[]
-    interface WithConvertI {
-        @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()
-    }
-    // end::withConvertInterfaceSpec[]
-
-    void testConvertInterface() {
-        // tag::withConvertInterface[]
-        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 = cli.parseFromSpec(WithConvertI, argz)
-        assert options.a() == 'john'
-        assert options.b() == 'MARY'
-        assert options.d() == newYears
-        assert options.remaining() == ['and', 'some', 'more']
-        // end::withConvertInterface[]
-    }
-
-    void testDefaultValue() {
-        // tag::withDefaultValue[]
-        def cli = new CliBuilder()
-        cli.f longOpt: 'from', type: String, args: 1, defaultValue: 'one', 'f option'
-        cli.t longOpt: 'to', type: int, defaultValue: '35', 't option'
-
-        def options = cli.parse('-f two'.split())
-        assert options.hasOption('f')
-        assert options.f == 'two'
-        assert !options.hasOption('t')
-        assert options.t == 35
-
-        options = cli.parse('-t 45'.split())
-        assert !options.hasOption('from')
-        assert options.from == 'one'
-        assert options.hasOption('to')
-        assert options.to == 45
-        // end::withDefaultValue[]
-    }
-
-    // tag::withDefaultValueInterfaceSpec[]
-    interface WithDefaultValueI {
-        @Option(shortName='f', defaultValue='one') String from()
-        @Option(shortName='t', defaultValue='35') int to()
-    }
-    // end::withDefaultValueInterfaceSpec[]
-
-    void testDefaultValueInterface() {
-        // tag::withDefaultValueInterface[]
-        def cli = new CliBuilder()
-
-        def options = cli.parseFromSpec(WithDefaultValueI, '-f two'.split())
-        assert options.from() == 'two'
-        assert options.to() == 35
-
-        options = cli.parseFromSpec(WithDefaultValueI, '-t 45'.split())
-        assert options.from() == 'one'
-        assert options.to() == 45
-        // end::withDefaultValueInterface[]
-    }
-
-    // tag::withTypeCheckedInterfaceSpec[]
-    interface TypeCheckedI{
-        @Option String name()
-        @Option int age()
-        @Unparsed List remaining()
-    }
-    // end::withTypeCheckedInterfaceSpec[]
-
-    // tag::withTypeCheckedInterface[]
-    @TypeChecked
-    void testTypeCheckedInterface() {
-        def argz = "--name John --age 21 and some more".split()
-        def cli = new CliBuilder()
-        def options = cli.parseFromSpec(TypeCheckedI, argz)
-        String n = options.name()
-        int a = options.age()
-        assert n == 'John' && a == 21
-        assert options.remaining() == ['and', 'some', 'more']
+''')
     }
-    // end::withTypeCheckedInterface[]
 
-    // tag::withTypeChecked[]
     @TypeChecked
-    void testTypeChecked() {
+    void testTypeChecked_addingSingleHyphenForLongOptSupport() {
         def cli = new CliBuilder(acceptLongOptionsWithSingleHyphen: true)
         TypedOption<String> name = cli.option(String, opt: 'n', longOpt: 'name', 'name option')
         TypedOption<Integer> age = cli.option(Integer, longOpt: 'age', 'age option')
@@ -388,7 +67,6 @@ Usage: groovy Greeter [-h] [-a=<audience>] [<remaining>...]
         assert n == 'John' && a == 21
         assert options.arguments() == ['and', 'some', 'more']
     }
-    // end::withTypeChecked[]
 
     @TypeChecked
     void testTypeChecked_defaultOnlyDoubleHyphen() {
@@ -450,8 +128,8 @@ Footer 2
         assertEquals(expected.normalize(), baos.toString().normalize())
     }
 
-    public void testMapOption() {
-        // tag::MapOption[]
+    void testMapOption() {
+        // tag::mapOption[]
         def cli = new CliBuilder()
         cli.D(args: 2,   valueSeparator: '=', 'the old way')                          // <1>
         cli.X(type: Map, 'the new way')                                               // <2>
@@ -461,10 +139,10 @@ Footer 2
         assert options.Ds == ['a', 'b', 'c', 'd']                                     // <5>
         assert options.Xs == [ 'x':'y', 'i':'j' ]                                     // <6>
         assert options.Zs == [ (DAYS as TimeUnit):2, (HOURS as TimeUnit):23 ]         // <7>
-        // end::MapOption[]
+        // end::mapOption[]
     }
 
-    public void testGroovyDocAntExample() {
+    void testGroovyDocAntExample() {
         def cli = new CliBuilder(usage:'ant [options] [targets]',
                 header:'Options:')
         cli.help('print this message')
@@ -493,7 +171,7 @@ Options:
         assertEquals(expected.normalize(), sw.toString().normalize())
     }
 
-    public void testGroovyDocCurlExample() {
+    void testGroovyDocCurlExample() {
         // suppress ANSI escape codes to make this test pass on all environments
         System.setProperty("picocli.ansi", "false")
         ByteArrayOutputStream baos = new ByteArrayOutputStream()


[2/3] groovy git commit: GROOVY-8569 minor tweak (fix tests for previous change)

Posted by pa...@apache.org.
GROOVY-8569 minor tweak (fix tests for previous change)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/ae40032b
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/ae40032b
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/ae40032b

Branch: refs/heads/GROOVY_2_5_X
Commit: ae40032b9e8c6f441ec5b575ba7858c9b2a8fb63
Parents: 926b24a
Author: Paul King <pa...@asert.com.au>
Authored: Sat May 19 23:33:54 2018 +1000
Committer: Paul King <pa...@asert.com.au>
Committed: Sun May 20 00:01:56 2018 +1000

----------------------------------------------------------------------
 src/test/groovy/ui/GroovyMainTest.groovy | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/ae40032b/src/test/groovy/ui/GroovyMainTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/ui/GroovyMainTest.groovy b/src/test/groovy/ui/GroovyMainTest.groovy
index ab788ac..018bc41 100644
--- a/src/test/groovy/ui/GroovyMainTest.groovy
+++ b/src/test/groovy/ui/GroovyMainTest.groovy
@@ -27,13 +27,13 @@ class GroovyMainTest extends GroovyTestCase {
         GroovyMain.processArgs(args, ps)
         def out = baos.toString()
         assert out.contains('Usage: groovy')
-        ['-a', '-c', '-d', '-e', '-h', '-i', '-l', '-n', '-p', '-V'].each{
+        ['-a', '-c', '-d', '-e', '-h', '-i', '-l', '-n', '-p', '-v'].each{
             assert out.contains(it)
         }
     }
 
     void testVersion() {
-        String[] args = ['-V']
+        String[] args = ['-v']
         GroovyMain.processArgs(args, ps)
         def out = baos.toString()
         assert out.contains('Groovy Version:')


[3/3] groovy git commit: GROOVY-8579: No bytecode level check is done before producing JDK8+ bytecode (avoid fix for DGM methods)

Posted by pa...@apache.org.
GROOVY-8579: No bytecode level check is done before producing JDK8+ bytecode (avoid fix for DGM methods)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/3fbd0b6f
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/3fbd0b6f
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/3fbd0b6f

Branch: refs/heads/GROOVY_2_5_X
Commit: 3fbd0b6f638165242d97ba4b372ba0593c80800c
Parents: ae40032
Author: Paul King <pa...@asert.com.au>
Authored: Sat May 19 23:57:11 2018 +1000
Committer: Paul King <pa...@asert.com.au>
Committed: Sun May 20 00:01:57 2018 +1000

----------------------------------------------------------------------
 .../codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/3fbd0b6f/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index d82b414..61d2f23 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -3252,7 +3252,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                             storeType(call, returnType);
                             storeTargetMethod(call, directMethodCallCandidate);
                             ClassNode declaringClass = directMethodCallCandidate.getDeclaringClass();
-                            if (declaringClass.isInterface() && directMethodCallCandidate.isStatic()) {
+                            if (declaringClass.isInterface() && directMethodCallCandidate.isStatic() && !(directMethodCallCandidate instanceof ExtensionMethodNode)) {
                                 typeCheckingContext.getEnclosingClassNode().putNodeMetaData(MINIMUM_BYTECODE_VERSION, Opcodes.V1_8);
                             }
                             String data = chosenReceiver.getData();