JAL-4353 bigger tighter test
[jalview.git] / test / jalview / bin / argparser / ArgParserTest.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.bin.argparser;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Properties;
30
31 import org.apache.logging.log4j.util.Strings;
32 import org.testng.Assert;
33 import org.testng.annotations.AfterClass;
34 import org.testng.annotations.AfterMethod;
35 import org.testng.annotations.DataProvider;
36 import org.testng.annotations.Test;
37
38 import jalview.bin.Cache;
39 import jalview.bin.Console;
40 import jalview.gui.Desktop;
41
42 @Test(singleThreaded = true)
43 public class ArgParserTest
44 {
45   @AfterClass(alwaysRun = true)
46   public static void resetProps()
47   {
48     Cache.loadProperties("test/jalview/testProps.jvprops");
49   }
50
51   @AfterMethod(alwaysRun = true)
52   public void tearDown()
53   {
54     if (Desktop.instance != null)
55       Desktop.instance.closeAll_actionPerformed(null);
56   }
57
58   @Test(groups = "Functional", dataProvider = "argLines")
59   public void parseArgsTest(String commandLineArgs, Arg a, String other)
60   {
61     String[] args = commandLineArgs.split("\\s+");
62     ArgParser argparser = new ArgParser(args);
63   }
64
65   @Test(groups = "Functional", dataProvider = "argSubValsAndLinkedIds")
66   public void parseSubValsAndLinkedIdsTest(String commandLineArgs,
67           String linkedId, Arg a, String subvalKey, String value,
68           boolean trueOrFalse)
69   {
70     String[] args = commandLineArgs.split("\\s+");
71     ArgParser argparser = new ArgParser(args);
72     ArgValuesMap avm = argparser.getLinkedArgs(linkedId);
73     ArgValue av = avm.getArgValue(a);
74     SubVals sv = av.getSubVals();
75     String testString = null;
76     if (subvalKey.equals("GETINDEX"))
77     {
78       testString = String.valueOf(sv.getIndex());
79     }
80     else
81     {
82       testString = sv.get(subvalKey);
83     }
84     if (trueOrFalse)
85     {
86       Assert.assertEquals(testString, value);
87     }
88     else
89     {
90       Assert.assertNotEquals(testString, value);
91     }
92   }
93
94   @Test(
95     groups = "Functional",
96     dataProvider = "argAutoIndexAndSubstitutions")
97   public void parseAutoIndexAndSubstitutionsTest(String commandLineArgs,
98           String linkedId, Arg a, String filename)
99   {
100     // { "--append=filename0 --new --append=filename1", "JALVIEW:1",
101     // Arg.OPEN, "filename1" },
102     String[] args = commandLineArgs.split("\\s+");
103     ArgParser argparser = new ArgParser(args);
104     ArgValuesMap avm = argparser.getLinkedArgs(linkedId);
105     ArgValue av = avm.getArgValue(a);
106     Assert.assertEquals(av.getValue(), filename);
107   }
108
109   @Test(groups = "Functional", dataProvider = "argLines")
110   public void bootstrapArgsTest(String commandLineArgs, Arg a, String other)
111   {
112     String[] args = commandLineArgs.split("\\s+");
113     BootstrapArgs b = BootstrapArgs.getBootstrapArgs(args);
114
115     Assert.assertTrue(b.contains(a));
116     if (a == Arg.PROPS)
117     {
118       Properties bP = Cache.bootstrapProperties(b.getValue(Arg.PROPS));
119       Assert.assertNotNull(bP);
120       Assert.assertTrue(other.equals(bP.get(Cache.BOOTSTRAP_TEST)));
121       Assert.assertFalse(bP.contains("NOT" + Cache.BOOTSTRAP_TEST));
122     }
123     else if (a == Arg.ARGFILE)
124     {
125       List<String> filenames = b.getValueList(a);
126       boolean found = false;
127       for (String s : filenames)
128       {
129         File f = new File(s);
130         File fo = new File(other);
131         try
132         {
133           if (fo.getCanonicalPath().equals(f.getCanonicalPath()))
134           {
135             found = true;
136             break;
137           }
138         } catch (IOException e)
139         {
140         }
141       }
142       Assert.assertTrue(found,
143               "File '" + other + "' not found in shell expanded glob '"
144                       + commandLineArgs + "'");
145     }
146   }
147
148   @Test(groups = "Functional", dataProvider = "argFiles")
149   public void argFilesTest(String commandLineArgs, Arg a, String other)
150   {
151     String[] args = commandLineArgs.split("\\s+");
152     BootstrapArgs b = BootstrapArgs.getBootstrapArgs(args);
153
154     Assert.assertTrue(b.contains(a));
155     Assert.assertFalse(b.contains(Arg.APPEND));
156     if (a == Arg.PROPS)
157     {
158       Properties bP = Cache.bootstrapProperties(b.getValue(Arg.PROPS));
159       Assert.assertTrue("true".equals(bP.get(Cache.BOOTSTRAP_TEST)));
160     }
161   }
162
163   @DataProvider(name = "argLinesNotworking")
164   public Object[][] argLinesTest()
165   {
166     return new Object[][] {
167         // can't use this one yet as it doesn't get shell glob expanded by the
168         // test
169         { "--argfile test/jalview/bin/argparser/testfiles/argfile*.txt",
170             Arg.ARGFILE,
171             "test/jalview/bin/argparser/testfiles/argfile0.txt" }, };
172   }
173
174   @DataProvider(name = "argLines")
175   public Object[][] argLines()
176   {
177     return new Object[][] { {
178         "--append=test/jalview/bin/argparser/testfiles/test1.fa --props=test/jalview/bin/argparser/testfiles/testProps.jvprops",
179         Arg.PROPS, "true" },
180         { "--debug --append=test/jalview/bin/argparser/testfiles/test1.fa",
181             Arg.DEBUG, null },
182         { "--append=test/jalview/bin/argparser/testfiles/test1.fa --headless",
183             Arg.HEADLESS, null },
184
185         { "--argfile test/jalview/bin/argparser/testfiles/argfile0.txt",
186             Arg.ARGFILE,
187             "test/jalview/bin/argparser/testfiles/argfile0.txt" },
188         // these next three are what a shell glob expansion would look like
189         { "--argfile test/jalview/bin/argparser/testfiles/argfile0.txt test/jalview/bin/argparser/testfiles/argfile1.txt test/jalview/bin/argparser/testfiles/argfile2.txt",
190             Arg.ARGFILE,
191             "test/jalview/bin/argparser/testfiles/argfile0.txt" },
192         { "--argfile test/jalview/bin/argparser/testfiles/argfile0.txt test/jalview/bin/argparser/testfiles/argfile1.txt test/jalview/bin/argparser/testfiles/argfile2.txt",
193             Arg.ARGFILE,
194             "test/jalview/bin/argparser/testfiles/argfile1.txt" },
195         { "--argfile test/jalview/bin/argparser/testfiles/argfile0.txt test/jalview/bin/argparser/testfiles/argfile1.txt test/jalview/bin/argparser/testfiles/argfile2.txt",
196             Arg.ARGFILE,
197             "test/jalview/bin/argparser/testfiles/argfile2.txt" },
198         { "--argfile=test/jalview/bin/argparser/testfiles/argfile*.txt",
199             Arg.ARGFILE,
200             "test/jalview/bin/argparser/testfiles/argfile0.txt" },
201         { "--argfile=test/jalview/bin/argparser/testfiles/argfile*.txt",
202             Arg.ARGFILE,
203             "test/jalview/bin/argparser/testfiles/argfile1.txt" },
204         { "--argfile=test/jalview/bin/argparser/testfiles/argfile*.txt",
205             Arg.ARGFILE,
206             "test/jalview/bin/argparser/testfiles/argfile2.txt" } };
207   }
208
209   @DataProvider(name = "argSubValsAndLinkedIds")
210   public Object[][] argSubValsAndLinkedIds()
211   {
212     return new Object[][] {
213         //
214         /*
215          */
216         { "--debug --append=[hi]test/jalview/bin/argparser/testfiles/test1.fa",
217             "JALVIEW:0", Arg.APPEND, "hi", "true", true },
218         { "--append[linkedId1]=[new,hello=world,1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
219             "linkedId1", Arg.APPEND, "new", "true", true },
220         { "--append[linkedId2]=[new,hello=world,1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
221             "linkedId2", Arg.APPEND, "hello", "world", true },
222         { "--append[linkedId3]=[new,hello=world,1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
223             "linkedId3", Arg.APPEND, "GETINDEX", "1", true },
224         { "--append[linkedId4]=[new,hello=world,1]test/jalview/bin/argparser/testfiles/test1.fa --append[linkedId5]=[notnew;hello=world;1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
225             "linkedId5", Arg.APPEND, "new", "true", false },
226         { "--append[linkedId5]=[new,hello=worlddomination,1]test/jalview/bin/argparser/testfiles/test1.fa --append[linkedId2]=[new;hello=world;1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
227             "linkedId5", Arg.APPEND, "hello", "world", false },
228         { "--append[linkedId6]=[new,hello=world,0]test/jalview/bin/argparser/testfiles/test1.fa --append[linkedId7]=[new;hello=world;1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
229             "linkedId7", Arg.APPEND, "GETINDEX", "0", false },
230         /*
231          */
232         //
233     };
234   }
235
236   @DataProvider(name = "argAutoIndexAndSubstitutions")
237   public Object[][] argAutoIndexAndSubstitutions()
238   {
239     return new Object[][] {
240         //
241         /*
242          */
243         { "--append=filename0 --append=filename1", "JALVIEW:0", Arg.APPEND,
244             "filename0" },
245         { "--append=filename0 --new --append=filename1", "JALVIEW:1",
246             Arg.APPEND, "filename1" },
247         { "--append=filename0 --new --new --append=filename2", "JALVIEW:0",
248             Arg.APPEND, "filename0" },
249         { "--append=filename0 --new --new --append=filename2", "JALVIEW:2",
250             Arg.APPEND, "filename2" },
251         { "--append[linkA-{n}]=filenameA0 --append[linkA-{++n}]=filenameA1",
252             "linkA-0", Arg.APPEND, "filenameA0" },
253         { "--append[linkB-{n}]=filenameB0 --append[linkB-{++n}]=filenameB1",
254             "linkB-1", Arg.APPEND, "filenameB1" },
255         { "--append[linkC-{n}]=filenameC0 --image[linkC-{n}]=outputC{n}.txt",
256             "linkC-0", Arg.IMAGE, "outputC{n}.txt" },
257         { "--append[linkD-{n}]=filenameD0 --substitutions --image[linkD-{n}]=outputD{n}.txt",
258             "linkD-0", Arg.IMAGE, "outputD0.txt" },
259         { "--append[linkE-{n}]=filenameE0 --substitutions --image[linkE-{n}]=output-E{n}.txt --nil[{++n}] --image[linkE-{n}]=outputE{n}.txt",
260             "linkE-0", Arg.IMAGE, "output-E0.txt" },
261         { "--append[linkF-{n}]=filenameF0 --substitutions --image[linkF-{n}]=output-F{n}.txt --nil[{++n}] --image[linkF-{n}]=outputF{n}.txt",
262             "linkF-1", Arg.IMAGE, "outputF1.txt" },
263         { "--append[linkG-{n}]=filenameG0 --substitutions --image[linkG-{n}]=output-G{n}.txt --nil[{++n}] --nosubstitutions --image[linkG-{n}]=outputG{n}.txt",
264             "linkG-1", Arg.IMAGE, "outputG{n}.txt" },
265         { "--append[linkH-{n}]=filenameH0 --substitutions --image[linkH-{n}]=output-H{n}.txt --nil[{++n}] --nosubstitutions --image[linkH-{n}]=outputH{n}.txt",
266             "linkH-0", Arg.IMAGE, "output-H0.txt" },
267         { "--open=filename0 --append=filename1", "JALVIEW:0", Arg.OPEN,
268             "filename0" },
269         { "--open=filename0 --new --append=filename1", "JALVIEW:1",
270             Arg.APPEND, "filename1" },
271         { "--open=filename0 --new --new --append=filename2", "JALVIEW:0",
272             Arg.OPEN, "filename0" },
273         { "--open=filename0 --new --new --append=filename2", "JALVIEW:2",
274             Arg.APPEND, "filename2" },
275         { "--open[linkA-{n}]=filenameA0 --append[linkA-{++n}]=filenameA1",
276             "linkA-0", Arg.OPEN, "filenameA0" },
277         { "--open[linkB-{n}]=filenameB0 --append[linkB-{++n}]=filenameB1",
278             "linkB-1", Arg.APPEND, "filenameB1" },
279         { "--open[linkC-{n}]=filenameC0 --image[linkC-{n}]=outputC{n}.txt",
280             "linkC-0", Arg.IMAGE, "outputC{n}.txt" },
281         { "--open[linkD-{n}]=filenameD0 --substitutions --image[linkD-{n}]=outputD{n}.txt",
282             "linkD-0", Arg.IMAGE, "outputD0.txt" },
283         { "--open[linkE-{n}]=filenameE0 --substitutions --image[linkE-{n}]=output-E{n}.txt --nil[{++n}] --image[linkE-{n}]=outputE{n}.txt",
284             "linkE-0", Arg.IMAGE, "output-E0.txt" },
285         { "--open[linkF-{n}]=filenameF0 --substitutions --image[linkF-{n}]=output-F{n}.txt --nil[{++n}] --image[linkF-{n}]=outputF{n}.txt",
286             "linkF-1", Arg.IMAGE, "outputF1.txt" },
287         { "--open[linkG-{n}]=filenameG0 --substitutions --image[linkG-{n}]=output-G{n}.txt --nil[{++n}] --nosubstitutions --image[linkG-{n}]=outputG{n}.txt",
288             "linkG-1", Arg.IMAGE, "outputG{n}.txt" },
289         { "--open[linkH-{n}]=filenameH0 --substitutions --image[linkH-{n}]=output-H{n}.txt --nil[{++n}] --nosubstitutions --image[linkH-{n}]=outputH{n}.txt",
290             "linkH-0", Arg.IMAGE, "output-H0.txt" },
291         /*
292          */
293
294         //
295     };
296   }
297
298   @DataProvider(name = "argFiles")
299   public Object[][] argFiles()
300   {
301     return new Object[][] { {
302         "--argfile=test/jalview/bin/argparser/testfiles/argfile0.txt --open=shouldntbeabootstrap",
303         Arg.ARGFILE, "test/jalview/bin/argfiles/testfiles/test1.fa" } };
304   }
305
306   @Test(groups = "Functional", dataProvider = "allLinkedIdsData")
307   public void allLinkedIdsTest(String pwd, String commandLineArgs, Arg a,
308           String[] values, String[] nonvalues)
309   {
310     String userDir = System.getProperty("user.dir");
311     if (pwd != null)
312     {
313       File pwdFile = new File(pwd);
314       System.setProperty("user.dir", pwdFile.getAbsolutePath());
315     }
316     String[] args = commandLineArgs.split("\\s+");
317     ArgParser argparser = new ArgParser(args);
318
319     int num = values.length;
320     List<String> linkedIds = argparser.getLinkedIds();
321     Assert.assertEquals(linkedIds.size(), num,
322             "Wrong number of linkedIds: " + linkedIds.toString());
323     for (int i = 0; i < num; i++)
324     {
325       String value = values[i];
326       String linkedId = linkedIds.get(i);
327       ArgValuesMap avm = argparser.getLinkedArgs(linkedId);
328       if (value == null)
329       {
330         Assert.assertTrue(avm.containsArg(a),
331                 "Arg value for " + a.argString()
332                         + " not applied correctly to linkedId '" + linkedId
333                         + "'");
334       }
335       else
336       {
337         ArgValues avs = avm.getArgValues(a);
338         ArgValue av = avs.getArgValue();
339         String v = av.getValue();
340         value = new File(value).getPath();
341         Assert.assertEquals(v, value, "Arg value for " + a.argString()
342                 + " not applied correctly to linkedId '" + linkedId + "'");
343       }
344     }
345
346     System.setProperty("user.dir", userDir);
347   }
348
349   @DataProvider(name = "allLinkedIdsData")
350   public Object[][] allLinkedIdsData()
351   {
352     return new Object[][] {
353         //
354         /*
355         */
356         { null,
357             "--open=test/jalview/bin/argparser/testfiles/*.fa --substitutions --all --image={dirname}/{basename}.png --close",
358             Arg.CLOSE, new String[]
359             { null, null, null },
360             null },
361         { null,
362             "--open=test/jalview/bin/argparser/testfiles/*.fa --substitutions --all --output={dirname}/{basename}.stk --close",
363             Arg.OUTPUT, new String[]
364             { "test/jalview/bin/argparser/testfiles/test1.stk",
365                 "test/jalview/bin/argparser/testfiles/test2.stk",
366                 "test/jalview/bin/argparser/testfiles/test3.stk", },
367             null },
368         { null,
369             "--open=test/jalview/bin/argparser/testfiles/*.fa --substitutions --all --image={dirname}/{basename}.png --close",
370             Arg.IMAGE, new String[]
371             { "test/jalview/bin/argparser/testfiles/test1.png",
372                 "test/jalview/bin/argparser/testfiles/test2.png",
373                 "test/jalview/bin/argparser/testfiles/test3.png", },
374             null },
375         /*
376          * Find a way to change pwd reliably -- need to match "*.fa" against some files!
377          { "test/jalview/bin/argparser/testfiles",
378          
379             "--open=*.fa --image={dirname}/{basename}.png --close",
380             Arg.IMAGE, new String[]
381             { "./test1.png", "./test2.png", "./test3.png", }, null },
382             */
383         //
384     };
385   }
386
387   @Test(groups = "Functional", dataProvider = "bootstrapArgsData")
388   public void bootstrapArgsValuesAndHeadlessModeTest(String commandLineArgs,
389           Arg a, String valS, boolean valB, boolean headlessValue)
390   {
391     String[] args = commandLineArgs.split("\\s+");
392     BootstrapArgs bsa = BootstrapArgs.getBootstrapArgs(args);
393     if (a != null)
394     {
395       if (valS != null)
396       {
397         Assert.assertEquals(bsa.getValue(a), valS,
398                 "BootstrapArg " + a.argString()
399                         + " value does not match expected '" + valS + "'");
400       }
401       else
402       {
403         Assert.assertEquals(bsa.getBoolean(a), valB,
404                 "Boolean/Unary value of BootstrapArg " + a.argString()
405                         + "' is not the expected '" + valB + "'");
406       }
407     }
408
409     boolean isHeadless = bsa.isHeadless();
410     Assert.assertEquals(isHeadless, headlessValue,
411             "Assumed headless setting '" + isHeadless + "' is wrong.");
412   }
413
414   @DataProvider(name = "bootstrapArgsData")
415   public Object[][] bootstrapArgsData()
416   {
417     return new Object[][] {
418         /*
419          * cmdline args
420          * Arg (null if only testing headless)
421          * String value if there is one (null otherwise)
422          * boolean value if String value is null
423          * expected value of isHeadless()
424          */
425         /*
426         */
427         { "--open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
428             Arg.JABAWS, "https://forwardsandbackwards.com/", false, true },
429         { "--help-all --open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
430             Arg.HELP, null, true, true },
431         { "--help-all --nonews --open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
432             Arg.NEWS, null, false, true },
433         { "--help --nonews --open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
434             Arg.NEWS, null, false, true },
435         { "--help-opening --nonews --open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
436             Arg.NEWS, null, false, true },
437         { "--nonews --open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
438             Arg.NEWS, null, false, true },
439         { "--open thisway.fa --image thatway.png", null, null, false,
440             true },
441         { "--open thisway.fa --output thatway.png", null, null, false,
442             true },
443         { "--open thisway.fa --image thatway.png --noheadless", null, null,
444             false, false },
445         { "--open thisway.fa --output thatway.png --noheadless", null, null,
446             false, false },
447         { "--open thisway.fa --image thatway.png --gui", null, null, false,
448             false },
449         { "--open thisway.fa --output thatway.png --gui", null, null, false,
450             false },
451         // --gui takes precedence
452         { "--open thisway.fa --image thatway.png --gui --headless", null,
453             null, false, false },
454         { "--open thisway.fa --output thatway.png --gui --headless", null,
455             null, false, false },
456         //
457     };
458   }
459
460   @Test(groups = "Functional", dataProvider = "argsWithinTypesData")
461   public void checkArgsWithinTypesTest(String commandLineArgs,
462           Object[] stuff)
463   {
464     String linkedId = "JALVIEW:0";
465     String[] args = commandLineArgs.split("\\s+");
466     ArgParser argparser = new ArgParser(args);
467     ArgValuesMap avm = argparser.getLinkedArgs(linkedId);
468
469     ArgAndValues avals = (ArgAndValues) stuff[0];
470
471     Object[] moreStuff = (Object[]) stuff[1];
472
473     ArgAndValues[] subAv = (ArgAndValues[]) moreStuff[0];
474
475     Object[] secondaryArgAndAuxStuff = (Object[]) stuff[2];
476
477     // Now look at the argparser
478
479     Arg primaryArg = avals.arg;
480
481     List<ArgValue> parsed_ArgValues = avm.getArgValueList(primaryArg);
482
483     Assert.assertTrue(
484             areEqualSets(avals.values,
485                     ArgValuesMap.toValues(parsed_ArgValues)),
486             "Primary arg (" + primaryArg.toString()
487                     + ") does not have the expected values. Expected "
488                     + avals.values + " and got "
489                     + ArgValuesMap.toValues(parsed_ArgValues));
490
491     for (int i = 0; i < parsed_ArgValues.size(); i++)
492     {
493       ArgValue parsed_ArgValue = parsed_ArgValues.get(i);
494       String value = avals.values.get(i);
495
496       Console.debug("- primary arg '" + primaryArg + "' = '" + value + "'");
497
498       Assert.assertEquals(parsed_ArgValue.getValue(), value,
499               "Primary arg value not as expected");
500
501       ArgAndValues[] aux_avals = (ArgAndValues[]) moreStuff[i];
502
503       for (ArgAndValues aux_aval : aux_avals)
504       {
505         Arg auxArg = aux_aval.arg;
506         List<String> auxValues = aux_aval.values;
507
508         String parsed_auxValue = avm.getFromSubValArgOrPref(auxArg,
509                 ArgValuesMap.Position.AFTER, parsed_ArgValue, null, null,
510                 null, null);
511
512         if (auxValues.isEmpty())
513         {
514           Assert.assertTrue(parsed_auxValue == null,
515                   "Not expecting to parse a value for '" + auxArg
516                           + "' but found '" + parsed_auxValue + "'");
517         }
518
519         for (String auxValue : auxValues)
520         {
521
522           Console.debug("- + primary aux arg '" + auxArg + "' = '"
523                   + auxValue + "'");
524
525           Assert.assertEquals(parsed_auxValue, auxValue,
526                   "Primary auxiliary arg (" + auxArg.toString()
527                           + ") values do not match");
528
529         }
530       }
531
532       // Now for the secondary args
533       Object[] secondaryStuff = (Object[]) secondaryArgAndAuxStuff[i];
534       ArgAndValues secondaryArgAndValues = (ArgAndValues) secondaryStuff[0];
535       Arg secondaryArg = secondaryArgAndValues.arg;
536       List<String> secondaryValues = secondaryArgAndValues.values;
537
538       List<ArgValue> parsed_secondaryArgValues = avm
539               .getArgValueListFromSubValOrArg(parsed_ArgValue, secondaryArg,
540                       null);
541
542       Assert.assertTrue(
543               areEqualSets(secondaryValues,
544                       ArgValuesMap.toValues(parsed_secondaryArgValues)),
545               "Secondary arg (" + secondaryArg.toString()
546                       + ") does not have the expected values");
547
548       Object[] secondaryMoreStuff = (Object[]) secondaryStuff[1];
549
550       for (int j = 0; j < parsed_secondaryArgValues.size(); j++)
551       {
552         ArgValue parsed_secondaryArgValue = parsed_secondaryArgValues
553                 .get(j);
554         String secondary_value = secondaryValues.get(j);
555
556         Console.debug("-- secondary arg '" + secondaryArg + "' = '"
557                 + secondary_value + "'");
558
559         Assert.assertEquals(parsed_secondaryArgValue.getValue(),
560                 secondary_value, "Secondary arg value not as expected");
561
562         ArgAndValues[] secondary_aux_avals = (ArgAndValues[]) secondaryMoreStuff[j];
563
564         for (ArgAndValues secondary_aux_aval : secondary_aux_avals)
565         {
566           Arg secondary_auxArg = secondary_aux_aval.arg;
567           List<String> secondary_auxValues = secondary_aux_aval.values;
568
569           String parsed_secondary_auxValue = avm.getValueFromSubValOrArg(
570                   parsed_secondaryArgValue, secondary_auxArg, null);
571
572           if (secondary_auxValues.isEmpty())
573           {
574             Assert.assertTrue(parsed_secondary_auxValue == null,
575                     "Not expecting to parse a value for '"
576                             + secondary_auxArg + "' but found '"
577                             + parsed_secondary_auxValue + "'");
578           }
579
580           for (String secondary_auxValue : secondary_auxValues)
581           {
582             Console.debug("-- + secondary aux arg '" + secondary_auxArg
583                     + "' for value '" + secondary_auxValue + "'");
584
585             Assert.assertEquals(parsed_secondary_auxValue,
586                     secondary_auxValue,
587                     "Secondary auxiliary arg ("
588                             + secondary_auxArg.toString()
589                             + ") values do not match");
590           }
591         }
592       }
593     }
594   }
595
596   @DataProvider(name = "argsWithinTypesData")
597   public Object[][] argsWithinTypesData()
598   {
599     return new Object[][] {
600         /*
601          * { cmdline args },
602          * {
603          *   { Primary Arg, Secondary Args },
604          *   { { Secondary Arg => { Values } } }
605          *  },
606          */
607         /*
608         */
609         { //
610           // commandLineArgs
611             "--open=alignment.fa --structure=structure0.pdb"
612                     // structureimage0a and structureimage args
613                     + " --structureimage=image0a.png --bgcolour=bg0a --imagecolour=col0a"
614                     // structureimage0b and structureimage args
615                     + " --structureimage=image0b.png --bgcolour=bg0b --imagecolour=col0b"
616                     // more structure args
617                     + " --structureviewer=sv0 --paematrix=pae0" //
618                     // structure1
619                     + " --structure=structure1.pdb"
620                     // structure args
621                     + " --structureviewer=sv1 --paematrix=pae1"
622                     // structureimage1a with NO structureimage args
623                     // (see `--all --bgcolour=pineapple` later)
624                     + " --structureimage=image1a.png"
625                     // structureimage1b and structureimage args
626                     + " --structureimage=image1b.png --bgcolour=bg1b --imagecolour=col1b"
627                     // --all args, should apply to structureimage1a only
628                     + " --all --bgcolour=pineapple" //
629             ,
630             // stuff
631             new Object[]
632             {
633                 // avals (0) and (1)
634                 av(Arg.STRUCTURE, "structure0.pdb", "structure1.pdb"),
635                 // moreStuff (0) and (1)
636                 new ArgAndValues[][]
637                 { //
638                     { av(Arg.STRUCTUREVIEWER, "sv0"),
639                         av(Arg.PAEMATRIX, "pae0") },
640                     { av(Arg.STRUCTUREVIEWER, "sv1"),
641                         av(Arg.PAEMATRIX, "pae1") } //
642                 },
643                 // secondaryArgAndAuxStuff
644                 // (same size as values of avals)
645                 new Object[][]
646                 {
647                     // secondaryStuff (0)
648                     {
649                         // secondaryArgAndValues (a) and (b)
650                         av(Arg.STRUCTUREIMAGE, "image0a.png",
651                                 "image0b.png"),
652                         // secondaryMoreStuff
653                         // (same size as values of secondaryArgAndValues)
654                         new ArgAndValues[][]
655                         {
656                             // secondary_aux_avals (a)
657                             { av(Arg.BGCOLOUR, "bg0a"),
658                                 av(Arg.IMAGECOLOUR, "col0a") },
659                             // secondary_aux_avals (b)
660                             { av(Arg.BGCOLOUR, "bg0b"),
661                                 av(Arg.IMAGECOLOUR, "col0b") }, //
662                         }, //
663                     },
664                     // secondaryStuff (1)
665                     {
666                         // secondaryArgAndValues (a) and (b)
667                         av(Arg.STRUCTUREIMAGE, "image1a.png",
668                                 "image1b.png"),
669                         // secondaryMoreStuff
670                         new ArgAndValues[][]
671                         {
672                             // secondary_aux_avals (a)
673                             { av(Arg.BGCOLOUR, "pineapple"),
674                                 av(Arg.IMAGECOLOUR) },
675                             // secondary_aux_avals (b)
676                             { av(Arg.BGCOLOUR, "bg1b"),
677                                 av(Arg.IMAGECOLOUR, "col1b") }, //
678                         }, //
679                     }, //
680                 } //
681             } //
682         }, //
683         { //
684             "--open=alignment.fa --wrap --colour=gecose-flower"
685                     // structure0
686                     + " --structure=structure0.pdb"
687                     + " --structureimage=image0a.png --bgcolour=bg0a --scale=3"
688                     + " --structureimage=image0b.png --imagecolour=col0b --scale=4"
689                     + " --structureviewer=sv0 --paematrix=pae0" //
690                     + " --structureimage=image0c.png"
691                     // structure1
692                     + " --structure=structure1.pdb"
693                     + " --structureviewer=sv1 --paematrix=pae1"
694                     + " --structureimage=image1a.png"
695                     + " --structureimage=image1b.png --bgcolour=bg1b --imagecolour=col1b"
696                     + " --structureimage=image1c.png --bgcolour=bg1c --imagecolour=col1c --scale=5"
697                     + " --structureimage=image1d.png --imagecolour=col1d --scale=6"
698                     + " --structureimage=image1e.png --bgcolour=bg1e"
699                     // structure2
700                     + " --structure=structure2.pdb"
701                     + " --structureimage=image2a.png --bgcolour=bg2a --scale=23"
702                     + " --all --bgcolour=pineapple --imagecolour=banana --scale=2" //
703             ,
704             // stuff
705             new Object[]
706             { av(Arg.STRUCTURE, "structure0.pdb", "structure1.pdb",
707                     "structure2.pdb"),
708                 new ArgAndValues[][]
709                 { //
710                     { av(Arg.STRUCTUREVIEWER, "sv0"),
711                         av(Arg.PAEMATRIX, "pae0") },
712                     { av(Arg.STRUCTUREVIEWER, "sv1"),
713                         av(Arg.PAEMATRIX, "pae1") },
714                     { av(Arg.STRUCTUREVIEWER), av(Arg.PAEMATRIX) } //
715                 }, //
716                 new Object[][]
717                 { //
718                     { //
719                         av(Arg.STRUCTUREIMAGE, "image0a.png", "image0b.png",
720                                 "image0c.png"), //
721                         new ArgAndValues[][]
722                         { //
723                             { av(Arg.BGCOLOUR, "bg0a"),
724                                 av(Arg.IMAGECOLOUR, "banana"),
725                                 av(Arg.SCALE, "3") },
726                             { av(Arg.BGCOLOUR, "pineapple"),
727                                 av(Arg.IMAGECOLOUR, "col0b"),
728                                 av(Arg.SCALE, "4") },
729                             { av(Arg.BGCOLOUR, "pineapple"),
730                                 av(Arg.IMAGECOLOUR, "banana"),
731                                 av(Arg.SCALE, "2") }, //
732                         }, //
733                     }, //
734                     { //
735                         av(Arg.STRUCTUREIMAGE, "image1a.png", "image1b.png",
736                                 "image1c.png", "image1d.png",
737                                 "image1e.png"),
738                         new ArgAndValues[][]
739                         { //
740                             { av(Arg.BGCOLOUR, "pineapple"),
741                                 av(Arg.IMAGECOLOUR, "banana"),
742                                 av(Arg.SCALE, "2") },
743                             { av(Arg.BGCOLOUR, "bg1b"),
744                                 av(Arg.IMAGECOLOUR, "col1b"),
745                                 av(Arg.SCALE, "2") },
746                             { av(Arg.BGCOLOUR, "bg1c"),
747                                 av(Arg.IMAGECOLOUR, "col1c"),
748                                 av(Arg.SCALE, "5") },
749                             { av(Arg.BGCOLOUR, "pineapple"),
750                                 av(Arg.IMAGECOLOUR, "col1d"),
751                                 av(Arg.SCALE, "6") },
752                             { av(Arg.BGCOLOUR, "bg1e"),
753                                 av(Arg.IMAGECOLOUR, "banana"),
754                                 av(Arg.SCALE, "2") } //
755                         }, //
756                     }, //
757                     { //
758                         av(Arg.STRUCTUREIMAGE, "image2a.png"),
759                         new ArgAndValues[][]
760                         { //
761                             { av(Arg.BGCOLOUR, "bg2a"),
762                                 av(Arg.IMAGECOLOUR, "banana"),
763                                 av(Arg.SCALE, "23") }, //
764                         }, //
765                     }, //
766                 } //
767             } //
768         } //
769     };
770   }
771
772   protected ArgAndValues av(Arg a, String... vals)
773   {
774     return new ArgAndValues(a, vals);
775   }
776
777   protected class ArgAndValues
778   {
779     protected Arg arg;
780
781     protected List<String> values;
782
783     ArgAndValues(Arg a, String... vals)
784     {
785       arg = a;
786       values = vals == null ? new ArrayList<String>() : Arrays.asList(vals);
787     }
788
789     @Override
790     public String toString()
791     {
792       StringBuilder sb = new StringBuilder();
793       sb.append(arg.argString());
794       sb.append("\n");
795       sb.append(Strings.join(values, ','));
796       sb.append("\n");
797       return sb.toString();
798     }
799   }
800
801   private static boolean areEqualSets(String[] strArray,
802           List<String> strList)
803   {
804     return areEqualSets(Arrays.asList(strArray), strList);
805   }
806
807   private static boolean areEqualSets(List<String> l1, List<String> l2)
808   {
809     if (l1 == null && l2 == null)
810     {
811       Console.info(
812               "Comparing null lists, should be okay but you might want to know");
813       return true;
814     }
815     if (l1 == null || l2 == null)
816     {
817       return false;
818     }
819     return new HashSet<String>(l1).equals(new HashSet<String>(l2));
820   }
821
822 }