JAL-2843 unit tests for Save/Load colours and filters as XML
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 9 Jan 2018 15:40:52 +0000 (15:40 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 9 Jan 2018 15:40:52 +0000 (15:40 +0000)
resources/lang/Messages.properties
src/jalview/gui/FeatureSettings.java
test/jalview/gui/FeatureSettingsTest.java [new file with mode: 0644]

index 00888d5..1277a86 100644 (file)
@@ -368,6 +368,8 @@ label.optimise_order = Optimise Order
 label.seq_sort_by_score = Sequence sort by Score
 label.load_colours = Load Colours
 label.save_colours = Save Colours
+label.load_colours_tooltip = Load feature colours and filters from file
+label.save_colours_tooltip = Save feature colours and filters to file
 label.fetch_das_features = Fetch DAS Features
 label.selected_database_to_fetch_from = Selected {0} database {1} to fetch from {2} 
 label.database_param = Database: {0}
@@ -1355,4 +1357,5 @@ label.by_text_of = By text of
 label.by_range_of = By range of
 label.filters_tooltip = Click to set or amend filters
 label.or = Or
-label.and = And
\ No newline at end of file
+label.and = And
+label.sequence_feature_colours = Sequence Feature Colours
index 6182885..1dbc9b8 100644 (file)
@@ -107,6 +107,9 @@ import javax.swing.table.TableColumn;
 public class FeatureSettings extends JPanel
         implements FeatureSettingsControllerI
 {
+  private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
+          .getString("label.sequence_feature_colours");
+
   /*
    * column indices of fields in Feature Settings table
    */
@@ -859,7 +862,7 @@ public class FeatureSettings extends JPanel
   void load()
   {
     JalviewFileChooser chooser = new JalviewFileChooser("fc",
-            "Sequence Feature Colours");
+            SEQUENCE_FEATURE_COLOURS);
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(
             MessageManager.getString("label.load_feature_colours"));
@@ -870,58 +873,67 @@ public class FeatureSettings extends JPanel
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
       File file = chooser.getSelectedFile();
+      load(file);
+    }
+  }
 
-      try
-      {
-        InputStreamReader in = new InputStreamReader(
-                new FileInputStream(file), "UTF-8");
+  /**
+   * Loads feature colours and filters from XML stored in the given file
+   * 
+   * @param file
+   */
+  void load(File file)
+  {
+    try
+    {
+      InputStreamReader in = new InputStreamReader(
+              new FileInputStream(file), "UTF-8");
 
-        JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
+      JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
 
-        /*
-         * load feature colours
-         */
-        for (int i = jucs.getColourCount() - 1; i >= 0; i--)
-        {
-          jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
-          FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
-          fr.setColour(newcol.getName(), colour);
-          fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
-        }
+      /*
+       * load feature colours
+       */
+      for (int i = jucs.getColourCount() - 1; i >= 0; i--)
+      {
+        jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
+        FeatureColourI colour = Jalview2XML.unmarshalColour(newcol);
+        fr.setColour(newcol.getName(), colour);
+        fr.setOrder(newcol.getName(), i / (float) jucs.getColourCount());
+      }
 
-        /*
-         * load feature filters; loaded filters will replace any that are
-         * currently defined, other defined filters are left unchanged 
-         */
-        for (int i = 0; i < jucs.getFilterCount(); i++)
+      /*
+       * load feature filters; loaded filters will replace any that are
+       * currently defined, other defined filters are left unchanged 
+       */
+      for (int i = 0; i < jucs.getFilterCount(); i++)
+      {
+        jalview.schemabinding.version2.Filter filterModel = jucs
+                .getFilter(i);
+        String featureType = filterModel.getFeatureType();
+        FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
+                filterModel.getMatcherSet());
+        if (!filter.isEmpty())
         {
-          jalview.schemabinding.version2.Filter filterModel = jucs
-                  .getFilter(i);
-          String featureType = filterModel.getFeatureType();
-          FeatureMatcherSetI filter = Jalview2XML.unmarshalFilter(featureType,
-                  filterModel.getMatcherSet());
-          if (!filter.isEmpty())
-          {
-            fr.setFeatureFilter(featureType, filter);
-          }
+          fr.setFeatureFilter(featureType, filter);
         }
+      }
 
-        /*
-         * update feature settings table
-         */
-        if (table != null)
-        {
-          resetTable(null);
-          Object[][] data = ((FeatureTableModel) table.getModel())
-                  .getData();
-          ensureOrder(data);
-          updateFeatureRenderer(data, false);
-          table.repaint();
-        }
-      } catch (Exception ex)
+      /*
+       * update feature settings table
+       */
+      if (table != null)
       {
-        System.out.println("Error loading User Colour File\n" + ex);
+        resetTable(null);
+        Object[][] data = ((FeatureTableModel) table.getModel())
+                .getData();
+        ensureOrder(data);
+        updateFeatureRenderer(data, false);
+        table.repaint();
       }
+    } catch (Exception ex)
+    {
+      System.out.println("Error loading User Colour File\n" + ex);
     }
   }
 
@@ -932,7 +944,7 @@ public class FeatureSettings extends JPanel
   void save()
   {
     JalviewFileChooser chooser = new JalviewFileChooser("fc",
-            "Sequence Feature Colours");
+            SEQUENCE_FEATURE_COLOURS);
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(
             MessageManager.getString("label.save_feature_colours"));
@@ -942,66 +954,75 @@ public class FeatureSettings extends JPanel
 
     if (value == JalviewFileChooser.APPROVE_OPTION)
     {
-      String choice = chooser.getSelectedFile().getPath();
-      JalviewUserColours ucs = new JalviewUserColours();
-      ucs.setSchemeName("Sequence Features");
-      try
-      {
-        PrintWriter out = new PrintWriter(new OutputStreamWriter(
-                new FileOutputStream(choice), "UTF-8"));
+      save(chooser.getSelectedFile());
+    }
+  }
 
-        /*
-         * sort feature types by colour order, from 0 (highest)
-         * to 1 (lowest)
-         */
-        Set<String> fr_colours = fr.getAllFeatureColours();
-        String[] sortedTypes = fr_colours
-                .toArray(new String[fr_colours.size()]);
-        Arrays.sort(sortedTypes, new Comparator<String>()
-        {
-          @Override
-          public int compare(String type1, String type2)
-          {
-            return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
-          }
-        });
+  /**
+   * Saves feature colours and filters to the given file
+   * 
+   * @param file
+   */
+  void save(File file)
+  {
+    JalviewUserColours ucs = new JalviewUserColours();
+    ucs.setSchemeName("Sequence Features");
+    try
+    {
+      PrintWriter out = new PrintWriter(new OutputStreamWriter(
+              new FileOutputStream(file), "UTF-8"));
 
-        /*
-         * save feature colours
-         */
-        for (String featureType : sortedTypes)
+      /*
+       * sort feature types by colour order, from 0 (highest)
+       * to 1 (lowest)
+       */
+      Set<String> fr_colours = fr.getAllFeatureColours();
+      String[] sortedTypes = fr_colours
+              .toArray(new String[fr_colours.size()]);
+      Arrays.sort(sortedTypes, new Comparator<String>()
+      {
+        @Override
+        public int compare(String type1, String type2)
         {
-          FeatureColourI fcol = fr.getFeatureStyle(featureType);
-          jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
-                  featureType, fcol);
-          ucs.addColour(col);
+          return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
         }
+      });
 
-        /*
-         * save any feature filters
-         */
-        for (String featureType : sortedTypes)
-        {
-          FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
-          if (filter != null && !filter.isEmpty())
-          {
-            Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
-            FeatureMatcherI firstMatcher = iterator.next();
-            MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
-                    filter.isAnded());
-            Filter filterModel = new Filter();
-            filterModel.setFeatureType(featureType);
-            filterModel.setMatcherSet(ms);
-            ucs.addFilter(filterModel);
-          }
-        }
+      /*
+       * save feature colours
+       */
+      for (String featureType : sortedTypes)
+      {
+        FeatureColourI fcol = fr.getFeatureStyle(featureType);
+        jalview.schemabinding.version2.Colour col = Jalview2XML.marshalColour(
+                featureType, fcol);
+        ucs.addColour(col);
+      }
 
-        ucs.marshal(out);
-        out.close();
-      } catch (Exception ex)
+      /*
+       * save any feature filters
+       */
+      for (String featureType : sortedTypes)
       {
-        ex.printStackTrace();
+        FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
+        if (filter != null && !filter.isEmpty())
+        {
+          Iterator<FeatureMatcherI> iterator = filter.getMatchers().iterator();
+          FeatureMatcherI firstMatcher = iterator.next();
+          MatcherSet ms = Jalview2XML.marshalFilter(firstMatcher, iterator,
+                  filter.isAnded());
+          Filter filterModel = new Filter();
+          filterModel.setFeatureType(featureType);
+          filterModel.setMatcherSet(ms);
+          ucs.addFilter(filterModel);
+        }
       }
+
+      ucs.marshal(out);
+      out.close();
+    } catch (Exception ex)
+    {
+      ex.printStackTrace();
     }
   }
 
@@ -1252,6 +1273,8 @@ public class FeatureSettings extends JPanel
     JButton loadColours = new JButton(
             MessageManager.getString("label.load_colours"));
     loadColours.setFont(JvSwingUtils.getLabelFont());
+    loadColours.setToolTipText(
+            MessageManager.getString("label.load_colours_tooltip"));
     loadColours.addActionListener(new ActionListener()
     {
       @Override
@@ -1264,6 +1287,8 @@ public class FeatureSettings extends JPanel
     JButton saveColours = new JButton(
             MessageManager.getString("label.save_colours"));
     saveColours.setFont(JvSwingUtils.getLabelFont());
+    saveColours.setToolTipText(
+            MessageManager.getString("label.save_colours_tooltip"));
     saveColours.addActionListener(new ActionListener()
     {
       @Override
diff --git a/test/jalview/gui/FeatureSettingsTest.java b/test/jalview/gui/FeatureSettingsTest.java
new file mode 100644 (file)
index 0000000..6ddebf8
--- /dev/null
@@ -0,0 +1,191 @@
+package jalview.gui;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import jalview.api.FeatureColourI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureMatcher;
+import jalview.datamodel.features.FeatureMatcherSet;
+import jalview.datamodel.features.FeatureMatcherSetI;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+import jalview.schemes.FeatureColour;
+import jalview.util.matcher.Condition;
+
+import java.awt.Color;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+
+import org.testng.annotations.Test;
+
+public class FeatureSettingsTest
+{
+  /**
+   * Test a roundtrip of save and reload of feature colours and filters as XML
+   * 
+   * @throws IOException
+   */
+  @Test(groups = "Functional")
+  public void testSaveLoad() throws IOException
+  {
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            ">Seq1\nACDEFGHIKLM", DataSourceType.PASTE);
+    SequenceI seq1 = af.getViewport().getAlignment().getSequenceAt(0);
+
+    /*
+     * add some features to the sequence
+     */
+    int score = 1;
+    addFeatures(seq1, "type1", score++);
+    addFeatures(seq1, "type2", score++);
+    addFeatures(seq1, "type3", score++);
+    addFeatures(seq1, "type4", score++);
+    addFeatures(seq1, "type5", score++);
+
+    /*
+     * set colour schemes for features
+     */
+    FeatureRenderer fr = af.getFeatureRenderer();
+
+    // type1: red
+    fr.setColour("type1", new FeatureColour(Color.red));
+
+    // type2: by label
+    FeatureColourI byLabel = new FeatureColour();
+    byLabel.setColourByLabel(true);
+    fr.setColour("type2", byLabel);
+
+    // type3: by score above threshold
+    FeatureColourI byScore = new FeatureColour(Color.BLACK, Color.BLUE, 1,
+            10);
+    byScore.setAboveThreshold(true);
+    byScore.setThreshold(2f);
+    fr.setColour("type3", byScore);
+
+    // type4: by attribute AF
+    FeatureColourI byAF = new FeatureColour();
+    byAF.setColourByLabel(true);
+    byAF.setAttributeName("AF");
+    fr.setColour("type4", byAF);
+
+    // type5: by attribute CSQ:PolyPhen below threshold
+    FeatureColourI byPolyPhen = new FeatureColour(Color.BLACK, Color.BLUE,
+            1, 10);
+    byPolyPhen.setBelowThreshold(true);
+    byPolyPhen.setThreshold(3f);
+    byPolyPhen.setAttributeName("CSQ", "PolyPhen");
+    fr.setColour("type5", byPolyPhen);
+
+    /*
+     * set filters for feature types
+     */
+
+    // filter type1 features by (label contains "x")
+    FeatureMatcherSetI filterByX = new FeatureMatcherSet();
+    filterByX.and(FeatureMatcher.byLabel(Condition.Contains, "x"));
+    fr.setFeatureFilter("type1", filterByX);
+
+    // filter type2 features by (score <= 2.4 and score > 1.1)
+    FeatureMatcherSetI filterByScore = new FeatureMatcherSet();
+    filterByScore.and(FeatureMatcher.byScore(Condition.LE, "2.4"));
+    filterByScore.and(FeatureMatcher.byScore(Condition.GT, "1.1"));
+    fr.setFeatureFilter("type2", filterByScore);
+
+    // filter type3 features by (AF contains X OR CSQ:PolyPhen != 0)
+    FeatureMatcherSetI filterByXY = new FeatureMatcherSet();
+    filterByXY
+            .and(FeatureMatcher.byAttribute(Condition.Contains, "X", "AF"));
+    filterByXY.or(FeatureMatcher.byAttribute(Condition.NE, "0", "CSQ",
+            "PolyPhen"));
+    fr.setFeatureFilter("type3", filterByXY);
+
+    /*
+     * save colours and filters to an XML file
+     */
+    File coloursFile = File.createTempFile("testSaveLoad", ".fc");
+    coloursFile.deleteOnExit();
+    FeatureSettings fs = new FeatureSettings(af);
+    fs.save(coloursFile);
+
+    /*
+     * change feature colours and filters
+     */
+    FeatureColourI pink = new FeatureColour(Color.pink);
+    fr.setColour("type1", pink);
+    fr.setColour("type2", pink);
+    fr.setColour("type3", pink);
+    fr.setColour("type4", pink);
+    fr.setColour("type5", pink);
+
+    FeatureMatcherSetI filter2 = new FeatureMatcherSet();
+    filter2.and(FeatureMatcher.byLabel(Condition.NotContains, "y"));
+    fr.setFeatureFilter("type1", filter2);
+    fr.setFeatureFilter("type2", filter2);
+    fr.setFeatureFilter("type3", filter2);
+    fr.setFeatureFilter("type4", filter2);
+    fr.setFeatureFilter("type5", filter2);
+
+    /*
+     * reload colours and filters from file and verify they are restored
+     */
+    fs.load(coloursFile);
+    FeatureColourI fc = fr.getFeatureStyle("type1");
+    assertTrue(fc.isSimpleColour());
+    assertEquals(fc.getColour(), Color.red);
+    fc = fr.getFeatureStyle("type2");
+    assertTrue(fc.isColourByLabel());
+    fc = fr.getFeatureStyle("type3");
+    assertTrue(fc.isGraduatedColour());
+    assertNull(fc.getAttributeName());
+    assertTrue(fc.isAboveThreshold());
+    assertEquals(fc.getThreshold(), 2f);
+    fc = fr.getFeatureStyle("type4");
+    assertTrue(fc.isColourByLabel());
+    assertTrue(fc.isColourByAttribute());
+    assertEquals(fc.getAttributeName(), new String[] { "AF" });
+    fc = fr.getFeatureStyle("type5");
+    assertTrue(fc.isGraduatedColour());
+    assertTrue(fc.isColourByAttribute());
+    assertEquals(fc.getAttributeName(), new String[] { "CSQ", "PolyPhen" });
+    assertTrue(fc.isBelowThreshold());
+    assertEquals(fc.getThreshold(), 3f);
+
+    assertEquals(fr.getFeatureFilter("type1").toStableString(), "Label Contains x");
+    assertEquals(fr.getFeatureFilter("type2").toStableString(),
+            "(Score LE 2.4) AND (Score GT 1.1)");
+    assertEquals(fr.getFeatureFilter("type3").toStableString(),
+            "(AF Contains X) OR (CSQ:PolyPhen NE 0.0)");
+  }
+
+  /**
+   * Adds two features of the given type to the given sequence, also setting the
+   * score as the value of attribute "AF" and sub-attribute "CSQ:PolyPhen"
+   * 
+   * @param seq
+   * @param featureType
+   * @param score
+   */
+  private void addFeatures(SequenceI seq, String featureType, int score)
+  {
+    addFeature(seq, featureType, score++);
+    addFeature(seq, featureType, score);
+  }
+
+  private void addFeature(SequenceI seq, String featureType, int score)
+  {
+    SequenceFeature sf = new SequenceFeature(featureType, "desc", 1, 2,
+            score, "grp");
+    sf.setValue("AF", score);
+    sf.setValue("CSQ", new HashMap<String, String>()
+    {
+      {
+        put("PolyPhen", Integer.toString(score));
+      }
+    });
+    seq.addSequenceFeature(sf);
+  }
+}