JAL-3438 spotless for 2.11.2.0
[jalview.git] / test / jalview / analysis / AnnotationSorterTest.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
25 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
26 import jalview.datamodel.Alignment;
27 import jalview.datamodel.AlignmentAnnotation;
28 import jalview.datamodel.Sequence;
29 import jalview.datamodel.SequenceI;
30 import jalview.gui.JvOptionPane;
31
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Random;
35
36 import org.testng.annotations.BeforeClass;
37 import org.testng.annotations.BeforeMethod;
38 import org.testng.annotations.Test;
39
40 public class AnnotationSorterTest
41 {
42
43   @BeforeClass(alwaysRun = true)
44   public void setUpJvOptionPane()
45   {
46     JvOptionPane.setInteractiveMode(false);
47     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
48   }
49
50   private static final int NUM_SEQS = 6;
51
52   private static final int NUM_ANNS = 7;
53
54   private static final String SS = "secondary structure";
55
56   AlignmentAnnotation[] anns = new AlignmentAnnotation[0];
57
58   Alignment al = null;
59
60   /*
61    * Set up 6 sequences and 7 annotations.
62    */
63   @BeforeMethod(alwaysRun = true)
64   public void setUp()
65   {
66     al = buildAlignment(NUM_SEQS);
67     anns = buildAnnotations(NUM_ANNS);
68   }
69
70   /**
71    * Construct an array of numAnns annotations
72    * 
73    * @param numAnns
74    * 
75    * @return
76    */
77   protected AlignmentAnnotation[] buildAnnotations(int numAnns)
78   {
79     List<AlignmentAnnotation> annlist = new ArrayList<AlignmentAnnotation>();
80     for (int i = 0; i < numAnns; i++)
81     {
82       AlignmentAnnotation ann = new AlignmentAnnotation(SS + i, "", 0);
83       annlist.add(ann);
84     }
85     return annlist.toArray(anns);
86   }
87
88   /**
89    * Make an alignment with numSeqs sequences in it.
90    * 
91    * @param numSeqs
92    * 
93    * @return
94    */
95   private Alignment buildAlignment(int numSeqs)
96   {
97     SequenceI[] seqs = new Sequence[numSeqs];
98     for (int i = 0; i < numSeqs; i++)
99     {
100       seqs[i] = new Sequence("Sequence" + i, "axrdkfp");
101     }
102     return new Alignment(seqs);
103   }
104
105   /**
106    * Test sorting by annotation type (label) within sequence order, including
107    * <ul>
108    * <li>annotations with no sequence reference - sort to end keeping mutual
109    * ordering</li>
110    * <li>annotations with sequence ref = sort in sequence order</li>
111    * <li>multiple annotations for same sequence ref - sort by label
112    * non-case-specific</li>
113    * <li>annotations with reference to sequence not in alignment - treat like no
114    * sequence ref</li>
115    * </ul>
116    */
117   @Test(groups = { "Functional" })
118   public void testSortBySequenceAndType_autocalcLast()
119   {
120     // @formatter:off
121     anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
122     anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
123     anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
124     anns[3].autoCalculated = true;             anns[3].label = "Quality";
125     anns[4].autoCalculated = true;             anns[4].label = "Consensus";
126     anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "label5";
127     anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP";
128     // @formatter:on
129
130     AnnotationSorter testee = new AnnotationSorter(al, false);
131     testee.sort(anns, SequenceAnnotationOrder.SEQUENCE_AND_LABEL);
132     assertEquals("label5", anns[0].label); // for sequence 0
133     assertEquals("label0", anns[1].label); // for sequence 1
134     assertEquals("iron", anns[2].label); // sequence 3 /iron
135     assertEquals("IRP", anns[3].label); // sequence 3/IRP
136     assertEquals("structure", anns[4].label); // sequence 3/structure
137     assertEquals("Quality", anns[5].label); // autocalc annotations
138     assertEquals("Consensus", anns[6].label); // retain ordering
139   }
140
141   /**
142    * Variant with autocalculated annotations sorting to front
143    */
144   @Test(groups = { "Functional" })
145   public void testSortBySequenceAndType_autocalcFirst()
146   {
147     // @formatter:off
148     anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
149     anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
150     anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
151     anns[3].autoCalculated = true;             anns[3].label = "Quality";
152     anns[4].autoCalculated = true;             anns[4].label = "Consensus";
153     anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "label5";
154     anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP";
155     // @formatter:on
156
157     AnnotationSorter testee = new AnnotationSorter(al, true);
158     testee.sort(anns, SequenceAnnotationOrder.SEQUENCE_AND_LABEL);
159     assertEquals("Quality", anns[0].label); // autocalc annotations
160     assertEquals("Consensus", anns[1].label); // retain ordering
161     assertEquals("label5", anns[2].label); // for sequence 0
162     assertEquals("label0", anns[3].label); // for sequence 1
163     assertEquals("iron", anns[4].label); // sequence 3 /iron
164     assertEquals("IRP", anns[5].label); // sequence 3/IRP
165     assertEquals("structure", anns[6].label); // sequence 3/structure
166   }
167
168   /**
169    * Test sorting by annotation type (label) within sequence order, including
170    * <ul>
171    * <li>annotations with no sequence reference - sort to end keeping mutual
172    * ordering</li>
173    * <li>annotations with sequence ref = sort in sequence order</li>
174    * <li>multiple annotations for same sequence ref - sort by label
175    * non-case-specific</li>
176    * <li>annotations with reference to sequence not in alignment - treat like no
177    * sequence ref</li>
178    * </ul>
179    */
180   @Test(groups = { "Functional" })
181   public void testSortByTypeAndSequence_autocalcLast()
182   {
183     // @formatter:off
184     anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
185     anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
186     anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
187     anns[3].autoCalculated = true;             anns[3].label = "Quality";
188     anns[4].autoCalculated = true;             anns[4].label = "Consensus";
189     anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "IRON";
190     anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure";
191     // @formatter:on
192
193     AnnotationSorter testee = new AnnotationSorter(al, false);
194     testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
195     assertEquals("IRON", anns[0].label); // IRON / sequence 0
196     assertEquals("iron", anns[1].label); // iron / sequence 3
197     assertEquals("label0", anns[2].label); // label0 / sequence 1
198     assertEquals("Structure", anns[3].label); // Structure / sequence 2
199     assertEquals("structure", anns[4].label); // structure / sequence 3
200     assertEquals("Quality", anns[5].label); // autocalc annotations
201     assertEquals("Consensus", anns[6].label); // retain ordering
202   }
203
204   /**
205    * Variant of test with autocalculated annotations sorted to front
206    */
207   @Test(groups = { "Functional" })
208   public void testSortByTypeAndSequence_autocalcFirst()
209   {
210     // @formatter:off
211     anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
212     anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
213     anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
214     anns[3].autoCalculated = true;             anns[3].label = "Quality";
215     anns[4].autoCalculated = true;             anns[4].label = "Consensus";
216     anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "IRON";
217     anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure";
218     // @formatter:on
219
220     AnnotationSorter testee = new AnnotationSorter(al, true);
221     testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
222     assertEquals("Quality", anns[0].label); // autocalc annotations
223     assertEquals("Consensus", anns[1].label); // retain ordering
224     assertEquals("IRON", anns[2].label); // IRON / sequence 0
225     assertEquals("iron", anns[3].label); // iron / sequence 3
226     assertEquals("label0", anns[4].label); // label0 / sequence 1
227     assertEquals("Structure", anns[5].label); // Structure / sequence 2
228     assertEquals("structure", anns[6].label); // structure / sequence 3
229   }
230
231   /**
232    * Variant of test with autocalculated annotations sorted to front but
233    * otherwise no change.
234    */
235   @Test(groups = { "Functional" })
236   public void testNoSort_autocalcFirst()
237   {
238     // @formatter:off
239     anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0";
240     anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure";
241     anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron";
242     anns[3].autoCalculated = true;             anns[3].label = "Quality";
243     anns[4].autoCalculated = true;             anns[4].label = "Consensus";
244     anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "IRON";
245     anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure";
246     // @formatter:on
247
248     AnnotationSorter testee = new AnnotationSorter(al, true);
249     testee.sort(anns, SequenceAnnotationOrder.NONE);
250     assertEquals("Quality", anns[0].label); // autocalc annotations
251     assertEquals("Consensus", anns[1].label); // retain ordering
252     assertEquals("label0", anns[2].label);
253     assertEquals("structure", anns[3].label);
254     assertEquals("iron", anns[4].label);
255     assertEquals("IRON", anns[5].label);
256     assertEquals("Structure", anns[6].label);
257   }
258
259   @Test(groups = { "Functional" })
260   public void testSort_timingPresorted()
261   {
262     testTiming_presorted(50, 100);
263     testTiming_presorted(500, 1000);
264     testTiming_presorted(5000, 10000);
265   }
266
267   /**
268    * Test timing to sort annotations already in the sort order.
269    * 
270    * @param numSeqs
271    * @param numAnns
272    */
273   private void testTiming_presorted(final int numSeqs, final int numAnns)
274   {
275     Alignment alignment = buildAlignment(numSeqs);
276     AlignmentAnnotation[] annotations = buildAnnotations(numAnns);
277
278     /*
279      * Set the annotations presorted by label
280      */
281     Random r = new Random();
282     final SequenceI[] sequences = alignment.getSequencesArray();
283     for (int i = 0; i < annotations.length; i++)
284     {
285       SequenceI randomSequenceRef = sequences[r.nextInt(sequences.length)];
286       annotations[i].sequenceRef = randomSequenceRef;
287       annotations[i].label = "label" + i;
288     }
289     long startTime = System.currentTimeMillis();
290     AnnotationSorter testee = new AnnotationSorter(alignment, false);
291     testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
292     long endTime = System.currentTimeMillis();
293     final long elapsed = endTime - startTime;
294     System.out.println(
295             "Timing test for presorted " + numSeqs + " sequences and "
296                     + numAnns + " annotations took " + elapsed + "ms");
297   }
298
299   /**
300    * Timing tests for sorting randomly sorted annotations for various sizes.
301    */
302   @Test(groups = { "Functional" })
303   public void testSort_timingUnsorted()
304   {
305     testTiming_unsorted(50, 100);
306     testTiming_unsorted(500, 1000);
307     testTiming_unsorted(5000, 10000);
308   }
309
310   /**
311    * Generate annotations randomly sorted with respect to sequences, and time
312    * sorting.
313    * 
314    * @param numSeqs
315    * @param numAnns
316    */
317   private void testTiming_unsorted(final int numSeqs, final int numAnns)
318   {
319     Alignment alignment = buildAlignment(numSeqs);
320     AlignmentAnnotation[] annotations = buildAnnotations(numAnns);
321
322     /*
323      * Set the annotations in random order with respect to the sequences
324      */
325     Random r = new Random();
326     final SequenceI[] sequences = alignment.getSequencesArray();
327     for (int i = 0; i < annotations.length; i++)
328     {
329       SequenceI randomSequenceRef = sequences[r.nextInt(sequences.length)];
330       annotations[i].sequenceRef = randomSequenceRef;
331       annotations[i].label = "label" + i;
332     }
333     long startTime = System.currentTimeMillis();
334     AnnotationSorter testee = new AnnotationSorter(alignment, false);
335     testee.sort(annotations, SequenceAnnotationOrder.SEQUENCE_AND_LABEL);
336     long endTime = System.currentTimeMillis();
337     final long elapsed = endTime - startTime;
338     System.out.println(
339             "Timing test for unsorted " + numSeqs + " sequences and "
340                     + numAnns + " annotations took " + elapsed + "ms");
341   }
342
343   /**
344    * Timing test for sorting annotations with a limited range of types (labels).
345    */
346   @Test(groups = { "Functional" })
347   public void testSort_timingSemisorted()
348   {
349     testTiming_semiSorted(50, 100);
350     testTiming_semiSorted(500, 1000);
351     testTiming_semiSorted(5000, 10000);
352   }
353
354   /**
355    * Mimic 'semi-sorted' annotations:
356    * <ul>
357    * <li>set up in sequence order, with randomly assigned labels from a limited
358    * range</li>
359    * <li>sort by label and sequence order, report timing</li>
360    * <li>resort by sequence and label, report timing</li>
361    * <li>resort by label and sequence, report timing</li>
362    * </ul>
363    * 
364    * @param numSeqs
365    * @param numAnns
366    */
367   private void testTiming_semiSorted(final int numSeqs, final int numAnns)
368   {
369     Alignment alignment = buildAlignment(numSeqs);
370     AlignmentAnnotation[] annotations = buildAnnotations(numAnns);
371
372     String[] labels = new String[] { "label1", "label2", "label3", "label4",
373         "label5", "label6" };
374
375     /*
376      * Set the annotations in sequence order with randomly assigned labels.
377      */
378     Random r = new Random();
379     final SequenceI[] sequences = alignment.getSequencesArray();
380     for (int i = 0; i < annotations.length; i++)
381     {
382       SequenceI sequenceRef = sequences[i % sequences.length];
383       annotations[i].sequenceRef = sequenceRef;
384       annotations[i].label = labels[r.nextInt(labels.length)];
385     }
386     long startTime = System.currentTimeMillis();
387     AnnotationSorter testee = new AnnotationSorter(alignment, false);
388     testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
389     long endTime = System.currentTimeMillis();
390     long elapsed = endTime - startTime;
391     System.out.println(
392             "Sort by label for semisorted " + numSeqs + " sequences and "
393                     + numAnns + " annotations took " + elapsed + "ms");
394
395     // now resort by sequence
396     startTime = System.currentTimeMillis();
397     testee.sort(annotations, SequenceAnnotationOrder.SEQUENCE_AND_LABEL);
398     endTime = System.currentTimeMillis();
399     elapsed = endTime - startTime;
400     System.out.println("Resort by sequence for semisorted " + numSeqs
401             + " sequences and " + numAnns + " annotations took " + elapsed
402             + "ms");
403
404     // now resort by label
405     startTime = System.currentTimeMillis();
406     testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE);
407     endTime = System.currentTimeMillis();
408     elapsed = endTime - startTime;
409     System.out.println(
410             "Resort by label for semisorted " + numSeqs + " sequences and "
411                     + numAnns + " annotations took " + elapsed + "ms");
412   }
413 }