Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
AnnotationSorterTest | 40 | 190 | 20 |
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 | 1 | @BeforeClass(alwaysRun = true) |
44 | public void setUpJvOptionPane() | |
45 | { | |
46 | 1 | JvOptionPane.setInteractiveMode(false); |
47 | 1 | 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 | 8 | @BeforeMethod(alwaysRun = true) |
64 | public void setUp() | |
65 | { | |
66 | 8 | al = buildAlignment(NUM_SEQS); |
67 | 8 | anns = buildAnnotations(NUM_ANNS); |
68 | } | |
69 | ||
70 | /** | |
71 | * Construct an array of numAnns annotations | |
72 | * | |
73 | * @param numAnns | |
74 | * | |
75 | * @return | |
76 | */ | |
77 | 17 | protected AlignmentAnnotation[] buildAnnotations(int numAnns) |
78 | { | |
79 | 17 | List<AlignmentAnnotation> annlist = new ArrayList<AlignmentAnnotation>(); |
80 | 33373 | for (int i = 0; i < numAnns; i++) |
81 | { | |
82 | 33356 | AlignmentAnnotation ann = new AlignmentAnnotation(SS + i, "", 0); |
83 | 33356 | annlist.add(ann); |
84 | } | |
85 | 17 | 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 | 17 | private Alignment buildAlignment(int numSeqs) |
96 | { | |
97 | 17 | SequenceI[] seqs = new Sequence[numSeqs]; |
98 | 16715 | for (int i = 0; i < numSeqs; i++) |
99 | { | |
100 | 16698 | seqs[i] = new Sequence("Sequence" + i, "axrdkfp"); |
101 | } | |
102 | 17 | 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 | 1 | @Test(groups = { "Functional" }) |
118 | public void testSortBySequenceAndType_autocalcLast() | |
119 | { | |
120 | // @formatter:off | |
121 | 1 | anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; |
122 | 1 | anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; |
123 | 1 | anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron"; |
124 | 1 | anns[3].autoCalculated = true; anns[3].label = "Quality"; |
125 | 1 | anns[4].autoCalculated = true; anns[4].label = "Consensus"; |
126 | 1 | anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "label5"; |
127 | 1 | anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP"; |
128 | // @formatter:on | |
129 | ||
130 | 1 | AnnotationSorter testee = new AnnotationSorter(al, false); |
131 | 1 | testee.sort(anns, SequenceAnnotationOrder.SEQUENCE_AND_LABEL); |
132 | 1 | assertEquals("label5", anns[0].label); // for sequence 0 |
133 | 1 | assertEquals("label0", anns[1].label); // for sequence 1 |
134 | 1 | assertEquals("iron", anns[2].label); // sequence 3 /iron |
135 | 1 | assertEquals("IRP", anns[3].label); // sequence 3/IRP |
136 | 1 | assertEquals("structure", anns[4].label); // sequence 3/structure |
137 | 1 | assertEquals("Quality", anns[5].label); // autocalc annotations |
138 | 1 | assertEquals("Consensus", anns[6].label); // retain ordering |
139 | } | |
140 | ||
141 | /** | |
142 | * Variant with autocalculated annotations sorting to front | |
143 | */ | |
144 | 1 | @Test(groups = { "Functional" }) |
145 | public void testSortBySequenceAndType_autocalcFirst() | |
146 | { | |
147 | // @formatter:off | |
148 | 1 | anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; |
149 | 1 | anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; |
150 | 1 | anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron"; |
151 | 1 | anns[3].autoCalculated = true; anns[3].label = "Quality"; |
152 | 1 | anns[4].autoCalculated = true; anns[4].label = "Consensus"; |
153 | 1 | anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "label5"; |
154 | 1 | anns[6].sequenceRef = al.getSequenceAt(3); anns[6].label = "IRP"; |
155 | // @formatter:on | |
156 | ||
157 | 1 | AnnotationSorter testee = new AnnotationSorter(al, true); |
158 | 1 | testee.sort(anns, SequenceAnnotationOrder.SEQUENCE_AND_LABEL); |
159 | 1 | assertEquals("Quality", anns[0].label); // autocalc annotations |
160 | 1 | assertEquals("Consensus", anns[1].label); // retain ordering |
161 | 1 | assertEquals("label5", anns[2].label); // for sequence 0 |
162 | 1 | assertEquals("label0", anns[3].label); // for sequence 1 |
163 | 1 | assertEquals("iron", anns[4].label); // sequence 3 /iron |
164 | 1 | assertEquals("IRP", anns[5].label); // sequence 3/IRP |
165 | 1 | 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 | 1 | @Test(groups = { "Functional" }) |
181 | public void testSortByTypeAndSequence_autocalcLast() | |
182 | { | |
183 | // @formatter:off | |
184 | 1 | anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; |
185 | 1 | anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; |
186 | 1 | anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron"; |
187 | 1 | anns[3].autoCalculated = true; anns[3].label = "Quality"; |
188 | 1 | anns[4].autoCalculated = true; anns[4].label = "Consensus"; |
189 | 1 | anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "IRON"; |
190 | 1 | anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure"; |
191 | // @formatter:on | |
192 | ||
193 | 1 | AnnotationSorter testee = new AnnotationSorter(al, false); |
194 | 1 | testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); |
195 | 1 | assertEquals("IRON", anns[0].label); // IRON / sequence 0 |
196 | 1 | assertEquals("iron", anns[1].label); // iron / sequence 3 |
197 | 1 | assertEquals("label0", anns[2].label); // label0 / sequence 1 |
198 | 1 | assertEquals("Structure", anns[3].label); // Structure / sequence 2 |
199 | 1 | assertEquals("structure", anns[4].label); // structure / sequence 3 |
200 | 1 | assertEquals("Quality", anns[5].label); // autocalc annotations |
201 | 1 | assertEquals("Consensus", anns[6].label); // retain ordering |
202 | } | |
203 | ||
204 | /** | |
205 | * Variant of test with autocalculated annotations sorted to front | |
206 | */ | |
207 | 1 | @Test(groups = { "Functional" }) |
208 | public void testSortByTypeAndSequence_autocalcFirst() | |
209 | { | |
210 | // @formatter:off | |
211 | 1 | anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; |
212 | 1 | anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; |
213 | 1 | anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron"; |
214 | 1 | anns[3].autoCalculated = true; anns[3].label = "Quality"; |
215 | 1 | anns[4].autoCalculated = true; anns[4].label = "Consensus"; |
216 | 1 | anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "IRON"; |
217 | 1 | anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure"; |
218 | // @formatter:on | |
219 | ||
220 | 1 | AnnotationSorter testee = new AnnotationSorter(al, true); |
221 | 1 | testee.sort(anns, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); |
222 | 1 | assertEquals("Quality", anns[0].label); // autocalc annotations |
223 | 1 | assertEquals("Consensus", anns[1].label); // retain ordering |
224 | 1 | assertEquals("IRON", anns[2].label); // IRON / sequence 0 |
225 | 1 | assertEquals("iron", anns[3].label); // iron / sequence 3 |
226 | 1 | assertEquals("label0", anns[4].label); // label0 / sequence 1 |
227 | 1 | assertEquals("Structure", anns[5].label); // Structure / sequence 2 |
228 | 1 | 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 | 1 | @Test(groups = { "Functional" }) |
236 | public void testNoSort_autocalcFirst() | |
237 | { | |
238 | // @formatter:off | |
239 | 1 | anns[0].sequenceRef = al.getSequenceAt(1); anns[0].label = "label0"; |
240 | 1 | anns[1].sequenceRef = al.getSequenceAt(3); anns[1].label = "structure"; |
241 | 1 | anns[2].sequenceRef = al.getSequenceAt(3); anns[2].label = "iron"; |
242 | 1 | anns[3].autoCalculated = true; anns[3].label = "Quality"; |
243 | 1 | anns[4].autoCalculated = true; anns[4].label = "Consensus"; |
244 | 1 | anns[5].sequenceRef = al.getSequenceAt(0); anns[5].label = "IRON"; |
245 | 1 | anns[6].sequenceRef = al.getSequenceAt(2); anns[6].label = "Structure"; |
246 | // @formatter:on | |
247 | ||
248 | 1 | AnnotationSorter testee = new AnnotationSorter(al, true); |
249 | 1 | testee.sort(anns, SequenceAnnotationOrder.NONE); |
250 | 1 | assertEquals("Quality", anns[0].label); // autocalc annotations |
251 | 1 | assertEquals("Consensus", anns[1].label); // retain ordering |
252 | 1 | assertEquals("label0", anns[2].label); |
253 | 1 | assertEquals("structure", anns[3].label); |
254 | 1 | assertEquals("iron", anns[4].label); |
255 | 1 | assertEquals("IRON", anns[5].label); |
256 | 1 | assertEquals("Structure", anns[6].label); |
257 | } | |
258 | ||
259 | 1 | @Test(groups = { "Functional" }) |
260 | public void testSort_timingPresorted() | |
261 | { | |
262 | 1 | testTiming_presorted(50, 100); |
263 | 1 | testTiming_presorted(500, 1000); |
264 | 1 | 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 | 3 | private void testTiming_presorted(final int numSeqs, final int numAnns) |
274 | { | |
275 | 3 | Alignment alignment = buildAlignment(numSeqs); |
276 | 3 | AlignmentAnnotation[] annotations = buildAnnotations(numAnns); |
277 | ||
278 | /* | |
279 | * Set the annotations presorted by label | |
280 | */ | |
281 | 3 | Random r = new Random(); |
282 | 3 | final SequenceI[] sequences = alignment.getSequencesArray(); |
283 | 11103 | for (int i = 0; i < annotations.length; i++) |
284 | { | |
285 | 11100 | SequenceI randomSequenceRef = sequences[r.nextInt(sequences.length)]; |
286 | 11100 | annotations[i].sequenceRef = randomSequenceRef; |
287 | 11100 | annotations[i].label = "label" + i; |
288 | } | |
289 | 3 | long startTime = System.currentTimeMillis(); |
290 | 3 | AnnotationSorter testee = new AnnotationSorter(alignment, false); |
291 | 3 | testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); |
292 | 3 | long endTime = System.currentTimeMillis(); |
293 | 3 | final long elapsed = endTime - startTime; |
294 | 3 | 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 | 1 | @Test(groups = { "Functional" }) |
303 | public void testSort_timingUnsorted() | |
304 | { | |
305 | 1 | testTiming_unsorted(50, 100); |
306 | 1 | testTiming_unsorted(500, 1000); |
307 | 1 | 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 | 3 | private void testTiming_unsorted(final int numSeqs, final int numAnns) |
318 | { | |
319 | 3 | Alignment alignment = buildAlignment(numSeqs); |
320 | 3 | AlignmentAnnotation[] annotations = buildAnnotations(numAnns); |
321 | ||
322 | /* | |
323 | * Set the annotations in random order with respect to the sequences | |
324 | */ | |
325 | 3 | Random r = new Random(); |
326 | 3 | final SequenceI[] sequences = alignment.getSequencesArray(); |
327 | 11103 | for (int i = 0; i < annotations.length; i++) |
328 | { | |
329 | 11100 | SequenceI randomSequenceRef = sequences[r.nextInt(sequences.length)]; |
330 | 11100 | annotations[i].sequenceRef = randomSequenceRef; |
331 | 11100 | annotations[i].label = "label" + i; |
332 | } | |
333 | 3 | long startTime = System.currentTimeMillis(); |
334 | 3 | AnnotationSorter testee = new AnnotationSorter(alignment, false); |
335 | 3 | testee.sort(annotations, SequenceAnnotationOrder.SEQUENCE_AND_LABEL); |
336 | 3 | long endTime = System.currentTimeMillis(); |
337 | 3 | final long elapsed = endTime - startTime; |
338 | 3 | 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 | 1 | @Test(groups = { "Functional" }) |
347 | public void testSort_timingSemisorted() | |
348 | { | |
349 | 1 | testTiming_semiSorted(50, 100); |
350 | 1 | testTiming_semiSorted(500, 1000); |
351 | 1 | 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 | 3 | private void testTiming_semiSorted(final int numSeqs, final int numAnns) |
368 | { | |
369 | 3 | Alignment alignment = buildAlignment(numSeqs); |
370 | 3 | AlignmentAnnotation[] annotations = buildAnnotations(numAnns); |
371 | ||
372 | 3 | 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 | 3 | Random r = new Random(); |
379 | 3 | final SequenceI[] sequences = alignment.getSequencesArray(); |
380 | 11103 | for (int i = 0; i < annotations.length; i++) |
381 | { | |
382 | 11100 | SequenceI sequenceRef = sequences[i % sequences.length]; |
383 | 11100 | annotations[i].sequenceRef = sequenceRef; |
384 | 11100 | annotations[i].label = labels[r.nextInt(labels.length)]; |
385 | } | |
386 | 3 | long startTime = System.currentTimeMillis(); |
387 | 3 | AnnotationSorter testee = new AnnotationSorter(alignment, false); |
388 | 3 | testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); |
389 | 3 | long endTime = System.currentTimeMillis(); |
390 | 3 | long elapsed = endTime - startTime; |
391 | 3 | 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 | 3 | startTime = System.currentTimeMillis(); |
397 | 3 | testee.sort(annotations, SequenceAnnotationOrder.SEQUENCE_AND_LABEL); |
398 | 3 | endTime = System.currentTimeMillis(); |
399 | 3 | elapsed = endTime - startTime; |
400 | 3 | 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 | 3 | startTime = System.currentTimeMillis(); |
406 | 3 | testee.sort(annotations, SequenceAnnotationOrder.LABEL_AND_SEQUENCE); |
407 | 3 | endTime = System.currentTimeMillis(); |
408 | 3 | elapsed = endTime - startTime; |
409 | 3 | System.out.println( |
410 | "Resort by label for semisorted " + numSeqs + " sequences and " | |
411 | + numAnns + " annotations took " + elapsed + "ms"); | |
412 | } | |
413 | } |