Merge branch 'develop' into bug/JAL-3184restoreGroupVisibility
[jalview.git] / test / jalview / gui / FeatureSettingsTest.java
1 package jalview.gui;
2
3 import static jalview.gui.FeatureSettings.COLOUR_COLUMN;
4 import static jalview.gui.FeatureSettings.FILTER_COLUMN;
5 import static jalview.gui.FeatureSettings.SHOW_COLUMN;
6 import static jalview.gui.FeatureSettings.TYPE_COLUMN;
7 import static org.testng.Assert.assertEquals;
8 import static org.testng.Assert.assertFalse;
9 import static org.testng.Assert.assertNull;
10 import static org.testng.Assert.assertTrue;
11
12 import jalview.api.FeatureColourI;
13 import jalview.datamodel.SequenceFeature;
14 import jalview.datamodel.SequenceI;
15 import jalview.datamodel.features.FeatureMatcher;
16 import jalview.datamodel.features.FeatureMatcherSet;
17 import jalview.datamodel.features.FeatureMatcherSetI;
18 import jalview.io.DataSourceType;
19 import jalview.io.FileFormat;
20 import jalview.io.FileLoader;
21 import jalview.schemes.FeatureColour;
22 import jalview.util.matcher.Condition;
23
24 import java.awt.Color;
25 import java.io.File;
26 import java.io.IOException;
27 import java.util.HashMap;
28
29 import org.testng.annotations.Test;
30
31 public class FeatureSettingsTest
32 {
33   /**
34    * Test a roundtrip of save and reload of feature colours and filters as XML
35    * 
36    * @throws IOException
37    */
38   @Test(groups = "Functional")
39   public void testSaveLoad() throws IOException
40   {
41     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
42             ">Seq1\nACDEFGHIKLM", DataSourceType.PASTE, FileFormat.Fasta);
43     SequenceI seq1 = af.getViewport().getAlignment().getSequenceAt(0);
44
45     /*
46      * add some features to the sequence
47      */
48     int score = 1;
49     addFeatures(seq1, "type1", score++);
50     addFeatures(seq1, "type2", score++);
51     addFeatures(seq1, "type3", score++);
52     addFeatures(seq1, "type4", score++);
53     addFeatures(seq1, "type5", score++);
54
55     /*
56      * set colour schemes for features
57      */
58     FeatureRenderer fr = af.getFeatureRenderer();
59
60     // type1: red
61     fr.setColour("type1", new FeatureColour(Color.red));
62
63     // type2: by label
64     FeatureColourI byLabel = new FeatureColour();
65     byLabel.setColourByLabel(true);
66     fr.setColour("type2", byLabel);
67
68     // type3: by score above threshold
69     FeatureColourI byScore = new FeatureColour(Color.BLACK, Color.BLUE, 1,
70             10);
71     byScore.setAboveThreshold(true);
72     byScore.setThreshold(2f);
73     fr.setColour("type3", byScore);
74
75     // type4: by attribute AF
76     FeatureColourI byAF = new FeatureColour();
77     byAF.setColourByLabel(true);
78     byAF.setAttributeName("AF");
79     fr.setColour("type4", byAF);
80
81     // type5: by attribute CSQ:PolyPhen below threshold
82     FeatureColourI byPolyPhen = new FeatureColour(Color.BLACK, Color.BLUE,
83             1, 10);
84     byPolyPhen.setBelowThreshold(true);
85     byPolyPhen.setThreshold(3f);
86     byPolyPhen.setAttributeName("CSQ", "PolyPhen");
87     fr.setColour("type5", byPolyPhen);
88
89     /*
90      * set filters for feature types
91      */
92
93     // filter type1 features by (label contains "x")
94     FeatureMatcherSetI filterByX = new FeatureMatcherSet();
95     filterByX.and(FeatureMatcher.byLabel(Condition.Contains, "x"));
96     fr.setFeatureFilter("type1", filterByX);
97
98     // filter type2 features by (score <= 2.4 and score > 1.1)
99     FeatureMatcherSetI filterByScore = new FeatureMatcherSet();
100     filterByScore.and(FeatureMatcher.byScore(Condition.LE, "2.4"));
101     filterByScore.and(FeatureMatcher.byScore(Condition.GT, "1.1"));
102     fr.setFeatureFilter("type2", filterByScore);
103
104     // filter type3 features by (AF contains X OR CSQ:PolyPhen != 0)
105     FeatureMatcherSetI filterByXY = new FeatureMatcherSet();
106     filterByXY
107             .and(FeatureMatcher.byAttribute(Condition.Contains, "X", "AF"));
108     filterByXY.or(FeatureMatcher.byAttribute(Condition.NE, "0", "CSQ",
109             "PolyPhen"));
110     fr.setFeatureFilter("type3", filterByXY);
111
112     /*
113      * save colours and filters to an XML file
114      */
115     File coloursFile = File.createTempFile("testSaveLoad", ".fc");
116     coloursFile.deleteOnExit();
117     FeatureSettings fs = new FeatureSettings(af);
118     fs.save(coloursFile);
119
120     /*
121      * change feature colours and filters
122      */
123     FeatureColourI pink = new FeatureColour(Color.pink);
124     fr.setColour("type1", pink);
125     fr.setColour("type2", pink);
126     fr.setColour("type3", pink);
127     fr.setColour("type4", pink);
128     fr.setColour("type5", pink);
129
130     FeatureMatcherSetI filter2 = new FeatureMatcherSet();
131     filter2.and(FeatureMatcher.byLabel(Condition.NotContains, "y"));
132     fr.setFeatureFilter("type1", filter2);
133     fr.setFeatureFilter("type2", filter2);
134     fr.setFeatureFilter("type3", filter2);
135     fr.setFeatureFilter("type4", filter2);
136     fr.setFeatureFilter("type5", filter2);
137
138     /*
139      * reload colours and filters from file and verify they are restored
140      */
141     fs.load(coloursFile);
142     FeatureColourI fc = fr.getFeatureStyle("type1");
143     assertTrue(fc.isSimpleColour());
144     assertEquals(fc.getColour(), Color.red);
145     fc = fr.getFeatureStyle("type2");
146     assertTrue(fc.isColourByLabel());
147     fc = fr.getFeatureStyle("type3");
148     assertTrue(fc.isGraduatedColour());
149     assertNull(fc.getAttributeName());
150     assertTrue(fc.isAboveThreshold());
151     assertEquals(fc.getThreshold(), 2f);
152     fc = fr.getFeatureStyle("type4");
153     assertTrue(fc.isColourByLabel());
154     assertTrue(fc.isColourByAttribute());
155     assertEquals(fc.getAttributeName(), new String[] { "AF" });
156     fc = fr.getFeatureStyle("type5");
157     assertTrue(fc.isGraduatedColour());
158     assertTrue(fc.isColourByAttribute());
159     assertEquals(fc.getAttributeName(), new String[] { "CSQ", "PolyPhen" });
160     assertTrue(fc.isBelowThreshold());
161     assertEquals(fc.getThreshold(), 3f);
162
163     assertEquals(fr.getFeatureFilter("type1").toStableString(), "Label Contains x");
164     assertEquals(fr.getFeatureFilter("type2").toStableString(),
165             "(Score LE 2.4) AND (Score GT 1.1)");
166     assertEquals(fr.getFeatureFilter("type3").toStableString(),
167             "(AF Contains X) OR (CSQ:PolyPhen NE 0.0)");
168   }
169
170   /**
171    * Adds two features of the given type to the given sequence, also setting the
172    * score as the value of attribute "AF" and sub-attribute "CSQ:PolyPhen"
173    * 
174    * @param seq
175    * @param featureType
176    * @param score
177    */
178   private void addFeatures(SequenceI seq, String featureType, int score)
179   {
180     addFeature(seq, featureType, score++);
181     addFeature(seq, featureType, score);
182   }
183
184   private void addFeature(SequenceI seq, String featureType, int score)
185   {
186     SequenceFeature sf = new SequenceFeature(featureType, "desc", 1, 2,
187             score, "grp");
188     sf.setValue("AF", score);
189     sf.setValue("CSQ", new HashMap<String, String>()
190     {
191       {
192         put("PolyPhen", Integer.toString(score));
193       }
194     });
195     seq.addSequenceFeature(sf);
196   }
197
198   /**
199    * Test of the behaviour of the Show / Hide checkbox
200    */
201   @Test(groups = "Functional")
202   public void testHideShow()
203   {
204     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
205             ">Seq1\nACDEFGHIKLM", DataSourceType.PASTE, FileFormat.Fasta);
206     SequenceI seq1 = af.getViewport().getAlignment().getSequenceAt(0);
207     seq1.addSequenceFeature(
208             new SequenceFeature("type1", "", 1, 4, "group1"));
209
210     FeatureRenderer fr = af.getFeatureRenderer();
211     fr.setColour("type1", new FeatureColour(Color.red));
212
213     af.showSeqFeatures_actionPerformed(null);
214     FeatureSettings dialog = new FeatureSettings(af);
215
216     assertTrue(fr.getDisplayedFeatureTypes().contains("type1"));
217
218     /*
219      * check the table has one row, for type1, visible, no filter, red
220      */
221     assertEquals(dialog.table.getRowCount(), 1);
222     assertEquals(dialog.table.getColumnCount(), 4);
223     assertEquals(dialog.table.getModel().getValueAt(0, TYPE_COLUMN),
224             "type1");
225     FeatureColourI colour = (FeatureColourI) dialog.table.getModel()
226             .getValueAt(0, COLOUR_COLUMN);
227     assertTrue(colour.isSimpleColour());
228     assertEquals(colour.getColour(), Color.red);
229     FeatureMatcherSetI filter = (FeatureMatcherSetI) dialog.table.getModel()
230             .getValueAt(0, FILTER_COLUMN);
231     assertTrue(filter.isEmpty());
232     assertEquals(dialog.table.getModel().getValueAt(0, SHOW_COLUMN),
233             Boolean.TRUE);
234
235     /*
236      * set feature type to hidden by clicking the checkbox in column 4,
237      * and verify that now no feature types are displayed
238      */
239     dialog.table.setValueAt(Boolean.FALSE, 0, SHOW_COLUMN);
240     assertTrue(fr.getDisplayedFeatureTypes().isEmpty());
241
242     /*
243      * set feature type back to visible by clicking the checkbox in column 4,
244      * and verify that now the feature type is displayed
245      */
246     dialog.table.setValueAt(Boolean.TRUE, 0, SHOW_COLUMN);
247     assertTrue(fr.getDisplayedFeatureTypes().contains("type1"));
248   }
249
250   /**
251    * Test Cancel resets any changes made
252    */
253   @Test(groups = "Functional")
254   public void testCancel()
255   {
256     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
257             ">Seq1\nACDEFGHIKLM", DataSourceType.PASTE, FileFormat.Fasta);
258     SequenceI seq1 = af.getViewport().getAlignment().getSequenceAt(0);
259     seq1.addSequenceFeature(
260             new SequenceFeature("type1", "", 1, 4, "group1"));
261     seq1.addSequenceFeature(
262             new SequenceFeature("type2", "", 1, 4, "group2"));
263   
264     /*
265      * set type1: red, filter 'Label Contains metal'
266      *     type2: variable colour red:blue, no filter
267      */
268     FeatureRenderer fr = af.getFeatureRenderer();
269     fr.setColour("type1", new FeatureColour(Color.red));
270     fr.setColour("type2", new FeatureColour(Color.red, Color.blue, 0f, 1f));
271     FeatureMatcherSetI f = new FeatureMatcherSet();
272     f.and(FeatureMatcher.byLabel(Condition.Contains, "metal"));
273     fr.setFeatureFilter("type1", f);
274
275     af.showSeqFeatures_actionPerformed(null);
276     FeatureSettings dialog = new FeatureSettings(af);
277   
278     assertTrue(fr.getDisplayedFeatureTypes().contains("type1"));
279     assertTrue(fr.getDisplayedFeatureTypes().contains("type2"));
280   
281     /*
282      * check the table has two rows, for type1 and type2
283      * note type2 is shown first; the initial ordering is 'random' as driven by
284      * the Set of feature groups for the sequence
285      */
286     assertEquals(dialog.table.getRowCount(), 2);
287     assertEquals(dialog.table.getColumnCount(), 4);
288
289     assertEquals(dialog.table.getModel().getValueAt(0, TYPE_COLUMN),
290             "type2");
291     FeatureColourI colour = (FeatureColourI) dialog.table.getModel()
292             .getValueAt(0, COLOUR_COLUMN);
293     assertFalse(colour.isSimpleColour());
294     assertEquals(colour.getMinColour(), Color.red);
295     assertEquals(colour.getMaxColour(), Color.blue);
296     FeatureMatcherSetI filter = (FeatureMatcherSetI) dialog.table.getModel()
297             .getValueAt(0, FILTER_COLUMN);
298     assertTrue(filter.isEmpty());
299     assertEquals(dialog.table.getModel().getValueAt(0, SHOW_COLUMN),
300             Boolean.TRUE);
301
302     assertEquals(dialog.table.getModel().getValueAt(1, TYPE_COLUMN),
303             "type1");
304     colour = (FeatureColourI) dialog.table.getModel().getValueAt(1,
305             COLOUR_COLUMN);
306     assertTrue(colour.isSimpleColour());
307     assertEquals(colour.getColour(), Color.red);
308     filter = (FeatureMatcherSetI) dialog.table.getModel().getValueAt(1,
309             FILTER_COLUMN);
310     assertFalse(filter.isEmpty());
311     assertEquals(dialog.table.getModel().getValueAt(1, SHOW_COLUMN),
312             Boolean.TRUE);
313   
314     /*
315      * Make some changes:
316      * - set type1 to hidden by clicking the checkbox in column 4
317      * - change type2 to plain colour green
318      * We can do these by setting values in the table model -
319      * updateFeatureRenderer then updates visibility and colour
320      */
321     dialog.table.setValueAt(Boolean.FALSE, 1, SHOW_COLUMN);
322     dialog.table.setValueAt(new FeatureColour(Color.green), 0,
323             COLOUR_COLUMN);
324
325     /*
326      * - remove the filter on type1
327      * - set a filter on type2
328      * We have to do this directly on FeatureRendererModel (as done in the
329      * application from FeatureTypeSettings)
330      */
331     fr.setFeatureFilter("type1", null);
332     fr.setFeatureFilter("type2",
333             FeatureMatcherSet.fromString("Label matches Metal"));
334
335     /*
336      * verify the changes reached the FeatureRender
337      */
338     assertFalse(fr.getDisplayedFeatureTypes().contains("type1"));
339     assertNull(fr.getFeatureFilter("type1"));
340     colour = fr.getFeatureColours().get("type2");
341     assertTrue(colour.isSimpleColour());
342     assertEquals(colour.getColour(), Color.green);
343     filter = fr.getFeatureFilter("type2");
344     assertFalse(filter.isEmpty());
345     assertEquals(filter.toStableString(), "Label Matches Metal");
346
347     /*
348      * add a new feature type and 'notify' FeatureRenderer
349      * it should appear in the first row of FeatureSettings table
350      */
351     seq1.addSequenceFeature(
352             new SequenceFeature("type3", "desc", 4, 5, null));
353     fr.featuresAdded();
354     assertEquals(dialog.table.getRowCount(), 3);
355     assertEquals(dialog.table.getModel().getValueAt(0, TYPE_COLUMN),
356             "type3");
357     assertEquals(dialog.table.getModel().getValueAt(0, SHOW_COLUMN),
358             Boolean.TRUE);
359     assertTrue(fr.getDisplayedFeatureTypes().contains("type3"));
360
361     /*
362      * cancel the dialog, and verify FeatureRenderer data has been reset
363      */
364     dialog.cancel();
365     // type1 has been reset to visible:
366     assertTrue(fr.getDisplayedFeatureTypes().contains("type1"));
367     // type3 has been reset to not visible:
368     assertTrue(fr.getDisplayedFeatureTypes().contains("type3"));
369     // type2 colour has been reset to graduated:
370     colour = fr.getFeatureColours().get("type2");
371     assertFalse(colour.isSimpleColour());
372     assertEquals(colour.getMaxColour(), Color.blue);
373     // type2 filter has been reset to none (held as null):
374     assertNull(fr.getFeatureFilter("type2"));
375     // type1 filter has been reset:
376     filter = fr.getFeatureFilter("type1");
377     assertEquals(filter.toStableString(), "Label Contains metal");
378   }
379 }