From adff51745d97ff4fd30a188a68efa29cc83202e3 Mon Sep 17 00:00:00 2001 From: Ben Soares Date: Thu, 4 May 2023 16:49:00 +0100 Subject: [PATCH] JAL-629 More NG arguments: --features, --annotations, --sortbytree, --tree, --groovy. More consistent arg naming and processing of test output. Actual java process tests for ng commandline. Store BootstrapArgs in ArgParser. --- src/jalview/bin/Commands.java | 134 ++++++-- src/jalview/bin/Jalview.java | 157 ++++++++- src/jalview/bin/argparser/Arg.java | 41 +-- src/jalview/bin/argparser/ArgParser.java | 45 ++- src/jalview/bin/argparser/BootstrapArgs.java | 5 + src/jalview/io/FileFormat.java | 8 +- test/jalview/bin/CommandLineOperations.java | 9 +- test/jalview/bin/CommandLineOperationsNG.java | 455 +++++++++++++++++++++++++ test/jalview/bin/CommandsTest2.java | 6 +- test/jalview/bin/commandsTest2.argfile1 | 2 +- test/jalview/bin/commandsTest2.argfile2 | 4 +- 11 files changed, 771 insertions(+), 95 deletions(-) create mode 100644 test/jalview/bin/CommandLineOperationsNG.java diff --git a/src/jalview/bin/Commands.java b/src/jalview/bin/Commands.java index 79b6ab6..7e33b17 100644 --- a/src/jalview/bin/Commands.java +++ b/src/jalview/bin/Commands.java @@ -42,6 +42,7 @@ import jalview.io.FileFormats; import jalview.io.FileLoader; import jalview.io.HtmlSvgOutput; import jalview.io.IdentifyFile; +import jalview.io.NewickFile; import jalview.structure.StructureImportSettings.TFType; import jalview.structure.StructureSelectionManager; import jalview.util.HttpUtils; @@ -92,6 +93,7 @@ public class Commands ArgValuesMap avm = argParser.getLinkedArgs(id); theseArgsWereParsed = true; theseArgsWereParsed &= processLinked(id); + processGroovyScript(id); boolean processLinkedOkay = theseArgsWereParsed; theseArgsWereParsed &= processImages(id); if (processLinkedOkay) @@ -110,7 +112,7 @@ public class Commands } } - if (argParser.getBool(Arg.QUIT)) + if (argParser.getBoolean(Arg.QUIT)) { Jalview.getInstance().quit(); return true; @@ -219,21 +221,11 @@ public class Commands if (af == null || "true".equals(av.getSubVal("new")) || a == Arg.OPEN || format == FileFormat.Jalview) { - /* - * this approach isn't working yet // get default annotations before opening - * AlignFrame if (m.get(Arg.SSANNOTATIONS) != null) { - * Console.debug("##### SSANNOTATIONS=" + m.get(Arg.SSANNOTATIONS).getBoolean()); - * } if (m.get(Arg.NOTEMPFAC) != null) { Console.debug( "##### NOTEMPFAC=" + - * m.get(Arg.NOTEMPFAC).getBoolean()); } boolean showSecondaryStructure = - * (m.get(Arg.SSANNOTATIONS) != null) ? m.get(Arg.SSANNOTATIONS).getBoolean() : - * false; boolean showTemperatureFactor = (m.get(Arg.NOTEMPFAC) != null) ? - * !m.get(Arg.NOTEMPFAC).getBoolean() : false; Console.debug("##### tempfac=" + - * showTemperatureFactor + ", showSS=" + showSecondaryStructure); - * StructureSelectionManager ssm = StructureSelectionManager - * .getStructureSelectionManager(Desktop.instance); if (ssm != null) { - * ssm.setAddTempFacAnnot(showTemperatureFactor); - * ssm.setProcessSecondaryStructure(showSecondaryStructure); } - */ + if (a == Arg.OPEN) + { + Jalview.testoutput(argParser, Arg.OPEN, "examples/uniref50.fa", + openFile); + } Console.debug( "Opening '" + openFile + "' in new alignment frame"); @@ -241,45 +233,91 @@ public class Commands af = fileLoader.LoadFileWaitTillLoaded(openFile, protocol, format); - boolean showAnnotations = ArgParser.getFromSubValArgOrPref(avm, - Arg.ANNOTATIONS, av.getSubVals(), null, - "SHOW_ANNOTATIONS", true); - af.setAnnotationsVisibility(showAnnotations, false, true); // wrap alignment? boolean wrap = ArgParser.getFromSubValArgOrPref(avm, Arg.WRAP, sv, null, "WRAP_ALIGNMENT", false); af.getCurrentView().setWrapAlignment(wrap); - // colour aligment? + // colour alignment? String colour = ArgParser.getFromSubValArgOrPref(avm, av, Arg.COLOUR, sv, null, "DEFAULT_COLOUR_PROT", ""); - if ("" != colour) { af.changeColour_actionPerformed(colour); + Jalview.testoutput(argParser, Arg.COLOUR, "zappo", colour); } - // change alignment frame title + // Change alignment frame title String title = ArgParser.getFromSubValArgOrPref(avm, av, Arg.TITLE, sv, null, null, null); if (title != null) + { af.setTitle(title); + Jalview.testoutput(argParser, Arg.TITLE, "test title", title); + } + + // Add features + String featuresfile = ArgParser.getValueFromSubValOrArg(avm, av, + Arg.FEATURES, sv); + if (featuresfile != null) + { + af.parseFeaturesFile(featuresfile, + AppletFormatAdapter.checkProtocol(featuresfile)); + Jalview.testoutput(argParser, Arg.FEATURES, + "examples/testdata/plantfdx.features", featuresfile); + } - // show secondary structure annotations? + // Add annotations from file + String annotationsfile = ArgParser.getValueFromSubValOrArg(avm, + av, Arg.ANNOTATIONS, sv); + if (annotationsfile != null) + { + af.loadJalviewDataFile(annotationsfile, null, null, null); + Jalview.testoutput(argParser, Arg.ANNOTATIONS, + "examples/testdata/plantfdx.annotations", + annotationsfile); + } + + // Set or clear the sortbytree flag + boolean sortbytree = ArgParser.getBoolFromSubValOrArg(avm, + Arg.SORTBYTREE, sv); + if (sortbytree) + { + af.getViewport().setSortByTree(true); + Jalview.testoutput(argParser, Arg.SORTBYTREE); + } + + // Load tree from file + String treefile = ArgParser.getValueFromSubValOrArg(avm, av, + Arg.TREE, sv); + if (treefile != null) + { + try + { + NewickFile nf = new NewickFile(treefile, + AppletFormatAdapter.checkProtocol(treefile)); + af.getViewport().setCurrentTree( + af.showNewickTree(nf, treefile).getTree()); + Jalview.testoutput(argParser, Arg.TREE, + "examples/testdata/uniref50_test_tree", treefile); + } catch (IOException e) + { + Console.warn("Couldn't add tree " + treefile, e); + } + } + + // Show secondary structure annotations? boolean showSSAnnotations = ArgParser.getFromSubValArgOrPref(avm, Arg.SSANNOTATIONS, av.getSubVals(), null, "STRUCT_FROM_PDB", true); - if (avm.getBoolean(Arg.SSANNOTATIONS)) - { - af.setAnnotationsVisibility(showSSAnnotations, true, false); - /* - AlignmentUtils.showOrHideSequenceAnnotations( - af.getCurrentView().getAlignment(), - Collections.singleton("Secondary Structure"), null, - false, false); - */ - } + af.setAnnotationsVisibility(showSSAnnotations, true, false); + + // Show sequence annotations? + boolean showAnnotations = ArgParser.getFromSubValArgOrPref(avm, + Arg.SHOWANNOTATIONS, av.getSubVals(), null, + "SHOW_ANNOTATIONS", true); + af.setAnnotationsVisibility(showAnnotations, false, true); // show temperature factor annotations? if (avm.getBoolean(Arg.NOTEMPFAC)) @@ -521,8 +559,8 @@ public class Commands } String sViewer = ArgParser.getFromSubValArgOrPref(avm, - Arg.STRUCTUREVIEWER, Position.AFTER, av, subVals, - "viewer", null, "jmol"); + Arg.STRUCTUREVIEWER, Position.AFTER, av, subVals, null, + null, "jmol"); ViewerType viewerType = null; if (!"none".equals(sViewer)) { @@ -569,6 +607,30 @@ public class Commands return theseArgsWereParsed; } + protected void processGroovyScript(String id) + { + ArgValuesMap avm = argParser.getLinkedArgs(id); + AlignFrame af = afMap.get(id); + + if (af == null) + { + Console.warn("Did not have an alignment window for id=" + id); + return; + } + + if (avm.containsArg(Arg.GROOVY)) + { + String groovyscript = avm.getValue(Arg.GROOVY); + if (groovyscript != null) + { + // Execute the groovy script after we've done all the rendering stuff + // and before any images or figures are generated. + Console.info("Executing script " + groovyscript); + Jalview.getInstance().executeGroovyScript(groovyscript, af); + } + } + } + protected boolean processImages(String id) { ArgValuesMap avm = argParser.getLinkedArgs(id); diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java index 4c204fa..895268d 100755 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@ -67,6 +67,7 @@ import com.threerings.getdown.util.LaunchUtil; import groovy.lang.Binding; import groovy.util.GroovyScriptEngine; import jalview.bin.argparser.Arg; +import jalview.bin.argparser.Arg.Opt; import jalview.bin.argparser.ArgParser; import jalview.bin.argparser.BootstrapArgs; import jalview.ext.so.SequenceOntology; @@ -436,6 +437,8 @@ public class Jalview { System.out.println( "CMD [-props " + usrPropsFile + "] executed successfully!"); + testoutput(bootstrapArgs, Arg.PROPS, + "test/jalview/bin/testProps.jvprops", usrPropsFile); } // new ArgParser @@ -445,12 +448,14 @@ public class Jalview { argparser = ArgParser.parseArgFiles( bootstrapArgs.getList(Arg.ARGFILE), - bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS)); + bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS), + bootstrapArgs); } else { argparser = new ArgParser(args, - bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS)); + bootstrapArgs.getBoolean(Arg.INITSUBSTITUTIONS), + bootstrapArgs); } if (!Platform.isJS()) @@ -489,7 +494,9 @@ public class Jalview // allow https handshakes to download intermediate certs if necessary System.setProperty("com.sun.security.enableAIAcaIssuers", "true"); - final String jabawsUrl = aparser.getValue("jabaws"); + String jabawsUrl = bootstrapArgs.get(Arg.JABAWS); + if (jabawsUrl == null) + jabawsUrl = aparser.getValue("jabaws"); if (jabawsUrl != null) { try @@ -497,6 +504,8 @@ public class Jalview Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl); System.out.println( "CMD [-jabaws " + jabawsUrl + "] executed successfully!"); + testoutput(bootstrapArgs, Arg.JABAWS, + "http://www.compbio.dundee.ac.uk/jabaws", jabawsUrl); } catch (MalformedURLException e) { System.err.println( @@ -639,25 +648,37 @@ public class Jalview } } - boolean doWebServiceDiscovery = !aparser - .contains("nowebservicediscovery"); - if (bootstrapArgs.contains(Arg.WEBSERVICEDISCOVERY)) - doWebServiceDiscovery = bootstrapArgs - .getBoolean(Arg.WEBSERVICEDISCOVERY); - if (doWebServiceDiscovery) + boolean webservicediscovery = bootstrapArgs + .getBoolean(Arg.WEBSERVICEDISCOVERY); + if (aparser.contains("nowebservicediscovery")) + webservicediscovery = false; + if (webservicediscovery) { desktop.startServiceDiscovery(); } - if (!aparser.contains("nousagestats")) + else + { + testoutput(argparser, Arg.WEBSERVICEDISCOVERY); + } + + boolean usagestats = bootstrapArgs.getBoolean(Arg.USAGESTATS); + if (aparser.contains("nousagestats")) + usagestats = false; + if (usagestats) { startUsageStats(desktop); + testoutput(argparser, Arg.USAGESTATS); } else { - System.err.println("CMD [-nousagestats] executed successfully!"); + System.out.println("CMD [-nousagestats] executed successfully!"); + testoutput(argparser, Arg.USAGESTATS); } - if (!aparser.contains("noquestionnaire")) + boolean questionnaire = bootstrapArgs.getBoolean(Arg.QUESTIONNAIRE); + if (aparser.contains("noquestionnaire")) + questionnaire = false; + if (questionnaire) { String url = aparser.getValue("questionnaire"); if (url != null) @@ -687,8 +708,9 @@ public class Jalview } else { - System.err + System.out .println("CMD [-noquestionnaire] executed successfully!"); + testoutput(argparser, Arg.QUESTIONNAIRE); } if ((!aparser.contains("nonews") @@ -1492,7 +1514,7 @@ public class Jalview * the Jalview Desktop object passed in to the groovy binding as the * 'Jalview' object. */ - private void executeGroovyScript(String groovyscript, AlignFrame af) + protected void executeGroovyScript(String groovyscript, AlignFrame af) { /** * for scripts contained in files @@ -1653,4 +1675,111 @@ public class Jalview if (exitcode > -1) System.exit(exitcode); } + + /* + * testoutput for string values + */ + protected static void testoutput(ArgParser ap, Arg a, String s1, + String s2) + { + BootstrapArgs bsa = ap.getBootstrapArgs(); + if (!bsa.getBoolean(Arg.TESTOUTPUT)) + return; + if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2)))) + { + Console.debug("testoutput with unmatching values '" + s1 + "' and '" + + s2 + "' for arg " + a.argString()); + return; + } + boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a) + : ap.isSet(a); + if (!isset) + { + Console.warn("Arg '" + a.getName() + "' not set at all"); + return; + } + testoutput(true, a, s1, s2); + } + + protected static void testoutput(BootstrapArgs bsa, Arg a, String s1, + String s2) + { + if (!bsa.getBoolean(Arg.TESTOUTPUT)) + return; + if (!((s1 == null && s2 == null) || (s1 != null && s1.equals(s2)))) + { + Console.debug("testoutput with unmatching values '" + s1 + "' and '" + + s2 + "' for arg " + a.argString()); + return; + } + if (!a.hasOption(Opt.BOOTSTRAP)) + { + Console.error("Non-bootstrap Arg '" + a.getName() + + "' given to testoutput(BootstrapArgs bsa, Arg a, String s1, String s2) with only BootstrapArgs"); + } + if (!bsa.contains(a)) + { + Console.warn("Arg '" + a.getName() + "' not set at all"); + return; + } + testoutput(true, a, s1, s2); + } + + private static void testoutput(boolean yes, Arg a, String s1, String s2) + { + if (yes && ((s1 == null && s2 == null) + || (s1 != null && s1.equals(s2)))) + { + System.out.println("[TESTOUTPUT] arg " + a.argString() + "='" + s1 + + "' was set"); + } + } + + /* + * testoutput for boolean values + */ + protected static void testoutput(ArgParser ap, Arg a) + { + if (ap == null) + return; + BootstrapArgs bsa = ap.getBootstrapArgs(); + if (bsa == null) + return; + if (!bsa.getBoolean(Arg.TESTOUTPUT)) + return; + boolean val = a.hasOption(Opt.BOOTSTRAP) ? bsa.getBoolean(a) + : ap.getBoolean(a); + boolean isset = a.hasOption(Opt.BOOTSTRAP) ? bsa.contains(a) + : ap.isSet(a); + if (!isset) + { + Console.warn("Arg '" + a.getName() + "' not set at all"); + return; + } + testoutput(val, a); + } + + protected static void testoutput(BootstrapArgs bsa, Arg a) + { + if (!bsa.getBoolean(Arg.TESTOUTPUT)) + return; + if (!a.hasOption(Opt.BOOTSTRAP)) + { + Console.warn("Non-bootstrap Arg '" + a.getName() + + "' given to testoutput(BootstrapArgs bsa, Arg a) with only BootstrapArgs"); + + } + if (!bsa.contains(a)) + { + Console.warn("Arg '" + a.getName() + "' not set at all"); + return; + } + testoutput(bsa.getBoolean(a), a); + } + + private static void testoutput(boolean yes, Arg a) + { + System.out.println("[TESTOUTPUT] arg " + + (yes ? a.argString() : a.negateArgString()) + " was set"); + } } diff --git a/src/jalview/bin/argparser/Arg.java b/src/jalview/bin/argparser/Arg.java index f8454df..f83f267 100644 --- a/src/jalview/bin/argparser/Arg.java +++ b/src/jalview/bin/argparser/Arg.java @@ -9,16 +9,17 @@ import java.util.stream.Collectors; public enum Arg { - HELP("h"), CALCULATION, MENUBAR, STATUS, SHOWOVERVIEW, ANNOTATIONS, - COLOUR, FEATURES, GROOVY, GROUPS, HEADLESS, JABAWS, DISPLAY, GUI, NEWS, - SORTBYTREE, USAGESTATS, APPEND, OPEN, PROPS, QUESTIONNAIRE, SETPROP, TREE, - VDOC, VSESS, OUTPUT, SSANNOTATIONS, NOTEMPFAC, TEMPFAC, TITLE, PAEMATRIX, - WRAP, NOSTRUCTURE, STRUCTURE, STRUCTUREVIEWER, IMAGE, TYPE, FORMAT, - OVERWRITE, RENDERER, QUIT, CLOSE, DEBUG("d"), TRACE, QUIET("q"), ARGFILE, - NEW, NPP("n++"), SUBSTITUTIONS, INITSUBSTITUTIONS, NIL, SPLASH, - SETARGFILE, UNSETARGFILE, WEBSERVICEDISCOVERY, ALL, BACKUPS; - - protected static enum Opt + HELP("h"), CALCULATION, MENUBAR, STATUS, SHOWOVERVIEW, SHOWANNOTATIONS, + COLOUR("color"), FEATURES, ANNOTATIONS, GROOVY, GROUPS, HEADLESS, JABAWS, + DISPLAY, NEWS, SORTBYTREE, USAGESTATS, APPEND, OPEN, PROPS, QUESTIONNAIRE, + SETPROP, TREE, VDOC, VSESS, OUTPUT, SSANNOTATIONS, NOTEMPFAC, TEMPFAC, + TITLE, PAEMATRIX, WRAP, NOSTRUCTURE, STRUCTURE, STRUCTUREVIEWER, IMAGE, + TYPE, FORMAT, OVERWRITE, RENDERER, QUIT, CLOSE, DEBUG("d"), TRACE, + QUIET("q"), ARGFILE, NEW, NPP("n++"), SUBSTITUTIONS, INITSUBSTITUTIONS, + NIL, SPLASH, SETARGFILE, UNSETARGFILE, WEBSERVICEDISCOVERY, ALL, BACKUPS, + TESTOUTPUT; + + public static enum Opt { BOOLEAN, // This Arg can be specified as --arg or --noarg to give true or // false. A default can be given with setOptions(bool, Opt....). @@ -70,30 +71,33 @@ public enum Arg MENUBAR.setOptions(true, Opt.BOOLEAN); STATUS.setOptions(true, Opt.BOOLEAN); SHOWOVERVIEW.setOptions(Opt.UNARY, Opt.LINKED); - ANNOTATIONS.setOptions(Opt.BOOLEAN, Opt.LINKED); + SHOWANNOTATIONS.setOptions(Opt.BOOLEAN, Opt.LINKED); COLOUR.setOptions(Opt.STRING, Opt.LINKED); FEATURES.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS); + ANNOTATIONS.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, + Opt.ALLOWSUBSTITUTIONS); + TREE.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, + Opt.ALLOWSUBSTITUTIONS); GROOVY.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.ALLOWSUBSTITUTIONS); GROUPS.setOptions(Opt.STRING, Opt.LINKED); HEADLESS.setOptions(Opt.UNARY, Opt.BOOTSTRAP); - JABAWS.setOptions(Opt.STRING); + TESTOUTPUT.setOptions(Opt.UNARY, Opt.BOOTSTRAP); + JABAWS.setOptions(Opt.STRING, Opt.BOOTSTRAP); DISPLAY.setOptions(true, Opt.BOOLEAN); - GUI.setOptions(true, Opt.BOOLEAN); NEWS.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP); SPLASH.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP); - // expects a string value - SORTBYTREE.setOptions(true, Opt.BOOLEAN); - USAGESTATS.setOptions(true, Opt.BOOLEAN); + SORTBYTREE.setOptions(true, Opt.LINKED, Opt.BOOLEAN); + QUESTIONNAIRE.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP); + USAGESTATS.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP); + WEBSERVICEDISCOVERY.setOptions(true, Opt.BOOLEAN, Opt.BOOTSTRAP); APPEND.setOptions(Opt.STRING, Opt.LINKED, Opt.MULTI, Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT); OPEN.setOptions(Opt.STRING, Opt.LINKED, Opt.INCREMENTDEFAULTCOUNTER, Opt.MULTI, Opt.GLOB, Opt.ALLOWSUBSTITUTIONS, Opt.INPUT); PROPS.setOptions(Opt.STRING, Opt.BOOTSTRAP); - QUESTIONNAIRE.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP); SETPROP.setOptions(Opt.STRING, Opt.MULTI, Opt.BOOTSTRAP); - TREE.setOptions(Opt.STRING); VDOC.setOptions(Opt.UNARY); VSESS.setOptions(Opt.UNARY); @@ -135,7 +139,6 @@ public enum Arg NIL.setOptions(Opt.UNARY, Opt.LINKED, Opt.MULTI, Opt.NOACTION); SETARGFILE.setOptions(Opt.STRING, Opt.MULTI, Opt.PRIVATE, Opt.NOACTION); UNSETARGFILE.setOptions(Opt.MULTI, Opt.PRIVATE, Opt.NOACTION); - WEBSERVICEDISCOVERY.setOptions(Opt.BOOLEAN, Opt.BOOTSTRAP); ALL.setOptions(Opt.BOOLEAN, Opt.MULTI, Opt.NOACTION); } diff --git a/src/jalview/bin/argparser/ArgParser.java b/src/jalview/bin/argparser/ArgParser.java index 42868ae..dd863d1 100644 --- a/src/jalview/bin/argparser/ArgParser.java +++ b/src/jalview/bin/argparser/ArgParser.java @@ -118,6 +118,8 @@ public class ArgParser private int argIndex = 0; + private BootstrapArgs bootstrapArgs = null; + static { argMap = new HashMap<>(); @@ -147,10 +149,11 @@ public class ArgParser public ArgParser(String[] args) { - this(args, false); + this(args, false, null); } - public ArgParser(String[] args, boolean initsubstitutions) + public ArgParser(String[] args, boolean initsubstitutions, + BootstrapArgs bsa) { // Make a mutable new ArrayList so that shell globbing parser works. // (When shell file globbing is used, there are a sequence of non-Arg @@ -158,16 +161,17 @@ public class ArgParser // consumed by the --append/--argfile/etc Arg which is most easily done by // removing these filenames from the list one at a time. This can't be done // with an ArrayList made with only Arrays.asList(String[] args). ) - this(new ArrayList<>(Arrays.asList(args)), initsubstitutions); + this(new ArrayList<>(Arrays.asList(args)), initsubstitutions, false, + bsa); } public ArgParser(List args, boolean initsubstitutions) { - this(args, initsubstitutions, false); + this(args, initsubstitutions, false, null); } public ArgParser(List args, boolean initsubstitutions, - boolean allowPrivate) + boolean allowPrivate, BootstrapArgs bsa) { // do nothing if there are no "--" args and (some "-" args || >0 arg is // "open") @@ -191,6 +195,10 @@ public class ArgParser parse(new ArrayList(), false, false); return; } + if (bsa != null) + this.bootstrapArgs = bsa; + else + this.bootstrapArgs = BootstrapArgs.getBootstrapArgs(args); parse(args, initsubstitutions, allowPrivate); } @@ -620,9 +628,24 @@ public class ArgParser return vals; } + public BootstrapArgs getBootstrapArgs() + { + return bootstrapArgs; + } + public boolean isSet(Arg a) { - return a.hasOption(Opt.LINKED) ? isSet("", a) : isSet(null, a); + return a.hasOption(Opt.LINKED) ? isSetAtAll(a) : isSet(null, a); + } + + public boolean isSetAtAll(Arg a) + { + for (String linkedId : linkedOrder) + { + if (isSet(linkedId, a)) + return true; + } + return false; } public boolean isSet(String linkedId, Arg a) @@ -631,7 +654,7 @@ public class ArgParser return avm == null ? false : avm.containsArg(a); } - public boolean getBool(Arg a) + public boolean getBoolean(Arg a) { if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY)) { @@ -698,7 +721,7 @@ public class ArgParser } public static ArgParser parseArgFiles(List argFilenameGlobs, - boolean initsubstitutions) + boolean initsubstitutions, BootstrapArgs bsa) { List argFiles = new ArrayList<>(); @@ -708,11 +731,11 @@ public class ArgParser argFiles.addAll(FileUtils.getFilesFromGlob(pattern)); } - return parseArgFileList(argFiles, initsubstitutions); + return parseArgFileList(argFiles, initsubstitutions, bsa); } public static ArgParser parseArgFileList(List argFiles, - boolean initsubstitutions) + boolean initsubstitutions, BootstrapArgs bsa) { List argsList = new ArrayList<>(); for (File argFile : argFiles) @@ -740,7 +763,7 @@ public class ArgParser } // Third param "true" uses Opt.PRIVATE args --setargile=argfile and // --unsetargfile - return new ArgParser(argsList, initsubstitutions, true); + return new ArgParser(argsList, initsubstitutions, true, bsa); } protected static List readArgFile(File argFile) diff --git a/src/jalview/bin/argparser/BootstrapArgs.java b/src/jalview/bin/argparser/BootstrapArgs.java index 4829ef8..14b7fe6 100644 --- a/src/jalview/bin/argparser/BootstrapArgs.java +++ b/src/jalview/bin/argparser/BootstrapArgs.java @@ -25,6 +25,11 @@ public class BootstrapArgs return new BootstrapArgs(argList); } + public static BootstrapArgs getBootstrapArgs(List args) + { + return new BootstrapArgs(args); + } + private BootstrapArgs(List args) { parse(args, null); diff --git a/src/jalview/io/FileFormat.java b/src/jalview/io/FileFormat.java index b701963..43c6dcf 100644 --- a/src/jalview/io/FileFormat.java +++ b/src/jalview/io/FileFormat.java @@ -30,7 +30,7 @@ import jalview.structure.StructureImportSettings; public enum FileFormat implements FileFormatI { - Fasta("Fasta", "fa, fasta, mfa, fastq", true, true) + Fasta("Fasta", "fa,fasta,mfa,fastq", true, true) { @Override public AlignmentFileReaderI getReader(FileParse source) @@ -92,7 +92,7 @@ public enum FileFormat implements FileFormatI return new PIRFile(); } }, - BLC("BLC", "BLC", true, true) + BLC("BLC", "blc", true, true) { @Override public AlignmentFileReaderI getReader(FileParse source) @@ -244,7 +244,7 @@ public enum FileFormat implements FileFormatI return new PhylipFile(); } }, - GenBank("GenBank Flatfile", "gb, gbk", true, false) + GenBank("GenBank Flatfile", "gb,gbk", true, false) { @Override public AlignmentFileReaderI getReader(FileParse source) @@ -379,7 +379,7 @@ public enum FileFormat implements FileFormatI return true; } }, - Jalview("Jalview", "jvp, jar", true, true) + Jalview("Jalview", "jvp,jar", true, true) { @Override public AlignmentFileReaderI getReader(FileParse source) diff --git a/test/jalview/bin/CommandLineOperations.java b/test/jalview/bin/CommandLineOperations.java index 9b9adf8..0ab18d9 100644 --- a/test/jalview/bin/CommandLineOperations.java +++ b/test/jalview/bin/CommandLineOperations.java @@ -34,7 +34,6 @@ import java.util.ArrayList; import org.testng.Assert; import org.testng.FileAssert; import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -213,13 +212,13 @@ public class CommandLineOperations } } - @BeforeTest(alwaysRun = true) + @BeforeClass(alwaysRun = true) public void initialize() { new CommandLineOperations(); } - @BeforeTest(alwaysRun = true) + @BeforeClass(alwaysRun = true) public void setUpForHeadlessCommandLineInputOperations() throws IOException { @@ -239,7 +238,7 @@ public class CommandLineOperations } } - @BeforeTest(alwaysRun = true) + @BeforeClass(alwaysRun = true) public void setUpForCommandLineInputOperations() throws IOException { String cmds = "-open examples/uniref50.fa -noquestionnaire -nousagestats"; @@ -261,7 +260,7 @@ public class CommandLineOperations int count = 0; try { - while ((ln = worker.getErrorReader().readLine()) != null) + while ((ln = worker.getOutputReader().readLine()) != null) { System.out.println(ln); successfulCMDs.add(ln); diff --git a/test/jalview/bin/CommandLineOperationsNG.java b/test/jalview/bin/CommandLineOperationsNG.java new file mode 100644 index 0000000..4d05c4e --- /dev/null +++ b/test/jalview/bin/CommandLineOperationsNG.java @@ -0,0 +1,455 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.bin; + +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.testng.Assert; +import org.testng.FileAssert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import io.github.classgraph.ClassGraph; +import jalview.gui.JvOptionPane; + +public class CommandLineOperationsNG +{ + + @BeforeClass(alwaysRun = true) + public void setUpJvOptionPane() + { + JvOptionPane.setInteractiveMode(false); + JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); + } + + // Note longer timeout needed on full test run than on individual tests + private static final int TEST_TIMEOUT = 13000; + + private static final int SETUP_TIMEOUT = 9500; + + private static final int MINFILESIZE_SMALL = 2096; + + private static final int MINFILESIZE_BIG = 4096; + + private List successfulCMDs = new ArrayList<>(); + + /*** + * from + * http://stackoverflow.com/questions/808276/how-to-add-a-timeout-value-when + * -using-javas-runtime-exec + * + * @author jimp + * + */ + private static class Worker extends Thread + { + private final Process process; + + private BufferedReader outputReader; + + private BufferedReader errorReader; + + private Integer exit; + + private Worker(Process process) + { + this.process = process; + } + + @Override + public void run() + { + try + { + exit = process.waitFor(); + } catch (InterruptedException ignore) + { + return; + } + } + + public BufferedReader getOutputReader() + { + return outputReader; + } + + public void setOutputReader(BufferedReader outputReader) + { + this.outputReader = outputReader; + } + + public BufferedReader getErrorReader() + { + return errorReader; + } + + public void setErrorReader(BufferedReader errorReader) + { + this.errorReader = errorReader; + } + } + + private static ClassGraph scanner = null; + + private static String classpath = null; + + private static String modules = null; + + private static String java_exe = null; + + public synchronized static String getClassPath() + { + // if (scanner == null) + // { + // scanner = new ClassGraph(); + // ScanResult scan = scanner.scan(); + // classpath = scan.getClasspath(); + modules = ""; + // for (ModuleRef mr : scan.getModules()) + // { + // modules.concat(mr.getName()); + // } + java_exe = System.getProperty("java.home") + File.separator + "bin" + + File.separator + "java"; + + // } + + // while (classpath == null) + // { + // try + // { + // Thread.sleep(10); + // } catch (InterruptedException x) + // { + // + // } + // } + return classpath; + } + + private Worker getJalviewDesktopRunner(boolean withAwt, String cmd, + int timeout) + { + // Note: JAL-3065 - don't include quotes for lib/* because the arguments are + // not expanded by the shell + // String classpath = getClassPath(); + getClassPath(); + boolean win = System.getProperty("os.name").indexOf("Win") >= 0; + String pwd = ""; + try + { + Path currentRelativePath = Paths.get(""); + pwd = currentRelativePath.toAbsolutePath().toString(); + } catch (Exception q) + { + q.printStackTrace(); + } + if (pwd == null || pwd.length() == 0) + pwd = "."; + String[] classpaths = new String[] { pwd + "/bin/main", + pwd + "/j11lib/*", pwd + "/resources", pwd + "/help" }; + String classpath = String.join(win ? ";" : ":", classpaths); + String _cmd = java_exe + " " + + (withAwt ? "-Djava.awt.headless=true" : "") + " -classpath " + + classpath + + ((modules != null && modules.length() > 2) + ? "--add-modules=\"" + modules + "\"" + : "") + + " jalview.bin.Jalview "; + Process ls2_proc = null; + Worker worker = null; + try + { + cmd = " --testoutput " + cmd; + System.out.println("Running '" + _cmd + cmd + "'"); + ls2_proc = Runtime.getRuntime().exec(_cmd + cmd); + } catch (Throwable e1) + { + e1.printStackTrace(); + } + if (ls2_proc != null) + { + BufferedReader outputReader = new BufferedReader( + new InputStreamReader(ls2_proc.getInputStream())); + BufferedReader errorReader = new BufferedReader( + new InputStreamReader(ls2_proc.getErrorStream())); + worker = new Worker(ls2_proc); + worker.start(); + try + { + worker.join(timeout); + } catch (InterruptedException e) + { + System.err.println("Thread interrupted"); + } + worker.setOutputReader(outputReader); + worker.setErrorReader(errorReader); + } + return worker; + } + + @Test(groups = { "Functional" }) + public void reportCurrentWorkingDirectory() + { + try + { + Path currentRelativePath = Paths.get(""); + String s = currentRelativePath.toAbsolutePath().toString(); + System.out.println("Test CWD is " + s); + } catch (Exception q) + { + q.printStackTrace(); + } + } + + @BeforeClass(alwaysRun = true) + public void initialize() + { + new CommandLineOperationsNG(); + } + + @BeforeClass(alwaysRun = true) + public void setUpForHeadlessCommandLineInputOperations() + throws IOException + { + String cmds = "--headless " + "--open examples/uniref50.fa " + + "--sortbytree " + + "--props test/jalview/bin/testProps.jvprops " + + "--colour zappo " + + "--jabaws http://www.compbio.dundee.ac.uk/jabaws " + + "--features examples/testdata/plantfdx.features " + + "--annotations examples/testdata/plantfdx.annotations " + + "--tree examples/testdata/uniref50_test_tree " + + "--nousagestats "; + Worker worker = getJalviewDesktopRunner(true, cmds, SETUP_TIMEOUT); + String ln = null; + while ((ln = worker.getOutputReader().readLine()) != null) + { + System.out.println(ln); + successfulCMDs.add(ln); + } + while ((ln = worker.getErrorReader().readLine()) != null) + { + System.err.println(ln); + } + } + + @BeforeClass(alwaysRun = true) + public void setUpForCommandLineInputOperations() throws IOException + { + String cmds = "--open examples/uniref50.fa --noquestionnaire --nousagestats"; + final Worker worker = getJalviewDesktopRunner(false, cmds, + SETUP_TIMEOUT); + + // number of lines expected on STDERR when Jalview starts up normally + // may need to adjust this if Jalview is excessively noisy ? + final int STDERR_SETUPLINES = 50; + + // thread monitors stderr - bails after SETUP_TIMEOUT or when + // STDERR_SETUPLINES have been read + Thread runner = new Thread(new Runnable() + { + @Override + public void run() + { + String ln = null; + int count = 0; + try + { + while ((ln = worker.getOutputReader().readLine()) != null) + { + System.out.println(ln); + successfulCMDs.add(ln); + if (++count > STDERR_SETUPLINES) + { + break; + } + } + } catch (Exception e) + { + System.err.println( + "Unexpected Exception reading stderr from the Jalview process"); + e.printStackTrace(); + } + } + }); + long t = System.currentTimeMillis() + SETUP_TIMEOUT; + runner.start(); + while (!runner.isInterrupted() && System.currentTimeMillis() < t) + { + try + { + Thread.sleep(500); + } catch (InterruptedException e) + { + } + } + runner.interrupt(); + if (worker != null && worker.exit == null) + { + worker.interrupt(); + Thread.currentThread().interrupt(); + worker.process.destroy(); + } + } + + @Test(groups = { "Functional" }, dataProvider = "allInputOperationsData") + public void testAllInputOperations(String expectedString, + String failureMsg) + { + if ("[TESTOUTPUT] arg --nousagestats was set".equals(expectedString)) + Assert.assertTrue(successfulCMDs.contains(expectedString), + failureMsg); + } + + @Test( + groups = + { "Functional" }, + dataProvider = "headlessModeOutputOperationsData") + public void testHeadlessModeOutputOperations(String harg, String type, + String fileName, boolean withAWT, int expectedMinFileSize, + int timeout) + { + String cmd = harg + type + " " + fileName; + // System.out.println(">>>>>>>>>>>>>>>> Command : " + cmd); + File file = new File(fileName); + file.deleteOnExit(); + Worker worker = getJalviewDesktopRunner(withAWT, cmd, timeout); + assertNotNull(worker, "worker is null"); + String msg = "Didn't create an output" + type + " file.[" + cmd + "]"; + assertTrue(file.exists(), msg); + FileAssert.assertFile(file, msg); + FileAssert.assertMinLength(file, expectedMinFileSize); + if (worker != null && worker.exit == null) + { + worker.interrupt(); + Thread.currentThread().interrupt(); + worker.process.destroy(); + Assert.fail("Jalview did not exit after " + type + + " generation (try running test again to verify - timeout at " + + timeout + "ms). [" + harg + "]"); + } + file.delete(); + } + + @DataProvider(name = "allInputOperationsData") + public Object[][] getHeadlessModeInputParams() + { + return new Object[][] { + // headless mode input operations + { "[TESTOUTPUT] arg --colour='zappo' was set", + "Failed setting arg --colour" }, + { "[TESTOUTPUT] arg --props='test/jalview/bin/testProps.jvprops' was set", + "Failed setting arg --props" }, + { "[TESTOUTPUT] arg --sortbytree was set", + "Failed setting arg --sortbytree" }, + { "[TESTOUTPUT] arg --jabaws='http://www.compbio.dundee.ac.uk/jabaws' was set", + "Failed setting arg --jabaws" }, + { "[TESTOUTPUT] arg --open='examples/uniref50.fa' was set", + "Failed setting arg --open" }, + { "[TESTOUTPUT] arg --features='examples/testdata/plantfdx.features' was set", + "Failed setting arg --features" }, + { "[TESTOUTPUT] arg --annotations='examples/testdata/plantfdx.annotations' was set", + "Failed setting arg --annotations" }, + { "[TESTOUTPUT] arg --tree='examples/testdata/uniref50_test_tree' was set", + "Failed setting arg --tree" }, + // non headless mode input operations + { "[TESTOUTPUT] arg --nousagestats was set", + "Failed setting arg --nousagestats" }, + { "[TESTOUTPUT] arg --noquestionnaire was set", + "Failed setting arg --noquestionnaire" } + // + }; + } + + @DataProvider(name = "headlessModeOutputOperationsData") + public static Object[][] getHeadlessModeOutputParams() + { + // JBPNote: I'm not clear why need to specify full path for output file + // when running tests on build server, but we will keep this patch for now + // since it works. + // https://issues.jalview.org/browse/JAL-1889?focusedCommentId=21609&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-21609 + String workingDir = "test/jalview/bin/"; + return new Object[][] { + // + { "--headless --open examples/uniref50.fa", " --image", + workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --image", + workingDir + "test_uniref50_out.eps", false, MINFILESIZE_BIG, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --image", + workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --image", + workingDir + "test_uniref50_out.eps", false, MINFILESIZE_BIG, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --image", + workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --image", + workingDir + "test_uniref50_out.svg", false, MINFILESIZE_BIG, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --image", + workingDir + "test_uniref50_out.png", true, MINFILESIZE_BIG, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --image", + workingDir + "test_uniref50_out.html", true, MINFILESIZE_BIG, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --output", + workingDir + "test_uniref50_out.mfa", true, MINFILESIZE_SMALL, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --output", + workingDir + "test_uniref50_out.aln", true, MINFILESIZE_SMALL, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --output", + workingDir + "test_uniref50_out.msf", true, MINFILESIZE_SMALL, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --output", + workingDir + "test_uniref50_out.aln", true, MINFILESIZE_SMALL, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --output", + workingDir + "test_uniref50_out.pir", true, MINFILESIZE_SMALL, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --output", + workingDir + "test_uniref50_out.pfam", true, MINFILESIZE_SMALL, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --output", + workingDir + "test_uniref50_out.blc", true, MINFILESIZE_SMALL, + TEST_TIMEOUT }, + { "--headless --open examples/uniref50.fa", " --output", + workingDir + "test_uniref50_out.jvp", true, MINFILESIZE_SMALL, + TEST_TIMEOUT }, + // + }; + } +} diff --git a/test/jalview/bin/CommandsTest2.java b/test/jalview/bin/CommandsTest2.java index dc7d061..40faccc 100644 --- a/test/jalview/bin/CommandsTest2.java +++ b/test/jalview/bin/CommandsTest2.java @@ -161,21 +161,21 @@ public class CommandsTest2 + "--colour=gecos-flower " + "--structure=[seqid=FER1_SPIOL]examples/AlphaFold/AF-P00221-F1-model_v4.cif " + "--paematrix=examples/AlphaFold/AF-P00221-F1-predicted_aligned_error_v4.json " - + "--noannotations " + + "--noshowannotations " + "--props=test/jalview/bin/commandsTest2.jvprops1 ", 15, 3, 1 }, { "--nonews --nosplash --debug " + "--append=examples/uniref50.fa " + "--colour=gecos-flower " + "--structure=[seqid=FER1_SPIOL]examples/AlphaFold/AF-P00221-F1-model_v4.cif " + "--paematrix=examples/AlphaFold/AF-P00221-F1-predicted_aligned_error_v4.json " - + "--noannotations " + "--nossannotations " + + "--noshowannotations " + "--nossannotations " + "--props=test/jalview/bin/commandsTest2.jvprops1 ", 15, 0, 1 }, { "--nonews --nosplash --debug " + "--append=examples/uniref50.fa " + "--colour=gecos-flower " + "--structure=[seqid=FER1_SPIOL]examples/AlphaFold/AF-P00221-F1-model_v4.cif " + "--paematrix=examples/AlphaFold/AF-P00221-F1-predicted_aligned_error_v4.json " - + "--noannotations " + "--nossannotations " + + "--noshowannotations " + "--nossannotations " + "--props=test/jalview/bin/commandsTest2.jvprops1 ", 15, 0, 1 }, { "--nonews --nosplash --debug --nowebservicediscovery --props=test/jalview/bin/commandsTest.jvprops --argfile=test/jalview/bin/commandsTest2.argfile1 ", diff --git a/test/jalview/bin/commandsTest2.argfile1 b/test/jalview/bin/commandsTest2.argfile1 index bb1a250..8a19038 100644 --- a/test/jalview/bin/commandsTest2.argfile1 +++ b/test/jalview/bin/commandsTest2.argfile1 @@ -1,6 +1,6 @@ --substitutions --append=examples/test_fab41.result/sample.a2m ---annotations +--showannotations --ssannotations --colour=gecos-flower --structure=[viewer=jmol,tempfac=plddt,paematrix={dirname}/test_fab41_unrelaxed_rank_1_model_3_scores.json]{dirname}/test_fab41_unrelaxed_rank_1_model_3.pdb diff --git a/test/jalview/bin/commandsTest2.argfile2 b/test/jalview/bin/commandsTest2.argfile2 index 30cecf7..ea7f0bc 100644 --- a/test/jalview/bin/commandsTest2.argfile2 +++ b/test/jalview/bin/commandsTest2.argfile2 @@ -3,7 +3,7 @@ --nosplash --substitutions --append=examples/test_fab41.result/sample.a2m ---noannotations +--noshowannotations --nossannotations --colour=gecos-flower --structure=[tempfac=plddt,paematrix={dirname}/test_fab41_unrelaxed_rank_1_model_3_scores.json]{dirname}/test_fab41_unrelaxed_rank_1_model_3.pdb @@ -16,7 +16,7 @@ --structureviewer=jmol --paematrix={dirname}/test_fab41_unrelaxed_rank_3_model_2_scores.json --tempfac=plddt ---structure=[viewer=none]{dirname}/test_fab41_unrelaxed_rank_4_model_5.pdb +--structure=[structureviewer=none]{dirname}/test_fab41_unrelaxed_rank_4_model_5.pdb --paematrix={dirname}/test_fab41_unrelaxed_rank_4_model_5_scores.json --tempfac=plddt --structure={dirname}/test_fab41_unrelaxed_rank_5_model_1.pdb -- 1.7.10.2