Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
AlignmentAnnotationUtilsTest | 47 | 192 | 10 |
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 | 1 | @BeforeClass(alwaysRun = true) |
51 | public void setUpJvOptionPane() | |
52 | { | |
53 | 1 | JvOptionPane.setInteractiveMode(false); |
54 | 1 | 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 | 1 | @Test(groups = { "Functional" }) |
80 | public void testAsList() | |
81 | { | |
82 | // null array | |
83 | 1 | Collection<AlignmentAnnotation> c1 = AlignmentAnnotationUtils |
84 | .asList(null); | |
85 | 1 | assertTrue(c1.isEmpty()); |
86 | ||
87 | // empty array | |
88 | 1 | AlignmentAnnotation[] anns = new AlignmentAnnotation[0]; |
89 | 1 | c1 = AlignmentAnnotationUtils.asList(anns); |
90 | 1 | assertTrue(c1.isEmpty()); |
91 | ||
92 | // non-empty array | |
93 | 1 | anns = new AlignmentAnnotation[2]; |
94 | 1 | anns[0] = new AlignmentAnnotation("label0", "desc0", 0.0f); |
95 | 1 | anns[1] = new AlignmentAnnotation("label1", "desc1", 1.0f); |
96 | 1 | c1 = AlignmentAnnotationUtils.asList(anns); |
97 | 1 | assertEquals(2, c1.size()); |
98 | 1 | assertTrue(c1.contains(anns[0])); |
99 | 1 | 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 | 2 | protected void consoleDebug(Map<String, List<List<String>>> shownTypes, |
109 | Map<String, List<List<String>>> hiddenTypes) | |
110 | { | |
111 | 2 | for (String calcId : shownTypes.keySet()) |
112 | { | |
113 | 3 | System.out.println("Visible annotation types for calcId=" + calcId); |
114 | 3 | for (List<String> type : shownTypes.get(calcId)) |
115 | { | |
116 | 3 | System.out.println(" " + type); |
117 | } | |
118 | } | |
119 | 2 | for (String calcId : hiddenTypes.keySet()) |
120 | { | |
121 | 4 | System.out.println("Hidden annotation types for calcId=" + calcId); |
122 | 4 | for (List<String> type : hiddenTypes.get(calcId)) |
123 | { | |
124 | 5 | 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 | 2 | private List<SequenceI> selectSequences(int... selected) |
137 | { | |
138 | 2 | List<SequenceI> result = new ArrayList<SequenceI>(); |
139 | 2 | SequenceI[] seqs = alignment.getSequencesArray(); |
140 | 2 | for (int i : selected) |
141 | { | |
142 | 4 | result.add(seqs[i]); |
143 | } | |
144 | 2 | return result; |
145 | } | |
146 | ||
147 | /** | |
148 | * Load the test alignment and generate annotations on it | |
149 | * | |
150 | * @throws IOException | |
151 | */ | |
152 | 5 | @BeforeMethod(alwaysRun = true) |
153 | public void setUp() throws IOException | |
154 | { | |
155 | 5 | alignment = new jalview.io.FormatAdapter().readFile(TEST_DATA, |
156 | DataSourceType.PASTE, FileFormat.Fasta); | |
157 | ||
158 | 5 | AlignmentAnnotation[] anns = new AlignmentAnnotation[SEQ_ANN_COUNT]; |
159 | 65 | 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 | 60 | anns[i] = new AlignmentAnnotation("Label" + i, "Desc " + i, |
166 | new Annotation[] {}); | |
167 | 60 | anns[i].setCalcId("CalcId" + i); |
168 | 60 | anns[i].visible = true; |
169 | 60 | alignment.addAnnotation(anns[i]); |
170 | } | |
171 | } | |
172 | ||
173 | /** | |
174 | * Test a mixture of show/hidden annotations in/outside selection group. | |
175 | */ | |
176 | 1 | @Test(groups = { "Functional" }) |
177 | public void testGetShownHiddenTypes_forSelectionGroup() | |
178 | { | |
179 | 1 | Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>(); |
180 | 1 | Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>(); |
181 | 1 | AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation(); |
182 | 1 | 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 | 1 | anns[4].sequenceRef = seqs[2]; |
190 | 1 | anns[4].visible = false; |
191 | 1 | anns[7].sequenceRef = seqs[1]; |
192 | 1 | anns[7].visible = true; |
193 | ||
194 | /* | |
195 | * in selection group, hidden: | |
196 | */ | |
197 | 1 | anns[2].sequenceRef = seqs[3]; // CalcId2/Label2 |
198 | 1 | anns[2].visible = false; |
199 | 1 | anns[3].sequenceRef = seqs[3]; // CalcId3/Label2 |
200 | 1 | anns[3].visible = false; |
201 | 1 | anns[3].label = "Label2"; |
202 | 1 | anns[4].sequenceRef = seqs[3]; // CalcId2/Label3 |
203 | 1 | anns[4].visible = false; |
204 | 1 | anns[4].label = "Label3"; |
205 | 1 | anns[4].setCalcId("CalcId2"); |
206 | 1 | anns[8].sequenceRef = seqs[0]; // CalcId9/Label9 |
207 | 1 | anns[8].visible = false; |
208 | 1 | anns[8].label = "Label9"; |
209 | 1 | anns[8].setCalcId("CalcId9"); |
210 | /* | |
211 | * in selection group, visible | |
212 | */ | |
213 | 1 | anns[6].sequenceRef = seqs[0]; // CalcId6/Label6 |
214 | 1 | anns[6].visible = true; |
215 | 1 | anns[9].sequenceRef = seqs[3]; // CalcId9/Label9 |
216 | 1 | anns[9].visible = true; |
217 | ||
218 | 1 | List<SequenceI> selected = selectSequences(0, 3); |
219 | 1 | 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 | 1 | assertEquals(2, shownTypes.size()); |
226 | 1 | assertEquals(1, shownTypes.get("CalcId6").size()); |
227 | 1 | assertEquals(1, shownTypes.get("CalcId6").get(0).size()); |
228 | 1 | assertEquals("Label6", shownTypes.get("CalcId6").get(0).get(0)); |
229 | 1 | assertEquals(1, shownTypes.get("CalcId9").size()); |
230 | 1 | assertEquals(1, shownTypes.get("CalcId9").get(0).size()); |
231 | 1 | assertEquals("Label9", shownTypes.get("CalcId9").get(0).get(0)); |
232 | ||
233 | // hidden: CalcId2/Label2, CalcId2/Label3, CalcId3/Label2, CalcId9/Label9 | |
234 | 1 | assertEquals(3, hiddenTypes.size()); |
235 | 1 | assertEquals(2, hiddenTypes.get("CalcId2").size()); |
236 | 1 | assertEquals(1, hiddenTypes.get("CalcId2").get(0).size()); |
237 | 1 | assertEquals("Label2", hiddenTypes.get("CalcId2").get(0).get(0)); |
238 | 1 | assertEquals(1, hiddenTypes.get("CalcId2").get(1).size()); |
239 | 1 | assertEquals("Label3", hiddenTypes.get("CalcId2").get(1).get(0)); |
240 | 1 | assertEquals(1, hiddenTypes.get("CalcId3").size()); |
241 | 1 | assertEquals(1, hiddenTypes.get("CalcId3").get(0).size()); |
242 | 1 | assertEquals("Label2", hiddenTypes.get("CalcId3").get(0).get(0)); |
243 | 1 | assertEquals(1, hiddenTypes.get("CalcId9").size()); |
244 | 1 | assertEquals(1, hiddenTypes.get("CalcId9").get(0).size()); |
245 | 1 | assertEquals("Label9", hiddenTypes.get("CalcId9").get(0).get(0)); |
246 | ||
247 | 1 | 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 | 1 | @Test(groups = { "Functional" }) |
255 | public void testGetShownHiddenTypes_withGraphGroups() | |
256 | { | |
257 | 1 | final int GROUP_3 = 3; |
258 | 1 | final int GROUP_4 = 4; |
259 | 1 | final int GROUP_5 = 5; |
260 | 1 | final int GROUP_6 = 6; |
261 | ||
262 | 1 | Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>(); |
263 | 1 | Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>(); |
264 | 1 | AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation(); |
265 | 1 | 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 | 1 | anns[2].sequenceRef = seqs[3]; |
273 | 1 | anns[2].visible = false; |
274 | 1 | anns[2].graph = AlignmentAnnotation.LINE_GRAPH; |
275 | 1 | anns[2].graphGroup = GROUP_5; // not a visible group |
276 | 1 | anns[3].sequenceRef = seqs[0]; |
277 | 1 | anns[3].visible = false; |
278 | 1 | anns[3].graph = AlignmentAnnotation.LINE_GRAPH; |
279 | 1 | anns[3].graphGroup = GROUP_5; |
280 | // need to ensure annotations have the same calcId as well | |
281 | 1 | anns[3].setCalcId("CalcId2"); |
282 | // annotations for a different hidden group generating the same group label | |
283 | 1 | anns[10].sequenceRef = seqs[0]; |
284 | 1 | anns[10].visible = false; |
285 | 1 | anns[10].graph = AlignmentAnnotation.LINE_GRAPH; |
286 | 1 | anns[10].graphGroup = GROUP_3; |
287 | 1 | anns[10].label = "Label3"; |
288 | 1 | anns[10].setCalcId("CalcId2"); |
289 | 1 | anns[11].sequenceRef = seqs[3]; |
290 | 1 | anns[11].visible = false; |
291 | 1 | anns[11].graph = AlignmentAnnotation.LINE_GRAPH; |
292 | 1 | anns[11].graphGroup = GROUP_3; |
293 | 1 | anns[11].label = "Label2"; |
294 | 1 | anns[11].setCalcId("CalcId2"); |
295 | ||
296 | // annotations Label1 (hidden), Label5 (visible) in group 6 (visible) | |
297 | 1 | anns[1].sequenceRef = seqs[3]; |
298 | // being in a visible group should take precedence over this visibility | |
299 | 1 | anns[1].visible = false; |
300 | 1 | anns[1].graph = AlignmentAnnotation.LINE_GRAPH; |
301 | 1 | anns[1].graphGroup = GROUP_6; |
302 | 1 | anns[5].sequenceRef = seqs[0]; |
303 | 1 | anns[5].visible = true; |
304 | 1 | anns[5].graph = AlignmentAnnotation.LINE_GRAPH; |
305 | 1 | anns[5].graphGroup = GROUP_6; |
306 | 1 | 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 | 1 | anns[4].sequenceRef = seqs[0]; |
314 | 1 | anns[4].visible = false; |
315 | 1 | anns[4].graph = AlignmentAnnotation.LINE_GRAPH; |
316 | 1 | anns[4].graphGroup = GROUP_4; |
317 | 1 | anns[4].label = "Label1"; |
318 | 1 | anns[4].setCalcId("CalcId1"); |
319 | 1 | anns[0].sequenceRef = seqs[0]; |
320 | 1 | anns[0].visible = true; |
321 | 1 | anns[0].graph = AlignmentAnnotation.LINE_GRAPH; |
322 | 1 | anns[0].graphGroup = GROUP_4; |
323 | 1 | anns[0].label = "Label5"; |
324 | 1 | anns[0].setCalcId("CalcId1"); |
325 | ||
326 | /* | |
327 | * Annotations outwith selection group - should be ignored. | |
328 | */ | |
329 | // Hidden grouped annotations | |
330 | 1 | anns[6].sequenceRef = seqs[2]; |
331 | 1 | anns[6].visible = false; |
332 | 1 | anns[6].graph = AlignmentAnnotation.LINE_GRAPH; |
333 | 1 | anns[6].graphGroup = GROUP_4; |
334 | 1 | anns[8].sequenceRef = seqs[1]; |
335 | 1 | anns[8].visible = false; |
336 | 1 | anns[8].graph = AlignmentAnnotation.LINE_GRAPH; |
337 | 1 | anns[8].graphGroup = GROUP_4; |
338 | ||
339 | // visible grouped annotations Label7, Label9 | |
340 | 1 | anns[7].sequenceRef = seqs[2]; |
341 | 1 | anns[7].visible = true; |
342 | 1 | anns[7].graph = AlignmentAnnotation.LINE_GRAPH; |
343 | 1 | anns[7].graphGroup = GROUP_4; |
344 | 1 | anns[9].sequenceRef = seqs[1]; |
345 | 1 | anns[9].visible = true; |
346 | 1 | anns[9].graph = AlignmentAnnotation.LINE_GRAPH; |
347 | 1 | anns[9].graphGroup = GROUP_4; |
348 | ||
349 | /* | |
350 | * Generate annotations[] arrays to match aligned columns | |
351 | */ | |
352 | // adjustForAlignment(anns); | |
353 | ||
354 | 1 | List<SequenceI> selected = selectSequences(0, 3); |
355 | 1 | AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes, |
356 | AlignmentAnnotationUtils.asList(anns), selected); | |
357 | ||
358 | 1 | consoleDebug(shownTypes, hiddenTypes); |
359 | ||
360 | // CalcId1 / Label1, Label5 (only) should be 'shown', once, as a compound | |
361 | // type | |
362 | 1 | assertEquals(1, shownTypes.size()); |
363 | 1 | assertEquals(1, shownTypes.get("CalcId1").size()); |
364 | 1 | assertEquals(2, shownTypes.get("CalcId1").get(0).size()); |
365 | 1 | assertEquals("Label1", shownTypes.get("CalcId1").get(0).get(0)); |
366 | 1 | assertEquals("Label5", shownTypes.get("CalcId1").get(0).get(1)); |
367 | ||
368 | // CalcId2 / Label2, Label3 (only) should be 'hidden' | |
369 | 1 | assertEquals(1, hiddenTypes.size()); |
370 | 1 | assertEquals(1, hiddenTypes.get("CalcId2").size()); |
371 | 1 | assertEquals(2, hiddenTypes.get("CalcId2").get(0).size()); |
372 | 1 | assertEquals("Label2", hiddenTypes.get("CalcId2").get(0).get(0)); |
373 | 1 | assertEquals("Label3", hiddenTypes.get("CalcId2").get(0).get(1)); |
374 | } | |
375 | ||
376 | /** | |
377 | * Test method that determines visible graph groups. | |
378 | */ | |
379 | 1 | @Test(groups = { "Functional" }) |
380 | public void testGetVisibleGraphGroups() | |
381 | { | |
382 | 1 | AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation(); |
383 | /* | |
384 | * a bar graph group is not included | |
385 | */ | |
386 | 1 | anns[0].graph = AlignmentAnnotation.BAR_GRAPH; |
387 | 1 | anns[0].graphGroup = 1; |
388 | 1 | anns[0].visible = true; |
389 | ||
390 | /* | |
391 | * a line graph group is included as long as one of its members is visible | |
392 | */ | |
393 | 1 | anns[1].graph = AlignmentAnnotation.LINE_GRAPH; |
394 | 1 | anns[1].graphGroup = 5; |
395 | 1 | anns[1].visible = false; |
396 | 1 | anns[2].graph = AlignmentAnnotation.LINE_GRAPH; |
397 | 1 | anns[2].graphGroup = 5; |
398 | 1 | anns[2].visible = true; |
399 | ||
400 | /* | |
401 | * a line graph group with no visible rows is not included | |
402 | */ | |
403 | 1 | anns[3].graph = AlignmentAnnotation.LINE_GRAPH; |
404 | 1 | anns[3].graphGroup = 3; |
405 | 1 | anns[3].visible = false; |
406 | ||
407 | // a visible line graph with no graph group is not included | |
408 | 1 | anns[4].graph = AlignmentAnnotation.LINE_GRAPH; |
409 | 1 | anns[4].graphGroup = -1; |
410 | 1 | anns[4].visible = true; |
411 | ||
412 | 1 | BitSet result = AlignmentAnnotationUtils.getVisibleLineGraphGroups( |
413 | AlignmentAnnotationUtils.asList(anns)); | |
414 | 1 | assertTrue(result.get(5)); |
415 | 1 | assertFalse(result.get(0)); |
416 | 1 | assertFalse(result.get(1)); |
417 | 1 | assertFalse(result.get(2)); |
418 | 1 | assertFalse(result.get(3)); |
419 | } | |
420 | ||
421 | /** | |
422 | * Test for case where no sequence is selected. Shouldn't normally arise but | |
423 | * check it handles it gracefully. | |
424 | */ | |
425 | 1 | @Test(groups = { "Functional" }) |
426 | public void testGetShownHiddenTypes_noSequenceSelected() | |
427 | { | |
428 | 1 | Map<String, List<List<String>>> shownTypes = new HashMap<String, List<List<String>>>(); |
429 | 1 | Map<String, List<List<String>>> hiddenTypes = new HashMap<String, List<List<String>>>(); |
430 | 1 | AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation(); |
431 | // selected sequences null | |
432 | 1 | AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes, |
433 | AlignmentAnnotationUtils.asList(anns), null); | |
434 | 1 | assertTrue(shownTypes.isEmpty()); |
435 | 1 | assertTrue(hiddenTypes.isEmpty()); |
436 | ||
437 | 1 | List<SequenceI> sequences = new ArrayList<SequenceI>(); |
438 | // selected sequences empty list | |
439 | 1 | AlignmentAnnotationUtils.getShownHiddenTypes(shownTypes, hiddenTypes, |
440 | AlignmentAnnotationUtils.asList(anns), sequences); | |
441 | 1 | assertTrue(shownTypes.isEmpty()); |
442 | 1 | assertTrue(hiddenTypes.isEmpty()); |
443 | } | |
444 | } |