JAL-4353 bigger tighter test
[jalview.git] / test / jalview / bin / argparser / ArgParserTest.java
index 17f19b7..89c034a 100644 (file)
@@ -1,10 +1,34 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
 package jalview.bin.argparser;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Properties;
 
+import org.apache.logging.log4j.util.Strings;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.AfterMethod;
@@ -12,6 +36,7 @@ import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import jalview.bin.Cache;
+import jalview.bin.Console;
 import jalview.gui.Desktop;
 
 @Test(singleThreaded = true)
@@ -90,14 +115,14 @@ public class ArgParserTest
     Assert.assertTrue(b.contains(a));
     if (a == Arg.PROPS)
     {
-      Properties bP = Cache.bootstrapProperties(b.get(Arg.PROPS));
+      Properties bP = Cache.bootstrapProperties(b.getValue(Arg.PROPS));
       Assert.assertNotNull(bP);
       Assert.assertTrue(other.equals(bP.get(Cache.BOOTSTRAP_TEST)));
       Assert.assertFalse(bP.contains("NOT" + Cache.BOOTSTRAP_TEST));
     }
     else if (a == Arg.ARGFILE)
     {
-      List<String> filenames = b.getList(a);
+      List<String> filenames = b.getValueList(a);
       boolean found = false;
       for (String s : filenames)
       {
@@ -130,7 +155,7 @@ public class ArgParserTest
     Assert.assertFalse(b.contains(Arg.APPEND));
     if (a == Arg.PROPS)
     {
-      Properties bP = Cache.bootstrapProperties(b.get(Arg.PROPS));
+      Properties bP = Cache.bootstrapProperties(b.getValue(Arg.PROPS));
       Assert.assertTrue("true".equals(bP.get(Cache.BOOTSTRAP_TEST)));
     }
   }
@@ -184,9 +209,12 @@ public class ArgParserTest
   @DataProvider(name = "argSubValsAndLinkedIds")
   public Object[][] argSubValsAndLinkedIds()
   {
-    return new Object[][] { {
-        "--debug --append=[hi]test/jalview/bin/argparser/testfiles/test1.fa",
-        "JALVIEW:0", Arg.APPEND, "hi", "true", true },
+    return new Object[][] {
+        //
+        /*
+         */
+        { "--debug --append=[hi]test/jalview/bin/argparser/testfiles/test1.fa",
+            "JALVIEW:0", Arg.APPEND, "hi", "true", true },
         { "--append[linkedId1]=[new,hello=world,1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
             "linkedId1", Arg.APPEND, "new", "true", true },
         { "--append[linkedId2]=[new,hello=world,1]test/jalview/bin/argparser/testfiles/test1.fa --headless",
@@ -198,7 +226,11 @@ public class ArgParserTest
         { "--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",
             "linkedId5", Arg.APPEND, "hello", "world", false },
         { "--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",
-            "linkedId7", Arg.APPEND, "GETINDEX", "0", false }, };
+            "linkedId7", Arg.APPEND, "GETINDEX", "0", false },
+        /*
+         */
+        //
+    };
   }
 
   @DataProvider(name = "argAutoIndexAndSubstitutions")
@@ -272,9 +304,15 @@ public class ArgParserTest
   }
 
   @Test(groups = "Functional", dataProvider = "allLinkedIdsData")
-  public void allLinkedIdsTest(String commandLineArgs, Arg a,
-          String[] values)
+  public void allLinkedIdsTest(String pwd, String commandLineArgs, Arg a,
+          String[] values, String[] nonvalues)
   {
+    String userDir = System.getProperty("user.dir");
+    if (pwd != null)
+    {
+      File pwdFile = new File(pwd);
+      System.setProperty("user.dir", pwdFile.getAbsolutePath());
+    }
     String[] args = commandLineArgs.split("\\s+");
     ArgParser argparser = new ArgParser(args);
 
@@ -299,12 +337,13 @@ public class ArgParserTest
         ArgValues avs = avm.getArgValues(a);
         ArgValue av = avs.getArgValue();
         String v = av.getValue();
-        value = new File(value).getAbsolutePath();
+        value = new File(value).getPath();
         Assert.assertEquals(v, value, "Arg value for " + a.argString()
                 + " not applied correctly to linkedId '" + linkedId + "'");
       }
     }
 
+    System.setProperty("user.dir", userDir);
   }
 
   @DataProvider(name = "allLinkedIdsData")
@@ -314,21 +353,470 @@ public class ArgParserTest
         //
         /*
         */
-        { "--open=test/jalview/bin/argparser/testfiles/*.fa --substitutions --all --image={dirname}/{basename}.png --close",
+        { null,
+            "--open=test/jalview/bin/argparser/testfiles/*.fa --substitutions --all --image={dirname}/{basename}.png --close",
             Arg.CLOSE, new String[]
-            { null, null,
-                null } },
-        { "--open=test/jalview/bin/argparser/testfiles/*.fa --substitutions --all --output={dirname}/{basename}.stk --close",
+            { null, null, null },
+            null },
+        { null,
+            "--open=test/jalview/bin/argparser/testfiles/*.fa --substitutions --all --output={dirname}/{basename}.stk --close",
             Arg.OUTPUT, new String[]
             { "test/jalview/bin/argparser/testfiles/test1.stk",
                 "test/jalview/bin/argparser/testfiles/test2.stk",
-                "test/jalview/bin/argparser/testfiles/test3.stk", } },
-        { "--open=test/jalview/bin/argparser/testfiles/*.fa --substitutions --all --image={dirname}/{basename}.png --close",
+                "test/jalview/bin/argparser/testfiles/test3.stk", },
+            null },
+        { null,
+            "--open=test/jalview/bin/argparser/testfiles/*.fa --substitutions --all --image={dirname}/{basename}.png --close",
             Arg.IMAGE, new String[]
             { "test/jalview/bin/argparser/testfiles/test1.png",
                 "test/jalview/bin/argparser/testfiles/test2.png",
-                "test/jalview/bin/argparser/testfiles/test3.png", } },
+                "test/jalview/bin/argparser/testfiles/test3.png", },
+            null },
+        /*
+         * Find a way to change pwd reliably -- need to match "*.fa" against some files!
+         { "test/jalview/bin/argparser/testfiles",
+         
+            "--open=*.fa --image={dirname}/{basename}.png --close",
+            Arg.IMAGE, new String[]
+            { "./test1.png", "./test2.png", "./test3.png", }, null },
+            */
         //
     };
   }
-}
+
+  @Test(groups = "Functional", dataProvider = "bootstrapArgsData")
+  public void bootstrapArgsValuesAndHeadlessModeTest(String commandLineArgs,
+          Arg a, String valS, boolean valB, boolean headlessValue)
+  {
+    String[] args = commandLineArgs.split("\\s+");
+    BootstrapArgs bsa = BootstrapArgs.getBootstrapArgs(args);
+    if (a != null)
+    {
+      if (valS != null)
+      {
+        Assert.assertEquals(bsa.getValue(a), valS,
+                "BootstrapArg " + a.argString()
+                        + " value does not match expected '" + valS + "'");
+      }
+      else
+      {
+        Assert.assertEquals(bsa.getBoolean(a), valB,
+                "Boolean/Unary value of BootstrapArg " + a.argString()
+                        + "' is not the expected '" + valB + "'");
+      }
+    }
+
+    boolean isHeadless = bsa.isHeadless();
+    Assert.assertEquals(isHeadless, headlessValue,
+            "Assumed headless setting '" + isHeadless + "' is wrong.");
+  }
+
+  @DataProvider(name = "bootstrapArgsData")
+  public Object[][] bootstrapArgsData()
+  {
+    return new Object[][] {
+        /*
+         * cmdline args
+         * Arg (null if only testing headless)
+         * String value if there is one (null otherwise)
+         * boolean value if String value is null
+         * expected value of isHeadless()
+         */
+        /*
+        */
+        { "--open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
+            Arg.JABAWS, "https://forwardsandbackwards.com/", false, true },
+        { "--help-all --open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
+            Arg.HELP, null, true, true },
+        { "--help-all --nonews --open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
+            Arg.NEWS, null, false, true },
+        { "--help --nonews --open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
+            Arg.NEWS, null, false, true },
+        { "--help-opening --nonews --open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
+            Arg.NEWS, null, false, true },
+        { "--nonews --open thisway.fa --output thatway.fa --jabaws https://forwardsandbackwards.com/",
+            Arg.NEWS, null, false, true },
+        { "--open thisway.fa --image thatway.png", null, null, false,
+            true },
+        { "--open thisway.fa --output thatway.png", null, null, false,
+            true },
+        { "--open thisway.fa --image thatway.png --noheadless", null, null,
+            false, false },
+        { "--open thisway.fa --output thatway.png --noheadless", null, null,
+            false, false },
+        { "--open thisway.fa --image thatway.png --gui", null, null, false,
+            false },
+        { "--open thisway.fa --output thatway.png --gui", null, null, false,
+            false },
+        // --gui takes precedence
+        { "--open thisway.fa --image thatway.png --gui --headless", null,
+            null, false, false },
+        { "--open thisway.fa --output thatway.png --gui --headless", null,
+            null, false, false },
+        //
+    };
+  }
+
+  @Test(groups = "Functional", dataProvider = "argsWithinTypesData")
+  public void checkArgsWithinTypesTest(String commandLineArgs,
+          Object[] stuff)
+  {
+    String linkedId = "JALVIEW:0";
+    String[] args = commandLineArgs.split("\\s+");
+    ArgParser argparser = new ArgParser(args);
+    ArgValuesMap avm = argparser.getLinkedArgs(linkedId);
+
+    ArgAndValues avals = (ArgAndValues) stuff[0];
+
+    Object[] moreStuff = (Object[]) stuff[1];
+
+    ArgAndValues[] subAv = (ArgAndValues[]) moreStuff[0];
+
+    Object[] secondaryArgAndAuxStuff = (Object[]) stuff[2];
+
+    // Now look at the argparser
+
+    Arg primaryArg = avals.arg;
+
+    List<ArgValue> parsed_ArgValues = avm.getArgValueList(primaryArg);
+
+    Assert.assertTrue(
+            areEqualSets(avals.values,
+                    ArgValuesMap.toValues(parsed_ArgValues)),
+            "Primary arg (" + primaryArg.toString()
+                    + ") does not have the expected values. Expected "
+                    + avals.values + " and got "
+                    + ArgValuesMap.toValues(parsed_ArgValues));
+
+    for (int i = 0; i < parsed_ArgValues.size(); i++)
+    {
+      ArgValue parsed_ArgValue = parsed_ArgValues.get(i);
+      String value = avals.values.get(i);
+
+      Console.debug("- primary arg '" + primaryArg + "' = '" + value + "'");
+
+      Assert.assertEquals(parsed_ArgValue.getValue(), value,
+              "Primary arg value not as expected");
+
+      ArgAndValues[] aux_avals = (ArgAndValues[]) moreStuff[i];
+
+      for (ArgAndValues aux_aval : aux_avals)
+      {
+        Arg auxArg = aux_aval.arg;
+        List<String> auxValues = aux_aval.values;
+
+        String parsed_auxValue = avm.getFromSubValArgOrPref(auxArg,
+                ArgValuesMap.Position.AFTER, parsed_ArgValue, null, null,
+                null, null);
+
+        if (auxValues.isEmpty())
+        {
+          Assert.assertTrue(parsed_auxValue == null,
+                  "Not expecting to parse a value for '" + auxArg
+                          + "' but found '" + parsed_auxValue + "'");
+        }
+
+        for (String auxValue : auxValues)
+        {
+
+          Console.debug("- + primary aux arg '" + auxArg + "' = '"
+                  + auxValue + "'");
+
+          Assert.assertEquals(parsed_auxValue, auxValue,
+                  "Primary auxiliary arg (" + auxArg.toString()
+                          + ") values do not match");
+
+        }
+      }
+
+      // Now for the secondary args
+      Object[] secondaryStuff = (Object[]) secondaryArgAndAuxStuff[i];
+      ArgAndValues secondaryArgAndValues = (ArgAndValues) secondaryStuff[0];
+      Arg secondaryArg = secondaryArgAndValues.arg;
+      List<String> secondaryValues = secondaryArgAndValues.values;
+
+      List<ArgValue> parsed_secondaryArgValues = avm
+              .getArgValueListFromSubValOrArg(parsed_ArgValue, secondaryArg,
+                      null);
+
+      Assert.assertTrue(
+              areEqualSets(secondaryValues,
+                      ArgValuesMap.toValues(parsed_secondaryArgValues)),
+              "Secondary arg (" + secondaryArg.toString()
+                      + ") does not have the expected values");
+
+      Object[] secondaryMoreStuff = (Object[]) secondaryStuff[1];
+
+      for (int j = 0; j < parsed_secondaryArgValues.size(); j++)
+      {
+        ArgValue parsed_secondaryArgValue = parsed_secondaryArgValues
+                .get(j);
+        String secondary_value = secondaryValues.get(j);
+
+        Console.debug("-- secondary arg '" + secondaryArg + "' = '"
+                + secondary_value + "'");
+
+        Assert.assertEquals(parsed_secondaryArgValue.getValue(),
+                secondary_value, "Secondary arg value not as expected");
+
+        ArgAndValues[] secondary_aux_avals = (ArgAndValues[]) secondaryMoreStuff[j];
+
+        for (ArgAndValues secondary_aux_aval : secondary_aux_avals)
+        {
+          Arg secondary_auxArg = secondary_aux_aval.arg;
+          List<String> secondary_auxValues = secondary_aux_aval.values;
+
+          String parsed_secondary_auxValue = avm.getValueFromSubValOrArg(
+                  parsed_secondaryArgValue, secondary_auxArg, null);
+
+          if (secondary_auxValues.isEmpty())
+          {
+            Assert.assertTrue(parsed_secondary_auxValue == null,
+                    "Not expecting to parse a value for '"
+                            + secondary_auxArg + "' but found '"
+                            + parsed_secondary_auxValue + "'");
+          }
+
+          for (String secondary_auxValue : secondary_auxValues)
+          {
+            Console.debug("-- + secondary aux arg '" + secondary_auxArg
+                    + "' for value '" + secondary_auxValue + "'");
+
+            Assert.assertEquals(parsed_secondary_auxValue,
+                    secondary_auxValue,
+                    "Secondary auxiliary arg ("
+                            + secondary_auxArg.toString()
+                            + ") values do not match");
+          }
+        }
+      }
+    }
+  }
+
+  @DataProvider(name = "argsWithinTypesData")
+  public Object[][] argsWithinTypesData()
+  {
+    return new Object[][] {
+        /*
+         * { cmdline args },
+         * {
+         *   { Primary Arg, Secondary Args },
+         *   { { Secondary Arg => { Values } } }
+         *  },
+         */
+        /*
+        */
+        { //
+          // commandLineArgs
+            "--open=alignment.fa --structure=structure0.pdb"
+                    // structureimage0a and structureimage args
+                    + " --structureimage=image0a.png --bgcolour=bg0a --imagecolour=col0a"
+                    // structureimage0b and structureimage args
+                    + " --structureimage=image0b.png --bgcolour=bg0b --imagecolour=col0b"
+                    // more structure args
+                    + " --structureviewer=sv0 --paematrix=pae0" //
+                    // structure1
+                    + " --structure=structure1.pdb"
+                    // structure args
+                    + " --structureviewer=sv1 --paematrix=pae1"
+                    // structureimage1a with NO structureimage args
+                    // (see `--all --bgcolour=pineapple` later)
+                    + " --structureimage=image1a.png"
+                    // structureimage1b and structureimage args
+                    + " --structureimage=image1b.png --bgcolour=bg1b --imagecolour=col1b"
+                    // --all args, should apply to structureimage1a only
+                    + " --all --bgcolour=pineapple" //
+            ,
+            // stuff
+            new Object[]
+            {
+                // avals (0) and (1)
+                av(Arg.STRUCTURE, "structure0.pdb", "structure1.pdb"),
+                // moreStuff (0) and (1)
+                new ArgAndValues[][]
+                { //
+                    { av(Arg.STRUCTUREVIEWER, "sv0"),
+                        av(Arg.PAEMATRIX, "pae0") },
+                    { av(Arg.STRUCTUREVIEWER, "sv1"),
+                        av(Arg.PAEMATRIX, "pae1") } //
+                },
+                // secondaryArgAndAuxStuff
+                // (same size as values of avals)
+                new Object[][]
+                {
+                    // secondaryStuff (0)
+                    {
+                        // secondaryArgAndValues (a) and (b)
+                        av(Arg.STRUCTUREIMAGE, "image0a.png",
+                                "image0b.png"),
+                        // secondaryMoreStuff
+                        // (same size as values of secondaryArgAndValues)
+                        new ArgAndValues[][]
+                        {
+                            // secondary_aux_avals (a)
+                            { av(Arg.BGCOLOUR, "bg0a"),
+                                av(Arg.IMAGECOLOUR, "col0a") },
+                            // secondary_aux_avals (b)
+                            { av(Arg.BGCOLOUR, "bg0b"),
+                                av(Arg.IMAGECOLOUR, "col0b") }, //
+                        }, //
+                    },
+                    // secondaryStuff (1)
+                    {
+                        // secondaryArgAndValues (a) and (b)
+                        av(Arg.STRUCTUREIMAGE, "image1a.png",
+                                "image1b.png"),
+                        // secondaryMoreStuff
+                        new ArgAndValues[][]
+                        {
+                            // secondary_aux_avals (a)
+                            { av(Arg.BGCOLOUR, "pineapple"),
+                                av(Arg.IMAGECOLOUR) },
+                            // secondary_aux_avals (b)
+                            { av(Arg.BGCOLOUR, "bg1b"),
+                                av(Arg.IMAGECOLOUR, "col1b") }, //
+                        }, //
+                    }, //
+                } //
+            } //
+        }, //
+        { //
+            "--open=alignment.fa --wrap --colour=gecose-flower"
+                    // structure0
+                    + " --structure=structure0.pdb"
+                    + " --structureimage=image0a.png --bgcolour=bg0a --scale=3"
+                    + " --structureimage=image0b.png --imagecolour=col0b --scale=4"
+                    + " --structureviewer=sv0 --paematrix=pae0" //
+                    + " --structureimage=image0c.png"
+                    // structure1
+                    + " --structure=structure1.pdb"
+                    + " --structureviewer=sv1 --paematrix=pae1"
+                    + " --structureimage=image1a.png"
+                    + " --structureimage=image1b.png --bgcolour=bg1b --imagecolour=col1b"
+                    + " --structureimage=image1c.png --bgcolour=bg1c --imagecolour=col1c --scale=5"
+                    + " --structureimage=image1d.png --imagecolour=col1d --scale=6"
+                    + " --structureimage=image1e.png --bgcolour=bg1e"
+                    // structure2
+                    + " --structure=structure2.pdb"
+                    + " --structureimage=image2a.png --bgcolour=bg2a --scale=23"
+                    + " --all --bgcolour=pineapple --imagecolour=banana --scale=2" //
+            ,
+            // stuff
+            new Object[]
+            { av(Arg.STRUCTURE, "structure0.pdb", "structure1.pdb",
+                    "structure2.pdb"),
+                new ArgAndValues[][]
+                { //
+                    { av(Arg.STRUCTUREVIEWER, "sv0"),
+                        av(Arg.PAEMATRIX, "pae0") },
+                    { av(Arg.STRUCTUREVIEWER, "sv1"),
+                        av(Arg.PAEMATRIX, "pae1") },
+                    { av(Arg.STRUCTUREVIEWER), av(Arg.PAEMATRIX) } //
+                }, //
+                new Object[][]
+                { //
+                    { //
+                        av(Arg.STRUCTUREIMAGE, "image0a.png", "image0b.png",
+                                "image0c.png"), //
+                        new ArgAndValues[][]
+                        { //
+                            { av(Arg.BGCOLOUR, "bg0a"),
+                                av(Arg.IMAGECOLOUR, "banana"),
+                                av(Arg.SCALE, "3") },
+                            { av(Arg.BGCOLOUR, "pineapple"),
+                                av(Arg.IMAGECOLOUR, "col0b"),
+                                av(Arg.SCALE, "4") },
+                            { av(Arg.BGCOLOUR, "pineapple"),
+                                av(Arg.IMAGECOLOUR, "banana"),
+                                av(Arg.SCALE, "2") }, //
+                        }, //
+                    }, //
+                    { //
+                        av(Arg.STRUCTUREIMAGE, "image1a.png", "image1b.png",
+                                "image1c.png", "image1d.png",
+                                "image1e.png"),
+                        new ArgAndValues[][]
+                        { //
+                            { av(Arg.BGCOLOUR, "pineapple"),
+                                av(Arg.IMAGECOLOUR, "banana"),
+                                av(Arg.SCALE, "2") },
+                            { av(Arg.BGCOLOUR, "bg1b"),
+                                av(Arg.IMAGECOLOUR, "col1b"),
+                                av(Arg.SCALE, "2") },
+                            { av(Arg.BGCOLOUR, "bg1c"),
+                                av(Arg.IMAGECOLOUR, "col1c"),
+                                av(Arg.SCALE, "5") },
+                            { av(Arg.BGCOLOUR, "pineapple"),
+                                av(Arg.IMAGECOLOUR, "col1d"),
+                                av(Arg.SCALE, "6") },
+                            { av(Arg.BGCOLOUR, "bg1e"),
+                                av(Arg.IMAGECOLOUR, "banana"),
+                                av(Arg.SCALE, "2") } //
+                        }, //
+                    }, //
+                    { //
+                        av(Arg.STRUCTUREIMAGE, "image2a.png"),
+                        new ArgAndValues[][]
+                        { //
+                            { av(Arg.BGCOLOUR, "bg2a"),
+                                av(Arg.IMAGECOLOUR, "banana"),
+                                av(Arg.SCALE, "23") }, //
+                        }, //
+                    }, //
+                } //
+            } //
+        } //
+    };
+  }
+
+  protected ArgAndValues av(Arg a, String... vals)
+  {
+    return new ArgAndValues(a, vals);
+  }
+
+  protected class ArgAndValues
+  {
+    protected Arg arg;
+
+    protected List<String> values;
+
+    ArgAndValues(Arg a, String... vals)
+    {
+      arg = a;
+      values = vals == null ? new ArrayList<String>() : Arrays.asList(vals);
+    }
+
+    @Override
+    public String toString()
+    {
+      StringBuilder sb = new StringBuilder();
+      sb.append(arg.argString());
+      sb.append("\n");
+      sb.append(Strings.join(values, ','));
+      sb.append("\n");
+      return sb.toString();
+    }
+  }
+
+  private static boolean areEqualSets(String[] strArray,
+          List<String> strList)
+  {
+    return areEqualSets(Arrays.asList(strArray), strList);
+  }
+
+  private static boolean areEqualSets(List<String> l1, List<String> l2)
+  {
+    if (l1 == null && l2 == null)
+    {
+      Console.info(
+              "Comparing null lists, should be okay but you might want to know");
+      return true;
+    }
+    if (l1 == null || l2 == null)
+    {
+      return false;
+    }
+    return new HashSet<String>(l1).equals(new HashSet<String>(l2));
+  }
+
+}
\ No newline at end of file