JAL-3597 slight tidying of code and Javadoc, passmark now heap <= 38MB
[jalview.git] / test / jalview / analysis / AlignmentAnnotationUtilsTest.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.analysis;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertFalse;
25 import static org.testng.AssertJUnit.assertTrue;
26
27 import jalview.datamodel.AlignmentAnnotation;
28 import jalview.datamodel.AlignmentI;
29 import jalview.datamodel.Annotation;
30 import jalview.datamodel.SequenceI;
31 import jalview.gui.JvOptionPane;
32 import jalview.io.DataSourceType;
33 import jalview.io.FileFormat;
34
35 import java.io.IOException;
36 import java.util.ArrayList;
37 import java.util.BitSet;
38 import java.util.Collection;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42
43 import org.testng.annotations.BeforeClass;
44 import org.testng.annotations.BeforeMethod;
45 import org.testng.annotations.Test;
46
47 public class AlignmentAnnotationUtilsTest
48 {
49
50   @BeforeClass(alwaysRun = true)
51   public void setUpJvOptionPane()
52   {
53     JvOptionPane.setInteractiveMode(false);
54     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
55   }
56
57   // 4 sequences x 13 positions
58   final static String EOL = "\n";
59
60   // @formatter:off
61   final static String TEST_DATA = 
62           ">FER_CAPAA Ferredoxin" + EOL +
63           "TIETHKEAELVG-" + EOL +
64           ">FER_CAPAN Ferredoxin, chloroplast precursor" + EOL +
65           "TIETHKEAELVG-" + EOL +
66           ">FER1_SOLLC Ferredoxin-1, chloroplast precursor" + EOL +
67           "TIETHKEEELTA-" + EOL + 
68           ">Q93XJ9_SOLTU Ferredoxin I precursor" + EOL +
69           "TIETHKEEELTA-" + EOL;
70   // @formatter:on
71
72   private static final int SEQ_ANN_COUNT = 12;
73
74   private AlignmentI alignment;
75
76   /**
77    * Test method that converts a (possibly null) array to a list.
78    */
79   @Test(groups = { "Functional" })
80   public void testAsList()
81   {
82     // null array
83     Collection<AlignmentAnnotation> c1 = AlignmentAnnotationUtils
84             .asList(null);
85     assertTrue(c1.isEmpty());
86
87     // empty array
88     AlignmentAnnotation[] anns = new AlignmentAnnotation[0];
89     c1 = AlignmentAnnotationUtils.asList(anns);
90     assertTrue(c1.isEmpty());
91
92     // non-empty array
93     anns = new AlignmentAnnotation[2];
94     anns[0] = new AlignmentAnnotation("label0", "desc0", 0.0f);
95     anns[1] = new AlignmentAnnotation("label1", "desc1", 1.0f);
96     c1 = AlignmentAnnotationUtils.asList(anns);
97     assertEquals(2, c1.size());
98     assertTrue(c1.contains(anns[0]));
99     assertTrue(c1.contains(anns[1]));
100   }
101
102   /**
103    * This output is not part of the test but may help make sense of it...
104    * 
105    * @param shownTypes
106    * @param hiddenTypes
107    */
108   protected void consoleDebug(Map<String, List<List<String>>> shownTypes,
109           Map<String, List<List<String>>> hiddenTypes)
110   {
111     for (String calcId : shownTypes.keySet())
112     {
113       System.out.println("Visible annotation types for calcId=" + calcId);
114       for (List<String> type : shownTypes.get(calcId))
115       {
116         System.out.println("   " + type);
117       }
118     }
119     for (String calcId : hiddenTypes.keySet())
120     {
121       System.out.println("Hidden annotation types for calcId=" + calcId);
122       for (List<String> type : hiddenTypes.get(calcId))
123       {
124         System.out.println("   " + type);
125       }
126     }
127   }
128
129   /**
130    * Add a sequence group to the alignment with the specified sequences (base 0)
131    * in it
132    * 
133    * @param i
134    * @param more
135    */
136   private List<SequenceI> selectSequences(int... selected)
137   {
138     List<SequenceI> result = new ArrayList<SequenceI>();
139     SequenceI[] seqs = alignment.getSequencesArray();
140     for (int i : selected)
141     {
142       result.add(seqs[i]);
143     }
144     return result;
145   }
146
147   /**
148    * Load the test alignment and generate annotations on it
149    * 
150    * @throws IOException
151    */
152   @BeforeMethod(alwaysRun = true)
153   public void setUp() throws IOException
154   {
155     alignment = new jalview.io.FormatAdapter().readFile(TEST_DATA,
156             DataSourceType.PASTE, FileFormat.Fasta);
157
158     AlignmentAnnotation[] anns = new AlignmentAnnotation[SEQ_ANN_COUNT];
159     for (int i = 0; i < anns.length; i++)
160     {
161       /*
162        * Use the constructor for a positional annotation (with an Annotation
163        * array)
164        */
165       anns[i] = new AlignmentAnnotation("Label" + i, "Desc " + i,
166               new Annotation[] {});
167       anns[i].setCalcId("CalcId" + i);
168       anns[i].visible = true;
169       alignment.addAnnotation(anns[i]);
170     }
171   }
172
173   /**
174    * Test a mixture of show/hidden annotations in/outside selection group.
175    */
176   @Test(groups = { "Functional" })
177   public void testGetShownHiddenTypes_forSelectionGroup()
178   {
179     Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
180     Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>();
181     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
182     SequenceI[] seqs = alignment.getSequencesArray();
183
184     /*
185      * Configure annotation properties for test
186      */
187     // not in selection group (should be ignored):
188     // hidden annotation Label4 not in selection group
189     anns[4].sequenceRef = seqs[2];
190     anns[4].visible = false;
191     anns[7].sequenceRef = seqs[1];
192     anns[7].visible = true;
193
194     /*
195      * in selection group, hidden:
196      */
197     anns[2].sequenceRef = seqs[3]; // CalcId2/Label2
198     anns[2].visible = false;
199     anns[3].sequenceRef = seqs[3]; // CalcId3/Label2
200     anns[3].visible = false;
201     anns[3].label = "Label2";
202     anns[4].sequenceRef = seqs[3]; // CalcId2/Label3
203     anns[4].visible = false;
204     anns[4].label = "Label3";
205     anns[4].setCalcId("CalcId2");
206     anns[8].sequenceRef = seqs[0]; // CalcId9/Label9
207     anns[8].visible = false;
208     anns[8].label = "Label9";
209     anns[8].setCalcId("CalcId9");
210     /*
211      * in selection group, visible
212      */
213     anns[6].sequenceRef = seqs[0]; // CalcId6/Label6
214     anns[6].visible = true;
215     anns[9].sequenceRef = seqs[3]; // CalcId9/Label9
216     anns[9].visible = true;
217
218     List<SequenceI> selected = selectSequences(0, 3);
219     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
220             AlignmentAnnotationUtils.asList(anns), selected);
221
222     // check results; note CalcId9/Label9 is both hidden and shown (for
223     // different sequences) so should be in both
224     // shown: CalcId6/Label6 and CalcId9/Label9
225     assertEquals(2, shownTypes.size());
226     assertEquals(1, shownTypes.get("CalcId6").size());
227     assertEquals(1, shownTypes.get("CalcId6").get(0).size());
228     assertEquals("Label6", shownTypes.get("CalcId6").get(0).get(0));
229     assertEquals(1, shownTypes.get("CalcId9").size());
230     assertEquals(1, shownTypes.get("CalcId9").get(0).size());
231     assertEquals("Label9", shownTypes.get("CalcId9").get(0).get(0));
232
233     // hidden: CalcId2/Label2, CalcId2/Label3, CalcId3/Label2, CalcId9/Label9
234     assertEquals(3, hiddenTypes.size());
235     assertEquals(2, hiddenTypes.get("CalcId2").size());
236     assertEquals(1, hiddenTypes.get("CalcId2").get(0).size());
237     assertEquals("Label2", hiddenTypes.get("CalcId2").get(0).get(0));
238     assertEquals(1, hiddenTypes.get("CalcId2").get(1).size());
239     assertEquals("Label3", hiddenTypes.get("CalcId2").get(1).get(0));
240     assertEquals(1, hiddenTypes.get("CalcId3").size());
241     assertEquals(1, hiddenTypes.get("CalcId3").get(0).size());
242     assertEquals("Label2", hiddenTypes.get("CalcId3").get(0).get(0));
243     assertEquals(1, hiddenTypes.get("CalcId9").size());
244     assertEquals(1, hiddenTypes.get("CalcId9").get(0).size());
245     assertEquals("Label9", hiddenTypes.get("CalcId9").get(0).get(0));
246
247     consoleDebug(shownTypes, hiddenTypes);
248   }
249
250   /**
251    * Test case where there are 'grouped' annotations, visible and hidden, within
252    * and without the selection group.
253    */
254   @Test(groups = { "Functional" })
255   public void testGetShownHiddenTypes_withGraphGroups()
256   {
257     final int GROUP_3 = 3;
258     final int GROUP_4 = 4;
259     final int GROUP_5 = 5;
260     final int GROUP_6 = 6;
261
262     Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
263     Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>();
264     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
265     SequenceI[] seqs = alignment.getSequencesArray();
266
267     /*
268      * Annotations for selection group and graph group
269      * 
270      * Hidden annotations Label2, Label3, in (hidden) group 5
271      */
272     anns[2].sequenceRef = seqs[3];
273     anns[2].visible = false;
274     anns[2].graph = AlignmentAnnotation.LINE_GRAPH;
275     anns[2].graphGroup = GROUP_5; // not a visible group
276     anns[3].sequenceRef = seqs[0];
277     anns[3].visible = false;
278     anns[3].graph = AlignmentAnnotation.LINE_GRAPH;
279     anns[3].graphGroup = GROUP_5;
280     // need to ensure annotations have the same calcId as well
281     anns[3].setCalcId("CalcId2");
282     // annotations for a different hidden group generating the same group label
283     anns[10].sequenceRef = seqs[0];
284     anns[10].visible = false;
285     anns[10].graph = AlignmentAnnotation.LINE_GRAPH;
286     anns[10].graphGroup = GROUP_3;
287     anns[10].label = "Label3";
288     anns[10].setCalcId("CalcId2");
289     anns[11].sequenceRef = seqs[3];
290     anns[11].visible = false;
291     anns[11].graph = AlignmentAnnotation.LINE_GRAPH;
292     anns[11].graphGroup = GROUP_3;
293     anns[11].label = "Label2";
294     anns[11].setCalcId("CalcId2");
295
296     // annotations Label1 (hidden), Label5 (visible) in group 6 (visible)
297     anns[1].sequenceRef = seqs[3];
298     // being in a visible group should take precedence over this visibility
299     anns[1].visible = false;
300     anns[1].graph = AlignmentAnnotation.LINE_GRAPH;
301     anns[1].graphGroup = GROUP_6;
302     anns[5].sequenceRef = seqs[0];
303     anns[5].visible = true;
304     anns[5].graph = AlignmentAnnotation.LINE_GRAPH;
305     anns[5].graphGroup = GROUP_6;
306     anns[5].setCalcId("CalcId1");
307     /*
308      * Annotations 0 and 4 are visible, for a different CalcId and graph group.
309      * They produce the same label as annotations 1 and 5, which should not be
310      * duplicated in the results. This case corresponds to (e.g.) many
311      * occurrences of an IUPred Short/Long annotation group, one per sequence.
312      */
313     anns[4].sequenceRef = seqs[0];
314     anns[4].visible = false;
315     anns[4].graph = AlignmentAnnotation.LINE_GRAPH;
316     anns[4].graphGroup = GROUP_4;
317     anns[4].label = "Label1";
318     anns[4].setCalcId("CalcId1");
319     anns[0].sequenceRef = seqs[0];
320     anns[0].visible = true;
321     anns[0].graph = AlignmentAnnotation.LINE_GRAPH;
322     anns[0].graphGroup = GROUP_4;
323     anns[0].label = "Label5";
324     anns[0].setCalcId("CalcId1");
325
326     /*
327      * Annotations outwith selection group - should be ignored.
328      */
329     // Hidden grouped annotations
330     anns[6].sequenceRef = seqs[2];
331     anns[6].visible = false;
332     anns[6].graph = AlignmentAnnotation.LINE_GRAPH;
333     anns[6].graphGroup = GROUP_4;
334     anns[8].sequenceRef = seqs[1];
335     anns[8].visible = false;
336     anns[8].graph = AlignmentAnnotation.LINE_GRAPH;
337     anns[8].graphGroup = GROUP_4;
338
339     // visible grouped annotations Label7, Label9
340     anns[7].sequenceRef = seqs[2];
341     anns[7].visible = true;
342     anns[7].graph = AlignmentAnnotation.LINE_GRAPH;
343     anns[7].graphGroup = GROUP_4;
344     anns[9].sequenceRef = seqs[1];
345     anns[9].visible = true;
346     anns[9].graph = AlignmentAnnotation.LINE_GRAPH;
347     anns[9].graphGroup = GROUP_4;
348
349     /*
350      * Generate annotations[] arrays to match aligned columns
351      */
352     // adjustForAlignment(anns);
353
354     List<SequenceI> selected = selectSequences(0, 3);
355     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
356             AlignmentAnnotationUtils.asList(anns), selected);
357
358     consoleDebug(shownTypes, hiddenTypes);
359
360     // CalcId1 / Label1, Label5 (only) should be 'shown', once, as a compound
361     // type
362     assertEquals(1, shownTypes.size());
363     assertEquals(1, shownTypes.get("CalcId1").size());
364     assertEquals(2, shownTypes.get("CalcId1").get(0).size());
365     assertEquals("Label1", shownTypes.get("CalcId1").get(0).get(0));
366     assertEquals("Label5", shownTypes.get("CalcId1").get(0).get(1));
367
368     // CalcId2 / Label2, Label3 (only) should be 'hidden'
369     assertEquals(1, hiddenTypes.size());
370     assertEquals(1, hiddenTypes.get("CalcId2").size());
371     assertEquals(2, hiddenTypes.get("CalcId2").get(0).size());
372     assertEquals("Label2", hiddenTypes.get("CalcId2").get(0).get(0));
373     assertEquals("Label3", hiddenTypes.get("CalcId2").get(0).get(1));
374   }
375
376   /**
377    * Test method that determines visible graph groups.
378    */
379   @Test(groups = { "Functional" })
380   public void testGetVisibleGraphGroups()
381   {
382     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
383     /*
384      * a bar graph group is not included
385      */
386     anns[0].graph = AlignmentAnnotation.BAR_GRAPH;
387     anns[0].graphGroup = 1;
388     anns[0].visible = true;
389
390     /*
391      * a line graph group is included as long as one of its members is visible
392      */
393     anns[1].graph = AlignmentAnnotation.LINE_GRAPH;
394     anns[1].graphGroup = 5;
395     anns[1].visible = false;
396     anns[2].graph = AlignmentAnnotation.LINE_GRAPH;
397     anns[2].graphGroup = 5;
398     anns[2].visible = true;
399
400     /*
401      * a line graph group with no visible rows is not included
402      */
403     anns[3].graph = AlignmentAnnotation.LINE_GRAPH;
404     anns[3].graphGroup = 3;
405     anns[3].visible = false;
406
407     // a visible line graph with no graph group is not included
408     anns[4].graph = AlignmentAnnotation.LINE_GRAPH;
409     anns[4].graphGroup = -1;
410     anns[4].visible = true;
411
412     BitSet result = AlignmentAnnotationUtils
413             .getVisibleLineGraphGroups(AlignmentAnnotationUtils
414                     .asList(anns));
415     assertTrue(result.get(5));
416     assertFalse(result.get(0));
417     assertFalse(result.get(1));
418     assertFalse(result.get(2));
419     assertFalse(result.get(3));
420   }
421
422   /**
423    * Test for case where no sequence is selected. Shouldn't normally arise but
424    * check it handles it gracefully.
425    */
426   @Test(groups = { "Functional" })
427   public void testGetShownHiddenTypes_noSequenceSelected()
428   {
429     Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
430     Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>();
431     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
432     // selected sequences null
433     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
434             AlignmentAnnotationUtils.asList(anns), null);
435     assertTrue(shownTypes.isEmpty());
436     assertTrue(hiddenTypes.isEmpty());
437
438     List<SequenceI> sequences = new ArrayList<SequenceI>();
439     // selected sequences empty list
440     AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
441             AlignmentAnnotationUtils.asList(anns), sequences);
442     assertTrue(shownTypes.isEmpty());
443     assertTrue(hiddenTypes.isEmpty());
444   }
445 }