/*
* The MIT License
*
* Copyright (c) 2009 The Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package picard.cmdline;
import htsjdk.samtools.util.CollectionUtil;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import picard.cmdline.programgroups.Testing;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CommandLineParserTest {
enum FrobnicationFlavor {
FOO, BAR, BAZ
}
@CommandLineProgramProperties(
usage = "Usage: frobnicate [options] input-file output-file\n\nRead input-file, frobnicate it, and write frobnicated results to output-file\n",
usageShort = "Read input-file, frobnicate it, and write frobnicated results to output-file"
)
class FrobnicateOptions {
@PositionalArguments(minElements = 2, maxElements = 2)
public List<File> positionalArguments = new ArrayList<File>();
@Option(shortName = "T", doc = "Frobnication threshold setting.")
public Integer FROBNICATION_THRESHOLD = 20;
@Option
public FrobnicationFlavor FROBNICATION_FLAVOR;
@Option(doc = "Allowed shmiggle types.", minElements = 1, maxElements = 3)
public List<String> SHMIGGLE_TYPE = new ArrayList<String>();
@Option
public Boolean TRUTHINESS;
}
@CommandLineProgramProperties(
usage = "Usage: frobnicate [options] input-file output-file\n\nRead input-file, frobnicate it, and write frobnicated results to output-file\n",
usageShort = "Read input-file, frobnicate it, and write frobnicated results to output-file"
)
class FrobnicateOptionsWithNullList {
@PositionalArguments(minElements = 2, maxElements = 2)
public List<File> positionalArguments = new ArrayList<File>();
@Option(shortName = "T", doc = "Frobnication threshold setting.")
public Integer FROBNICATION_THRESHOLD = 20;
@Option
public FrobnicationFlavor FROBNICATION_FLAVOR;
@Option(doc = "Allowed shmiggle types.", minElements = 0, maxElements = 3)
public List<String> SHMIGGLE_TYPE = new ArrayList<String>();
@Option
public Boolean TRUTHINESS;
}
@CommandLineProgramProperties(
usage = "Usage: framistat [options]\n\nCompute the plebnick of the freebozzle.\n",
usageShort = "ompute the plebnick of the freebozzle"
)
class OptionsWithoutPositional {
public static final int DEFAULT_FROBNICATION_THRESHOLD = 20;
@Option(shortName = "T", doc = "Frobnication threshold setting.")
public Integer FROBNICATION_THRESHOLD = DEFAULT_FROBNICATION_THRESHOLD;
@Option
public FrobnicationFlavor FROBNICATION_FLAVOR;
@Option(doc = "Allowed shmiggle types.", minElements = 1, maxElements = 3)
public List<String> SHMIGGLE_TYPE = new ArrayList<String>();
@Option
public Boolean TRUTHINESS;
}
class OptionsWithCaseClash {
@Option
public String FROB;
@Option
public String frob;
}
class OptionsWithSameShortName {
@Option(shortName = "SAME_SHORT_NAME", overridable = true, optional = true)
public String SAME_SHORT_NAME;
@Option(shortName = "SOMETHING_ELSE", overridable = true, optional = true)
public String DIFF_SHORT_NAME;
}
class MutexOptions {
@Option(mutex = {"M", "N", "Y", "Z"})
public String A;
@Option(mutex = {"M", "N", "Y", "Z"})
public String B;
@Option(mutex = {"A", "B", "Y", "Z"})
public String M;
@Option(mutex = {"A", "B", "Y", "Z"})
public String N;
@Option(mutex = {"A", "B", "M", "N"})
public String Y;
@Option(mutex = {"A", "B", "M", "N"})
public String Z;
}
@Test
public void testUsage() {
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
clp.usage(System.out, false);
}
@Test
public void testUsageWithDefault() {
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
clp.usage(System.out, true);
}
@Test
public void testUsageWithoutPositional() {
final OptionsWithoutPositional fo = new OptionsWithoutPositional();
final CommandLineParser clp = new CommandLineParser(fo);
clp.usage(System.out, false);
}
@Test
public void testUsageWithoutPositionalWithDefault() {
final OptionsWithoutPositional fo = new OptionsWithoutPositional();
final CommandLineParser clp = new CommandLineParser(fo);
clp.usage(System.out, true);
}
/**
* If the short name is set to be the same as the long name we still want the argument to appear in the commandLine.
*/
@Test
public void testForIdenticalShortName() {
final String[] args = {
"SAME_SHORT_NAME=FOO",
"SOMETHING_ELSE=BAR"
};
final OptionsWithSameShortName fo = new OptionsWithSameShortName();
final CommandLineParser clp = new CommandLineParser(fo);
clp.parseOptions(System.err, args);
final String commandLine = clp.getCommandLine();
Assert.assertTrue(commandLine.contains("DIFF_SHORT_NAME"));
Assert.assertTrue(commandLine.contains("SAME_SHORT_NAME"));
}
@Test
public void testPositive() {
final String[] args = {
"T=17",
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"SHMIGGLE_TYPE=shmiggle2",
"positional1",
"positional2",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertTrue(clp.parseOptions(System.err, args));
Assert.assertEquals(fo.positionalArguments.size(), 2);
final File[] expectedPositionalArguments = {new File("positional1"), new File("positional2")};
Assert.assertEquals(fo.positionalArguments.toArray(), expectedPositionalArguments);
Assert.assertEquals(fo.FROBNICATION_THRESHOLD.intValue(), 17);
Assert.assertEquals(fo.FROBNICATION_FLAVOR, FrobnicationFlavor.BAR);
Assert.assertEquals(fo.SHMIGGLE_TYPE.size(), 2);
final String[] expectedShmiggleTypes = {"shmiggle1", "shmiggle2"};
Assert.assertEquals(fo.SHMIGGLE_TYPE.toArray(), expectedShmiggleTypes);
Assert.assertFalse(fo.TRUTHINESS);
}
/**
* Allow a whitespace btw equal sign and option value.
*/
@Test
public void testPositiveWithSpaces() {
final String[] args = {
"T=", "17",
"FROBNICATION_FLAVOR=", "BAR",
"TRUTHINESS=", "False",
"SHMIGGLE_TYPE=", "shmiggle1",
"SHMIGGLE_TYPE=", "shmiggle2",
"positional1",
"positional2",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertTrue(clp.parseOptions(System.err, args));
Assert.assertEquals(fo.positionalArguments.size(), 2);
final File[] expectedPositionalArguments = {new File("positional1"), new File("positional2")};
Assert.assertEquals(fo.positionalArguments.toArray(), expectedPositionalArguments);
Assert.assertEquals(fo.FROBNICATION_THRESHOLD.intValue(), 17);
Assert.assertEquals(fo.FROBNICATION_FLAVOR, FrobnicationFlavor.BAR);
Assert.assertEquals(fo.SHMIGGLE_TYPE.size(), 2);
final String[] expectedShmiggleTypes = {"shmiggle1", "shmiggle2"};
Assert.assertEquals(fo.SHMIGGLE_TYPE.toArray(), expectedShmiggleTypes);
Assert.assertFalse(fo.TRUTHINESS);
}
@Test
public void testPositiveWithoutPositional() {
final String[] args = {
"T=17",
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"SHMIGGLE_TYPE=shmiggle2",
};
final OptionsWithoutPositional fo = new OptionsWithoutPositional();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertTrue(clp.parseOptions(System.err, args));
Assert.assertEquals(fo.FROBNICATION_THRESHOLD.intValue(), 17);
Assert.assertEquals(fo.FROBNICATION_FLAVOR, FrobnicationFlavor.BAR);
Assert.assertEquals(fo.SHMIGGLE_TYPE.size(), 2);
final String[] expectedShmiggleTypes = {"shmiggle1", "shmiggle2"};
Assert.assertEquals(fo.SHMIGGLE_TYPE.toArray(), expectedShmiggleTypes);
Assert.assertFalse(fo.TRUTHINESS);
}
/**
* If last character of command line is the equal sign in an option=value pair,
* make sure no crash, and that the value is empty string.
*/
@Test
public void testPositiveTerminalEqualSign() {
final String[] args = {
"T=17",
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"SHMIGGLE_TYPE=",
};
final OptionsWithoutPositional fo = new OptionsWithoutPositional();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertTrue(clp.parseOptions(System.err, args));
Assert.assertEquals(fo.FROBNICATION_THRESHOLD.intValue(), 17);
Assert.assertEquals(fo.FROBNICATION_FLAVOR, FrobnicationFlavor.BAR);
Assert.assertEquals(fo.SHMIGGLE_TYPE.size(), 2);
final String[] expectedShmiggleTypes = {"shmiggle1", ""};
Assert.assertEquals(fo.SHMIGGLE_TYPE.toArray(), expectedShmiggleTypes);
Assert.assertFalse(fo.TRUTHINESS);
}
@Test
public void testDefault() {
final String[] args = {
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"SHMIGGLE_TYPE=shmiggle2",
"positional1",
"positional2",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertTrue(clp.parseOptions(System.err, args));
Assert.assertEquals(fo.FROBNICATION_THRESHOLD.intValue(), 20);
}
@Test
public void testMissingRequiredArgument() {
final String[] args = {
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"SHMIGGLE_TYPE=shmiggle2",
"positional1",
"positional2",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertFalse(clp.parseOptions(System.err, args));
}
@Test
public void testBadValue() {
final String[] args = {
"FROBNICATION_THRESHOLD=ABC",
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"SHMIGGLE_TYPE=shmiggle2",
"positional1",
"positional2",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertFalse(clp.parseOptions(System.err, args));
}
@Test
public void testBadEnumValue() {
final String[] args = {
"FROBNICATION_FLAVOR=HiMom",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"SHMIGGLE_TYPE=shmiggle2",
"positional1",
"positional2",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertFalse(clp.parseOptions(System.err, args));
}
@Test
public void testNotEnoughOfListOption() {
final String[] args = {
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"positional1",
"positional2",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertFalse(clp.parseOptions(System.err, args));
}
@Test
public void testTooManyListOption() {
final String[] args = {
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"SHMIGGLE_TYPE=shmiggle2",
"SHMIGGLE_TYPE=shmiggle3",
"SHMIGGLE_TYPE=shmiggle4",
"positional1",
"positional2",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertFalse(clp.parseOptions(System.err, args));
}
@Test
public void testTooManyPositional() {
final String[] args = {
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"SHMIGGLE_TYPE=shmiggle2",
"positional1",
"positional2",
"positional3",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertFalse(clp.parseOptions(System.err, args));
}
@Test
public void testNotEnoughPositional() {
final String[] args = {
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"SHMIGGLE_TYPE=shmiggle2",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertFalse(clp.parseOptions(System.err, args));
}
@Test
public void testUnexpectedPositional() {
final String[] args = {
"T=17",
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"SHMIGGLE_TYPE=shmiggle2",
"positional"
};
final OptionsWithoutPositional fo = new OptionsWithoutPositional();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertFalse(clp.parseOptions(System.err, args));
}
@Test(expectedExceptions = CommandLineParserDefinitionException.class)
public void testOptionDefinitionCaseClash() {
final OptionsWithCaseClash options = new OptionsWithCaseClash();
new CommandLineParser(options);
Assert.fail("Should not be reached.");
}
@Test
public void testOptionUseCaseClash() {
final String[] args = {
"FROBNICATION_FLAVOR=BAR",
"FrOBNICATION_fLAVOR=BAR",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertFalse(clp.parseOptions(System.err, args));
}
@Test
public void testNullValue() {
final String[] args = {
"FROBNICATION_THRESHOLD=null",
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=null",
"positional1",
"positional2",
};
final FrobnicateOptionsWithNullList fownl = new FrobnicateOptionsWithNullList();
fownl.SHMIGGLE_TYPE.add("shmiggle1"); //providing null value should clear this list
final CommandLineParser clp = new CommandLineParser(fownl);
Assert.assertTrue(clp.parseOptions(System.err, args));
Assert.assertEquals(fownl.positionalArguments.size(), 2);
final File[] expectedPositionalArguments = {new File("positional1"), new File("positional2")};
Assert.assertEquals(fownl.positionalArguments.toArray(), expectedPositionalArguments);
Assert.assertEquals(fownl.FROBNICATION_THRESHOLD, null); //test null value
Assert.assertEquals(fownl.SHMIGGLE_TYPE.size(), 0); //test null value for list
Assert.assertFalse(fownl.TRUTHINESS);
//verify that required arg can't be set to null
args[2] = "TRUTHINESS=null";
final CommandLineParser clp2 = new CommandLineParser(fownl);
Assert.assertFalse(clp2.parseOptions(System.err, args));
//verify that positional arg can't be set to null
args[2] = "TRUTHINESS=False";
args[4] = "null";
final CommandLineParser clp3 = new CommandLineParser(fownl);
Assert.assertFalse(clp3.parseOptions(System.err, args));
}
@Test
public void testOptionsFile() throws Exception {
final File optionsFile = File.createTempFile("clp.", ".options");
optionsFile.deleteOnExit();
final PrintWriter writer = new PrintWriter(optionsFile);
writer.println("T=18");
writer.println("TRUTHINESS=True");
writer.println("SHMIGGLE_TYPE=shmiggle0");
writer.println("STRANGE_OPTION=shmiggle0");
writer.close();
final String[] args = {
"OPTIONS_FILE=" + optionsFile.getPath(),
// Multiple options files are allowed
"OPTIONS_FILE=" + optionsFile.getPath(),
"T=17",
"FROBNICATION_FLAVOR=BAR",
"TRUTHINESS=False",
"SHMIGGLE_TYPE=shmiggle1",
"positional1",
"positional2",
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertTrue(clp.parseOptions(System.err, args));
Assert.assertEquals(fo.positionalArguments.size(), 2);
final File[] expectedPositionalArguments = {new File("positional1"), new File("positional2")};
Assert.assertEquals(fo.positionalArguments.toArray(), expectedPositionalArguments);
Assert.assertEquals(fo.FROBNICATION_THRESHOLD.intValue(), 17);
Assert.assertEquals(fo.FROBNICATION_FLAVOR, FrobnicationFlavor.BAR);
Assert.assertEquals(fo.SHMIGGLE_TYPE.size(), 3);
final String[] expectedShmiggleTypes = {"shmiggle0", "shmiggle0", "shmiggle1"};
Assert.assertEquals(fo.SHMIGGLE_TYPE.toArray(), expectedShmiggleTypes);
Assert.assertFalse(fo.TRUTHINESS);
}
/**
* In an options file, should not be allowed to override an option set on the command line
*
* @throws Exception
*/
@Test
public void testOptionsFileWithDisallowedOverride() throws Exception {
final File optionsFile = File.createTempFile("clp.", ".options");
optionsFile.deleteOnExit();
final PrintWriter writer = new PrintWriter(optionsFile);
writer.println("T=18");
writer.close();
final String[] args = {
"T=17",
"OPTIONS_FILE=" + optionsFile.getPath()
};
final FrobnicateOptions fo = new FrobnicateOptions();
final CommandLineParser clp = new CommandLineParser(fo);
Assert.assertFalse(clp.parseOptions(System.err, args));
}
@DataProvider(name = "mutexScenarios")
public Object[][] mutexScenarios() {
return new Object[][]{
{"pass", new String[]{"A=1", "B=2"}, true},
{"no args", new String[0], false},
{"1 of group required", new String[]{"A=1"}, false},
{"mutex", new String[]{"A=1", "Y=3"}, false},
{"mega mutex", new String[]{"A=1", "B=2", "Y=3", "Z=1", "M=2", "N=3"}, false}
};
}
@Test(dataProvider = "mutexScenarios")
public void testMutex(final String testName, final String[] args, final boolean expected) {
final MutexOptions o = new MutexOptions();
final CommandLineParser clp = new CommandLineParser(o);
Assert.assertEquals(clp.parseOptions(System.err, args), expected);
}
class UninitializedCollectionOptions {
@Option
public List<String> LIST;
@Option
public ArrayList<String> ARRAY_LIST;
@Option
public HashSet<String> HASH_SET;
@PositionalArguments
public Collection<File> COLLECTION;
}
@Test
public void testUninitializedCollections() {
final UninitializedCollectionOptions o = new UninitializedCollectionOptions();
final CommandLineParser clp = new CommandLineParser(o);
final String[] args = {"LIST=L1", "LIST=L2", "ARRAY_LIST=S1", "HASH_SET=HS1", "P1", "P2"};
Assert.assertTrue(clp.parseOptions(System.err, args));
Assert.assertEquals(o.LIST.size(), 2);
Assert.assertEquals(o.ARRAY_LIST.size(), 1);
Assert.assertEquals(o.HASH_SET.size(), 1);
Assert.assertEquals(o.COLLECTION.size(), 2);
}
class UninitializedCollectionThatCannotBeAutoInitializedOptions {
@Option
public Set<String> SET;
}
@Test(expectedExceptions = CommandLineParserDefinitionException.class)
public void testCollectionThatCannotBeAutoInitialized() {
final UninitializedCollectionThatCannotBeAutoInitializedOptions o = new UninitializedCollectionThatCannotBeAutoInitializedOptions();
new CommandLineParser(o);
Assert.fail("Exception should have been thrown");
}
class CollectionWithDefaultValuesOptions {
@Option
public List<String> LIST = CollectionUtil.makeList("foo", "bar");
}
@Test
public void testClearDefaultValuesFromListOption() {
final CollectionWithDefaultValuesOptions o = new CollectionWithDefaultValuesOptions();
final CommandLineParser clp = new CommandLineParser(o);
final String[] args = {"LIST=null"};
Assert.assertTrue(clp.parseOptions(System.err, args));
Assert.assertEquals(o.LIST.size(), 0);
}
@Test
public void testClearDefaultValuesFromListOptionAndAddNew() {
final CollectionWithDefaultValuesOptions o = new CollectionWithDefaultValuesOptions();
final CommandLineParser clp = new CommandLineParser(o);
final String[] args = {"LIST=null", "LIST=baz", "LIST=frob"};
Assert.assertTrue(clp.parseOptions(System.err, args));
Assert.assertEquals(o.LIST, CollectionUtil.makeList("baz", "frob"));
}
@Test
public void testAddToDefaultValuesListOption() {
final CollectionWithDefaultValuesOptions o = new CollectionWithDefaultValuesOptions();
final CommandLineParser clp = new CommandLineParser(o);
final String[] args = {"LIST=baz", "LIST=frob"};
Assert.assertTrue(clp.parseOptions(System.err, args));
Assert.assertEquals(o.LIST, CollectionUtil.makeList("foo", "bar", "baz", "frob"));
}
@CommandLineProgramProperties(
usage = "Class with nested option",
usageShort = "Class with nested option"
)
class OptionsWithNested {
@Option
public Integer AN_INT;
@NestedOptions(doc = "Doc for FROB")
public OptionsWithoutPositional FROB = new OptionsWithoutPositional();
@NestedOptions
public OptionsWithNestedAgain NESTED = new OptionsWithNestedAgain();
@Option
public String A_STRING;
}
class OptionsWithNestedAgain {
@NestedOptions(doc = "Doc for inner FROB")
public OptionsWithoutPositional FROB = new OptionsWithoutPositional();
}
@Test
public void testStaticNestedOptions() {
final OptionsWithNested o = new OptionsWithNested();
final CommandLineParser clp = new CommandLineParser(o);
clp.usage(System.out, false);
clp.htmlUsage(System.out, "testStaticNestedOptions", false);
final int outerInt = 123;
final String outerString = "outerString";
final FrobnicationFlavor outerFlavor = FrobnicationFlavor.BAR;
final FrobnicationFlavor innerFlavor = FrobnicationFlavor.BAZ;
final boolean outerTruthiness = true;
final boolean innerTruthiness = false;
final int innerThreshold = 10;
final String[] outerShmiggle = {"shmiggle1", "shmiggle2"};
final String[] innerShmiggle = {"inner1", "inner2", "inner3"};
final List<String> args = new ArrayList<String>();
args.add("AN_INT=" + outerInt);
args.add("A_STRING=" + outerString);
args.add("frob.truThIness=" + outerTruthiness);
args.add("FrOb.FROBNICATION_FLAVOR=" + outerFlavor);
for (final String shmiggle : outerShmiggle) {
args.add("FROB.SHMIGGLE_TYPE=" + shmiggle);
}
args.add("NeStEd.Frob.FROBNICATION_THRESHOLD=" + innerThreshold);
args.add("NeStEd.Frob.FROBNICATION_FLAVOR=" + innerFlavor);
args.add("NeStEd.Frob.truthIness=" + innerTruthiness);
for (final String shmiggle : innerShmiggle) {
args.add("NESTED.FROB.SHMIGGLE_TYPE=");
args.add(shmiggle);
}
Assert.assertTrue(clp.parseOptions(System.err, args.toArray(new String[args.size()])));
System.out.println(clp.getCommandLine());
Assert.assertEquals(o.AN_INT.intValue(), outerInt);
Assert.assertEquals(o.A_STRING, outerString);
Assert.assertEquals(o.FROB.FROBNICATION_FLAVOR, outerFlavor);
Assert.assertEquals(o.FROB.FROBNICATION_THRESHOLD.intValue(), OptionsWithoutPositional.DEFAULT_FROBNICATION_THRESHOLD);
Assert.assertEquals(o.FROB.SHMIGGLE_TYPE, Arrays.asList(outerShmiggle));
Assert.assertEquals(o.FROB.TRUTHINESS.booleanValue(), outerTruthiness);
Assert.assertEquals(o.NESTED.FROB.SHMIGGLE_TYPE, Arrays.asList(innerShmiggle));
Assert.assertEquals(o.NESTED.FROB.FROBNICATION_THRESHOLD.intValue(), innerThreshold);
Assert.assertEquals(o.NESTED.FROB.FROBNICATION_FLAVOR, innerFlavor);
Assert.assertEquals(o.NESTED.FROB.TRUTHINESS.booleanValue(), innerTruthiness);
}
@Test
void testStaticNestedNegative() {
final OptionsWithNested o = new OptionsWithNested();
final CommandLineParser clp = new CommandLineParser(o);
final int outerInt = 123;
final String outerString = "outerString";
final FrobnicationFlavor outerFlavor = FrobnicationFlavor.BAR;
final boolean outerTruthiness = true;
final String[] outerShmiggle = {"shmiggle1", "shmiggle2"};
final List<String> args = new ArrayList<String>();
args.add("AN_INT=" + outerInt);
args.add("A_STRING=" + outerString);
Assert.assertFalse(clp.parseOptions(System.err, args.toArray(new String[args.size()])));
System.out.println(clp.getCommandLine());
}
@CommandLineProgramProperties(
usage = "Class with nested options.",
usageShort = "Class with nested options",
programGroup = Testing.class,
omitFromCommandLine = true
)
class ClpOptionsWithNested extends CommandLineProgram {
@Option
public Integer AN_INT;
@NestedOptions(doc = "This will be ignored")
public OptionsWithoutPositional FROB = new OptionsWithoutPositional();
@Option
public String A_STRING;
private final ClpOptionsWithNestedAgain NESTED = new ClpOptionsWithNestedAgain();
@Override
public Map<String, Object> getNestedOptions() {
final Map<String, Object> ret = new LinkedHashMap<String, Object>();
ret.put("CLP_NESTED", NESTED);
ret.put("FRAB", FROB);
return ret;
}
@Override
public Map<String, Object> getNestedOptionsForHelp() {
final Map<String, Object> ret = new LinkedHashMap<String, Object>();
ret.put("CLP_NESTED", NESTED);
return ret;
}
@Override
protected int doWork() {
return 0;
}
}
@CommandLineProgramProperties(
usage = "Class with nested options again.",
usageShort = "Class with nested options again",
programGroup = Testing.class,
omitFromCommandLine = true
)
class ClpOptionsWithNestedAgain extends CommandLineProgram {
private final OptionsWithoutPositional FROB = new OptionsWithoutPositional();
@Override
public Map<String, Object> getNestedOptions() {
final Map<String, Object> ret = new LinkedHashMap<String, Object>();
ret.put("FROB_NESTED", FROB);
return ret;
}
@Override
protected int doWork() {
return 0;
}
}
@Test
public void testDynamicNestedOptions() {
final ClpOptionsWithNested o = new ClpOptionsWithNested();
final int outerInt = 123;
final String outerString = "aString";
final int outerThreshold = 456;
final FrobnicationFlavor outerFlavor = FrobnicationFlavor.FOO;
final List<String> outerShmiggleType = Arrays.asList("shmiggle1");
final boolean outerTruthiness = true;
final int innerThreshold = -1000;
final FrobnicationFlavor innerFlavor = FrobnicationFlavor.BAZ;
final List<String> innerShmiggleType = Arrays.asList("innershmiggle1", "skeezwitz");
final boolean innerTruthiness = false;
final List<String> args = new ArrayList<String>();
args.add("AN_INT=" + outerInt);
args.add("A_STRING=" + outerString);
args.add("FRAB.FROBNICATION_THRESHOLD=" + outerThreshold);
args.add("FRAB.FROBNICATION_FLAVOR=" + outerFlavor);
args.add("FRAB.SHMIGGLE_TYPE=" + outerShmiggleType.get(0));
args.add("FRAB.TRUTHINESS=" + outerTruthiness);
args.add("CLP_NESTED.FROB_NESTED.FROBNICATION_THRESHOLD=" + innerThreshold);
args.add("CLP_NESTED.FROB_NESTED.FROBNICATION_FLAVOR=" + innerFlavor);
for (final String ist : innerShmiggleType) {
args.add("CLP_NESTED.FROB_NESTED.SHMIGGLE_TYPE=" + ist);
}
args.add("CLP_NESTED.FROB_NESTED.TRUTHINESS=" + innerTruthiness);
Assert.assertTrue(o.parseArgs(args.toArray(new String[args.size()])));
System.out.println(o.getCommandLine());
Assert.assertEquals(o.AN_INT.intValue(), outerInt);
Assert.assertEquals(o.A_STRING, outerString);
Assert.assertEquals(o.FROB.FROBNICATION_THRESHOLD.intValue(), outerThreshold);
Assert.assertEquals(o.FROB.FROBNICATION_FLAVOR, outerFlavor);
Assert.assertEquals(o.FROB.SHMIGGLE_TYPE, outerShmiggleType);
Assert.assertEquals(o.FROB.TRUTHINESS.booleanValue(), outerTruthiness);
Assert.assertEquals(o.NESTED.FROB.FROBNICATION_THRESHOLD.intValue(), innerThreshold);
Assert.assertEquals(o.NESTED.FROB.FROBNICATION_FLAVOR, innerFlavor);
Assert.assertEquals(o.NESTED.FROB.SHMIGGLE_TYPE, innerShmiggleType);
Assert.assertEquals(o.NESTED.FROB.TRUTHINESS.booleanValue(), innerTruthiness);
Assert.assertFalse(new ClpOptionsWithNested().parseArgs(new String[]{"-h"}));
new CommandLineParser(o).htmlUsage(System.err, o.getClass().getSimpleName(), false);
}
class StaticPropagationParent {
@Option
public String STRING1 = "String1ParentDefault";
@Option
public String STRING2 = "String2ParentDefault";
@Option(overridable = true)
public String STRING3 = "String3ParentDefault";
@Option
public String STRING4 = "String4ParentDefault";
@Option
public String STRING5;
@Option
public List<String> COLLECTION;
@NestedOptions
public PropagationChild CHILD = new PropagationChild();
}
class PropagationChild {
// Parent has default, child does not, should propagate
@Option
public String STRING1;
// Parent and child have default, should not propagate
@Option
public String STRING2 = "String2ChildDefault";
// Parent has explicitly set value, child has default, should propagate
@Option
public String STRING3 = "String3ChildDefault";
// Parent has default, child has explicitly set value, should not propagate
@Option
public String STRING4;
// Parent and child have explicitly set value, should not propagate
@Option
public String STRING5;
// Parent has explicitly set value, but collection should not propagate
@Option
public List<String> COLLECTION;
}
@Test
public void testStaticPropagation() {
final StaticPropagationParent o = new StaticPropagationParent();
final CommandLineParser clp = new CommandLineParser(o);
clp.usage(System.out, false);
clp.htmlUsage(System.out, "testStaticPropagation", false);
final List<String> args = new ArrayList<String>();
args.add("STRING3=String3Parent");
args.add("CHILD.STRING4=String4Child");
args.add("STRING5=String5Parent");
args.add("CHILD.STRING5=String5Child");
args.add("COLLECTION=CollectionParent");
Assert.assertTrue(clp.parseOptions(System.err, args.toArray(new String[args.size()])));
System.out.println(clp.getCommandLine());
Assert.assertEquals(o.CHILD.STRING1, "String1ParentDefault");
Assert.assertEquals(o.CHILD.STRING2, "String2ChildDefault");
Assert.assertEquals(o.CHILD.STRING3, "String3Parent");
Assert.assertEquals(o.CHILD.STRING4, "String4Child");
Assert.assertEquals(o.CHILD.STRING5, "String5Child");
Assert.assertEquals(o.CHILD.COLLECTION, new ArrayList<String>());
}
@CommandLineProgramProperties(
usage = "",
usageShort = "",
programGroup = Testing.class,
omitFromCommandLine = true
)
class DynamicPropagationParent extends CommandLineProgram {
@Option
public String STRING1 = "String1ParentDefault";
@Option
public String STRING2 = "String2ParentDefault";
@Option
public String STRING3 = "String3ParentDefault";
@Option
public String STRING4 = "String4ParentDefault";
@Option
public String STRING5;
@Option
public List<String> COLLECTION;
public PropagationChild CHILD = new PropagationChild();
@Override
protected int doWork() {
return 0;
}
@Override
public Map<String, Object> getNestedOptions() {
final Map<String, Object> ret = new HashMap<String, Object>();
ret.put("CHILD", CHILD);
return ret;
}
}
@Test
public void testDynamicPropagation() {
final DynamicPropagationParent o = new DynamicPropagationParent();
final List<String> args = new ArrayList<String>();
args.add("STRING3=String3Parent");
args.add("CHILD.STRING4=String4Child");
args.add("STRING5=String5Parent");
args.add("CHILD.STRING5=String5Child");
args.add("COLLECTION=CollectionParent");
Assert.assertTrue(o.parseArgs(args.toArray(new String[args.size()])));
System.out.println(o.getCommandLine());
Assert.assertFalse(new DynamicPropagationParent().parseArgs(new String[]{"-h"}));
new CommandLineParser(o).htmlUsage(System.err, o.getClass().getSimpleName(), false);
Assert.assertEquals(o.CHILD.STRING1, "String1ParentDefault");
Assert.assertEquals(o.CHILD.STRING2, "String2ChildDefault");
Assert.assertEquals(o.CHILD.STRING3, "String3Parent");
Assert.assertEquals(o.CHILD.STRING4, "String4Child");
Assert.assertEquals(o.CHILD.STRING5, "String5Child");
Assert.assertEquals(o.CHILD.COLLECTION, new ArrayList<String>());
}
class NegativePropagationParent {
@Option
public int STRING1 = 1;
@Option
public List<String> COLLECTION;
@NestedOptions
public PropagationChild CHILD = new PropagationChild();
}
/** parent and child option of the same name are not type-compatible. */
@Test(expectedExceptions = {IllegalArgumentException.class})
public void testStaticPropagationNegative() {
final NegativePropagationParent o = new NegativePropagationParent();
final CommandLineParser clp = new CommandLineParser(o);
clp.usage(System.out, false);
clp.parseOptions(System.err, new String[0]);
}
class StaticParent {
@Option
public String STRING1 = "String1ParentDefault";
@Option
public String STRING2 = "String2ParentDefault";
@Option(overridable = true)
public String STRING3 = "String3ParentDefault";
public void doSomething() {
System.out.println(STRING3);
}
}
class OverridePropagation extends StaticParent {
@Option
public String STRING3 = "String3Overriden";
}
@Test
public void testOveriddenOptions() {
final OverridePropagation overridden = new OverridePropagation();
final CommandLineParser overrideClp = new CommandLineParser(overridden);
overrideClp.parseOptions(System.err, new String[0]);
final OverridePropagation props = (OverridePropagation) overrideClp.getCallerOptions();
Assert.assertTrue(props.STRING3.equals("String3Overriden"));
Assert.assertTrue(((StaticParent) props).STRING3.equals("String3Overriden"));
overrideClp.parseOptions(System.err, new String[]{"STRING3=String3Supplied"});
final OverridePropagation propsSet = (OverridePropagation) overrideClp.getCallerOptions();
Assert.assertTrue(propsSet.STRING3.equals("String3Supplied"));
Assert.assertTrue(((StaticParent) propsSet).STRING3.equals("String3Supplied"));
}
@DataProvider(name = "testHtmlEscapeData")
public Object[][] testHtmlEscapeData() {
final List<Object[]> retval = new ArrayList<>();
retval.add(new Object[]{"<", "<"});
retval.add(new Object[]{"x<y", "x<y"});
retval.add(new Object[]{"x<y<z", "x<y<z"});
retval.add(new Object[]{"\n", "<p>"});
retval.add(new Object[]{"<html> x<y </html> y< <strong> x </strong>","<html> x<y </html> y< <strong> x </strong>"});
return retval.toArray(new Object[0][]);
}
// @Test(dataProvider = "testHtmlEscapeData")
// public void testHtmlEscape(final String text, final String expected) {
// Assert.assertEquals(CommandLineParser.htmlEscape(text), expected, "problems");
// }
@Test(dataProvider = "testHtmlEscapeData")
public void testHtmlUnescape(final String expected, final String html) {
Assert.assertEquals(CommandLineParser.htmlUnescape(html), expected, "problems");
}
@DataProvider(name = "testHTMLConverter")
public Object[][] testHTMLConverterData() {
final List<Object[]> retval = new ArrayList<>();
retval.add(new Object[]{"hello", "hello"});
retval.add(new Object[]{"", ""});
retval.add(new Object[]{"hi</th>bye", "hi\tbye"});
retval.add(new Object[]{"hi<th>bye", "hibye"});
retval.add(new Object[]{"hi<li>bye", "hi - bye"});
retval.add(new Object[]{"hi<NOT_A_REAL_TAG>bye", "hibye"});
retval.add(new Object[]{"</h4><pre>", "\n\n"});
retval.add(new Object[]{"<a href=\"http://go.here.org\"> string</ a >", " string (http://go.here.org)"});
retval.add(new Object[]{"<a href=\"http://go.here.org\" > string</ a>", " string (http://go.here.org)"});
retval.add(new Object[]{"< a href=\"http://go.here.org\"> string<a />", " string (http://go.here.org)"});
//for some reason, the next test seems to break intelliJ, but it works on the commandline
retval.add(new Object[]{"hi</li>bye", "hi\nbye"});
retval.add(new Object[]{"Using read outputs from high throughput sequencing (HTS) technologies, this tool provides " +
"metrics regarding the quality of read alignments to a reference sequence, as well as the proportion of the reads " +
"that passed machine signal-to-noise threshold quality filters (Illumina)." +
"<h4>Usage example:</h4>" +
"<pre>" +
" java -jar picard.jar CollectAlignmentSummaryMetrics \\<br />" +
" R=reference_sequence.fasta \\<br />" +
" I=input.bam \\<br />" +
" O=output.txt" +
"</pre>" +
"Please see <a href='http://broadinstitute.github.io/picard/picard-metric-definitions.html#AlignmentSummaryMetrics'>" +
"the AlignmentSummaryMetrics documentation</a> for detailed explanations of each metric. <br /> <br />" +
"Additional information about Illumina's quality filters can be found in the following documents on the Illumina website:" +
"<ul><li><a href=\"http://support.illumina.com/content/dam/illumina-marketing/documents/products/technotes/hiseq-x-percent-pf-technical-note-770-2014-043.pdf\">" +
"hiseq-x-percent-pf-technical-note</a></li>" +
"<li><a href=\"http://support.illumina.com/content/dam/illumina-support/documents/documentation/system_documentation/hiseqx/hiseq-x-system-guide-15050091-d.pdf\">" +
"hiseq-x-system-guide</a></li></ul>" +
"<hr />",
"Using read outputs from high throughput sequencing (HTS) technologies, this tool provides " +
"metrics regarding the quality of read alignments to a reference sequence, as well as the proportion of the reads " +
"that passed machine signal-to-noise threshold quality filters (Illumina)." +
"\nUsage example:\n" +
"\n" +
" java -jar picard.jar CollectAlignmentSummaryMetrics \\\n" +
" R=reference_sequence.fasta \\\n" +
" I=input.bam \\\n" +
" O=output.txt" +
"\n" +
"Please see the AlignmentSummaryMetrics documentation (http://broadinstitute.github.io/picard/picard-metric-definitions.html#AlignmentSummaryMetrics) for detailed explanations of each metric. \n \n" +
"Additional information about Illumina's quality filters can be found in the following documents on the Illumina website:" +
"\n" +
" - hiseq-x-percent-pf-technical-note (http://support.illumina.com/content/dam/illumina-marketing/documents/products/technotes/hiseq-x-percent-pf-technical-note-770-2014-043.pdf)\n" +
" - hiseq-x-system-guide (http://support.illumina.com/content/dam/illumina-support/documents/documentation/system_documentation/hiseqx/hiseq-x-system-guide-15050091-d.pdf)\n\n" +
"\n"});
return retval.toArray(new Object[0][]);
}
@Test(dataProvider = "testHTMLConverter")
public void testHTMLConverter(String input, String expected) {
final String converted = CommandLineParser.convertFromHtml(input);
Assert.assertEquals(converted, expected, "common part:\"" + expected.substring(0, lengthOfCommonSubstring(converted, expected)) + "\"\n\n");
}
@CommandLineProgramProperties(
usage = TestParserFail.USAGE_SUMMARY + TestParserFail.USAGE_DETAILS,
usageShort = TestParserFail.USAGE_SUMMARY,
programGroup = Testing.class
)
protected class TestParserFail extends CommandLineProgram {
static public final String USAGE_DETAILS = "blah &blah; blah ";
static public final String USAGE_SUMMARY = "This tool offers.....";
@Override
protected int doWork() {return 0;}
}
@CommandLineProgramProperties(
usage = TestParserSucceed.USAGE_SUMMARY + TestParserSucceed.USAGE_DETAILS,
usageShort = TestParserSucceed.USAGE_SUMMARY,
programGroup = Testing.class
)
protected class TestParserSucceed extends CommandLineProgram {
static public final String USAGE_DETAILS = "This is the first row <p>And this is the second";
static public final String USAGE_SUMMARY = " X < Y ";
@Override
protected int doWork() {return 0;}
}
@Test(expectedExceptions = AssertionError.class)
public void testNonAsciiAssertion() {
TestParserFail testparserFail = new TestParserFail();
testparserFail.parseArgs(new String[]{});
PrintStream stream = new PrintStream(new NullOutputStream());
testparserFail.getCommandLineParser().usage(stream, true);
}
@Test
public void testNonAsciiConverted() {
TestParserSucceed testparserSucceed = new TestParserSucceed();
testparserSucceed.parseArgs(new String[]{});
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PrintStream stream = new PrintStream(byteArrayOutputStream);
testparserSucceed.getCommandLineParser().usage(stream, true);
String expected = "USAGE: TestParserSucceed [options]\n" +
"\n" +
"Documentation: http://broadinstitute.github.io/picard/command-line-overview.html#TestParserSucceed\n" +
"\n" +
" X < Y This is the first row \n" +
"And this is the second";
Assert.assertEquals(byteArrayOutputStream.toString().substring(0, expected.length()), expected);
}
static private int lengthOfCommonSubstring(String lhs, String rhs) {
int i = 0;
while (i < Math.min(lhs.length(), rhs.length()) && lhs.charAt(i) == rhs.charAt(i)) i++;
return i;
}
private class NullOutputStream extends OutputStream {
@Override
public void write(final int b) throws IOException {
}
}
}