JAL-3621 corrected sequence feature sort and test
[jalview.git] / test / jalview / datamodel / features / SequenceFeaturesTest.java
1 package jalview.datamodel.features;
2
3 import static org.testng.Assert.assertEquals;
4 import static org.testng.Assert.assertFalse;
5 import static org.testng.Assert.assertSame;
6 import static org.testng.Assert.assertTrue;
7
8 import java.util.ArrayList;
9 import java.util.Iterator;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Set;
13
14 import org.testng.annotations.Test;
15
16 import jalview.datamodel.SequenceFeature;
17 import junit.extensions.PA;
18
19 public class SequenceFeaturesTest
20 {
21   @Test(groups = "Functional")
22   public void testConstructor()
23   {
24     SequenceFeaturesI store = new SequenceFeatures();
25     assertFalse(store.hasFeatures());
26
27     store = new SequenceFeatures((List<SequenceFeature>) null);
28     assertFalse(store.hasFeatures());
29
30     List<SequenceFeature> features = new ArrayList<>();
31     store = new SequenceFeatures(features);
32     assertFalse(store.hasFeatures());
33
34     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
35             Float.NaN, null);
36     features.add(sf1);
37     SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 15, 18,
38             Float.NaN, null);
39     features.add(sf2); // nested
40     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc2", 0, 0,
41             Float.NaN, null); // non-positional
42     features.add(sf3);
43     store = new SequenceFeatures(features);
44     assertTrue(store.hasFeatures());
45     assertEquals(2, store.getFeatureCount(true)); // positional
46     assertEquals(1, store.getFeatureCount(false)); // non-positional
47     assertFalse(store.add(sf1)); // already contained
48     assertFalse(store.add(sf2)); // already contained
49     assertFalse(store.add(sf3)); // already contained
50   }
51
52   @Test(groups = "Functional")
53   public void testGetPositionalFeatures()
54   {
55     SequenceFeaturesI store = new SequenceFeatures();
56     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
57             Float.NaN, null);
58     store.add(sf1);
59     // same range, different description
60     SequenceFeature sf2 = new SequenceFeature("Metal", "desc2", 10, 20,
61             Float.NaN, null);
62     store.add(sf2);
63     // discontiguous range
64     SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 30, 40,
65             Float.NaN, null);
66     store.add(sf3);
67     // overlapping range
68     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 15, 35,
69             Float.NaN, null);
70     store.add(sf4);
71     // enclosing range
72     SequenceFeature sf5 = new SequenceFeature("Metal", "desc", 5, 50,
73             Float.NaN, null);
74     store.add(sf5);
75     // non-positional feature
76     SequenceFeature sf6 = new SequenceFeature("Metal", "desc", 0, 0,
77             Float.NaN, null);
78     store.add(sf6);
79     // contact feature
80     SequenceFeature sf7 = new SequenceFeature("Disulphide bond", "desc",
81             18, 45, Float.NaN, null);
82     store.add(sf7);
83     // different feature type
84     SequenceFeature sf8 = new SequenceFeature("Pfam", "desc", 30, 40,
85             Float.NaN, null);
86     store.add(sf8);
87     SequenceFeature sf9 = new SequenceFeature("Pfam", "desc", 15, 35,
88             Float.NaN, null);
89     store.add(sf9);
90
91     /*
92      * get all positional features
93      */
94     List<SequenceFeature> features = store.getPositionalFeatures();
95     assertEquals(features.size(), 8);
96     assertTrue(features.contains(sf1));
97     assertTrue(features.contains(sf2));
98     assertTrue(features.contains(sf3));
99     assertTrue(features.contains(sf4));
100     assertTrue(features.contains(sf5));
101     assertFalse(features.contains(sf6)); // non-positional
102     assertTrue(features.contains(sf7));
103     assertTrue(features.contains(sf8));
104     assertTrue(features.contains(sf9));
105
106     /*
107      * get features by type
108      */
109     assertTrue(store.getPositionalFeatures((String) null).isEmpty());
110     assertTrue(store.getPositionalFeatures("Cath").isEmpty());
111     assertTrue(store.getPositionalFeatures("METAL").isEmpty());
112
113     features = store.getPositionalFeatures("Metal");
114     assertEquals(features.size(), 5);
115     assertTrue(features.contains(sf1));
116     assertTrue(features.contains(sf2));
117     assertTrue(features.contains(sf3));
118     assertTrue(features.contains(sf4));
119     assertTrue(features.contains(sf5));
120     assertFalse(features.contains(sf6));
121
122     features = store.getPositionalFeatures("Disulphide bond");
123     assertEquals(features.size(), 1);
124     assertTrue(features.contains(sf7));
125
126     features = store.getPositionalFeatures("Pfam");
127     assertEquals(features.size(), 2);
128     assertTrue(features.contains(sf8));
129     assertTrue(features.contains(sf9));
130   }
131
132   @Test(groups = "Functional")
133   public void testGetContactFeatures()
134   {
135     SequenceFeaturesI store = new SequenceFeatures();
136     // non-contact
137     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
138             Float.NaN, null);
139     store.add(sf1);
140     // non-positional
141     SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 0, 0,
142             Float.NaN, null);
143     store.add(sf2);
144     // contact feature
145     SequenceFeature sf3 = new SequenceFeature("Disulphide bond", "desc",
146             18, 45, Float.NaN, null);
147     store.add(sf3);
148     // repeat for different feature type
149     SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 10, 20,
150             Float.NaN, null);
151     store.add(sf4);
152     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 0, 0,
153             Float.NaN, null);
154     store.add(sf5);
155     SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "desc", 18,
156             45, Float.NaN, null);
157     store.add(sf6);
158   
159     /*
160      * get all contact features
161      */
162     List<SequenceFeature> features = store.getContactFeatures();
163     assertEquals(features.size(), 2);
164     assertTrue(features.contains(sf3));
165     assertTrue(features.contains(sf6));
166   
167     /*
168      * get contact features by type
169      */
170     assertTrue(store.getContactFeatures((String) null).isEmpty());
171     assertTrue(store.getContactFeatures("Cath").isEmpty());
172     assertTrue(store.getContactFeatures("Pfam").isEmpty());
173     assertTrue(store.getContactFeatures("DISULPHIDE BOND").isEmpty());
174   
175     features = store.getContactFeatures("Disulphide bond");
176     assertEquals(features.size(), 1);
177     assertTrue(features.contains(sf3));
178   
179     features = store.getContactFeatures("Disulfide bond");
180     assertEquals(features.size(), 1);
181     assertTrue(features.contains(sf6));
182   }
183
184   @Test(groups = "Functional")
185   public void testGetNonPositionalFeatures()
186   {
187     SequenceFeaturesI store = new SequenceFeatures();
188     // positional
189     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
190             Float.NaN, null);
191     store.add(sf1);
192     // non-positional
193     SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 0, 0,
194             Float.NaN, null);
195     store.add(sf2);
196     // contact feature
197     SequenceFeature sf3 = new SequenceFeature("Disulphide bond", "desc",
198             18, 45, Float.NaN, null);
199     store.add(sf3);
200     // repeat for different feature type
201     SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 10, 20,
202             Float.NaN, null);
203     store.add(sf4);
204     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 0, 0,
205             Float.NaN, null);
206     store.add(sf5);
207     SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "desc", 18,
208             45, Float.NaN, null);
209     store.add(sf6);
210     // one more non-positional, different description
211     SequenceFeature sf7 = new SequenceFeature("Pfam", "desc2", 0, 0,
212             Float.NaN, null);
213     store.add(sf7);
214   
215     /*
216      * get all non-positional features
217      */
218     List<SequenceFeature> features = store.getNonPositionalFeatures();
219     assertEquals(features.size(), 3);
220     assertTrue(features.contains(sf2));
221     assertTrue(features.contains(sf5));
222     assertTrue(features.contains(sf7));
223   
224     /*
225      * get non-positional features by type
226      */
227     assertTrue(store.getNonPositionalFeatures((String) null).isEmpty());
228     assertTrue(store.getNonPositionalFeatures("Cath").isEmpty());
229     assertTrue(store.getNonPositionalFeatures("PFAM").isEmpty());
230   
231     features = store.getNonPositionalFeatures("Metal");
232     assertEquals(features.size(), 1);
233     assertTrue(features.contains(sf2));
234   
235     features = store.getNonPositionalFeatures("Pfam");
236     assertEquals(features.size(), 2);
237     assertTrue(features.contains(sf5));
238     assertTrue(features.contains(sf7));
239   }
240
241   /**
242    * Helper method to add a feature of no particular type
243    * 
244    * @param sf
245    * @param type
246    * @param from
247    * @param to
248    * @return
249    */
250   SequenceFeature addFeature(SequenceFeaturesI sf, String type, int from,
251           int to)
252   {
253     SequenceFeature sf1 = new SequenceFeature(type, "", from, to,
254             Float.NaN,
255             null);
256     sf.add(sf1);
257     return sf1;
258   }
259
260   @Test(groups = "Functional")
261   public void testFindFeatures()
262   {
263     SequenceFeaturesI sf = new SequenceFeatures();
264     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
265     SequenceFeature sf2 = addFeature(sf, "Pfam", 1, 15);
266     SequenceFeature sf3 = addFeature(sf, "Pfam", 20, 30);
267     SequenceFeature sf4 = addFeature(sf, "Pfam", 40, 100);
268     SequenceFeature sf5 = addFeature(sf, "Pfam", 60, 100);
269     SequenceFeature sf6 = addFeature(sf, "Pfam", 70, 70);
270     SequenceFeature sf7 = addFeature(sf, "Cath", 10, 50);
271     SequenceFeature sf8 = addFeature(sf, "Cath", 1, 15);
272     SequenceFeature sf9 = addFeature(sf, "Cath", 20, 30);
273     SequenceFeature sf10 = addFeature(sf, "Cath", 40, 100);
274     SequenceFeature sf11 = addFeature(sf, "Cath", 60, 100);
275     SequenceFeature sf12 = addFeature(sf, "Cath", 70, 70);
276   
277     List<SequenceFeature> overlaps = sf.findFeatures(200, 200, "Pfam");
278     assertTrue(overlaps.isEmpty());
279   
280     overlaps = sf.findFeatures( 1, 9, "Pfam");
281     assertEquals(overlaps.size(), 1);
282     assertTrue(overlaps.contains(sf2));
283   
284     overlaps = sf.findFeatures( 5, 18, "Pfam");
285     assertEquals(overlaps.size(), 2);
286     assertTrue(overlaps.contains(sf1));
287     assertTrue(overlaps.contains(sf2));
288   
289     overlaps = sf.findFeatures(30, 40, "Pfam");
290     assertEquals(overlaps.size(), 3);
291     assertTrue(overlaps.contains(sf1));
292     assertTrue(overlaps.contains(sf3));
293     assertTrue(overlaps.contains(sf4));
294   
295     overlaps = sf.findFeatures( 80, 90, "Pfam");
296     assertEquals(overlaps.size(), 2);
297     assertTrue(overlaps.contains(sf4));
298     assertTrue(overlaps.contains(sf5));
299   
300     overlaps = sf.findFeatures( 68, 70, "Pfam");
301     assertEquals(overlaps.size(), 3);
302     assertTrue(overlaps.contains(sf4));
303     assertTrue(overlaps.contains(sf5));
304     assertTrue(overlaps.contains(sf6));
305
306     overlaps = sf.findFeatures(16, 69, "Cath");
307     assertEquals(overlaps.size(), 4);
308     assertTrue(overlaps.contains(sf7));
309     assertFalse(overlaps.contains(sf8));
310     assertTrue(overlaps.contains(sf9));
311     assertTrue(overlaps.contains(sf10));
312     assertTrue(overlaps.contains(sf11));
313     assertFalse(overlaps.contains(sf12));
314
315     assertTrue(sf.findFeatures(0, 1000, "Metal").isEmpty());
316
317     overlaps = sf.findFeatures(7, 7, (String) null);
318     assertTrue(overlaps.isEmpty());
319   }
320
321   @Test(groups = "Functional")
322   public void testDelete()
323   {
324     SequenceFeaturesI sf = new SequenceFeatures();
325     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
326     assertTrue(sf.getPositionalFeatures().contains(sf1));
327
328     assertFalse(sf.delete(null));
329     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 15, 0f, null);
330     assertFalse(sf.delete(sf2)); // not added, can't delete it
331     assertTrue(sf.delete(sf1));
332     assertTrue(sf.getPositionalFeatures().isEmpty());
333   }
334
335   @Test(groups = "Functional")
336   public void testHasFeatures()
337   {
338     SequenceFeaturesI sf = new SequenceFeatures();
339     assertFalse(sf.hasFeatures());
340
341     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
342     assertTrue(sf.hasFeatures());
343
344     sf.delete(sf1);
345     assertFalse(sf.hasFeatures());
346   }
347
348   /**
349    * Tests for the method that gets feature groups for positional or
350    * non-positional features
351    */
352   @Test(groups = "Functional")
353   public void testGetFeatureGroups()
354   {
355     SequenceFeaturesI sf = new SequenceFeatures();
356     assertTrue(sf.getFeatureGroups(true).isEmpty());
357     assertTrue(sf.getFeatureGroups(false).isEmpty());
358
359     /*
360      * add a non-positional feature (begin/end = 0/0)
361      */
362     SequenceFeature sfx = new SequenceFeature("AType", "Desc", 0, 0, 0f,
363             "AGroup");
364     sf.add(sfx);
365     Set<String> groups = sf.getFeatureGroups(true); // for positional
366     assertTrue(groups.isEmpty());
367     groups = sf.getFeatureGroups(false); // for non-positional
368     assertEquals(groups.size(), 1);
369     assertTrue(groups.contains("AGroup"));
370     groups = sf.getFeatureGroups(false, "AType");
371     assertEquals(groups.size(), 1);
372     assertTrue(groups.contains("AGroup"));
373     groups = sf.getFeatureGroups(true, "AnotherType");
374     assertTrue(groups.isEmpty());
375
376     /*
377      * add, then delete, more non-positional features of different types
378      */
379     SequenceFeature sfy = new SequenceFeature("AnotherType", "Desc", 0, 0,
380             0f,
381             "AnotherGroup");
382     sf.add(sfy);
383     SequenceFeature sfz = new SequenceFeature("AThirdType", "Desc", 0, 0,
384             0f,
385             null);
386     sf.add(sfz);
387     groups = sf.getFeatureGroups(false);
388     assertEquals(groups.size(), 3);
389     assertTrue(groups.contains("AGroup"));
390     assertTrue(groups.contains("AnotherGroup"));
391     assertTrue(groups.contains(null)); // null is a possible group
392     sf.delete(sfz);
393     sf.delete(sfy);
394     groups = sf.getFeatureGroups(false);
395     assertEquals(groups.size(), 1);
396     assertTrue(groups.contains("AGroup"));
397
398     /*
399      * add positional features
400      */
401     SequenceFeature sf1 = new SequenceFeature("Pfam", "Desc", 10, 50, 0f,
402             "PfamGroup");
403     sf.add(sf1);
404     groups = sf.getFeatureGroups(true);
405     assertEquals(groups.size(), 1);
406     assertTrue(groups.contains("PfamGroup"));
407     groups = sf.getFeatureGroups(false); // non-positional unchanged
408     assertEquals(groups.size(), 1);
409     assertTrue(groups.contains("AGroup"));
410
411     SequenceFeature sf2 = new SequenceFeature("Cath", "Desc", 10, 50, 0f,
412             null);
413     sf.add(sf2);
414     groups = sf.getFeatureGroups(true);
415     assertEquals(groups.size(), 2);
416     assertTrue(groups.contains("PfamGroup"));
417     assertTrue(groups.contains(null));
418
419     sf.delete(sf1);
420     sf.delete(sf2);
421     assertTrue(sf.getFeatureGroups(true).isEmpty());
422
423     SequenceFeature sf3 = new SequenceFeature("CDS", "", 10, 50, 0f,
424             "Ensembl");
425     sf.add(sf3);
426     SequenceFeature sf4 = new SequenceFeature("exon", "", 10, 50, 0f,
427             "Ensembl");
428     sf.add(sf4);
429     groups = sf.getFeatureGroups(true);
430     assertEquals(groups.size(), 1);
431     assertTrue(groups.contains("Ensembl"));
432
433     /*
434      * delete last Ensembl group feature from CDS features
435      * but still have one in exon features
436      */
437     sf.delete(sf3);
438     groups = sf.getFeatureGroups(true);
439     assertEquals(groups.size(), 1);
440     assertTrue(groups.contains("Ensembl"));
441
442     /*
443      * delete the last non-positional feature
444      */
445     sf.delete(sfx);
446     groups = sf.getFeatureGroups(false);
447     assertTrue(groups.isEmpty());
448   }
449
450   @Test(groups = "Functional")
451   public void testGetFeatureTypesForGroups()
452   {
453     SequenceFeaturesI sf = new SequenceFeatures();
454     assertTrue(sf.getFeatureTypesForGroups(true, (String) null).isEmpty());
455   
456     /*
457      * add feature with group = "Uniprot", type = "helix"
458      */
459     String groupUniprot = "Uniprot";
460     SequenceFeature sf1 = new SequenceFeature("helix", "Desc", 10, 50, 0f,
461             groupUniprot);
462     sf.add(sf1);
463     Set<String> groups = sf.getFeatureTypesForGroups(true, groupUniprot);
464     assertEquals(groups.size(), 1);
465     assertTrue(groups.contains("helix"));
466     assertTrue(sf.getFeatureTypesForGroups(true, (String) null).isEmpty());
467   
468     /*
469      * add feature with group = "Uniprot", type = "strand"
470      */
471     SequenceFeature sf2 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
472             groupUniprot);
473     sf.add(sf2);
474     groups = sf.getFeatureTypesForGroups(true, groupUniprot);
475     assertEquals(groups.size(), 2);
476     assertTrue(groups.contains("helix"));
477     assertTrue(groups.contains("strand"));
478
479     /*
480      * delete the "strand" Uniprot feature - still have "helix"
481      */
482     sf.delete(sf2);
483     groups = sf.getFeatureTypesForGroups(true, groupUniprot);
484     assertEquals(groups.size(), 1);
485     assertTrue(groups.contains("helix"));
486
487     /*
488      * delete the "helix" Uniprot feature - none left
489      */
490     sf.delete(sf1);
491     assertTrue(sf.getFeatureTypesForGroups(true, groupUniprot).isEmpty());
492
493     /*
494      * add some null group features
495      */
496     SequenceFeature sf3 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
497             null);
498     sf.add(sf3);
499     SequenceFeature sf4 = new SequenceFeature("turn", "Desc", 10, 50, 0f,
500             null);
501     sf.add(sf4);
502     groups = sf.getFeatureTypesForGroups(true, (String) null);
503     assertEquals(groups.size(), 2);
504     assertTrue(groups.contains("strand"));
505     assertTrue(groups.contains("turn"));
506
507     /*
508      * add strand/Cath  and turn/Scop and query for one or both groups
509      * (find feature types for groups selected in Feature Settings)
510      */
511     SequenceFeature sf5 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
512             "Cath");
513     sf.add(sf5);
514     SequenceFeature sf6 = new SequenceFeature("turn", "Desc", 10, 50, 0f,
515             "Scop");
516     sf.add(sf6);
517     groups = sf.getFeatureTypesForGroups(true, "Cath");
518     assertEquals(groups.size(), 1);
519     assertTrue(groups.contains("strand"));
520     groups = sf.getFeatureTypesForGroups(true, "Scop");
521     assertEquals(groups.size(), 1);
522     assertTrue(groups.contains("turn"));
523     groups = sf.getFeatureTypesForGroups(true, "Cath", "Scop");
524     assertEquals(groups.size(), 2);
525     assertTrue(groups.contains("turn"));
526     assertTrue(groups.contains("strand"));
527     // alternative vararg syntax
528     groups = sf.getFeatureTypesForGroups(true, new String[] { "Cath",
529         "Scop" });
530     assertEquals(groups.size(), 2);
531     assertTrue(groups.contains("turn"));
532     assertTrue(groups.contains("strand"));
533   }
534
535   @Test(groups = "Functional")
536   public void testGetFeatureTypes()
537   {
538     SequenceFeaturesI store = new SequenceFeatures();
539     Set<String> types = store.getFeatureTypes();
540     assertTrue(types.isEmpty());
541
542     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
543             Float.NaN, null);
544     store.add(sf1);
545     types = store.getFeatureTypes();
546     assertEquals(types.size(), 1);
547     assertTrue(types.contains("Metal"));
548
549     // null type is rejected...
550     SequenceFeature sf2 = new SequenceFeature(null, "desc", 10, 20,
551             Float.NaN, null);
552     assertFalse(store.add(sf2));
553     types = store.getFeatureTypes();
554     assertEquals(types.size(), 1);
555     assertFalse(types.contains(null));
556     assertTrue(types.contains("Metal"));
557
558     /*
559      * add non-positional feature
560      */
561     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
562             Float.NaN, null);
563     store.add(sf3);
564     types = store.getFeatureTypes();
565     assertEquals(types.size(), 2);
566     assertTrue(types.contains("Pfam"));
567
568     /*
569      * add contact feature
570      */
571     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
572             10, 20, Float.NaN, null);
573     store.add(sf4);
574     types = store.getFeatureTypes();
575     assertEquals(types.size(), 3);
576     assertTrue(types.contains("Disulphide Bond"));
577
578     /*
579      * add another Pfam
580      */
581     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
582             Float.NaN, null);
583     store.add(sf5);
584     types = store.getFeatureTypes();
585     assertEquals(types.size(), 3); // unchanged
586
587     /*
588      * delete first Pfam - still have one
589      */
590     assertTrue(store.delete(sf3));
591     types = store.getFeatureTypes();
592     assertEquals(types.size(), 3);
593     assertTrue(types.contains("Pfam"));
594
595     /*
596      * delete second Pfam - no longer have one
597      */
598     assertTrue(store.delete(sf5));
599     types = store.getFeatureTypes();
600     assertEquals(types.size(), 2);
601     assertFalse(types.contains("Pfam"));
602   }
603
604   @Test(groups = "Functional")
605   public void testGetFeatureCount()
606   {
607     SequenceFeaturesI store = new SequenceFeatures();
608     assertEquals(store.getFeatureCount(true), 0);
609     assertEquals(store.getFeatureCount(false), 0);
610   
611     /*
612      * add positional
613      */
614     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
615             Float.NaN, null);
616     store.add(sf1);
617     assertEquals(store.getFeatureCount(true), 1);
618     assertEquals(store.getFeatureCount(false), 0);
619
620     /*
621      * null feature type is rejected
622      */
623     SequenceFeature sf2 = new SequenceFeature(null, "desc", 10, 20,
624             Float.NaN, null);
625     assertFalse(store.add(sf2));
626     assertEquals(store.getFeatureCount(true), 1);
627     assertEquals(store.getFeatureCount(false), 0);
628   
629     /*
630      * add non-positional feature
631      */
632     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
633             Float.NaN, null);
634     store.add(sf3);
635     assertEquals(store.getFeatureCount(true), 1);
636     assertEquals(store.getFeatureCount(false), 1);
637   
638     /*
639      * add contact feature (counts as 1)
640      */
641     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
642             10, 20, Float.NaN, null);
643     store.add(sf4);
644     assertEquals(store.getFeatureCount(true), 2);
645     assertEquals(store.getFeatureCount(false), 1);
646   
647     /*
648      * add another Pfam but this time as a positional feature
649      */
650     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
651             Float.NaN, null);
652     store.add(sf5);
653     assertEquals(store.getFeatureCount(true), 3); // sf1, sf4, sf5
654     assertEquals(store.getFeatureCount(false), 1); // sf3
655     assertEquals(store.getFeatureCount(true, "Pfam"), 1); // positional
656     assertEquals(store.getFeatureCount(false, "Pfam"), 1); // non-positional
657     // search for type==null
658     assertEquals(store.getFeatureCount(true, (String) null), 0);
659     // search with no type specified
660     assertEquals(store.getFeatureCount(true, (String[]) null), 3);
661     assertEquals(store.getFeatureCount(true, "Metal", "Cath"), 1);
662     assertEquals(store.getFeatureCount(true, "Disulphide Bond"), 1);
663     assertEquals(store.getFeatureCount(true, "Metal", "Pfam", null), 2);
664
665     /*
666      * delete first Pfam (non-positional)
667      */
668     assertTrue(store.delete(sf3));
669     assertEquals(store.getFeatureCount(true), 3);
670     assertEquals(store.getFeatureCount(false), 0);
671   
672     /*
673      * delete second Pfam (positional)
674      */
675     assertTrue(store.delete(sf5));
676     assertEquals(store.getFeatureCount(true), 2);
677     assertEquals(store.getFeatureCount(false), 0);
678   }
679
680   @Test(groups = "Functional")
681   public void testGetAllFeatures()
682   {
683     SequenceFeaturesI store = new SequenceFeatures();
684     List<SequenceFeature> features = store.getAllFeatures();
685     assertTrue(features.isEmpty());
686   
687     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
688             Float.NaN, null);
689     store.add(sf1);
690     features = store.getAllFeatures();
691     assertEquals(features.size(), 1);
692     assertTrue(features.contains(sf1));
693   
694     SequenceFeature sf2 = new SequenceFeature("Metallic", "desc", 10, 20,
695             Float.NaN, null);
696     store.add(sf2);
697     features = store.getAllFeatures();
698     assertEquals(features.size(), 2);
699     assertTrue(features.contains(sf2));
700   
701     /*
702      * add non-positional feature
703      */
704     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
705             Float.NaN, null);
706     store.add(sf3);
707     features = store.getAllFeatures();
708     assertEquals(features.size(), 3);
709     assertTrue(features.contains(sf3));
710   
711     /*
712      * add contact feature
713      */
714     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
715             10, 20, Float.NaN, null);
716     store.add(sf4);
717     features = store.getAllFeatures();
718     assertEquals(features.size(), 4);
719     assertTrue(features.contains(sf4));
720   
721     /*
722      * add another Pfam
723      */
724     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
725             Float.NaN, null);
726     store.add(sf5);
727     features = store.getAllFeatures();
728     assertEquals(features.size(), 5);
729     assertTrue(features.contains(sf5));
730
731     /*
732      * select by type does not apply to non-positional features
733      */
734     features = store.getAllFeatures("Cath");
735     assertEquals(features.size(), 1);
736     assertTrue(features.contains(sf3));
737
738     features = store.getAllFeatures("Pfam", "Cath", "Metal");
739     assertEquals(features.size(), 3);
740     assertTrue(features.contains(sf1));
741     assertTrue(features.contains(sf3));
742     assertTrue(features.contains(sf5));
743   
744     /*
745      * delete first Pfam
746      */
747     assertTrue(store.delete(sf3));
748     features = store.getAllFeatures();
749     assertEquals(features.size(), 4);
750     assertFalse(features.contains(sf3));
751   
752     /*
753      * delete second Pfam
754      */
755     assertTrue(store.delete(sf5));
756     features = store.getAllFeatures();
757     assertEquals(features.size(), 3);
758     assertFalse(features.contains(sf3));
759   }
760
761   @Test(groups = "Functional")
762   public void testGetTotalFeatureLength()
763   {
764     SequenceFeaturesI store = new SequenceFeatures();
765     assertEquals(store.getTotalFeatureLength(), 0);
766
767     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
768             Float.NaN, null);
769     assertTrue(store.add(sf1));
770     assertEquals(store.getTotalFeatureLength(), 11);
771     assertEquals(store.getTotalFeatureLength("Metal"), 11);
772     assertEquals(store.getTotalFeatureLength("Plastic"), 0);
773
774     // re-add does nothing!
775     assertFalse(store.add(sf1));
776     assertEquals(store.getTotalFeatureLength(), 11);
777
778     /*
779      * add non-positional feature
780      */
781     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
782             Float.NaN, null);
783     store.add(sf3);
784     assertEquals(store.getTotalFeatureLength(), 11);
785
786     /*
787      * add contact feature - counts 1 to feature length
788      */
789     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
790             10, 20, Float.NaN, null);
791     store.add(sf4);
792     assertEquals(store.getTotalFeatureLength(), 12);
793
794     /*
795      * add another Pfam
796      */
797     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
798             Float.NaN, null);
799     store.add(sf5);
800     assertEquals(store.getTotalFeatureLength(), 23);
801
802     /*
803      * delete features
804      */
805     assertTrue(store.delete(sf3)); // non-positional
806     assertEquals(store.getTotalFeatureLength(), 23); // no change
807
808     assertTrue(store.delete(sf5));
809     assertEquals(store.getTotalFeatureLength(), 12);
810
811     assertTrue(store.delete(sf4)); // contact
812     assertEquals(store.getTotalFeatureLength(), 11);
813
814     assertTrue(store.delete(sf1));
815     assertEquals(store.getTotalFeatureLength(), 0);
816   }
817
818   @Test(groups = "Functional")
819   public void testGetMinimumScore_getMaximumScore()
820   {
821     SequenceFeatures sf = new SequenceFeatures();
822     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 0, 0,
823             Float.NaN, "group"); // non-positional, no score
824     sf.add(sf1);
825     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 10, 20,
826             Float.NaN, "group"); // positional, no score
827     sf.add(sf2);
828     SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 10, 20, 1f,
829             "group");
830     sf.add(sf3);
831     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 12, 16, 4f,
832             "group");
833     sf.add(sf4);
834     SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 0, 0, 11f,
835             "group");
836     sf.add(sf5);
837     SequenceFeature sf6 = new SequenceFeature("Cath", "desc", 0, 0, -7f,
838             "group");
839     sf.add(sf6);
840
841     assertEquals(sf.getMinimumScore("nosuchtype", true), Float.NaN);
842     assertEquals(sf.getMinimumScore("nosuchtype", false), Float.NaN);
843     assertEquals(sf.getMaximumScore("nosuchtype", true), Float.NaN);
844     assertEquals(sf.getMaximumScore("nosuchtype", false), Float.NaN);
845
846     // positional features min-max:
847     assertEquals(sf.getMinimumScore("Metal", true), 1f);
848     assertEquals(sf.getMaximumScore("Metal", true), 4f);
849     assertEquals(sf.getMinimumScore("Cath", true), Float.NaN);
850     assertEquals(sf.getMaximumScore("Cath", true), Float.NaN);
851
852     // non-positional features min-max:
853     assertEquals(sf.getMinimumScore("Cath", false), -7f);
854     assertEquals(sf.getMaximumScore("Cath", false), 11f);
855     assertEquals(sf.getMinimumScore("Metal", false), Float.NaN);
856     assertEquals(sf.getMaximumScore("Metal", false), Float.NaN);
857
858     // delete features; min-max should get recomputed
859     sf.delete(sf6);
860     assertEquals(sf.getMinimumScore("Cath", false), 11f);
861     assertEquals(sf.getMaximumScore("Cath", false), 11f);
862     sf.delete(sf4);
863     assertEquals(sf.getMinimumScore("Metal", true), 1f);
864     assertEquals(sf.getMaximumScore("Metal", true), 1f);
865     sf.delete(sf5);
866     assertEquals(sf.getMinimumScore("Cath", false), Float.NaN);
867     assertEquals(sf.getMaximumScore("Cath", false), Float.NaN);
868     sf.delete(sf3);
869     assertEquals(sf.getMinimumScore("Metal", true), Float.NaN);
870     assertEquals(sf.getMaximumScore("Metal", true), Float.NaN);
871     sf.delete(sf1);
872     sf.delete(sf2);
873     assertFalse(sf.hasFeatures());
874     assertEquals(sf.getMinimumScore("Cath", false), Float.NaN);
875     assertEquals(sf.getMaximumScore("Cath", false), Float.NaN);
876     assertEquals(sf.getMinimumScore("Metal", true), Float.NaN);
877     assertEquals(sf.getMaximumScore("Metal", true), Float.NaN);
878   }
879
880   @Test(groups = "Functional")
881   public void testVarargsToTypes()
882   {
883     SequenceFeatures sf = new SequenceFeatures();
884     sf.add(new SequenceFeature("Metal", "desc", 0, 0, Float.NaN, "group"));
885     sf.add(new SequenceFeature("Cath", "desc", 10, 20, Float.NaN, "group"));
886
887     /*
888      * no type specified - get all types stored
889      * they are returned in keyset (alphabetical) order
890      */
891     Map<String, FeatureStore> featureStores = (Map<String, FeatureStore>) PA
892             .getValue(sf, "featureStore");
893
894     Iterable<FeatureStore> types = sf.varargToTypes();
895     Iterator<FeatureStore> iterator = types.iterator();
896     assertTrue(iterator.hasNext());
897     assertSame(iterator.next(), featureStores.get("Cath"));
898     assertTrue(iterator.hasNext());
899     assertSame(iterator.next(), featureStores.get("Metal"));
900     assertFalse(iterator.hasNext());
901
902     /*
903      * empty array is the same as no vararg parameter supplied
904      * so treated as all stored types
905      */
906     types = sf.varargToTypes(new String[] {});
907     iterator = types.iterator();
908     assertTrue(iterator.hasNext());
909     assertSame(iterator.next(), featureStores.get("Cath"));
910     assertTrue(iterator.hasNext());
911     assertSame(iterator.next(), featureStores.get("Metal"));
912     assertFalse(iterator.hasNext());
913
914     /*
915      * null type specified; this is passed as vararg
916      * String[1] {null}
917      */
918     types = sf.varargToTypes((String) null);
919     assertFalse(types.iterator().hasNext());
920
921     /*
922      * null types array specified; this is passed as vararg null
923      */
924     types = sf.varargToTypes((String[]) null);
925     iterator = types.iterator();
926     assertTrue(iterator.hasNext());
927     assertSame(iterator.next(), featureStores.get("Cath"));
928     assertTrue(iterator.hasNext());
929     assertSame(iterator.next(), featureStores.get("Metal"));
930     assertFalse(iterator.hasNext());
931
932     /*
933      * one type specified
934      */
935     types = sf.varargToTypes("Metal");
936     iterator = types.iterator();
937     assertTrue(iterator.hasNext());
938     assertSame(iterator.next(), featureStores.get("Metal"));
939     assertFalse(iterator.hasNext());
940
941     /*
942      * two types specified - order is preserved
943      */
944     types = sf.varargToTypes("Metal", "Cath");
945     iterator = types.iterator();
946     assertTrue(iterator.hasNext());
947     assertSame(iterator.next(), featureStores.get("Metal"));
948     assertTrue(iterator.hasNext());
949     assertSame(iterator.next(), featureStores.get("Cath"));
950     assertFalse(iterator.hasNext());
951
952     /*
953      * null type included - should be ignored
954      */
955     types = sf.varargToTypes("Metal", null, "Helix");
956     iterator = types.iterator();
957     assertTrue(iterator.hasNext());
958     assertSame(iterator.next(), featureStores.get("Metal"));
959     assertFalse(iterator.hasNext());
960   }
961
962   @Test(groups = "Functional")
963   public void testGetFeatureTypes_byOntology()
964   {
965     SequenceFeaturesI store = new SequenceFeatures();
966   
967     SequenceFeature sf1 = new SequenceFeature("transcript", "desc", 10, 20,
968             Float.NaN, null);
969     store.add(sf1);
970     // mRNA isA mature_transcript isA transcript
971     SequenceFeature sf2 = new SequenceFeature("mRNA", "desc", 10, 20,
972             Float.NaN, null);
973     store.add(sf2);
974     // just to prove non-positional feature types are included
975     SequenceFeature sf3 = new SequenceFeature("mRNA", "desc", 0, 0,
976             Float.NaN, null);
977     store.add(sf3);
978     SequenceFeature sf4 = new SequenceFeature("CDS", "desc", 0, 0,
979             Float.NaN, null);
980     store.add(sf4);
981
982     Set<String> types = store.getFeatureTypes("transcript");
983     assertEquals(types.size(), 2);
984     assertTrue(types.contains("transcript"));
985     assertTrue(types.contains("mRNA"));
986
987     // matches include arguments whether SO terms or not
988     types = store.getFeatureTypes("transcript", "CDS");
989     assertEquals(types.size(), 3);
990     assertTrue(types.contains("transcript"));
991     assertTrue(types.contains("mRNA"));
992     assertTrue(types.contains("CDS"));
993
994     types = store.getFeatureTypes("exon");
995     assertTrue(types.isEmpty());
996   }
997
998   @Test(groups = "Functional")
999   public void testGetFeaturesByOntology()
1000   {
1001     SequenceFeaturesI store = new SequenceFeatures();
1002     List<SequenceFeature> features = store.getFeaturesByOntology();
1003     assertTrue(features.isEmpty());
1004     assertTrue(store.getFeaturesByOntology(new String[] {}).isEmpty());
1005     assertTrue(store.getFeaturesByOntology((String[]) null).isEmpty());
1006   
1007     SequenceFeature transcriptFeature = new SequenceFeature("transcript", "desc", 10, 20,
1008             Float.NaN, null);
1009     store.add(transcriptFeature);
1010
1011     /*
1012      * mRNA is a sub-type of transcript; added here 'as if' non-positional
1013      * just to show that non-positional features are included in results
1014      */
1015     SequenceFeature mrnaFeature = new SequenceFeature("mRNA", "desc", 0, 0,
1016             Float.NaN, null);
1017     store.add(mrnaFeature);
1018
1019     SequenceFeature pfamFeature = new SequenceFeature("Pfam", "desc", 30, 40,
1020             Float.NaN, null);
1021     store.add(pfamFeature);
1022
1023     /*
1024      * "transcript" matches both itself and the sub-term "mRNA"
1025      */
1026     features = store.getFeaturesByOntology("transcript");
1027     assertEquals(features.size(), 2);
1028     assertTrue(features.contains(transcriptFeature));
1029     assertTrue(features.contains(mrnaFeature));
1030
1031     /*
1032      * "mRNA" matches itself but not parent term "transcript"
1033      */
1034     features = store.getFeaturesByOntology("mRNA");
1035     assertEquals(features.size(), 1);
1036     assertTrue(features.contains(mrnaFeature));
1037
1038     /*
1039      * "pfam" is not an SO term but is included as an exact match
1040      */
1041     features = store.getFeaturesByOntology("mRNA", "Pfam");
1042     assertEquals(features.size(), 2);
1043     assertTrue(features.contains(mrnaFeature));
1044     assertTrue(features.contains(pfamFeature));
1045
1046     features = store.getFeaturesByOntology("sequence_variant");
1047     assertTrue(features.isEmpty());
1048   }
1049
1050   @Test(groups = "Functional")
1051   public void testSortFeatures()
1052   {
1053     List<SequenceFeature> sfs = new ArrayList<>();
1054     SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 30,
1055             60,
1056             Float.NaN, null);
1057     sfs.add(sf1);
1058     SequenceFeature sf2 = new SequenceFeature("Rfam", "desc", 40, 50,
1059             Float.NaN, null);
1060     sfs.add(sf2);
1061     SequenceFeature sf3 = new SequenceFeature("Rfam", "desc", 50, 60,
1062             Float.NaN, null);
1063     sfs.add(sf3);
1064     SequenceFeature sf4 = new SequenceFeature("Xfam", "desc", 30,
1065             80,
1066             Float.NaN, null);
1067     sfs.add(sf4);
1068     SequenceFeature sf5 = new SequenceFeature("Xfam", "desc", 30,
1069             90,
1070             Float.NaN, null);
1071     sfs.add(sf5);
1072
1073     /*
1074      * sort by end position descending, order unchanged if matched
1075      */
1076     SequenceFeatures.sortFeatures(sfs, false);
1077     assertSame(sfs.get(0), sf5); // end 90
1078     assertSame(sfs.get(1), sf4); // end 80
1079     assertSame(sfs.get(2), sf1); // end 60, start 50
1080     assertSame(sfs.get(3), sf3); // end 60, start 30
1081     assertSame(sfs.get(4), sf2); // end 50
1082
1083     /*
1084      * resort {5, 4, 1, 3, 2} by start position ascending, end descending
1085      */
1086     SequenceFeatures.sortFeatures(sfs, true);
1087     assertSame(sfs.get(0), sf5); // start 30, end 90
1088     assertSame(sfs.get(1), sf4); // start 30, end 80
1089     assertSame(sfs.get(2), sf1); // start 30, end 60
1090     assertSame(sfs.get(3), sf2); // start 40
1091     assertSame(sfs.get(4), sf3); // start 50
1092   }
1093
1094   @Test(groups = "Functional")
1095   public void testGetFeaturesForGroup()
1096   {
1097     SequenceFeaturesI store = new SequenceFeatures();
1098
1099     List<SequenceFeature> features = store.getFeaturesForGroup(true, null);
1100     assertTrue(features.isEmpty());
1101     assertTrue(store.getFeaturesForGroup(false, null).isEmpty());
1102     assertTrue(store.getFeaturesForGroup(true, "Uniprot").isEmpty());
1103     assertTrue(store.getFeaturesForGroup(false, "Uniprot").isEmpty());
1104
1105     SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 4, 10, 0f,
1106             null);
1107     SequenceFeature sf2 = new SequenceFeature("Pfam", "desc", 0, 0, 0f,
1108             null);
1109     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 4, 10, 0f,
1110             "Uniprot");
1111     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 0, 0, 0f,
1112             "Rfam");
1113     SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 5, 15, 0f,
1114             null);
1115     store.add(sf1);
1116     store.add(sf2);
1117     store.add(sf3);
1118     store.add(sf4);
1119     store.add(sf5);
1120
1121     // positional features for null group, any type
1122     features = store.getFeaturesForGroup(true, null);
1123     assertEquals(features.size(), 2);
1124     assertTrue(features.contains(sf1));
1125     assertTrue(features.contains(sf5));
1126
1127     // positional features for null group, specified type
1128     features = store.getFeaturesForGroup(true, null, new String[] { "Pfam",
1129         "Xfam" });
1130     assertEquals(features.size(), 1);
1131     assertTrue(features.contains(sf1));
1132     features = store.getFeaturesForGroup(true, null, new String[] { "Pfam",
1133         "Xfam", "Cath" });
1134     assertEquals(features.size(), 2);
1135     assertTrue(features.contains(sf1));
1136     assertTrue(features.contains(sf5));
1137
1138     // positional features for non-null group, any type
1139     features = store.getFeaturesForGroup(true, "Uniprot");
1140     assertEquals(features.size(), 1);
1141     assertTrue(features.contains(sf3));
1142     assertTrue(store.getFeaturesForGroup(true, "Rfam").isEmpty());
1143
1144     // positional features for non-null group, specified type
1145     features = store.getFeaturesForGroup(true, "Uniprot", "Pfam", "Xfam",
1146             "Rfam");
1147     assertEquals(features.size(), 1);
1148     assertTrue(features.contains(sf3));
1149     assertTrue(store.getFeaturesForGroup(true, "Uniprot", "Cath").isEmpty());
1150
1151     // non-positional features for null group, any type
1152     features = store.getFeaturesForGroup(false, null);
1153     assertEquals(features.size(), 1);
1154     assertTrue(features.contains(sf2));
1155
1156     // non-positional features for null group, specified type
1157     features = store.getFeaturesForGroup(false, null, "Pfam", "Xfam");
1158     assertEquals(features.size(), 1);
1159     assertTrue(features.contains(sf2));
1160     assertTrue(store.getFeaturesForGroup(false, null, "Cath").isEmpty());
1161
1162     // non-positional features for non-null group, any type
1163     features = store.getFeaturesForGroup(false, "Rfam");
1164     assertEquals(features.size(), 1);
1165     assertTrue(features.contains(sf4));
1166     assertTrue(store.getFeaturesForGroup(false, "Uniprot").isEmpty());
1167
1168     // non-positional features for non-null group, specified type
1169     features = store.getFeaturesForGroup(false, "Rfam", "Pfam", "Metal");
1170     assertEquals(features.size(), 1);
1171     assertTrue(features.contains(sf4));
1172     assertTrue(store.getFeaturesForGroup(false, "Rfam", "Cath", "Pfam")
1173             .isEmpty());
1174   }
1175
1176   @Test(groups = "Functional")
1177   public void testShiftFeatures()
1178   {
1179     SequenceFeatures store = new SequenceFeatures();
1180     assertFalse(store.shiftFeatures(0, 1));
1181
1182     SequenceFeature sf1 = new SequenceFeature("Cath", "", 2, 5, 0f, null);
1183     store.add(sf1);
1184     // nested feature:
1185     SequenceFeature sf2 = new SequenceFeature("Metal", "", 8, 14, 0f, null);
1186     store.add(sf2);
1187     // contact feature:
1188     SequenceFeature sf3 = new SequenceFeature("Disulfide bond", "", 23, 32,
1189             0f, null);
1190     store.add(sf3);
1191     // non-positional feature:
1192     SequenceFeature sf4 = new SequenceFeature("Pfam", "", 0, 0, 0f, null);
1193     store.add(sf4);
1194   
1195     /*
1196      * shift features right by 5
1197      */
1198     assertTrue(store.shiftFeatures(0, 5));
1199   
1200     // non-positional features untouched:
1201     List<SequenceFeature> nonPos = store.getNonPositionalFeatures();
1202     assertEquals(nonPos.size(), 1);
1203     assertTrue(nonPos.contains(sf4));
1204   
1205     // positional features are replaced
1206     List<SequenceFeature> pos = store.getPositionalFeatures();
1207     assertEquals(pos.size(), 3);
1208     assertFalse(pos.contains(sf1));
1209     assertFalse(pos.contains(sf2));
1210     assertFalse(pos.contains(sf3));
1211     SequenceFeatures.sortFeatures(pos, true); // ascending start pos
1212     assertEquals(pos.get(0).getBegin(), 7);
1213     assertEquals(pos.get(0).getEnd(), 10);
1214     assertEquals(pos.get(0).getType(), "Cath");
1215     assertEquals(pos.get(1).getBegin(), 13);
1216     assertEquals(pos.get(1).getEnd(), 19);
1217     assertEquals(pos.get(1).getType(), "Metal");
1218     assertEquals(pos.get(2).getBegin(), 28);
1219     assertEquals(pos.get(2).getEnd(), 37);
1220     assertEquals(pos.get(2).getType(), "Disulfide bond");
1221   
1222     /*
1223      * now shift left by 15
1224      * feature at [7-10] should be removed
1225      * feature at [13-19] should become [1-4] 
1226      */
1227     assertTrue(store.shiftFeatures(0, -15));
1228     pos = store.getPositionalFeatures();
1229     assertEquals(pos.size(), 2);
1230     SequenceFeatures.sortFeatures(pos, true);
1231     assertEquals(pos.get(0).getBegin(), 1);
1232     assertEquals(pos.get(0).getEnd(), 4);
1233     assertEquals(pos.get(0).getType(), "Metal");
1234     assertEquals(pos.get(1).getBegin(), 13);
1235     assertEquals(pos.get(1).getEnd(), 22);
1236     assertEquals(pos.get(1).getType(), "Disulfide bond");
1237
1238     /*
1239      * shift right by 4 from column 2
1240      * feature at [1-4] should be unchanged
1241      * feature at [13-22] should become [17-26] 
1242      */
1243     assertTrue(store.shiftFeatures(2, 4));
1244     pos = store.getPositionalFeatures();
1245     assertEquals(pos.size(), 2);
1246     SequenceFeatures.sortFeatures(pos, true);
1247     assertEquals(pos.get(0).getBegin(), 1);
1248     assertEquals(pos.get(0).getEnd(), 4);
1249     assertEquals(pos.get(0).getType(), "Metal");
1250     assertEquals(pos.get(1).getBegin(), 17);
1251     assertEquals(pos.get(1).getEnd(), 26);
1252     assertEquals(pos.get(1).getType(), "Disulfide bond");
1253
1254     /*
1255      * shift right from column 18
1256      * should be no updates
1257      */
1258     SequenceFeature f1 = pos.get(0);
1259     SequenceFeature f2 = pos.get(1);
1260     assertFalse(store.shiftFeatures(18, 6));
1261     pos = store.getPositionalFeatures();
1262     assertEquals(pos.size(), 2);
1263     SequenceFeatures.sortFeatures(pos, true);
1264     assertSame(pos.get(0), f1);
1265     assertSame(pos.get(1), f2);
1266   }
1267
1268   @Test(groups = "Functional")
1269   public void testIsOntologyTerm()
1270   {
1271     SequenceFeatures store = new SequenceFeatures();
1272     assertTrue(store.isOntologyTerm("gobbledygook"));
1273     assertTrue(store.isOntologyTerm("transcript", "transcript"));
1274     assertTrue(store.isOntologyTerm("mRNA", "transcript"));
1275     assertFalse(store.isOntologyTerm("transcript", "mRNA"));
1276     assertTrue(store.isOntologyTerm("junk", "transcript", "junk"));
1277     assertTrue(store.isOntologyTerm("junk", new String[] {}));
1278     assertTrue(store.isOntologyTerm("junk", (String[]) null));
1279   }
1280
1281   @Test(groups = "Functional")
1282   public void testDeleteAll()
1283   {
1284     SequenceFeaturesI store = new SequenceFeatures();
1285     assertFalse(store.hasFeatures());
1286     store.deleteAll();
1287     assertFalse(store.hasFeatures());
1288     store.add(new SequenceFeature("Cath", "Desc", 12, 20, 0f, "Group"));
1289     store.add(new SequenceFeature("Pfam", "Desc", 6, 12, 2f, "Group2"));
1290     assertTrue(store.hasFeatures());
1291     store.deleteAll();
1292     assertFalse(store.hasFeatures());
1293   }
1294 }