blob: c162323f04e0a04d8fa54c102649489b2b30af85 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
https://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 org.apache.commons.cli;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import org.apache.commons.cli.DefaultParser.Builder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
class DefaultParserTest extends AbstractParserTestCase {
static class ExternalArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(final ExtensionContext context) {
return Stream.of(
/* Arguments:
* 1. test case name
* 2. parser
* 3. input string
* 4. expected option value
* 5. checked option
* 6. assertion message
*/
Arguments.of(
"Long option quote handling DEFAULT behavior",
DefaultParser.builder().get(),
new String[]{"--bfile", "\"quoted string\""},
"quoted string",
"b",
"Confirm --bfile=\"arg\" strips quotes"
),
Arguments.of(
"Long option with equals quote handling DEFAULT behavior",
DefaultParser.builder().get(),
new String[]{"--bfile=\"quoted string\""},
"\"quoted string\"",
"b",
"Confirm --bfile=\"arg\" keeps quotes"
),
Arguments.of(
"Short option quote handling DEFAULT behavior",
DefaultParser.builder().get(),
new String[]{"-b", "\"quoted string\""},
"quoted string",
"b",
"Confirm -b\"arg\" strips quotes"
),
Arguments.of(
"Short option concatenated quote handling DEFAULT behavior",
DefaultParser.builder().get(),
new String[]{"-b\"quoted string\""},
"\"quoted string\"",
"b",
"Confirm -b\"arg\" keeps quotes"
),
Arguments.of(
"Long option quote handling WITHOUT strip",
DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
new String[]{"--bfile", "\"quoted string\""},
"\"quoted string\"",
"b",
"Confirm --bfile \"arg\" keeps quotes"
),
Arguments.of(
"Long option with equals quote handling WITHOUT strip",
DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
new String[]{"--bfile=\"quoted string\""},
"\"quoted string\"",
"b",
"Confirm --bfile=\"arg\" keeps quotes"
),
Arguments.of(
"Short option quote handling WITHOUT strip",
DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
new String[]{"-b", "\"quoted string\""},
"\"quoted string\"",
"b",
"Confirm -b\"arg\" keeps quotes"
),
Arguments.of(
"Short option concatenated quote handling WITHOUT strip",
DefaultParser.builder().setStripLeadingAndTrailingQuotes(false).get(),
new String[]{"-b\"quoted string\""},
"\"quoted string\"",
"b",
"Confirm -b\"arg\" keeps quotes"
),
Arguments.of(
"Long option quote handling WITH strip",
DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
new String[]{"--bfile", "\"quoted string\""},
"quoted string",
"b",
"Confirm --bfile \"arg\" strips quotes"
),
Arguments.of(
"Long option With Equals Quote Handling WITH Strip",
DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
new String[]{"--bfile=\"quoted string\""},
"quoted string",
"b",
"Confirm --bfile=\"arg\" strips quotes"
),
Arguments.of(
"Short option quote handling WITH strip",
DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
new String[]{"-b", "\"quoted string\""},
"quoted string",
"b",
"Confirm -b \"arg\" strips quotes"
),
Arguments.of(
"Short option concatenated quote handling WITH strip",
DefaultParser.builder().setStripLeadingAndTrailingQuotes(true).get(),
new String[]{"-b\"quoted string\""},
"quoted string",
"b",
"Confirm -b\"arg\" strips quotes"
)
);
}
}
@Override
@BeforeEach
public void setUp() {
super.setUp();
parser = new DefaultParser();
}
@Test
void testBuilder() {
// @formatter:off
final Builder builder = DefaultParser.builder()
.setStripLeadingAndTrailingQuotes(false)
.setAllowPartialMatching(false)
.setDeprecatedHandler(null);
// @formatter:on
parser = builder.build();
assertEquals(DefaultParser.class, parser.getClass());
parser = builder.get();
assertEquals(DefaultParser.class, parser.getClass());
}
@Test
void testDeprecated() throws ParseException {
final Set<Option> handler = new HashSet<>();
parser = DefaultParser.builder().setDeprecatedHandler(handler::add).build();
final Option opt1 = Option.builder().option("d1").deprecated().get();
// @formatter:off
final Option opt2 = Option.builder().option("d2").deprecated(DeprecatedAttributes.builder()
.setForRemoval(true)
.setSince("1.0")
.setDescription("Do this instead.").get()).get();
// @formatter:on
final Option opt3 = Option.builder().option("a").get();
// @formatter:off
final CommandLine cl = parser.parse(new Options()
.addOption(opt1)
.addOption(opt2)
.addOption(opt3),
new String[] {"-d1", "-d2", "-a"});
// @formatter:on
// Trigger handler:
assertTrue(cl.hasOption(opt1.getOpt()));
assertTrue(cl.hasOption(opt2.getOpt()));
assertTrue(cl.hasOption(opt3.getOpt()));
// Assert handler was triggered
assertTrue(handler.contains(opt1));
assertTrue(handler.contains(opt2));
assertFalse(handler.contains(opt3));
}
@Test
void testLegacyStopAtNonOption() throws ParseException {
final Option a = Option.builder().option("a").longOpt("first-letter").get();
final Option b = Option.builder().option("b").longOpt("second-letter").get();
final Option c = Option.builder().option("c").longOpt("third-letter").get();
final Options options = new Options();
options.addOption(a);
options.addOption(b);
options.addOption(c);
final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"}; // -d is rogue option
final DefaultParser parser = new DefaultParser();
final CommandLine commandLine = parser.parse(options, args, null, true);
assertEquals(3, commandLine.getOptions().length);
assertEquals(3, commandLine.getArgs().length);
assertTrue(commandLine.getArgList().contains("-d"));
assertTrue(commandLine.getArgList().contains("arg1"));
assertTrue(commandLine.getArgList().contains("arg2"));
final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class, () -> parser.parse(options, args, null, false));
assertTrue(e.getMessage().contains("-d"));
}
@Override
@Test
@Disabled("Test case handled in the parameterized tests as \"DEFAULT behavior\"")
void testLongOptionWithEqualsQuoteHandling() throws Exception {
}
@ParameterizedTest(name = "{index}. {0}")
@ArgumentsSource(ExternalArgumentsProvider.class)
void testParameterized(final String testName, final CommandLineParser parser, final String[] args, final String expected,
final String option, final String message) throws Exception {
final CommandLine cl = parser.parse(options, args);
assertEquals(expected, cl.getOptionValue(option), message);
}
@Test
void testParseIgnoreHappyPath() throws ParseException {
final Option a = Option.builder().option("a").longOpt("first-letter").get();
final Option b = Option.builder().option("b").longOpt("second-letter").get();
final Option c = Option.builder().option("c").longOpt("third-letter").get();
final Option d = Option.builder().option("d").longOpt("fourth-letter").get();
final Options baseOptions = new Options();
baseOptions.addOption(a);
baseOptions.addOption(b);
final Options specificOptions = new Options();
specificOptions.addOption(a);
specificOptions.addOption(b);
specificOptions.addOption(c);
specificOptions.addOption(d);
final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"};
final DefaultParser parser = new DefaultParser();
final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.IGNORE, args);
assertEquals(2, baseCommandLine.getOptions().length);
assertEquals(2, baseCommandLine.getArgs().length);
assertTrue(baseCommandLine.hasOption("a"));
assertTrue(baseCommandLine.hasOption("b"));
assertFalse(baseCommandLine.hasOption("c"));
assertFalse(baseCommandLine.hasOption("d"));
assertFalse(baseCommandLine.getArgList().contains("-a"));
assertFalse(baseCommandLine.getArgList().contains("-b"));
assertFalse(baseCommandLine.getArgList().contains("-c"));
assertFalse(baseCommandLine.getArgList().contains("-d"));
assertTrue(baseCommandLine.getArgList().contains("arg1"));
assertTrue(baseCommandLine.getArgList().contains("arg2"));
final CommandLine specificCommandLine = parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args);
assertEquals(4, specificCommandLine.getOptions().length);
assertEquals(2, specificCommandLine.getArgs().length);
assertTrue(specificCommandLine.hasOption("a"));
assertTrue(specificCommandLine.hasOption("b"));
assertTrue(specificCommandLine.hasOption("c"));
assertTrue(specificCommandLine.hasOption("d"));
assertFalse(specificCommandLine.getArgList().contains("-a"));
assertFalse(specificCommandLine.getArgList().contains("-b"));
assertFalse(specificCommandLine.getArgList().contains("-c"));
assertFalse(specificCommandLine.getArgList().contains("-d"));
assertTrue(specificCommandLine.getArgList().contains("arg1"));
assertTrue(specificCommandLine.getArgList().contains("arg2"));
}
@Test
void testParseIgnoreNonHappyPath() throws ParseException {
final Option a = Option.builder().option("a").longOpt("first-letter").get();
final Option b = Option.builder().option("b").longOpt("second-letter").get();
final Option c = Option.builder().option("c").longOpt("third-letter").get();
final Options baseOptions = new Options();
baseOptions.addOption(a);
baseOptions.addOption(b);
final Options specificOptions = new Options();
specificOptions.addOption(a);
specificOptions.addOption(b);
specificOptions.addOption(c);
final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"}; // -d is rogue option
final DefaultParser parser = new DefaultParser();
final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.IGNORE, args);
assertEquals(2, baseCommandLine.getOptions().length);
assertEquals(2, baseCommandLine.getArgs().length);
final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class,
() -> parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args));
assertTrue(e.getMessage().contains("-d"));
}
@Test
void testParseNullOption() throws ParseException {
// Edge case
assertThrows(NullPointerException.class, () -> new DefaultParser().parse(null, null, DefaultParser.NonOptionAction.IGNORE, "-a"));
}
@Test
void testParseSkipHappyPath() throws ParseException {
final Option a = Option.builder().option("a").longOpt("first-letter").get();
final Option b = Option.builder().option("b").longOpt("second-letter").get();
final Option c = Option.builder().option("c").longOpt("third-letter").get();
final Option d = Option.builder().option("d").longOpt("fourth-letter").get();
final Options baseOptions = new Options();
baseOptions.addOption(a);
baseOptions.addOption(b);
final Options specificOptions = new Options();
specificOptions.addOption(a);
specificOptions.addOption(b);
specificOptions.addOption(c);
specificOptions.addOption(d);
final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"};
final DefaultParser parser = new DefaultParser();
final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.SKIP, args);
assertEquals(2, baseCommandLine.getOptions().length);
assertEquals(4, baseCommandLine.getArgs().length);
assertTrue(baseCommandLine.hasOption("a"));
assertTrue(baseCommandLine.hasOption("b"));
assertFalse(baseCommandLine.hasOption("c"));
assertFalse(baseCommandLine.hasOption("d"));
assertFalse(baseCommandLine.getArgList().contains("-a"));
assertFalse(baseCommandLine.getArgList().contains("-b"));
assertTrue(baseCommandLine.getArgList().contains("-c"));
assertTrue(baseCommandLine.getArgList().contains("-d"));
assertTrue(baseCommandLine.getArgList().contains("arg1"));
assertTrue(baseCommandLine.getArgList().contains("arg2"));
final CommandLine specificCommandLine = parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args);
assertEquals(4, specificCommandLine.getOptions().length);
assertEquals(2, specificCommandLine.getArgs().length);
assertTrue(specificCommandLine.hasOption("a"));
assertTrue(specificCommandLine.hasOption("b"));
assertTrue(specificCommandLine.hasOption("c"));
assertTrue(specificCommandLine.hasOption("d"));
assertFalse(specificCommandLine.getArgList().contains("-a"));
assertFalse(specificCommandLine.getArgList().contains("-b"));
assertFalse(specificCommandLine.getArgList().contains("-c"));
assertFalse(specificCommandLine.getArgList().contains("-d"));
assertTrue(specificCommandLine.getArgList().contains("arg1"));
assertTrue(specificCommandLine.getArgList().contains("arg2"));
}
@Test
void testParseSkipNonHappyPath() throws ParseException {
final Option a = Option.builder().option("a").longOpt("first-letter").get();
final Option b = Option.builder().option("b").longOpt("second-letter").get();
final Option c = Option.builder().option("c").longOpt("third-letter").get();
final Options baseOptions = new Options();
baseOptions.addOption(a);
baseOptions.addOption(b);
final Options specificOptions = new Options();
specificOptions.addOption(a);
specificOptions.addOption(b);
specificOptions.addOption(c);
final String[] args = {"-a", "-b", "-c", "-d", "arg1", "arg2"}; // -d is rogue option
final DefaultParser parser = new DefaultParser();
final CommandLine baseCommandLine = parser.parse(baseOptions, null, DefaultParser.NonOptionAction.SKIP, args);
assertEquals(2, baseCommandLine.getOptions().length);
assertEquals(4, baseCommandLine.getArgs().length);
final UnrecognizedOptionException e = assertThrows(UnrecognizedOptionException.class,
() -> parser.parse(specificOptions, null, DefaultParser.NonOptionAction.THROW, args));
assertTrue(e.getMessage().contains("-d"));
}
@Override
@Test
@Disabled("Test case handled in the parameterized tests as \"DEFAULT behavior\"")
void testShortOptionConcatenatedQuoteHandling() throws Exception {
}
}