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