1 package jalview.analysis;
3 import static org.testng.AssertJUnit.assertEquals;
4 import static org.testng.AssertJUnit.assertFalse;
5 import static org.testng.AssertJUnit.assertTrue;
7 import jalview.datamodel.AlignmentAnnotation;
8 import jalview.datamodel.AlignmentI;
9 import jalview.datamodel.Annotation;
10 import jalview.datamodel.SequenceI;
11 import jalview.io.AppletFormatAdapter;
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.BitSet;
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.List;
21 import org.testng.annotations.BeforeMethod;
22 import org.testng.annotations.Test;
24 public class AlignmentAnnotationUtilsTest
26 // 4 sequences x 13 positions
27 final static String EOL = "\n";
30 final static String TEST_DATA =
31 ">FER_CAPAA Ferredoxin" + EOL +
32 "TIETHKEAELVG-" + EOL +
33 ">FER_CAPAN Ferredoxin, chloroplast precursor" + EOL +
34 "TIETHKEAELVG-" + EOL +
35 ">FER1_SOLLC Ferredoxin-1, chloroplast precursor" + EOL +
36 "TIETHKEEELTA-" + EOL +
37 ">Q93XJ9_SOLTU Ferredoxin I precursor" + EOL +
38 "TIETHKEEELTA-" + EOL;
41 private static final int SEQ_ANN_COUNT = 12;
43 private AlignmentI alignment;
46 * Test method that converts a (possibly null) array to a list.
48 @Test(groups = { "Functional" })
49 public void testAsList()
52 Collection<AlignmentAnnotation> c1 = AlignmentAnnotationUtils
54 assertTrue(c1.isEmpty());
57 AlignmentAnnotation[] anns = new AlignmentAnnotation[0];
58 c1 = AlignmentAnnotationUtils.asList(anns);
59 assertTrue(c1.isEmpty());
62 anns = new AlignmentAnnotation[2];
63 anns[0] = new AlignmentAnnotation("label0", "desc0", 0.0f);
64 anns[1] = new AlignmentAnnotation("label1", "desc1", 1.0f);
65 c1 = AlignmentAnnotationUtils.asList(anns);
66 assertEquals(2, c1.size());
67 assertTrue(c1.contains(anns[0]));
68 assertTrue(c1.contains(anns[1]));
72 * This output is not part of the test but may help make sense of it...
77 protected void consoleDebug(Map<String, List<List<String>>> shownTypes,
78 Map<String, List<List<String>>> hiddenTypes)
80 for (String calcId : shownTypes.keySet())
82 System.out.println("Visible annotation types for calcId=" + calcId);
83 for (List<String> type : shownTypes.get(calcId))
85 System.out.println(" " + type);
88 for (String calcId : hiddenTypes.keySet())
90 System.out.println("Hidden annotation types for calcId=" + calcId);
91 for (List<String> type : hiddenTypes.get(calcId))
93 System.out.println(" " + type);
99 * Add a sequence group to the alignment with the specified sequences (base 0)
105 private List<SequenceI> selectSequences(int... selected)
107 List<SequenceI> result = new ArrayList<SequenceI>();
108 SequenceI[] seqs = alignment.getSequencesArray();
109 for (int i : selected)
117 * Load the test alignment and generate annotations on it
119 * @throws IOException
121 @BeforeMethod(alwaysRun = true)
122 public void setUp() throws IOException
124 alignment = new jalview.io.FormatAdapter().readFile(TEST_DATA,
125 AppletFormatAdapter.PASTE, "FASTA");
127 AlignmentAnnotation[] anns = new AlignmentAnnotation[SEQ_ANN_COUNT];
128 for (int i = 0; i < anns.length; i++)
131 * Use the constructor for a positional annotation (with an Annotation
134 anns[i] = new AlignmentAnnotation("Label" + i, "Desc " + i,
135 new Annotation[] {});
136 anns[i].setCalcId("CalcId" + i);
137 anns[i].visible = true;
138 alignment.addAnnotation(anns[i]);
143 * Test a mixture of show/hidden annotations in/outside selection group.
145 @Test(groups = { "Functional" })
146 public void testGetShownHiddenTypes_forSelectionGroup()
148 Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
149 Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>();
150 AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
151 SequenceI[] seqs = alignment.getSequencesArray();
154 * Configure annotation properties for test
156 // not in selection group (should be ignored):
157 // hidden annotation Label4 not in selection group
158 anns[4].sequenceRef = seqs[2];
159 anns[4].visible = false;
160 anns[7].sequenceRef = seqs[1];
161 anns[7].visible = true;
164 * in selection group, hidden:
166 anns[2].sequenceRef = seqs[3]; // CalcId2/Label2
167 anns[2].visible = false;
168 anns[3].sequenceRef = seqs[3]; // CalcId3/Label2
169 anns[3].visible = false;
170 anns[3].label = "Label2";
171 anns[4].sequenceRef = seqs[3]; // CalcId2/Label3
172 anns[4].visible = false;
173 anns[4].label = "Label3";
174 anns[4].setCalcId("CalcId2");
175 anns[8].sequenceRef = seqs[0]; // CalcId9/Label9
176 anns[8].visible = false;
177 anns[8].label = "Label9";
178 anns[8].setCalcId("CalcId9");
180 * in selection group, visible
182 anns[6].sequenceRef = seqs[0]; // CalcId6/Label6
183 anns[6].visible = true;
184 anns[9].sequenceRef = seqs[3]; // CalcId9/Label9
185 anns[9].visible = true;
187 List<SequenceI> selected = selectSequences(0, 3);
188 AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
189 AlignmentAnnotationUtils.asList(anns), selected);
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));
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));
216 consoleDebug(shownTypes, hiddenTypes);
220 * Test case where there are 'grouped' annotations, visible and hidden, within
221 * and without the selection group.
223 @Test(groups = { "Functional" })
224 public void testGetShownHiddenTypes_withGraphGroups()
226 final int GROUP_3 = 3;
227 final int GROUP_4 = 4;
228 final int GROUP_5 = 5;
229 final int GROUP_6 = 6;
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();
237 * Annotations for selection group and graph group
239 * Hidden annotations Label2, Label3, in (hidden) group 5
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");
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");
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.
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");
296 * Annotations outwith selection group - should be ignored.
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;
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;
319 * Generate annotations[] arrays to match aligned columns
321 // adjustForAlignment(anns);
323 List<SequenceI> selected = selectSequences(0, 3);
324 AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
325 AlignmentAnnotationUtils.asList(anns), selected);
327 consoleDebug(shownTypes, hiddenTypes);
329 // CalcId1 / Label1, Label5 (only) should be 'shown', once, as a compound
331 assertEquals(1, shownTypes.size());
332 assertEquals(1, shownTypes.get("CalcId1").size());
333 assertEquals(2, shownTypes.get("CalcId1").get(0).size());
334 assertEquals("Label1", shownTypes.get("CalcId1").get(0).get(0));
335 assertEquals("Label5", shownTypes.get("CalcId1").get(0).get(1));
337 // CalcId2 / Label2, Label3 (only) should be 'hidden'
338 assertEquals(1, hiddenTypes.size());
339 assertEquals(1, hiddenTypes.get("CalcId2").size());
340 assertEquals(2, hiddenTypes.get("CalcId2").get(0).size());
341 assertEquals("Label2", hiddenTypes.get("CalcId2").get(0).get(0));
342 assertEquals("Label3", hiddenTypes.get("CalcId2").get(0).get(1));
346 * Test method that determines visible graph groups.
348 @Test(groups = { "Functional" })
349 public void testGetVisibleGraphGroups()
351 AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
353 * a bar graph group is not included
355 anns[0].graph = AlignmentAnnotation.BAR_GRAPH;
356 anns[0].graphGroup = 1;
357 anns[0].visible = true;
360 * a line graph group is included as long as one of its members is visible
362 anns[1].graph = AlignmentAnnotation.LINE_GRAPH;
363 anns[1].graphGroup = 5;
364 anns[1].visible = false;
365 anns[2].graph = AlignmentAnnotation.LINE_GRAPH;
366 anns[2].graphGroup = 5;
367 anns[2].visible = true;
370 * a line graph group with no visible rows is not included
372 anns[3].graph = AlignmentAnnotation.LINE_GRAPH;
373 anns[3].graphGroup = 3;
374 anns[3].visible = false;
376 // a visible line graph with no graph group is not included
377 anns[4].graph = AlignmentAnnotation.LINE_GRAPH;
378 anns[4].graphGroup = -1;
379 anns[4].visible = true;
381 BitSet result = AlignmentAnnotationUtils
382 .getVisibleLineGraphGroups(AlignmentAnnotationUtils
384 assertTrue(result.get(5));
385 assertFalse(result.get(0));
386 assertFalse(result.get(1));
387 assertFalse(result.get(2));
388 assertFalse(result.get(3));
392 * Test for case where no sequence is selected. Shouldn't normally arise but
393 * check it handles it gracefully.
395 @Test(groups = { "Functional" })
396 public void testGetShownHiddenTypes_noSequenceSelected()
398 Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>();
399 Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>();
400 AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
401 // selected sequences null
402 AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
403 AlignmentAnnotationUtils.asList(anns), null);
404 assertTrue(shownTypes.isEmpty());
405 assertTrue(hiddenTypes.isEmpty());
407 List<SequenceI> sequences = new ArrayList<SequenceI>();
408 // selected sequences empty list
409 AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes,
410 AlignmentAnnotationUtils.asList(anns), sequences);
411 assertTrue(shownTypes.isEmpty());
412 assertTrue(hiddenTypes.isEmpty());