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