5ff2d7bac1b3c5fd2ac475fbc16d744b0751689c
[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 jalview.datamodel.SequenceFeature;
9
10 import java.util.ArrayList;
11 import java.util.Iterator;
12 import java.util.List;
13 import java.util.Set;
14
15 import org.testng.annotations.Test;
16
17 public class SequenceFeaturesTest
18 {
19   @Test(groups = "Functional")
20   public void testGetPositionalFeatures()
21   {
22     SequenceFeaturesI store = new SequenceFeatures();
23     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
24             Float.NaN, null);
25     store.add(sf1);
26     // same range, different description
27     SequenceFeature sf2 = new SequenceFeature("Metal", "desc2", 10, 20,
28             Float.NaN, null);
29     store.add(sf2);
30     // discontiguous range
31     SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 30, 40,
32             Float.NaN, null);
33     store.add(sf3);
34     // overlapping range
35     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 15, 35,
36             Float.NaN, null);
37     store.add(sf4);
38     // enclosing range
39     SequenceFeature sf5 = new SequenceFeature("Metal", "desc", 5, 50,
40             Float.NaN, null);
41     store.add(sf5);
42     // non-positional feature
43     SequenceFeature sf6 = new SequenceFeature("Metal", "desc", 0, 0,
44             Float.NaN, null);
45     store.add(sf6);
46     // contact feature
47     SequenceFeature sf7 = new SequenceFeature("Disulphide bond", "desc",
48             18, 45, Float.NaN, null);
49     store.add(sf7);
50     // different feature type
51     SequenceFeature sf8 = new SequenceFeature("Pfam", "desc", 30, 40,
52             Float.NaN, null);
53     store.add(sf8);
54     SequenceFeature sf9 = new SequenceFeature("Pfam", "desc", 15, 35,
55             Float.NaN, null);
56     store.add(sf9);
57
58     /*
59      * get all positional features
60      */
61     List<SequenceFeature> features = store.getPositionalFeatures();
62     assertEquals(features.size(), 8);
63     assertTrue(features.contains(sf1));
64     assertTrue(features.contains(sf2));
65     assertTrue(features.contains(sf3));
66     assertTrue(features.contains(sf4));
67     assertTrue(features.contains(sf5));
68     assertFalse(features.contains(sf6)); // non-positional
69     assertTrue(features.contains(sf7));
70     assertTrue(features.contains(sf8));
71     assertTrue(features.contains(sf9));
72
73     /*
74      * get features by type
75      */
76     assertTrue(store.getPositionalFeatures((String) null).isEmpty());
77     assertTrue(store.getPositionalFeatures("Cath").isEmpty());
78     assertTrue(store.getPositionalFeatures("METAL").isEmpty());
79
80     features = store.getPositionalFeatures("Metal");
81     assertEquals(features.size(), 5);
82     assertTrue(features.contains(sf1));
83     assertTrue(features.contains(sf2));
84     assertTrue(features.contains(sf3));
85     assertTrue(features.contains(sf4));
86     assertTrue(features.contains(sf5));
87     assertFalse(features.contains(sf6));
88
89     features = store.getPositionalFeatures("Disulphide bond");
90     assertEquals(features.size(), 1);
91     assertTrue(features.contains(sf7));
92
93     features = store.getPositionalFeatures("Pfam");
94     assertEquals(features.size(), 2);
95     assertTrue(features.contains(sf8));
96     assertTrue(features.contains(sf9));
97   }
98
99   @Test(groups = "Functional")
100   public void testGetContactFeatures()
101   {
102     SequenceFeaturesI store = new SequenceFeatures();
103     // non-contact
104     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
105             Float.NaN, null);
106     store.add(sf1);
107     // non-positional
108     SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 0, 0,
109             Float.NaN, null);
110     store.add(sf2);
111     // contact feature
112     SequenceFeature sf3 = new SequenceFeature("Disulphide bond", "desc",
113             18, 45, Float.NaN, null);
114     store.add(sf3);
115     // repeat for different feature type
116     SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 10, 20,
117             Float.NaN, null);
118     store.add(sf4);
119     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 0, 0,
120             Float.NaN, null);
121     store.add(sf5);
122     SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "desc", 18,
123             45, Float.NaN, null);
124     store.add(sf6);
125   
126     /*
127      * get all contact features
128      */
129     List<SequenceFeature> features = store.getContactFeatures();
130     assertEquals(features.size(), 2);
131     assertTrue(features.contains(sf3));
132     assertTrue(features.contains(sf6));
133   
134     /*
135      * get contact features by type
136      */
137     assertTrue(store.getContactFeatures((String) null).isEmpty());
138     assertTrue(store.getContactFeatures("Cath").isEmpty());
139     assertTrue(store.getContactFeatures("Pfam").isEmpty());
140     assertTrue(store.getContactFeatures("DISULPHIDE BOND").isEmpty());
141   
142     features = store.getContactFeatures("Disulphide bond");
143     assertEquals(features.size(), 1);
144     assertTrue(features.contains(sf3));
145   
146     features = store.getContactFeatures("Disulfide bond");
147     assertEquals(features.size(), 1);
148     assertTrue(features.contains(sf6));
149   }
150
151   @Test(groups = "Functional")
152   public void testGetNonPositionalFeatures()
153   {
154     SequenceFeaturesI store = new SequenceFeatures();
155     // positional
156     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
157             Float.NaN, null);
158     store.add(sf1);
159     // non-positional
160     SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 0, 0,
161             Float.NaN, null);
162     store.add(sf2);
163     // contact feature
164     SequenceFeature sf3 = new SequenceFeature("Disulphide bond", "desc",
165             18, 45, Float.NaN, null);
166     store.add(sf3);
167     // repeat for different feature type
168     SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 10, 20,
169             Float.NaN, null);
170     store.add(sf4);
171     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 0, 0,
172             Float.NaN, null);
173     store.add(sf5);
174     SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "desc", 18,
175             45, Float.NaN, null);
176     store.add(sf6);
177     // one more non-positional, different description
178     SequenceFeature sf7 = new SequenceFeature("Pfam", "desc2", 0, 0,
179             Float.NaN, null);
180     store.add(sf7);
181   
182     /*
183      * get all non-positional features
184      */
185     List<SequenceFeature> features = store.getNonPositionalFeatures();
186     assertEquals(features.size(), 3);
187     assertTrue(features.contains(sf2));
188     assertTrue(features.contains(sf5));
189     assertTrue(features.contains(sf7));
190   
191     /*
192      * get non-positional features by type
193      */
194     assertTrue(store.getNonPositionalFeatures((String) null).isEmpty());
195     assertTrue(store.getNonPositionalFeatures("Cath").isEmpty());
196     assertTrue(store.getNonPositionalFeatures("PFAM").isEmpty());
197   
198     features = store.getNonPositionalFeatures("Metal");
199     assertEquals(features.size(), 1);
200     assertTrue(features.contains(sf2));
201   
202     features = store.getNonPositionalFeatures("Pfam");
203     assertEquals(features.size(), 2);
204     assertTrue(features.contains(sf5));
205     assertTrue(features.contains(sf7));
206   }
207
208   /**
209    * Helper method to add a feature of no particular type
210    * 
211    * @param sf
212    * @param type
213    * @param from
214    * @param to
215    * @return
216    */
217   SequenceFeature addFeature(SequenceFeaturesI sf, String type, int from,
218           int to)
219   {
220     SequenceFeature sf1 = new SequenceFeature(type, "", from, to,
221             Float.NaN,
222             null);
223     sf.add(sf1);
224     return sf1;
225   }
226
227   @Test(groups = "Functional")
228   public void testFindFeatures()
229   {
230     SequenceFeaturesI sf = new SequenceFeatures();
231     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
232     SequenceFeature sf2 = addFeature(sf, "Pfam", 1, 15);
233     SequenceFeature sf3 = addFeature(sf, "Pfam", 20, 30);
234     SequenceFeature sf4 = addFeature(sf, "Pfam", 40, 100);
235     SequenceFeature sf5 = addFeature(sf, "Pfam", 60, 100);
236     SequenceFeature sf6 = addFeature(sf, "Pfam", 70, 70);
237     SequenceFeature sf7 = addFeature(sf, "Cath", 10, 50);
238     SequenceFeature sf8 = addFeature(sf, "Cath", 1, 15);
239     SequenceFeature sf9 = addFeature(sf, "Cath", 20, 30);
240     SequenceFeature sf10 = addFeature(sf, "Cath", 40, 100);
241     SequenceFeature sf11 = addFeature(sf, "Cath", 60, 100);
242     SequenceFeature sf12 = addFeature(sf, "Cath", 70, 70);
243   
244     List<SequenceFeature> overlaps = sf.findFeatures(200, 200, "Pfam");
245     assertTrue(overlaps.isEmpty());
246   
247     overlaps = sf.findFeatures( 1, 9, "Pfam");
248     assertEquals(overlaps.size(), 1);
249     assertTrue(overlaps.contains(sf2));
250   
251     overlaps = sf.findFeatures( 5, 18, "Pfam");
252     assertEquals(overlaps.size(), 2);
253     assertTrue(overlaps.contains(sf1));
254     assertTrue(overlaps.contains(sf2));
255   
256     overlaps = sf.findFeatures(30, 40, "Pfam");
257     assertEquals(overlaps.size(), 3);
258     assertTrue(overlaps.contains(sf1));
259     assertTrue(overlaps.contains(sf3));
260     assertTrue(overlaps.contains(sf4));
261   
262     overlaps = sf.findFeatures( 80, 90, "Pfam");
263     assertEquals(overlaps.size(), 2);
264     assertTrue(overlaps.contains(sf4));
265     assertTrue(overlaps.contains(sf5));
266   
267     overlaps = sf.findFeatures( 68, 70, "Pfam");
268     assertEquals(overlaps.size(), 3);
269     assertTrue(overlaps.contains(sf4));
270     assertTrue(overlaps.contains(sf5));
271     assertTrue(overlaps.contains(sf6));
272
273     overlaps = sf.findFeatures(16, 69, "Cath");
274     assertEquals(overlaps.size(), 4);
275     assertTrue(overlaps.contains(sf7));
276     assertFalse(overlaps.contains(sf8));
277     assertTrue(overlaps.contains(sf9));
278     assertTrue(overlaps.contains(sf10));
279     assertTrue(overlaps.contains(sf11));
280     assertFalse(overlaps.contains(sf12));
281
282     assertTrue(sf.findFeatures(0, 1000, "Metal").isEmpty());
283
284     overlaps = sf.findFeatures(7, 7, (String) null);
285     assertTrue(overlaps.isEmpty());
286   }
287
288   @Test(groups = "Functional")
289   public void testDelete()
290   {
291     SequenceFeaturesI sf = new SequenceFeatures();
292     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
293     assertTrue(sf.getPositionalFeatures().contains(sf1));
294
295     assertFalse(sf.delete(null));
296     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 15, 0f, null);
297     assertFalse(sf.delete(sf2)); // not added, can't delete it
298     assertTrue(sf.delete(sf1));
299     assertTrue(sf.getPositionalFeatures().isEmpty());
300   }
301
302   @Test(groups = "Functional")
303   public void testHasFeatures()
304   {
305     SequenceFeaturesI sf = new SequenceFeatures();
306     assertFalse(sf.hasFeatures());
307
308     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
309     assertTrue(sf.hasFeatures());
310
311     sf.delete(sf1);
312     assertFalse(sf.hasFeatures());
313   }
314
315   /**
316    * Tests for the method that gets feature groups for positional or
317    * non-positional features
318    */
319   @Test(groups = "Functional")
320   public void testGetFeatureGroups()
321   {
322     SequenceFeaturesI sf = new SequenceFeatures();
323     assertTrue(sf.getFeatureGroups(true).isEmpty());
324     assertTrue(sf.getFeatureGroups(false).isEmpty());
325
326     /*
327      * add a non-positional feature (begin/end = 0/0)
328      */
329     SequenceFeature sfx = new SequenceFeature("AType", "Desc", 0, 0, 0f,
330             "AGroup");
331     sf.add(sfx);
332     Set<String> groups = sf.getFeatureGroups(true); // for positional
333     assertTrue(groups.isEmpty());
334     groups = sf.getFeatureGroups(false); // for non-positional
335     assertEquals(groups.size(), 1);
336     assertTrue(groups.contains("AGroup"));
337
338     /*
339      * add, then delete, more non-positional features of different types
340      */
341     SequenceFeature sfy = new SequenceFeature("AnotherType", "Desc", 0, 0,
342             0f,
343             "AnotherGroup");
344     sf.add(sfy);
345     SequenceFeature sfz = new SequenceFeature("AThirdType", "Desc", 0, 0,
346             0f,
347             null);
348     sf.add(sfz);
349     groups = sf.getFeatureGroups(false);
350     assertEquals(groups.size(), 3);
351     assertTrue(groups.contains("AGroup"));
352     assertTrue(groups.contains("AnotherGroup"));
353     assertTrue(groups.contains(null)); // null is a possible group
354     sf.delete(sfz);
355     sf.delete(sfy);
356     groups = sf.getFeatureGroups(false);
357     assertEquals(groups.size(), 1);
358     assertTrue(groups.contains("AGroup"));
359
360     /*
361      * add positional features
362      */
363     SequenceFeature sf1 = new SequenceFeature("Pfam", "Desc", 10, 50, 0f,
364             "PfamGroup");
365     sf.add(sf1);
366     groups = sf.getFeatureGroups(true);
367     assertEquals(groups.size(), 1);
368     assertTrue(groups.contains("PfamGroup"));
369     groups = sf.getFeatureGroups(false); // non-positional unchanged
370     assertEquals(groups.size(), 1);
371     assertTrue(groups.contains("AGroup"));
372
373     SequenceFeature sf2 = new SequenceFeature("Cath", "Desc", 10, 50, 0f,
374             null);
375     sf.add(sf2);
376     groups = sf.getFeatureGroups(true);
377     assertEquals(groups.size(), 2);
378     assertTrue(groups.contains("PfamGroup"));
379     assertTrue(groups.contains(null));
380
381     sf.delete(sf1);
382     sf.delete(sf2);
383     assertTrue(sf.getFeatureGroups(true).isEmpty());
384
385     SequenceFeature sf3 = new SequenceFeature("CDS", "", 10, 50, 0f,
386             "Ensembl");
387     sf.add(sf3);
388     SequenceFeature sf4 = new SequenceFeature("exon", "", 10, 50, 0f,
389             "Ensembl");
390     sf.add(sf4);
391     groups = sf.getFeatureGroups(true);
392     assertEquals(groups.size(), 1);
393     assertTrue(groups.contains("Ensembl"));
394
395     /*
396      * delete last Ensembl group feature from CDS features
397      * but still have one in exon features
398      */
399     sf.delete(sf3);
400     groups = sf.getFeatureGroups(true);
401     assertEquals(groups.size(), 1);
402     assertTrue(groups.contains("Ensembl"));
403
404     /*
405      * delete the last non-positional feature
406      */
407     sf.delete(sfx);
408     groups = sf.getFeatureGroups(false);
409     assertTrue(groups.isEmpty());
410   }
411
412   @Test(groups = "Functional")
413   public void testGetFeatureTypesForGroups()
414   {
415     SequenceFeaturesI sf = new SequenceFeatures();
416     assertTrue(sf.getFeatureTypesForGroups(true, (String) null).isEmpty());
417   
418     /*
419      * add feature with group = "Uniprot", type = "helix"
420      */
421     String groupUniprot = "Uniprot";
422     SequenceFeature sf1 = new SequenceFeature("helix", "Desc", 10, 50, 0f,
423             groupUniprot);
424     sf.add(sf1);
425     Set<String> groups = sf.getFeatureTypesForGroups(true, groupUniprot);
426     assertEquals(groups.size(), 1);
427     assertTrue(groups.contains("helix"));
428     assertTrue(sf.getFeatureTypesForGroups(true, (String) null).isEmpty());
429   
430     /*
431      * add feature with group = "Uniprot", type = "strand"
432      */
433     SequenceFeature sf2 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
434             groupUniprot);
435     sf.add(sf2);
436     groups = sf.getFeatureTypesForGroups(true, groupUniprot);
437     assertEquals(groups.size(), 2);
438     assertTrue(groups.contains("helix"));
439     assertTrue(groups.contains("strand"));
440
441     /*
442      * delete the "strand" Uniprot feature - still have "helix"
443      */
444     sf.delete(sf2);
445     groups = sf.getFeatureTypesForGroups(true, groupUniprot);
446     assertEquals(groups.size(), 1);
447     assertTrue(groups.contains("helix"));
448
449     /*
450      * delete the "helix" Uniprot feature - none left
451      */
452     sf.delete(sf1);
453     assertTrue(sf.getFeatureTypesForGroups(true, groupUniprot).isEmpty());
454
455     /*
456      * add some null group features
457      */
458     SequenceFeature sf3 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
459             null);
460     sf.add(sf3);
461     SequenceFeature sf4 = new SequenceFeature("turn", "Desc", 10, 50, 0f,
462             null);
463     sf.add(sf4);
464     groups = sf.getFeatureTypesForGroups(true, (String) null);
465     assertEquals(groups.size(), 2);
466     assertTrue(groups.contains("strand"));
467     assertTrue(groups.contains("turn"));
468
469     /*
470      * add strand/Cath  and turn/Scop and query for one or both groups
471      * (find feature types for groups selected in Feature Settings)
472      */
473     SequenceFeature sf5 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
474             "Cath");
475     sf.add(sf5);
476     SequenceFeature sf6 = new SequenceFeature("turn", "Desc", 10, 50, 0f,
477             "Scop");
478     sf.add(sf6);
479     groups = sf.getFeatureTypesForGroups(true, "Cath");
480     assertEquals(groups.size(), 1);
481     assertTrue(groups.contains("strand"));
482     groups = sf.getFeatureTypesForGroups(true, "Scop");
483     assertEquals(groups.size(), 1);
484     assertTrue(groups.contains("turn"));
485     groups = sf.getFeatureTypesForGroups(true, "Cath", "Scop");
486     assertEquals(groups.size(), 2);
487     assertTrue(groups.contains("turn"));
488     assertTrue(groups.contains("strand"));
489     // alternative vararg syntax
490     groups = sf.getFeatureTypesForGroups(true, new String[] { "Cath",
491         "Scop" });
492     assertEquals(groups.size(), 2);
493     assertTrue(groups.contains("turn"));
494     assertTrue(groups.contains("strand"));
495   }
496
497   @Test(groups = "Functional")
498   public void testGetFeatureTypes()
499   {
500     SequenceFeaturesI store = new SequenceFeatures();
501     Set<String> types = store.getFeatureTypes();
502     assertTrue(types.isEmpty());
503
504     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
505             Float.NaN, null);
506     store.add(sf1);
507     types = store.getFeatureTypes();
508     assertEquals(types.size(), 1);
509     assertTrue(types.contains("Metal"));
510
511     // null type is rejected...
512     SequenceFeature sf2 = new SequenceFeature(null, "desc", 10, 20,
513             Float.NaN, null);
514     assertFalse(store.add(sf2));
515     types = store.getFeatureTypes();
516     assertEquals(types.size(), 1);
517     assertFalse(types.contains(null));
518     assertTrue(types.contains("Metal"));
519
520     /*
521      * add non-positional feature
522      */
523     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
524             Float.NaN, null);
525     store.add(sf3);
526     types = store.getFeatureTypes();
527     assertEquals(types.size(), 2);
528     assertTrue(types.contains("Pfam"));
529
530     /*
531      * add contact feature
532      */
533     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
534             10, 20, Float.NaN, null);
535     store.add(sf4);
536     types = store.getFeatureTypes();
537     assertEquals(types.size(), 3);
538     assertTrue(types.contains("Disulphide Bond"));
539
540     /*
541      * add another Pfam
542      */
543     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
544             Float.NaN, null);
545     store.add(sf5);
546     types = store.getFeatureTypes();
547     assertEquals(types.size(), 3); // unchanged
548
549     /*
550      * delete first Pfam - still have one
551      */
552     assertTrue(store.delete(sf3));
553     types = store.getFeatureTypes();
554     assertEquals(types.size(), 3);
555     assertTrue(types.contains("Pfam"));
556
557     /*
558      * delete second Pfam - no longer have one
559      */
560     assertTrue(store.delete(sf5));
561     types = store.getFeatureTypes();
562     assertEquals(types.size(), 2);
563     assertFalse(types.contains("Pfam"));
564   }
565
566   @Test(groups = "Functional")
567   public void testGetFeatureCount()
568   {
569     SequenceFeaturesI store = new SequenceFeatures();
570     assertEquals(store.getFeatureCount(true), 0);
571     assertEquals(store.getFeatureCount(false), 0);
572   
573     /*
574      * add positional
575      */
576     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
577             Float.NaN, null);
578     store.add(sf1);
579     assertEquals(store.getFeatureCount(true), 1);
580     assertEquals(store.getFeatureCount(false), 0);
581
582     /*
583      * null feature type is rejected
584      */
585     SequenceFeature sf2 = new SequenceFeature(null, "desc", 10, 20,
586             Float.NaN, null);
587     assertFalse(store.add(sf2));
588     assertEquals(store.getFeatureCount(true), 1);
589     assertEquals(store.getFeatureCount(false), 0);
590   
591     /*
592      * add non-positional feature
593      */
594     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
595             Float.NaN, null);
596     store.add(sf3);
597     assertEquals(store.getFeatureCount(true), 1);
598     assertEquals(store.getFeatureCount(false), 1);
599   
600     /*
601      * add contact feature (counts as 1)
602      */
603     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
604             10, 20, Float.NaN, null);
605     store.add(sf4);
606     assertEquals(store.getFeatureCount(true), 2);
607     assertEquals(store.getFeatureCount(false), 1);
608   
609     /*
610      * add another Pfam but this time as a positional feature
611      */
612     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
613             Float.NaN, null);
614     store.add(sf5);
615     assertEquals(store.getFeatureCount(true), 3); // sf1, sf4, sf5
616     assertEquals(store.getFeatureCount(false), 1); // sf3
617     assertEquals(store.getFeatureCount(true, "Pfam"), 1); // positional
618     assertEquals(store.getFeatureCount(false, "Pfam"), 1); // non-positional
619     // search for type==null
620     assertEquals(store.getFeatureCount(true, (String) null), 0);
621     // search with no type specified
622     assertEquals(store.getFeatureCount(true, (String[]) null), 3);
623     assertEquals(store.getFeatureCount(true, "Metal", "Cath"), 1);
624     assertEquals(store.getFeatureCount(true, "Disulphide Bond"), 1);
625     assertEquals(store.getFeatureCount(true, "Metal", "Pfam", null), 2);
626
627     /*
628      * delete first Pfam (non-positional)
629      */
630     assertTrue(store.delete(sf3));
631     assertEquals(store.getFeatureCount(true), 3);
632     assertEquals(store.getFeatureCount(false), 0);
633   
634     /*
635      * delete second Pfam (positional)
636      */
637     assertTrue(store.delete(sf5));
638     assertEquals(store.getFeatureCount(true), 2);
639     assertEquals(store.getFeatureCount(false), 0);
640   }
641
642   @Test(groups = "Functional")
643   public void testGetAllFeatures()
644   {
645     SequenceFeaturesI store = new SequenceFeatures();
646     List<SequenceFeature> features = store.getAllFeatures();
647     assertTrue(features.isEmpty());
648   
649     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
650             Float.NaN, null);
651     store.add(sf1);
652     features = store.getAllFeatures();
653     assertEquals(features.size(), 1);
654     assertTrue(features.contains(sf1));
655   
656     SequenceFeature sf2 = new SequenceFeature("Metallic", "desc", 10, 20,
657             Float.NaN, null);
658     store.add(sf2);
659     features = store.getAllFeatures();
660     assertEquals(features.size(), 2);
661     assertTrue(features.contains(sf2));
662   
663     /*
664      * add non-positional feature
665      */
666     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
667             Float.NaN, null);
668     store.add(sf3);
669     features = store.getAllFeatures();
670     assertEquals(features.size(), 3);
671     assertTrue(features.contains(sf3));
672   
673     /*
674      * add contact feature
675      */
676     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
677             10, 20, Float.NaN, null);
678     store.add(sf4);
679     features = store.getAllFeatures();
680     assertEquals(features.size(), 4);
681     assertTrue(features.contains(sf4));
682   
683     /*
684      * add another Pfam
685      */
686     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
687             Float.NaN, null);
688     store.add(sf5);
689     features = store.getAllFeatures();
690     assertEquals(features.size(), 5);
691     assertTrue(features.contains(sf5));
692     features = store.getAllFeatures("Cath");
693     assertTrue(features.isEmpty());
694     features = store.getAllFeatures("Pfam", "Cath", "Metal");
695     assertEquals(features.size(), 3);
696     assertTrue(features.contains(sf1));
697     assertTrue(features.contains(sf3));
698     assertTrue(features.contains(sf5));
699   
700     /*
701      * delete first Pfam
702      */
703     assertTrue(store.delete(sf3));
704     features = store.getAllFeatures();
705     assertEquals(features.size(), 4);
706     assertFalse(features.contains(sf3));
707   
708     /*
709      * delete second Pfam
710      */
711     assertTrue(store.delete(sf5));
712     features = store.getAllFeatures();
713     assertEquals(features.size(), 3);
714     assertFalse(features.contains(sf3));
715   }
716
717   @Test(groups = "Functional")
718   public void testGetTotalFeatureLength()
719   {
720     SequenceFeaturesI store = new SequenceFeatures();
721     assertEquals(store.getTotalFeatureLength(), 0);
722
723     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
724             Float.NaN, null);
725     assertTrue(store.add(sf1));
726     assertEquals(store.getTotalFeatureLength(), 11);
727
728     // re-add does nothing!
729     assertFalse(store.add(sf1));
730     assertEquals(store.getTotalFeatureLength(), 11);
731
732     /*
733      * add non-positional feature
734      */
735     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 0, 0,
736             Float.NaN, null);
737     store.add(sf3);
738     assertEquals(store.getTotalFeatureLength(), 11);
739
740     /*
741      * add contact feature - counts 1 to feature length
742      */
743     SequenceFeature sf4 = new SequenceFeature("Disulphide Bond", "desc",
744             10, 20, Float.NaN, null);
745     store.add(sf4);
746     assertEquals(store.getTotalFeatureLength(), 12);
747
748     /*
749      * add another Pfam
750      */
751     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 10, 20,
752             Float.NaN, null);
753     store.add(sf5);
754     assertEquals(store.getTotalFeatureLength(), 23);
755
756     /*
757      * delete features
758      */
759     assertTrue(store.delete(sf3)); // non-positional
760     assertEquals(store.getTotalFeatureLength(), 23); // no change
761
762     assertTrue(store.delete(sf5));
763     assertEquals(store.getTotalFeatureLength(), 12);
764
765     assertTrue(store.delete(sf4)); // contact
766     assertEquals(store.getTotalFeatureLength(), 11);
767
768     assertTrue(store.delete(sf1));
769     assertEquals(store.getTotalFeatureLength(), 0);
770   }
771
772   @Test(groups = "Functional")
773   public void testGetMinimumScore_getMaximumScore()
774   {
775     SequenceFeatures sf = new SequenceFeatures();
776     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 0, 0,
777             Float.NaN, "group"); // non-positional, no score
778     sf.add(sf1);
779     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 10, 20,
780             Float.NaN, "group"); // positional, no score
781     sf.add(sf2);
782     SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 10, 20, 1f,
783             "group");
784     sf.add(sf3);
785     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 12, 16, 4f,
786             "group");
787     sf.add(sf4);
788     SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 0, 0, 11f,
789             "group");
790     sf.add(sf5);
791     SequenceFeature sf6 = new SequenceFeature("Cath", "desc", 0, 0, -7f,
792             "group");
793     sf.add(sf6);
794
795     assertEquals(sf.getMinimumScore("nosuchtype", true), Float.NaN);
796     assertEquals(sf.getMinimumScore("nosuchtype", false), Float.NaN);
797     assertEquals(sf.getMaximumScore("nosuchtype", true), Float.NaN);
798     assertEquals(sf.getMaximumScore("nosuchtype", false), Float.NaN);
799
800     // positional features min-max:
801     assertEquals(sf.getMinimumScore("Metal", true), 1f);
802     assertEquals(sf.getMaximumScore("Metal", true), 4f);
803     assertEquals(sf.getMinimumScore("Cath", true), Float.NaN);
804     assertEquals(sf.getMaximumScore("Cath", true), Float.NaN);
805
806     // non-positional features min-max:
807     assertEquals(sf.getMinimumScore("Cath", false), -7f);
808     assertEquals(sf.getMaximumScore("Cath", false), 11f);
809     assertEquals(sf.getMinimumScore("Metal", false), Float.NaN);
810     assertEquals(sf.getMaximumScore("Metal", false), Float.NaN);
811
812     // delete features; min-max should get recomputed
813     sf.delete(sf6);
814     assertEquals(sf.getMinimumScore("Cath", false), 11f);
815     assertEquals(sf.getMaximumScore("Cath", false), 11f);
816     sf.delete(sf4);
817     assertEquals(sf.getMinimumScore("Metal", true), 1f);
818     assertEquals(sf.getMaximumScore("Metal", true), 1f);
819     sf.delete(sf5);
820     assertEquals(sf.getMinimumScore("Cath", false), Float.NaN);
821     assertEquals(sf.getMaximumScore("Cath", false), Float.NaN);
822     sf.delete(sf3);
823     assertEquals(sf.getMinimumScore("Metal", true), Float.NaN);
824     assertEquals(sf.getMaximumScore("Metal", true), Float.NaN);
825     sf.delete(sf1);
826     sf.delete(sf2);
827     assertFalse(sf.hasFeatures());
828     assertEquals(sf.getMinimumScore("Cath", false), Float.NaN);
829     assertEquals(sf.getMaximumScore("Cath", false), Float.NaN);
830     assertEquals(sf.getMinimumScore("Metal", true), Float.NaN);
831     assertEquals(sf.getMaximumScore("Metal", true), Float.NaN);
832   }
833
834   @Test(groups = "Functional")
835   public void testVarargsToTypes()
836   {
837     SequenceFeatures sf = new SequenceFeatures();
838     sf.add(new SequenceFeature("Metal", "desc", 0, 0, Float.NaN, "group"));
839     sf.add(new SequenceFeature("Cath", "desc", 10, 20, Float.NaN, "group"));
840
841     /*
842      * no type specified - get all types stored
843      * they are returned in keyset (alphabetical) order
844      */
845     Iterable<String> types = sf.varargToTypes();
846     Iterator<String> iterator = types.iterator();
847     assertTrue(iterator.hasNext());
848     assertEquals(iterator.next(), "Cath");
849     assertTrue(iterator.hasNext());
850     assertEquals(iterator.next(), "Metal");
851     assertFalse(iterator.hasNext());
852
853     /*
854      * empty array is the same as no vararg parameter supplied
855      * so treated as all stored types
856      */
857     types = sf.varargToTypes(new String[] {});
858     iterator = types.iterator();
859     assertTrue(iterator.hasNext());
860     assertEquals(iterator.next(), "Cath");
861     assertTrue(iterator.hasNext());
862     assertEquals(iterator.next(), "Metal");
863     assertFalse(iterator.hasNext());
864
865     /*
866      * null type specified; this is passed as vararg
867      * String[1] {null}
868      */
869     types = sf.varargToTypes((String) null);
870     assertFalse(types.iterator().hasNext());
871
872     /*
873      * null types array specified; this is passed as vararg null
874      */
875     types = sf.varargToTypes((String[]) null);
876     iterator = types.iterator();
877     assertTrue(iterator.hasNext());
878     assertEquals(iterator.next(), "Cath");
879     assertTrue(iterator.hasNext());
880     assertEquals(iterator.next(), "Metal");
881     assertFalse(iterator.hasNext());
882
883     /*
884      * one type specified
885      */
886     types = sf.varargToTypes("Metal");
887     iterator = types.iterator();
888     assertTrue(iterator.hasNext());
889     assertEquals(iterator.next(), "Metal");
890     assertFalse(iterator.hasNext());
891
892     /*
893      * two types specified
894      */
895     types = sf.varargToTypes("Metal", "Helix");
896     iterator = types.iterator();
897     assertTrue(iterator.hasNext());
898     assertEquals(iterator.next(), "Metal");
899     assertTrue(iterator.hasNext());
900     assertEquals(iterator.next(), "Helix");
901     assertFalse(iterator.hasNext());
902
903     /*
904      * null type included - should get removed
905      */
906     types = sf.varargToTypes("Metal", null, "Helix");
907     iterator = types.iterator();
908     assertTrue(iterator.hasNext());
909     assertEquals(iterator.next(), "Metal");
910     assertTrue(iterator.hasNext());
911     assertEquals(iterator.next(), "Helix");
912     assertFalse(iterator.hasNext());
913   }
914
915   @Test(groups = "Functional")
916   public void testGetFeatureTypes_byOntology()
917   {
918     SequenceFeaturesI store = new SequenceFeatures();
919   
920     SequenceFeature sf1 = new SequenceFeature("transcript", "desc", 10, 20,
921             Float.NaN, null);
922     store.add(sf1);
923     // mRNA isA mature_transcript isA transcript
924     SequenceFeature sf2 = new SequenceFeature("mRNA", "desc", 10, 20,
925             Float.NaN, null);
926     store.add(sf2);
927     // just to prove non-positional feature types are included
928     SequenceFeature sf3 = new SequenceFeature("mRNA", "desc", 0, 0,
929             Float.NaN, null);
930     store.add(sf3);
931     SequenceFeature sf4 = new SequenceFeature("CDS", "desc", 0, 0,
932             Float.NaN, null);
933     store.add(sf4);
934
935     Set<String> types = store.getFeatureTypes("transcript");
936     assertEquals(types.size(), 2);
937     assertTrue(types.contains("transcript"));
938     assertTrue(types.contains("mRNA"));
939
940     // matches include arguments whether SO terms or not
941     types = store.getFeatureTypes("transcript", "CDS");
942     assertEquals(types.size(), 3);
943     assertTrue(types.contains("transcript"));
944     assertTrue(types.contains("mRNA"));
945     assertTrue(types.contains("CDS"));
946
947     types = store.getFeatureTypes("exon");
948     assertTrue(types.isEmpty());
949   }
950
951   @Test(groups = "Functional")
952   public void testGetFeaturesByOntology()
953   {
954     SequenceFeaturesI store = new SequenceFeatures();
955     List<SequenceFeature> features = store.getFeaturesByOntology();
956     assertTrue(features.isEmpty());
957     assertTrue(store.getFeaturesByOntology(new String[] {}).isEmpty());
958     assertTrue(store.getFeaturesByOntology((String[]) null).isEmpty());
959   
960     SequenceFeature sf1 = new SequenceFeature("transcript", "desc", 10, 20,
961             Float.NaN, null);
962     store.add(sf1);
963
964     // mRNA isA transcript; added here 'as if' non-positional
965     // just to show that non-positional features are included in results
966     SequenceFeature sf2 = new SequenceFeature("mRNA", "desc", 0, 0,
967             Float.NaN, null);
968     store.add(sf2);
969
970     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 30, 40,
971             Float.NaN, null);
972     store.add(sf3);
973
974     features = store.getFeaturesByOntology("transcript");
975     assertEquals(features.size(), 2);
976     assertTrue(features.contains(sf1));
977     assertTrue(features.contains(sf2));
978
979     features = store.getFeaturesByOntology("mRNA");
980     assertEquals(features.size(), 1);
981     assertTrue(features.contains(sf2));
982
983     features = store.getFeaturesByOntology("mRNA", "Pfam");
984     assertEquals(features.size(), 2);
985     assertTrue(features.contains(sf2));
986     assertTrue(features.contains(sf3));
987   }
988
989   @Test(groups = "Functional")
990   public void testSortFeatures()
991   {
992     List<SequenceFeature> sfs = new ArrayList<SequenceFeature>();
993     SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 30, 80,
994             Float.NaN, null);
995     sfs.add(sf1);
996     SequenceFeature sf2 = new SequenceFeature("Rfam", "desc", 40, 50,
997             Float.NaN, null);
998     sfs.add(sf2);
999     SequenceFeature sf3 = new SequenceFeature("Rfam", "desc", 50, 60,
1000             Float.NaN, null);
1001     sfs.add(sf3);
1002
1003     // sort by end position descending
1004     SequenceFeatures.sortFeatures(sfs, false);
1005     assertSame(sfs.get(0), sf1);
1006     assertSame(sfs.get(1), sf3);
1007     assertSame(sfs.get(2), sf2);
1008
1009     // sort by start position ascending
1010     SequenceFeatures.sortFeatures(sfs, true);
1011     assertSame(sfs.get(0), sf1);
1012     assertSame(sfs.get(1), sf2);
1013     assertSame(sfs.get(2), sf3);
1014   }
1015 }