2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.analysis;
23 import static org.testng.AssertJUnit.assertEquals;
25 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
26 import jalview.api.AlignViewportI;
27 import jalview.bin.Cache;
28 import jalview.datamodel.Alignment;
29 import jalview.datamodel.AlignmentAnnotation;
30 import jalview.datamodel.AlignmentI;
31 import jalview.datamodel.Sequence;
32 import jalview.datamodel.SequenceI;
33 import jalview.gui.AlignViewport;
34 import jalview.gui.JvOptionPane;
36 import java.util.Arrays;
37 import java.util.List;
38 import java.util.Random;
40 import org.testng.annotations.BeforeClass;
41 import org.testng.annotations.BeforeMethod;
42 import org.testng.annotations.Test;
44 public class AnnotationSorterTest
47 @BeforeClass(alwaysRun = true)
48 public void setUpJvOptionPane()
50 JvOptionPane.setInteractiveMode(false);
51 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
54 private static final int NUM_SEQS = 6;
56 private static final int NUM_ANNS = 7;
58 private static final String SS = "secondary structure";
60 AlignViewportI av = null;
63 * Configure so that the viewport does not create autocalculated annotation -
64 * test methods flag selected annotation as autocalculated instead
66 @BeforeClass(alwaysRun = true)
67 public void setUpBeforeClass()
69 Cache.loadProperties("test/jalview/io/testProps.jvprops");
70 Cache.setProperty("SHOW_QUALITY", "false");
71 Cache.setProperty("SHOW_CONSERVATION", "false");
72 Cache.setProperty("SHOW_IDENTITY", "false");
73 Cache.setProperty("SHOW_OCCUPANCY", "false");
77 * Set up 6 sequences and 7 annotations.
79 @BeforeMethod(alwaysRun = true)
82 av = buildAlignment(NUM_SEQS, NUM_ANNS);
86 * Make an alignment viewport with numSeqs sequences and numAnns annotations
94 private AlignViewportI buildAlignment(int numSeqs, int numAnns)
96 SequenceI[] seqs = new Sequence[numSeqs];
97 for (int i = 0; i < numSeqs; i++)
99 seqs[i] = new Sequence("Sequence" + i, "axrdkfp");
101 Alignment al = new Alignment(seqs);
103 for (int i = 0; i < numAnns; i++)
105 AlignmentAnnotation ann = new AlignmentAnnotation(SS + i, "", 0);
106 al.addAnnotation(ann);
109 return new AlignViewport(al);
113 * Test sorting by annotation type (label) within sequence order, including
115 * <li>annotations with no sequence reference - sort to end keeping mutual
117 * <li>annotations with sequence ref = sort in sequence order</li>
118 * <li>multiple annotations for same sequence ref - sort by label
119 * non-case-specific</li>
120 * <li>annotations with reference to sequence not in alignment - treat like no
124 @Test(groups = { "Functional" })
125 public void testSortBySequenceAndLabel_autocalcLast()
127 AlignmentI al = av.getAlignment();
128 AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
131 anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
132 anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
133 anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
134 anns[3].autoCalculated = true; anns[3].label = "Quality";
135 anns[4].autoCalculated = true; anns[4].label = "Consensus";
136 anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "label5";
137 anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP";
140 av.setShowAutocalculatedAbove(false);
141 AnnotationSorter testee = new AnnotationSorter(av);
142 testee.sort(SequenceAnnotationOrder.SEQUENCE_AND_LABEL, false);
143 anns = al.getAlignmentAnnotation();
144 assertEquals("label5", anns[0].label); // for sequence 0
145 assertEquals("label0", anns[1].label); // for sequence 1
146 assertEquals("iron", anns[2].label); // sequence 3 /iron
147 assertEquals("IRP", anns[3].label); // sequence 3/IRP
148 assertEquals("structure", anns[4].label); // sequence 3/structure
149 assertEquals("Quality", anns[5].label); // autocalc annotations
150 assertEquals("Consensus", anns[6].label); // retain ordering
154 * Variant with autocalculated annotations sorting to front
156 @Test(groups = { "Functional" })
157 public void testSortBySequenceAndLabel_autocalcFirst()
159 AlignmentI al = av.getAlignment();
160 AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
163 anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
164 anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
165 anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
166 anns[3].autoCalculated = true; anns[3].label = "Quality";
167 anns[4].autoCalculated = true; anns[4].label = "Consensus";
168 anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "label5";
169 anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP";
172 av.setShowAutocalculatedAbove(true);
173 AnnotationSorter testee = new AnnotationSorter(av);
174 testee.sort(SequenceAnnotationOrder.SEQUENCE_AND_LABEL, false);
175 anns = al.getAlignmentAnnotation();
176 assertEquals("Quality", anns[0].label); // autocalc annotations
177 assertEquals("Consensus", anns[1].label); // retain ordering
178 assertEquals("label5", anns[2].label); // for sequence 0
179 assertEquals("label0", anns[3].label); // for sequence 1
180 assertEquals("iron", anns[4].label); // sequence 3 /iron
181 assertEquals("IRP", anns[5].label); // sequence 3/IRP
182 assertEquals("structure", anns[6].label); // sequence 3/structure
186 * Test sorting by annotation type (label) within sequence order, including
188 * <li>annotations with no sequence reference - sort to end keeping mutual
190 * <li>annotations with sequence ref = sort in sequence order</li>
191 * <li>multiple annotations for same sequence ref - sort by label
192 * non-case-specific</li>
193 * <li>annotations with reference to sequence not in alignment - treat like no
197 @Test(groups = { "Functional" })
198 public void testSortByLabelAndSequence_autocalcLast()
200 AlignmentI al = av.getAlignment();
201 AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
204 anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
205 anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
206 anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
207 anns[3].autoCalculated = true; anns[3].label = "Quality";
208 anns[4].autoCalculated = true; anns[4].label = "Consensus";
209 anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "IRON";
210 anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure";
213 av.setShowAutocalculatedAbove(false);
214 AnnotationSorter testee = new AnnotationSorter(av);
215 testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false);
216 anns = al.getAlignmentAnnotation();
217 assertEquals("IRON", anns[0].label); // IRON / sequence 0
218 assertEquals("iron", anns[1].label); // iron / sequence 3
219 assertEquals("label0", anns[2].label); // label0 / sequence 1
220 assertEquals("Structure", anns[3].label); // Structure / sequence 2
221 assertEquals("structure", anns[4].label); // structure / sequence 3
222 assertEquals("Quality", anns[5].label); // autocalc annotations
223 assertEquals("Consensus", anns[6].label); // retain ordering
227 * Variant of test with autocalculated annotations sorted to front
229 @Test(groups = { "Functional" })
230 public void testSortByLabelAndSequence_autocalcFirst()
232 AlignmentI al = av.getAlignment();
233 AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
236 anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
237 anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
238 anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
239 anns[3].autoCalculated = true; anns[3].label = "Quality";
240 anns[4].autoCalculated = true; anns[4].label = "Consensus";
241 anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "IRON";
242 anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure";
245 av.setShowAutocalculatedAbove(true);
246 AnnotationSorter testee = new AnnotationSorter(av);
247 testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false);
248 anns = al.getAlignmentAnnotation();
249 assertEquals("Quality", anns[0].label); // autocalc annotations
250 assertEquals("Consensus", anns[1].label); // retain ordering
251 assertEquals("IRON", anns[2].label); // IRON / sequence 0
252 assertEquals("iron", anns[3].label); // iron / sequence 3
253 assertEquals("label0", anns[4].label); // label0 / sequence 1
254 assertEquals("Structure", anns[5].label); // Structure / sequence 2
255 assertEquals("structure", anns[6].label); // structure / sequence 3
259 * Variant of test with autocalculated annotations sorted to front but
260 * otherwise no change.
262 @Test(groups = { "Functional" })
263 public void testNoSort_autocalcFirst()
265 AlignmentI al = av.getAlignment();
266 AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
269 anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
270 anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
271 anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
272 anns[3].autoCalculated = true; anns[3].label = "Quality";
273 anns[4].autoCalculated = true; anns[4].label = "Consensus";
274 anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "IRON";
275 anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure";
278 av.setShowAutocalculatedAbove(true);
279 AnnotationSorter testee = new AnnotationSorter(av);
280 testee.sort(SequenceAnnotationOrder.NONE, false);
281 anns = al.getAlignmentAnnotation();
282 assertEquals("Quality", anns[0].label); // autocalc annotations
283 assertEquals("Consensus", anns[1].label); // retain ordering
284 assertEquals("label0", anns[2].label);
285 assertEquals("structure", anns[3].label);
286 assertEquals("iron", anns[4].label);
287 assertEquals("IRON", anns[5].label);
288 assertEquals("Structure", anns[6].label);
291 @Test(groups = { "Functional" })
292 public void testSort_timingPresorted()
294 testTiming_presorted(50, 100);
295 testTiming_presorted(500, 1000);
296 testTiming_presorted(5000, 10000);
300 * Test timing to sort annotations already in the sort order.
305 private void testTiming_presorted(final int numSeqs, final int numAnns)
307 AlignViewportI viewport = buildAlignment(numSeqs, numAnns);
308 AlignmentI alignment = viewport.getAlignment();
309 AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation();
312 * Set the annotations presorted by label
314 Random r = new Random();
315 final SequenceI[] sequences = alignment.getSequencesArray();
316 for (int i = 0; i < annotations.length; i++)
318 SequenceI randomSequenceRef = sequences[r.nextInt(sequences.length)];
319 annotations[i].sequenceRef = randomSequenceRef;
320 annotations[i].label = "label" + i;
322 long startTime = System.currentTimeMillis();
323 viewport.setShowAutocalculatedAbove(false);
324 AnnotationSorter testee = new AnnotationSorter(viewport);
325 testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false);
326 long endTime = System.currentTimeMillis();
327 final long elapsed = endTime - startTime;
328 System.out.println("Timing test for presorted " + numSeqs
329 + " sequences and " + numAnns + " annotations took " + elapsed
334 * Timing tests for sorting randomly sorted annotations for various sizes.
336 @Test(groups = { "Functional" })
337 public void testSort_timingUnsorted()
339 testTiming_unsorted(50, 100);
340 testTiming_unsorted(500, 1000);
341 testTiming_unsorted(5000, 10000);
345 * Generate annotations randomly sorted with respect to sequences, and time
351 private void testTiming_unsorted(final int numSeqs, final int numAnns)
353 AlignViewportI viewport = buildAlignment(numSeqs, numAnns);
354 AlignmentI alignment = viewport.getAlignment();
355 AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation();
358 * Set the annotations in random order with respect to the sequences
360 Random r = new Random();
361 final SequenceI[] sequences = alignment.getSequencesArray();
362 for (int i = 0; i < annotations.length; i++)
364 SequenceI randomSequenceRef = sequences[r.nextInt(sequences.length)];
365 annotations[i].sequenceRef = randomSequenceRef;
366 annotations[i].label = "label" + i;
368 long startTime = System.currentTimeMillis();
369 av.setShowAutocalculatedAbove(false);
370 AnnotationSorter testee = new AnnotationSorter(av);
371 testee.sort(SequenceAnnotationOrder.SEQUENCE_AND_LABEL, false);
372 long endTime = System.currentTimeMillis();
373 final long elapsed = endTime - startTime;
374 System.out.println("Timing test for unsorted " + numSeqs
375 + " sequences and " + numAnns + " annotations took " + elapsed
380 * Timing test for sorting annotations with a limited range of types (labels).
382 @Test(groups = { "Functional" })
383 public void testSort_timingSemisorted()
385 testTiming_semiSorted(50, 100);
386 testTiming_semiSorted(500, 1000);
387 testTiming_semiSorted(5000, 10000);
391 * Mimic 'semi-sorted' annotations:
393 * <li>set up in sequence order, with randomly assigned labels from a limited
395 * <li>sort by label and sequence order, report timing</li>
396 * <li>resort by sequence and label, report timing</li>
397 * <li>resort by label and sequence, report timing</li>
403 private void testTiming_semiSorted(final int numSeqs, final int numAnns)
405 AlignViewportI viewport = buildAlignment(numSeqs, numAnns);
406 AlignmentI alignment = viewport.getAlignment();
407 AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation();
409 String[] labels = new String[] { "label1", "label2", "label3",
410 "label4", "label5", "label6" };
413 * Set the annotations in sequence order with randomly assigned labels.
415 Random r = new Random();
416 final SequenceI[] sequences = alignment.getSequencesArray();
417 for (int i = 0; i < annotations.length; i++)
419 SequenceI sequenceRef = sequences[i % sequences.length];
420 annotations[i].sequenceRef = sequenceRef;
421 annotations[i].label = labels[r.nextInt(labels.length)];
423 long startTime = System.currentTimeMillis();
424 av.setShowAutocalculatedAbove(false);
425 AnnotationSorter testee = new AnnotationSorter(av);
426 testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false);
427 long endTime = System.currentTimeMillis();
428 long elapsed = endTime - startTime;
429 System.out.println("Sort by label for semisorted " + numSeqs
430 + " sequences and " + numAnns + " annotations took " + elapsed
433 // now resort by sequence
434 startTime = System.currentTimeMillis();
435 testee.sort(SequenceAnnotationOrder.SEQUENCE_AND_LABEL, false);
436 endTime = System.currentTimeMillis();
437 elapsed = endTime - startTime;
438 System.out.println("Resort by sequence for semisorted " + numSeqs
439 + " sequences and " + numAnns + " annotations took " + elapsed
442 // now resort by label
443 startTime = System.currentTimeMillis();
444 testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, false);
445 endTime = System.currentTimeMillis();
446 elapsed = endTime - startTime;
447 System.out.println("Resort by label for semisorted " + numSeqs
448 + " sequences and " + numAnns + " annotations took " + elapsed
453 * Test that sort does nothing if sort order is CUSTOM (manually ordered
456 @Test(groups = { "Functional" })
457 public void testSort_custom()
459 AlignmentI al = av.getAlignment();
460 AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
463 anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
464 anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
465 anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
466 anns[3].autoCalculated = true; anns[3].label = "Quality";
467 anns[4].autoCalculated = true; anns[4].label = "Consensus";
468 anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "label5";
469 anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP";
473 * showAutocalcAbove=true ignored if CUSTOM ordering
475 av.setShowAutocalculatedAbove(true);
476 AnnotationSorter testee = new AnnotationSorter(av);
477 testee.sort(SequenceAnnotationOrder.CUSTOM, false);
478 anns = al.getAlignmentAnnotation();
479 assertEquals("label0", anns[0].label); // all unchanged
480 assertEquals("structure", anns[1].label);
481 assertEquals("iron", anns[2].label);
482 assertEquals("Quality", anns[3].label);
483 assertEquals("Consensus", anns[4].label);
484 assertEquals("label5", anns[5].label);
485 assertEquals("IRP", anns[6].label);
489 * Test of sorting only autocalculated annotations
491 @Test(groups = { "Functional" })
492 public void testSort_autocalcOnly()
494 AlignmentI al = av.getAlignment();
495 AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
498 anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
499 anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
500 anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
501 anns[3].autoCalculated = true; anns[3].label = "Quality";
502 anns[4].autoCalculated = true; anns[4].label = "Consensus";
503 anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "label5";
504 anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP";
508 * showAutocalcAbove=true, autocalcOnly=true
510 av.setShowAutocalculatedAbove(true);
511 AnnotationSorter testee = new AnnotationSorter(av);
512 testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, true);
513 anns = al.getAlignmentAnnotation();
514 assertEquals("Quality", anns[0].label); // moved to top
515 assertEquals("Consensus", anns[1].label); // moved to top
516 assertEquals("label0", anns[2].label); // the rest unchanged
517 assertEquals("structure", anns[3].label);
518 assertEquals("iron", anns[4].label);
519 assertEquals("label5", anns[5].label);
520 assertEquals("IRP", anns[6].label);
523 * showAutocalcAbove=false, autocalcOnly=true
525 av.setShowAutocalculatedAbove(false);
526 testee = new AnnotationSorter(av);
527 testee.sort(SequenceAnnotationOrder.LABEL_AND_SEQUENCE, true);
528 assertEquals("label0", anns[0].label); // unchanged
529 assertEquals("structure", anns[1].label);
530 assertEquals("iron", anns[2].label);
531 assertEquals("label5", anns[3].label);
532 assertEquals("IRP", anns[4].label);
533 assertEquals("Quality", anns[5].label); // moved to bottom
534 assertEquals("Consensus", anns[6].label); // moved to bottom
538 * Test sorting by annotation order
540 @Test(groups = { "Functional" })
541 public void testSortByAnnotation()
543 AlignmentI al = av.getAlignment();
544 AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
547 anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
548 anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
549 anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
550 anns[3].autoCalculated = true; anns[3].label = "Quality";
551 anns[4].autoCalculated = true; anns[4].label = "Consensus";
552 anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "label5";
553 anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP";
556 av.setShowAutocalculatedAbove(false);
557 AnnotationSorter testee = new AnnotationSorter(av);
558 List<AlignmentAnnotation> reorder = Arrays.asList(anns[2], anns[6],
559 anns[3], anns[0], anns[5], anns[1], anns[4]);
560 testee.sort(reorder);
563 * should now be ordered as specified by the list
565 anns = al.getAlignmentAnnotation();
566 assertEquals("iron", anns[0].label);
567 assertEquals("IRP", anns[1].label);
568 assertEquals("Quality", anns[2].label);
569 assertEquals("label0", anns[3].label);
570 assertEquals("label5", anns[4].label);
571 assertEquals("structure", anns[5].label);
572 assertEquals("Consensus", anns[6].label);