You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by pa...@apache.org on 2015/06/07 11:36:54 UTC

incubator-groovy git commit: GROOVY-6090: Reimplement CliBuilder over another CLI API (cheat and use CLI 1.3 DefaultParser as the other api)

Repository: incubator-groovy
Updated Branches:
  refs/heads/master d94c56585 -> 792027ea0


GROOVY-6090: Reimplement CliBuilder over another CLI API (cheat and use CLI 1.3 DefaultParser as the other api)


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

Branch: refs/heads/master
Commit: 792027ea08746cae15e104662555c890d281e5c4
Parents: d94c565
Author: Paul King <pa...@asert.com.au>
Authored: Sun Jun 7 19:08:10 2015 +1000
Committer: Paul King <pa...@asert.com.au>
Committed: Sun Jun 7 19:36:45 2015 +1000

----------------------------------------------------------------------
 src/main/groovy/util/CliBuilder.groovy          |  22 +-
 .../codehaus/groovy/cli/GroovyPosixParser.java  |  11 +-
 src/test/groovy/util/CliBuilderTest.groovy      | 472 ++++++++-----------
 3 files changed, 220 insertions(+), 285 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/792027ea/src/main/groovy/util/CliBuilder.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/util/CliBuilder.groovy b/src/main/groovy/util/CliBuilder.groovy
index dd71606..4ecbe82 100644
--- a/src/main/groovy/util/CliBuilder.groovy
+++ b/src/main/groovy/util/CliBuilder.groovy
@@ -18,8 +18,14 @@
  */
 package groovy.util
 
-import org.apache.commons.cli.*
-import org.codehaus.groovy.cli.GroovyPosixParser
+import org.apache.commons.cli.CommandLine
+import org.apache.commons.cli.CommandLineParser
+import org.apache.commons.cli.DefaultParser
+import org.apache.commons.cli.GnuParser
+import org.apache.commons.cli.HelpFormatter
+import org.apache.commons.cli.Option
+import org.apache.commons.cli.Options
+import org.apache.commons.cli.ParseException
 import org.codehaus.groovy.runtime.InvokerHelper
 
 /**
@@ -48,9 +54,9 @@ import org.codehaus.groovy.runtime.InvokerHelper
  * option exists with value 'alt' and provided that none of 'a', 'l' or 't'
  * takes an argument (in fact the last one is allowed to take an argument).
  * The bursting behavior can be turned off by using an
- * alternate underlying parser. The simplest way to achieve this is by setting
- * the posix property on the CliBuilder to false, i.e. include
- * <code>posix: false</code> in the constructor call.
+ * alternate underlying parser. The simplest way to achieve this is by using
+ * the deprecated GnuParser from Commons CLI with the parser property on the CliBuilder,
+ * i.e. include <code>parser: new GnuParser()</code> in the constructor call.
  * <p>
  * Another example (partial emulation of arg processing for 'ant' command line):
  * <pre>
@@ -224,7 +230,7 @@ class CliBuilder {
     /**
      * Allows customisation of the usage message width.
      */
-    int width = formatter.defaultWidth
+    int width = HelpFormatter.DEFAULT_WIDTH
 
     /**
      * Not normally accessed directly but full access to underlying options if needed.
@@ -259,7 +265,7 @@ class CliBuilder {
     OptionAccessor parse(args) {
         if (expandArgumentFiles) args = expandArgumentFiles(args)
         if (!parser) {
-            parser = posix == null ? new GroovyPosixParser() : posix == true ? new PosixParser() : new GnuParser()
+            parser = posix != null && posix == false ? new GnuParser() : new DefaultParser()
         }
         try {
             return new OptionAccessor(parser.parse(options, args as String[], stopAtNonOption))
@@ -274,7 +280,7 @@ class CliBuilder {
      * Print the usage message with writer (default: System.out) and formatter (default: HelpFormatter)
      */
     void usage() {
-        formatter.printHelp(writer, width, usage, header, options, formatter.defaultLeftPad, formatter.defaultDescPad, footer)
+        formatter.printHelp(writer, width, usage, header, options, HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, footer)
         writer.flush()
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/792027ea/src/main/org/codehaus/groovy/cli/GroovyPosixParser.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/cli/GroovyPosixParser.java b/src/main/org/codehaus/groovy/cli/GroovyPosixParser.java
index cd78c44..e583ac9 100644
--- a/src/main/org/codehaus/groovy/cli/GroovyPosixParser.java
+++ b/src/main/org/codehaus/groovy/cli/GroovyPosixParser.java
@@ -28,15 +28,16 @@ import java.util.Iterator;
 import java.util.List;
 
 /**
- * DO NOT USE. Hacked version until Commons CLI 1.3 is released.
- * NOTE: this is a mirror copy of org.apache.commons.cli.GroovyInternalPosixParser
- * DON'T MAKE CHANGES without keeping the other file in sync!
- * The class GroovyPosixParser provides an implementation of the
- * {@link org.apache.commons.cli.Parser#flatten(org.apache.commons.cli.Options,String[],boolean) flatten} method.
+ * This is a hacked version of Commons CLI 1.2 PosixParser with some bug fixes added.
+ * We aren't aware of any use cases where it isn't now preferable to use the
+ * Commons CLI 1.3 DefaultParser but this class is retained for the time being for
+ * backwards compatibility if someone is relying on this class's exact functionality.
  *
  * @author John Keyes (john at integralsource.com)
  * @author Paul King (Groovy hacks/fixes)
+ * @deprecated use the DefaultParser from Commons CLI
  */
+@Deprecated
 public class GroovyPosixParser extends Parser
 {
     /** holder for flattened tokens */

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/792027ea/src/test/groovy/util/CliBuilderTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/util/CliBuilderTest.groovy b/src/test/groovy/util/CliBuilderTest.groovy
index d1760e1..72fb25d 100644
--- a/src/test/groovy/util/CliBuilderTest.groovy
+++ b/src/test/groovy/util/CliBuilderTest.groovy
@@ -18,39 +18,19 @@
  */
 package groovy.util
 
-import org.codehaus.groovy.cli.GroovyPosixParser
+import org.apache.commons.cli.BasicParser
+import org.apache.commons.cli.DefaultParser
 import org.apache.commons.cli.GnuParser
 import org.apache.commons.cli.Option
-import org.apache.commons.cli.PosixParser
-import org.apache.commons.cli.BasicParser
+import org.codehaus.groovy.cli.GroovyPosixParser
 
 /**
- * Test class for the CliBuilder -- but then that is obvious from the name :-)
- * <p>
- * There appear to be issues when using the <code>PosixParser</code> in 1.0 and 1.1 &ndash; when an
- * option with a parameter is passed using a long form and a single letter parameter of some sort has been
- * declared (the problem does not occur if no single letter option has been declared) then the value "--"
- * is returned instead of the option parameter value.  This problem does not happen using the
- * <code>GnuParser</code>.
- * <p>
- * There appears to be an issue with <code>GnuParser</code> in 1.0 and 1.1 &ndash; if only a long option
- * is defined then the usual Groovy syntax for accessing the option fails to work.  It is fine if a short
- * option of some sort is defined.  This must be a <code>CliBuilder</code>/<code>OptionAccessor</code>
- * problem.  This problem does not happen with the <code>PosixParser</code>.
- * <p>
- * Commons CLI 1.0 appears not to be able to access arguments using a long name, if that option has a
- * short name -- in this case access is only using a short name.  This means it is possible to work with
- * long name option if and only if they have no short name.
+ * Test class for the CliBuilder.
  * <p>
- * Commons CLI 1.1 has fixed most of the problems in 1.0, but appears to have a broken getOptionValues
- * -- it returns only the first value -- and so is worse than useless.
- * <p>
- * 1.0 PosixBuilder removes unrecognized single letter options silently.  1.1 version may also do this.
- * GnuParser behaves according to the <code>stopAtNonOption</code> parameter -- throw
- * <code>UnrecognizedOptionException</code> when <code>false</code>, terminate parse leaving everything
- * following unprocessed if <code>true</code>.
- * <p>
- * Commons CLI 1.2 is supposed to fix all the bugs!
+ * Commons CLI has a long history of different parsers with slightly differing behavior and bugs.
+ * In nearly all cases, we now recommend using DefaultParser. In case you have very unique circumstances
+ * and really need behavior that can only be supplied by one of the legacy parsers, we also include
+ * some test case runs against some of the legacy parsers.
  *
  * @author Dierk König
  * @author Russel Winder
@@ -63,29 +43,29 @@ class CliBuilderTest extends GroovyTestCase {
     private PrintWriter printWriter
 
     void setUp() {
-        stringWriter = new StringWriter()
-        printWriter = new PrintWriter(stringWriter)
+        resetPrintWriter()
     }
 
     private final expectedParameter = 'ASCII'
     private final usageString = 'groovy [option]* filename'
 
     private void runSample(parser, optionList) {
+        resetPrintWriter()
         def cli = new CliBuilder(usage: usageString, writer: printWriter, parser: parser)
         cli.h(longOpt: 'help', 'usage information')
         cli.c(argName: 'charset', args: 1, longOpt: 'encoding', 'character encoding')
         cli.i(argName: 'extension', optionalArg: true, 'modify files in place, create backup if extension is given (e.g. \'.bak\')')
         def stringified = cli.options.toString()
         assert stringified =~ /i=\[ option: i  :: modify files in place, create backup if extension is given/
-        assert stringified =~ /c=\[ option: c encoding  \[ARG] :: character encoding/ // 1.2 behaves differently to 1.0 and 1.1 here.
+        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/ // 1.2 behaves differently to 1.0 and 1.1 here.
+        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')  //// Fails in 1.0, works for 1.x where x > 1.
+        assert options.hasOption('help')
         assert options.h
-        assert options.help  //// Fails in 1.0, works for 1.x where x > 1.
+        assert options.help
         if (options.h) { cli.usage() }
         def expectedUsage = """usage: $usageString
  -c,--encoding <charset>   character encoding
@@ -93,258 +73,167 @@ class CliBuilderTest extends GroovyTestCase {
  -i                        modify files in place, create backup if
                            extension is given (e.g. '.bak')"""
         assertEquals(expectedUsage, stringWriter.toString().tokenize('\r\n').join('\n'))
-        stringWriter = new StringWriter()
-        printWriter = new PrintWriter(stringWriter)
+        resetPrintWriter()
         cli.writer = printWriter
-        if (options.help) { cli.usage() }  //// Fails in 1.0, works for 1.x where x > 1.
-        assertEquals(expectedUsage, stringWriter.toString().tokenize('\r\n').join('\n'))  //// Fails in 1.0, works for 1.x where x > 1.
+        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')  //// Fails in 1.0, works for 1.x where x > 1.
-        assert options.encoding  //// Fails in 1.0, works for 1.x where x > 1.
+        assert options.hasOption('encoding')
+        assert options.encoding
         assertEquals(expectedParameter, options.getOptionValue('c'))
         assertEquals(expectedParameter, options.c)
-        assertEquals(expectedParameter, options.getOptionValue('encoding'))  //// Fails in 1.0, works for 1.x where x > 1.
-        assertEquals(expectedParameter, options.encoding)  //// Fails in 1.0, works for 1.x where x > 1.
+        assertEquals(expectedParameter, options.getOptionValue('encoding'))
+        assertEquals(expectedParameter, options.encoding)
         assertEquals(false, options.noSuchOptionGiven)
         assertEquals(false, options.hasOption('noSuchOptionGiven'))
         assertEquals(false, options.x)
         assertEquals(false, options.hasOption('x'))
     }
 
-    void testSampleShort_BasicParser() {
-        runSample(new BasicParser(), ['-h', '-c', expectedParameter])
-    }
-
-    void testSampleShort_GnuParser() {
-        runSample(new GnuParser(), ['-h', '-c', expectedParameter])
-    }
-
-    void testSampleShort_PosixParser() {
-        runSample(new PosixParser(), ['-h', '-c', expectedParameter])
-    }
-
-    void testSampleShort_DefaultParser() {
-        runSample(new GroovyPosixParser(), ['-h', '-c', expectedParameter])
-    }
-
-    void testSampleLong_BasicParser() {
-        runSample(new BasicParser(), ['--help', '--encoding', expectedParameter])
-    }
-
-    void testSampleLong_GnuParser() {
-        runSample(new GnuParser(), ['--help', '--encoding', expectedParameter])
+    private void resetPrintWriter() {
+        stringWriter = new StringWriter()
+        printWriter = new PrintWriter(stringWriter)
     }
 
-    void testSampleLong_PosixParser() {
-        runSample(new PosixParser(), ['--help', '--encoding', expectedParameter])
+    void testSampleShort() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser(), new BasicParser()].each { parser ->
+            runSample(parser, ['-h', '-c', expectedParameter])
+        }
     }
 
-    void testSampleLong_DefaultParser() {
-        runSample(new GroovyPosixParser(), ['--help', '--encoding', expectedParameter])
+    void testSampleLong() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser(), new BasicParser()].each { parser ->
+            runSample(parser, ['--help', '--encoding', expectedParameter])
+        }
     }
 
-    private void multipleArgs(parser) {
-        def cli = new CliBuilder(parser: parser)
-        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)  //// Fails in 1.0, works for 1.x where x > 1.
-        assertEquals(['1', '2'], options.args)  //// Fails in 1.0, works for 1.x where x > 1.
+    void testSimpleArg() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser(), new BasicParser()].each { parser ->
+            def cli = new CliBuilder(parser: parser)
+            cli.a([:], '')
+            def options = cli.parse(['-a', '1', '2'])
+            assertEquals(['1', '2'], options.arguments())
+        }
     }
 
-    void testMultipleArgs_BasicParser() { multipleArgs(new BasicParser()) }
-
-    void testMultipleArgs_GnuParser() { multipleArgs(new GnuParser()) }
-
-    void testMultipleArgs_PosixParser() { multipleArgs(new PosixParser()) }
-
-    private void doArgs(parser) {
-        def cli = new CliBuilder(parser: parser)
-        cli.a([:], '')
-        def options = cli.parse(['-a', '1', '2'])
-        assertEquals(['1', '2'], options.arguments())
+    void testMultipleArgs() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser(), new BasicParser()].each { parser ->
+            def cli = new CliBuilder(parser: parser)
+            cli.a(longOpt: 'arg', args: 2, valueSeparator: ',' as char, 'arguments')
+            def options = cli.parse(['-a', '1,2'])
+            assertEquals('1', options.a)
+            assertEquals(['1', '2'], options.as)
+            assertEquals('1', options.arg)
+            assertEquals(['1', '2'], options.args)
+        }
     }
 
-    void testArgs_BasicParser() { doArgs(new BasicParser()) }
-
-    void testArgs_GnuParser() { doArgs(new GnuParser()) }
-
-    void testArgs_PosixParser() { doArgs(new PosixParser()) }
-
     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().normalize() == '''error: Missing required option: x
 usage: groovy
  -x   message
 '''
     }
 
-    private void checkLongOptsOnly_nonOptionShouldStopArgProcessing(CliBuilder cli) {
-        def anOption = Option.builder().longOpt('anOption').hasArg().desc('An option.').build()
-        cli.options.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 testLongOptsOnly_GnuParser() {
-        def cli = new CliBuilder(parser: new GnuParser())
-        checkLongOptsOnly_nonOptionShouldStopArgProcessing(cli)
-    }
-
-    void testLongOptsOnly_PosixParser() {
-        def cli = new CliBuilder(parser: new PosixParser())
-        checkLongOptsOnly_nonOptionShouldStopArgProcessing(cli)
-    }
-
-    void testLongOptsOnly_GnuParser_settingPosixBooleanFalse() {
-        def cli = new CliBuilder(posix: false)
-        checkLongOptsOnly_nonOptionShouldStopArgProcessing(cli)
-    }
-
-    private void checkLongAndShortOpts_allOptionsValid(parser) {
-        def cli = new CliBuilder(parser: parser)
-        def anOption = Option.builder().longOpt('anOption').hasArg().desc('An option.').build()
-        cli.options.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 testLongAndShortOpts_BasicParser() {
-        checkLongAndShortOpts_allOptionsValid(new BasicParser())
-    }
-
-    void testLongAndShortOpts_PosixParser() {
-        checkLongAndShortOpts_allOptionsValid(new PosixParser())
-    }
-
-    void testLongAndShortOpts_GnuParser() {
-        checkLongAndShortOpts_allOptionsValid(new GnuParser())
-    }
-
-    void unknownOptions(parser) {
-        def cli = new CliBuilder(parser: parser)
-        cli.v(longOpt: 'verbose', 'verbose mode')
-        def options = cli.parse(['-x', '-yyy', '--zzz', 'something'])
-        assertEquals(['-x', '-yyy', '--zzz', 'something'], options.arguments())
-    }
-
-    void testUnrecognizedOptions_BasicParser() { unknownOptions(new BasicParser()) }
-
-    void testUnrecognizedOptions_GnuParser() { unknownOptions(new GnuParser()) }
-
-    void testUnrecognizedOptions_PosixParser() { unknownOptions(new PosixParser()) }
-
-    void bizarreProcessing(parser) {
-        def cli = new CliBuilder(parser: parser)
-        def options = cli.parse(['-xxxx'])
-        assertEquals(['-xxxx'], options.arguments())
-    }
-
-    void testBizarreProcessing_BasicParser() { bizarreProcessing(new BasicParser()) }
-
-    void testBizarreProcessing_GnuParser() { bizarreProcessing(new GnuParser()) }
-
-    void testPosixBizarreness() {
-        def cli = new CliBuilder(parser: new PosixParser())
-        def options = cli.parse(['-xxxx'])
-        assertEquals(['xxxx'], options.arguments())
-    }
-
-    private void multipleOccurrencesSeparateSeparate(parser) {
-        def cli = new CliBuilder(parser: parser)
-        cli.a(longOpt: 'arg', args: Option.UNLIMITED_VALUES, 'arguments')
-        def options = cli.parse(['-a', '1', '-a', '2', '-a', '3'])
-        assertEquals('1', options.a)
-        assertEquals(['1', '2', '3'], options.as)
-        assertEquals('1', options.arg)  //// Fails in 1.0, works for 1.x where x > 1.
-        assertEquals(['1', '2', '3'], options.args)  //// Fails in 1.0, works for 1.x where x > 1.
-        assertEquals([], options.arguments())
-    }
-
-    void testMultipleOccurrencesSeparateSeparate_BasicParser() { multipleOccurrencesSeparateSeparate(new BasicParser()) }
-
-    void testMultipleOccurrencesSeparateSeparate_GnuParser() { multipleOccurrencesSeparateSeparate(new GnuParser()) }
-
-    void testMultipleOccurrencesSeparateSeparate_PosixParser() { multipleOccurrencesSeparateSeparate(new PosixParser()) }
-
-    private void multipleOccurrencesSeparateJuxtaposed(parser) {
-        def cli = new CliBuilder(parser: parser)
-        //cli.a ( longOpt : 'arg' , args : Option.UNLIMITED_VALUES , 'arguments' )
-        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)  //// Fails in 1.0, works for 1.x where x > 1.
-        assertEquals(['1', '2', '3'], options.args)  //// Fails in 1.0, works for 1.x where x > 1.
-        assertEquals([], options.arguments())
-    }
-    //
-    //  BasicParser cannot handle this one.
-    //
-    //void testMultipleOccurrencesSeparateJuxtaposed_BasicParser ( ) { multipleOccurrencesSeparateJuxtaposed ( new BasicParser ( ) ) }
-
-    void testMultipleOccurrencesSeparateJuxtaposed_GnuParser() { multipleOccurrencesSeparateJuxtaposed(new GnuParser()) }
-
-    void testMultipleOccurrencesSeparateJuxtaposed_PosixParser() { multipleOccurrencesSeparateJuxtaposed(new PosixParser()) }
-
-    private void multipleOccurrencesTogetherSeparate(parser) {
-        def cli = new CliBuilder(parser: parser)
-        cli.a(longOpt: 'arg', args: Option.UNLIMITED_VALUES, valueSeparator: ',' as char, 'arguments')
-        def options = cli.parse(['-a 1,2,3'])
-        assertEquals(' 1', options.a)
-        assertEquals([' 1', '2', '3'], options.as)
-        assertEquals(' 1', options.arg)  //// Fails in 1.0, works for 1.x where x > 1.
-        assertEquals([' 1', '2', '3'], options.args)  //// Fails in 1.0, works for 1.x where x > 1.
-        assertEquals([], options.arguments())
-    }
-    //
-    //  BasicParser cannot handle this one.
-    //
-    //void testMultipleOccurrencesTogetherSeparate_BasicParser ( ) { multipleOccurrencesTogetherSeparate ( new BasicParser ( ) ) }
-
-    void testMultipleOccurrencesTogetherSeparate_GnuParser() { multipleOccurrencesTogetherSeparate(new GnuParser()) }
-
-    void testMultipleOccurrencesTogetherSeparate_PosixParser() { multipleOccurrencesTogetherSeparate(new PosixParser()) }
-
-    private void multipleOccurrencesTogetherJuxtaposed(parser) {
-        def cli = new CliBuilder(parser: parser)
-        cli.a(longOpt: 'arg', args: Option.UNLIMITED_VALUES, valueSeparator: ',' as char, 'arguments')
-        def options = cli.parse(['-a1,2,3'])
-        assertEquals('1', options.a)
-        assertEquals(['1', '2', '3'], options.as)
-        assertEquals('1', options.arg)  //// Fails in 1.0, works for 1.x where x > 1.
-        assertEquals(['1', '2', '3'], options.args)  //// Fails in 1.0, works for 1.x where x > 1.
-        assertEquals([], options.arguments())
-    }
-    //
-    //  BasicParser cannot handle this one.
-    //
-    //void testMultipleOccurrencesTogetherJuxtaposed_BasicParser ( ) { multipleOccurrencesTogetherJuxtaposed ( new BasicParser ( ) ) }
-
-    void testMultipleOccurrencesTogetherJuxtaposed_GnuParser() { multipleOccurrencesTogetherJuxtaposed(new GnuParser()) }
-
-    void testMultipleOccurrencesTogetherJuxtaposed_PosixParser() { multipleOccurrencesTogetherJuxtaposed(new PosixParser()) }
+    void testLongOptsOnly_nonOptionShouldStopArgProcessing() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser()].each { parser ->
+            def cli = new CliBuilder(parser: parser)
+            def anOption = Option.builder().longOpt('anOption').hasArg().desc('An option.').build()
+            cli.options.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() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser(), new BasicParser()].each { parser ->
+            def cli = new CliBuilder(parser: parser)
+            def anOption = Option.builder().longOpt('anOption').hasArg().desc('An option.').build()
+            cli.options.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() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser(), new BasicParser()].each { parser ->
+            def cli = new CliBuilder(parser: parser)
+            cli.v(longOpt: 'verbose', 'verbose mode')
+            def options = cli.parse(['-x', '-yyy', '--zzz', 'something'])
+            assertEquals(['-x', '-yyy', '--zzz', 'something'], options.arguments())
+        }
+    }
+
+    void testMultipleOccurrencesSeparateSeparate() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser(), new BasicParser()].each { parser ->
+            def cli = new CliBuilder(parser: parser)
+            cli.a(longOpt: 'arg', args: Option.UNLIMITED_VALUES, 'arguments')
+            def options = cli.parse(['-a', '1', '-a', '2', '-a', '3'])
+            assertEquals('1', options.a)
+            assertEquals(['1', '2', '3'], options.as)
+            assertEquals('1', options.arg)
+            assertEquals(['1', '2', '3'], options.args)
+            assertEquals([], options.arguments())
+        }
+    }
+
+    void testMultipleOccurrencesSeparateJuxtaposed() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser()].each { parser ->
+            def cli = new CliBuilder(parser: parser)
+            //cli.a ( longOpt : 'arg' , args : Option.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() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser()].each { parser ->
+            def cli = new CliBuilder(parser: parser)
+            cli.a(longOpt: 'arg', args: Option.UNLIMITED_VALUES, valueSeparator: ',' as char, 'arguments')
+            def options = cli.parse(['-a 1,2,3'])
+            assertEquals(' 1', options.a)
+            assertEquals([' 1', '2', '3'], options.as)
+            assertEquals(' 1', options.arg)
+            assertEquals([' 1', '2', '3'], options.args)
+            assertEquals([], options.arguments())
+        }
+    }
+
+    void testMultipleOccurrencesTogetherJuxtaposed() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser()].each { parser ->
+            def cli1 = new CliBuilder(parser: parser)
+            cli1.a(longOpt: 'arg', args: Option.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.
     *
-    *  TODO: Should add the BasicParser here as well.
+    *  TODO: Should add the BasicParser here as well?
     */
 
     void testUnrecognizedOptionSilentlyIgnored_GnuParser() {
@@ -358,8 +247,8 @@ usage: groovy
         assert stringWriter.toString().tokenize('\r\n').join('\n') == ''''''
     }
 
-    void testUnrecognizedOptionSilentlyIgnored_PosixParser() {
-        def cli = new CliBuilder(usage: usageString, writer: printWriter, parser: new PosixParser())
+    void testUnrecognizedOptionSilentlyIgnored_DefaultParser() {
+        def cli = new CliBuilder(usage: usageString, writer: printWriter, parser: new DefaultParser())
         def options = cli.parse(['-v'])
         checkNoOutput()
         assert !options.v
@@ -375,8 +264,8 @@ usage: groovy
         assertEquals(['-v', '-h'], options.arguments())
     }
 
-    void testUnrecognizedOptionTerminatesParse_PosixParser() {
-        def cli = new CliBuilder(usage: usageString, writer: printWriter, parser: new PosixParser())
+    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()
@@ -385,26 +274,20 @@ usage: groovy
         assertEquals(['-v', '-h'], options.arguments())
     }
 
-    private checkMultiCharShortOpt(posix) {
-        def cli = new CliBuilder(writer: printWriter, posix:posix)
-        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 testMultiCharShortOpt_PosixParser() {
-        checkMultiCharShortOpt(true)
+    void testMultiCharShortOpt() {
+        [new DefaultParser(), new GroovyPosixParser(), new GnuParser()].each { parser ->
+            def cli = new CliBuilder(writer: printWriter, parser: parser)
+            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 testMultiCharShortOpt_GnuParser() {
-        checkMultiCharShortOpt(false)
-    }
-
-    void testArgumentBursting_PosixParserOnly() {
+    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')
@@ -496,4 +379,49 @@ usage: groovy
         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']
+    }
 }