JAL-3383 removed FeatureStore.getFeatures() as 'breaks encapsulation'
[jalview.git] / test / jalview / datamodel / features / FeatureStoreTest.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.Collection;
12 import java.util.List;
13 import java.util.Set;
14
15 import org.testng.annotations.Test;
16
17 import junit.extensions.PA;
18
19 public class FeatureStoreTest
20 {
21   private FeatureStore newFeatureStore()
22   {
23     return new FeatureStore();
24   }
25
26   @Test(groups = "Functional")
27   public void testFindFeatures_nonNested()
28   {
29     FeatureStore fs = newFeatureStore();
30     fs.addFeature(new SequenceFeature("", "", 10, 20, Float.NaN,
31             null));
32     // same range different description
33     fs.addFeature(new SequenceFeature("", "desc", 10, 20, Float.NaN, null));
34     fs.addFeature(new SequenceFeature("", "", 15, 25, Float.NaN, null));
35     fs.addFeature(new SequenceFeature("", "", 20, 35, Float.NaN, null));
36
37     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
38     assertTrue(overlaps.isEmpty());
39
40     overlaps = fs.findOverlappingFeatures(8, 10);
41     assertEquals(overlaps.size(), 2);
42     assertEquals(overlaps.get(0).getEnd(), 20);
43     assertEquals(overlaps.get(1).getEnd(), 20);
44
45     overlaps = fs.findOverlappingFeatures(12, 16);
46     assertEquals(overlaps.size(), 3);
47     assertEquals(overlaps.get(0).getEnd(), 20);
48     assertEquals(overlaps.get(1).getEnd(), 20);
49     assertEquals(overlaps.get(2).getEnd(), 25);
50
51     overlaps = fs.findOverlappingFeatures(33, 33);
52     assertEquals(overlaps.size(), 1);
53     assertEquals(overlaps.get(0).getEnd(), 35);
54   }
55
56   @Test(groups = "Functional")
57   public void testFindFeatures_nested()
58   {
59     FeatureStore fs = newFeatureStore();
60     SequenceFeature sf1 = addFeature(fs, 10, 50);
61     SequenceFeature sf2 = addFeature(fs, 10, 40);
62     SequenceFeature sf3 = addFeature(fs, 20, 30);
63     // fudge feature at same location but different group (so is added)
64     SequenceFeature sf4 = new SequenceFeature("", "", 20, 30, Float.NaN,
65             "different group");
66     fs.addFeature(sf4);
67     SequenceFeature sf5 = addFeature(fs, 35, 36);
68
69     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
70     assertTrue(overlaps.isEmpty());
71
72     overlaps = fs.findOverlappingFeatures(10, 15);
73     assertEquals(overlaps.size(), 2);
74     assertTrue(overlaps.contains(sf1));
75     assertTrue(overlaps.contains(sf2));
76
77     overlaps = fs.findOverlappingFeatures(45, 60);
78     assertEquals(overlaps.size(), 1);
79     assertTrue(overlaps.contains(sf1));
80
81     overlaps = fs.findOverlappingFeatures(32, 38);
82     assertEquals(overlaps.size(), 3);
83     assertTrue(overlaps.contains(sf1));
84     assertTrue(overlaps.contains(sf2));
85     assertTrue(overlaps.contains(sf5));
86
87     overlaps = fs.findOverlappingFeatures(15, 25);
88     assertEquals(overlaps.size(), 4);
89     assertTrue(overlaps.contains(sf1));
90     assertTrue(overlaps.contains(sf2));
91     assertTrue(overlaps.contains(sf3));
92     assertTrue(overlaps.contains(sf4));
93   }
94
95   @Test(groups = "Functional")
96   public void testFindFeatures_mixed()
97   {
98     FeatureStore fs = newFeatureStore();
99     SequenceFeature sf1 = addFeature(fs, 10, 50);
100     SequenceFeature sf2 = addFeature(fs, 1, 15);
101     SequenceFeature sf3 = addFeature(fs, 20, 30);
102     SequenceFeature sf4 = addFeature(fs, 40, 100);
103     SequenceFeature sf5 = addFeature(fs, 60, 100);
104     SequenceFeature sf6 = addFeature(fs, 70, 70);
105
106     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(200, 200);
107     assertTrue(overlaps.isEmpty());
108
109     overlaps = fs.findOverlappingFeatures(1, 9);
110     assertEquals(overlaps.size(), 1);
111     assertTrue(overlaps.contains(sf2));
112
113     overlaps = fs.findOverlappingFeatures(5, 18);
114     assertEquals(overlaps.size(), 2);
115     assertTrue(overlaps.contains(sf1));
116     assertTrue(overlaps.contains(sf2));
117
118     overlaps = fs.findOverlappingFeatures(30, 40);
119     assertEquals(overlaps.size(), 3);
120     assertTrue(overlaps.contains(sf1));
121     assertTrue(overlaps.contains(sf3));
122     assertTrue(overlaps.contains(sf4));
123
124     overlaps = fs.findOverlappingFeatures(80, 90);
125     assertEquals(overlaps.size(), 2);
126     assertTrue(overlaps.contains(sf4));
127     assertTrue(overlaps.contains(sf5));
128
129     overlaps = fs.findOverlappingFeatures(68, 70);
130     assertEquals(overlaps.size(), 3);
131     assertTrue(overlaps.contains(sf4));
132     assertTrue(overlaps.contains(sf5));
133     assertTrue(overlaps.contains(sf6));
134   }
135
136   /**
137    * Helper method to add a feature of no particular type
138    * 
139    * @param fs
140    * @param from
141    * @param to
142    * @return
143    */
144   SequenceFeature addFeature(FeatureStore fs, int from, int to)
145   {
146     SequenceFeature sf1 = new SequenceFeature("", "", from, to, Float.NaN,
147             null);
148     fs.addFeature(sf1);
149     return sf1;
150   }
151
152   @Test(groups = "Functional")
153   public void testFindFeatures_contactFeatures()
154   {
155     FeatureStore fs = newFeatureStore();
156
157     SequenceFeature sf = new SequenceFeature("disulphide bond", "bond", 10,
158             20, Float.NaN, null);
159     fs.addFeature(sf);
160
161     /*
162      * neither contact point in range
163      */
164     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
165     assertTrue(overlaps.isEmpty());
166
167     /*
168      * neither contact point in range
169      */
170     overlaps = fs.findOverlappingFeatures(11, 19);
171     assertTrue(overlaps.isEmpty());
172
173     /*
174      * first contact point in range
175      */
176     overlaps = fs.findOverlappingFeatures(5, 15);
177     assertEquals(overlaps.size(), 1);
178     assertTrue(overlaps.contains(sf));
179
180     /*
181      * second contact point in range
182      */
183     overlaps = fs.findOverlappingFeatures(15, 25);
184     assertEquals(overlaps.size(), 1);
185     assertTrue(overlaps.contains(sf));
186
187     /*
188      * both contact points in range
189      */
190     overlaps = fs.findOverlappingFeatures(5, 25);
191     assertEquals(overlaps.size(), 1);
192     assertTrue(overlaps.contains(sf));
193   }
194
195   @Test(groups = "Functional")
196   public void testGetPositionalFeatures()
197   {
198     FeatureStore store = newFeatureStore();
199     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
200             Float.NaN, null);
201     store.addFeature(sf1);
202     // same range, different description
203     SequenceFeature sf2 = new SequenceFeature("Metal", "desc2", 10, 20,
204             Float.NaN, null);
205     store.addFeature(sf2);
206     // discontiguous range
207     SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 30, 40,
208             Float.NaN, null);
209     store.addFeature(sf3);
210     // overlapping range
211     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 15, 35,
212             Float.NaN, null);
213     store.addFeature(sf4);
214     // enclosing range
215     SequenceFeature sf5 = new SequenceFeature("Metal", "desc", 5, 50,
216             Float.NaN, null);
217     store.addFeature(sf5);
218     // non-positional feature
219     SequenceFeature sf6 = new SequenceFeature("Metal", "desc", 0, 0,
220             Float.NaN, null);
221     store.addFeature(sf6);
222     // contact feature
223     SequenceFeature sf7 = new SequenceFeature("Disulphide bond", "desc",
224             18, 45, Float.NaN, null);
225     store.addFeature(sf7);
226
227     List<SequenceFeature> features = store.getPositionalFeatures();
228     assertEquals(features.size(), 6);
229     assertTrue(features.contains(sf1));
230     assertTrue(features.contains(sf2));
231     assertTrue(features.contains(sf3));
232     assertTrue(features.contains(sf4));
233     assertTrue(features.contains(sf5));
234     assertFalse(features.contains(sf6));
235     assertTrue(features.contains(sf7));
236
237     features = store.getNonPositionalFeatures();
238     assertEquals(features.size(), 1);
239     assertTrue(features.contains(sf6));
240   }
241
242   @Test(groups = "Functional")
243   public void testDelete()
244   {
245     FeatureStore store = newFeatureStore();
246     SequenceFeature sf1 = addFeature(store, 10, 20);
247     assertTrue(store.getPositionalFeatures().contains(sf1));
248
249     /*
250      * simple deletion
251      */
252     assertTrue(store.delete(sf1));
253     assertTrue(store.getPositionalFeatures().isEmpty());
254
255     /*
256      * non-positional feature deletion
257      */
258     SequenceFeature sf2 = addFeature(store, 0, 0);
259     assertFalse(store.getPositionalFeatures().contains(sf2));
260     assertTrue(store.getNonPositionalFeatures().contains(sf2));
261     assertTrue(store.delete(sf2));
262     assertTrue(store.getNonPositionalFeatures().isEmpty());
263
264     /*
265      * contact feature deletion
266      */
267     SequenceFeature sf3 = new SequenceFeature("", "Disulphide Bond", 11,
268             23, Float.NaN, null);
269     store.addFeature(sf3);
270     assertEquals(store.getPositionalFeatures().size(), 1);
271     assertTrue(store.getPositionalFeatures().contains(sf3));
272     assertTrue(store.delete(sf3));
273     assertTrue(store.getPositionalFeatures().isEmpty());
274
275     /*
276      * nested feature deletion
277      */
278     SequenceFeature sf4 = addFeature(store, 20, 30);
279     SequenceFeature sf5 = addFeature(store, 22, 26); // to NCList
280     SequenceFeature sf6 = addFeature(store, 23, 24); // child of sf5
281     SequenceFeature sf7 = addFeature(store, 25, 25); // sibling of sf6
282     SequenceFeature sf8 = addFeature(store, 24, 24); // child of sf6
283     SequenceFeature sf9 = addFeature(store, 23, 23); // child of sf6
284     assertEquals(store.getPositionalFeatures().size(), 6);
285
286     // delete a node with children - they take its place
287     assertTrue(store.delete(sf6)); // sf8, sf9 should become children of sf5
288     assertEquals(store.getPositionalFeatures().size(), 5);
289     assertFalse(store.getPositionalFeatures().contains(sf6));
290
291     // delete a node with no children
292     assertTrue(store.delete(sf7));
293     assertEquals(store.getPositionalFeatures().size(), 4);
294     assertFalse(store.getPositionalFeatures().contains(sf7));
295
296     // delete root of NCList
297     assertTrue(store.delete(sf5));
298     assertEquals(store.getPositionalFeatures().size(), 3);
299     assertFalse(store.getPositionalFeatures().contains(sf5));
300
301     // continue the killing fields
302     assertTrue(store.delete(sf4));
303     assertEquals(store.getPositionalFeatures().size(), 2);
304     assertFalse(store.getPositionalFeatures().contains(sf4));
305
306     assertTrue(store.delete(sf9));
307     assertEquals(store.getPositionalFeatures().size(), 1);
308     assertFalse(store.getPositionalFeatures().contains(sf9));
309
310     assertTrue(store.delete(sf8));
311     assertTrue(store.getPositionalFeatures().isEmpty());
312   }
313
314   @Test(groups = "Functional")
315   public void testAddFeature()
316   {
317     FeatureStore fs = newFeatureStore();
318
319     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
320             Float.NaN, null);
321     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 20,
322             Float.NaN, null);
323
324     assertTrue(fs.addFeature(sf1));
325     assertEquals(fs.getFeatureCount(true), 1); // positional
326     assertEquals(fs.getFeatureCount(false), 0); // non-positional
327
328     /*
329      * re-adding the same or an identical feature should fail
330      */
331     assertFalse(fs.addFeature(sf1));
332     assertEquals(fs.getFeatureCount(true), 1);
333     assertFalse(fs.addFeature(sf2));
334     assertEquals(fs.getFeatureCount(true), 1);
335
336     /*
337      * add non-positional
338      */
339     SequenceFeature sf3 = new SequenceFeature("Cath", "", 0, 0, Float.NaN,
340             null);
341     assertTrue(fs.addFeature(sf3));
342     assertEquals(fs.getFeatureCount(true), 1); // positional
343     assertEquals(fs.getFeatureCount(false), 1); // non-positional
344     SequenceFeature sf4 = new SequenceFeature("Cath", "", 0, 0, Float.NaN,
345             null);
346     assertFalse(fs.addFeature(sf4)); // already stored
347     assertEquals(fs.getFeatureCount(true), 1); // positional
348     assertEquals(fs.getFeatureCount(false), 1); // non-positional
349
350     /*
351      * add contact
352      */
353     SequenceFeature sf5 = new SequenceFeature("Disulfide bond", "", 10, 20,
354             Float.NaN, null);
355     assertTrue(fs.addFeature(sf5));
356     assertEquals(fs.getFeatureCount(true), 2); // positional - add 1 for contact
357     assertEquals(fs.getFeatureCount(false), 1); // non-positional
358     SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "", 10, 20,
359             Float.NaN, null);
360     assertFalse(fs.addFeature(sf6)); // already stored
361     assertEquals(fs.getFeatureCount(true), 2); // no change
362     assertEquals(fs.getFeatureCount(false), 1); // no change
363   }
364
365   @Test(groups = "Functional")
366   public void testIsEmpty()
367   {
368     FeatureStore fs = newFeatureStore();
369     assertTrue(fs.isEmpty());
370     assertEquals(fs.getFeatureCount(true), 0);
371
372     /*
373      * non-nested feature
374      */
375     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
376             Float.NaN, null);
377     fs.addFeature(sf1);
378     assertFalse(fs.isEmpty());
379     assertEquals(fs.getFeatureCount(true), 1);
380     fs.delete(sf1);
381     assertTrue(fs.isEmpty());
382     assertEquals(fs.getFeatureCount(true), 0);
383
384     /*
385      * non-positional feature
386      */
387     sf1 = new SequenceFeature("Cath", "", 0, 0, Float.NaN, null);
388     fs.addFeature(sf1);
389     assertFalse(fs.isEmpty());
390     assertEquals(fs.getFeatureCount(false), 1); // non-positional
391     assertEquals(fs.getFeatureCount(true), 0); // positional
392     fs.delete(sf1);
393     assertTrue(fs.isEmpty());
394     assertEquals(fs.getFeatureCount(false), 0);
395
396     /*
397      * contact feature
398      */
399     sf1 = new SequenceFeature("Disulfide bond", "", 19, 49, Float.NaN, null);
400     fs.addFeature(sf1);
401     assertFalse(fs.isEmpty());
402     assertEquals(fs.getFeatureCount(true), 1);
403     fs.delete(sf1);
404     assertTrue(fs.isEmpty());
405     assertEquals(fs.getFeatureCount(true), 0);
406
407     /*
408      * sf2, sf3 added as nested features
409      */
410     sf1 = new SequenceFeature("Cath", "", 19, 49, Float.NaN, null);
411     SequenceFeature sf2 = new SequenceFeature("Cath", "", 20, 40,
412             Float.NaN, null);
413     SequenceFeature sf3 = new SequenceFeature("Cath", "", 25, 35,
414             Float.NaN, null);
415     fs.addFeature(sf1);
416     fs.addFeature(sf2);
417     fs.addFeature(sf3);
418     assertEquals(fs.getFeatureCount(true), 3);
419     assertTrue(fs.delete(sf1));
420     assertEquals(fs.getFeatureCount(true), 2);
421     Collection<SequenceFeature> intervalStore = (Collection<SequenceFeature>) PA
422             .getValue(fs, "features");
423     assertEquals(intervalStore.size(), 2);
424     assertFalse(fs.isEmpty());
425     assertTrue(fs.delete(sf2));
426     assertEquals(fs.getFeatureCount(true), 1);
427     assertFalse(fs.isEmpty());
428     assertTrue(fs.delete(sf3));
429     assertEquals(fs.getFeatureCount(true), 0);
430     assertTrue(fs.isEmpty()); // all gone
431   }
432
433   @Test(groups = "Functional")
434   public void testGetFeatureGroups()
435   {
436     FeatureStore fs = newFeatureStore();
437     assertTrue(fs.getFeatureGroups(true).isEmpty());
438     assertTrue(fs.getFeatureGroups(false).isEmpty());
439
440     SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 10, 20, 1f, "group1");
441     fs.addFeature(sf1);
442     Set<String> groups = fs.getFeatureGroups(true);
443     assertEquals(groups.size(), 1);
444     assertTrue(groups.contains("group1"));
445
446     /*
447      * add another feature of the same group, delete one, delete both
448      */
449     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group1");
450     fs.addFeature(sf2);
451     groups = fs.getFeatureGroups(true);
452     assertEquals(groups.size(), 1);
453     assertTrue(groups.contains("group1"));
454     fs.delete(sf2);
455     groups = fs.getFeatureGroups(true);
456     assertEquals(groups.size(), 1);
457     assertTrue(groups.contains("group1"));
458     fs.delete(sf1);
459     groups = fs.getFeatureGroups(true);
460     assertTrue(fs.getFeatureGroups(true).isEmpty());
461
462     SequenceFeature sf3 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group2");
463     fs.addFeature(sf3);
464     SequenceFeature sf4 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "Group2");
465     fs.addFeature(sf4);
466     SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 20, 30, 1f, null);
467     fs.addFeature(sf5);
468     groups = fs.getFeatureGroups(true);
469     assertEquals(groups.size(), 3);
470     assertTrue(groups.contains("group2"));
471     assertTrue(groups.contains("Group2")); // case sensitive
472     assertTrue(groups.contains(null)); // null allowed
473     assertTrue(fs.getFeatureGroups(false).isEmpty()); // non-positional
474
475     fs.delete(sf3);
476     groups = fs.getFeatureGroups(true);
477     assertEquals(groups.size(), 2);
478     assertFalse(groups.contains("group2"));
479     fs.delete(sf4);
480     groups = fs.getFeatureGroups(true);
481     assertEquals(groups.size(), 1);
482     assertFalse(groups.contains("Group2"));
483     fs.delete(sf5);
484     groups = fs.getFeatureGroups(true);
485     assertTrue(groups.isEmpty());
486
487     /*
488      * add non-positional feature
489      */
490     SequenceFeature sf6 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
491             "CathGroup");
492     fs.addFeature(sf6);
493     groups = fs.getFeatureGroups(false);
494     assertEquals(groups.size(), 1);
495     assertTrue(groups.contains("CathGroup"));
496     assertTrue(fs.delete(sf6));
497     assertTrue(fs.getFeatureGroups(false).isEmpty());
498   }
499
500   @Test(groups = "Functional")
501   public void testGetTotalFeatureLength()
502   {
503     FeatureStore fs = newFeatureStore();
504     assertEquals(fs.getTotalFeatureLength(), 0);
505
506     addFeature(fs, 10, 20); // 11
507     assertEquals(fs.getTotalFeatureLength(), 11);
508     addFeature(fs, 17, 37); // 21
509     SequenceFeature sf1 = addFeature(fs, 14, 74); // 61
510     assertEquals(fs.getTotalFeatureLength(), 93);
511
512     // non-positional features don't count
513     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
514             "group1");
515     fs.addFeature(sf2);
516     assertEquals(fs.getTotalFeatureLength(), 93);
517
518     // contact features count 1
519     SequenceFeature sf3 = new SequenceFeature("disulphide bond", "desc",
520             15, 35, 1f, "group1");
521     fs.addFeature(sf3);
522     assertEquals(fs.getTotalFeatureLength(), 94);
523
524     assertTrue(fs.delete(sf1));
525     assertEquals(fs.getTotalFeatureLength(), 33);
526     assertFalse(fs.delete(sf1));
527     assertEquals(fs.getTotalFeatureLength(), 33);
528     assertTrue(fs.delete(sf2));
529     assertEquals(fs.getTotalFeatureLength(), 33);
530     assertTrue(fs.delete(sf3));
531     assertEquals(fs.getTotalFeatureLength(), 32);
532   }
533
534   @Test(groups = "Functional")
535   public void testGetFeatureLength()
536   {
537     /*
538      * positional feature
539      */
540     SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 10, 20, 1f, "group1");
541     assertEquals(FeatureStore.getFeatureLength(sf1), 11);
542   
543     /*
544      * non-positional feature
545      */
546     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
547             "CathGroup");
548     assertEquals(FeatureStore.getFeatureLength(sf2), 0);
549
550     /*
551      * contact feature counts 1
552      */
553     SequenceFeature sf3 = new SequenceFeature("Disulphide Bond", "desc",
554             14, 28, 1f, "AGroup");
555     assertEquals(FeatureStore.getFeatureLength(sf3), 1);
556   }
557
558   @Test(groups = "Functional")
559   public void testMin()
560   {
561     assertEquals(FeatureStore.min(Float.NaN, Float.NaN), Float.NaN);
562     assertEquals(FeatureStore.min(Float.NaN, 2f), 2f);
563     assertEquals(FeatureStore.min(-2f, Float.NaN), -2f);
564     assertEquals(FeatureStore.min(2f, -3f), -3f);
565   }
566
567   @Test(groups = "Functional")
568   public void testMax()
569   {
570     assertEquals(FeatureStore.max(Float.NaN, Float.NaN), Float.NaN);
571     assertEquals(FeatureStore.max(Float.NaN, 2f), 2f);
572     assertEquals(FeatureStore.max(-2f, Float.NaN), -2f);
573     assertEquals(FeatureStore.max(2f, -3f), 2f);
574   }
575
576   @Test(groups = "Functional")
577   public void testGetMinimumScore_getMaximumScore()
578   {
579     FeatureStore fs = newFeatureStore();
580     assertEquals(fs.getMinimumScore(true), Float.NaN); // positional
581     assertEquals(fs.getMaximumScore(true), Float.NaN);
582     assertEquals(fs.getMinimumScore(false), Float.NaN); // non-positional
583     assertEquals(fs.getMaximumScore(false), Float.NaN);
584
585     // add features with no score
586     SequenceFeature sf1 = new SequenceFeature("type", "desc", 0, 0,
587             Float.NaN, "group");
588     fs.addFeature(sf1);
589     SequenceFeature sf2 = new SequenceFeature("type", "desc", 10, 20,
590             Float.NaN, "group");
591     fs.addFeature(sf2);
592     assertEquals(fs.getMinimumScore(true), Float.NaN);
593     assertEquals(fs.getMaximumScore(true), Float.NaN);
594     assertEquals(fs.getMinimumScore(false), Float.NaN);
595     assertEquals(fs.getMaximumScore(false), Float.NaN);
596
597     // add positional features with score
598     SequenceFeature sf3 = new SequenceFeature("type", "desc", 10, 20, 1f,
599             "group");
600     fs.addFeature(sf3);
601     SequenceFeature sf4 = new SequenceFeature("type", "desc", 12, 16, 4f,
602             "group");
603     fs.addFeature(sf4);
604     assertEquals(fs.getMinimumScore(true), 1f);
605     assertEquals(fs.getMaximumScore(true), 4f);
606     assertEquals(fs.getMinimumScore(false), Float.NaN);
607     assertEquals(fs.getMaximumScore(false), Float.NaN);
608
609     // add non-positional features with score
610     SequenceFeature sf5 = new SequenceFeature("type", "desc", 0, 0, 11f,
611             "group");
612     fs.addFeature(sf5);
613     SequenceFeature sf6 = new SequenceFeature("type", "desc", 0, 0, -7f,
614             "group");
615     fs.addFeature(sf6);
616     assertEquals(fs.getMinimumScore(true), 1f);
617     assertEquals(fs.getMaximumScore(true), 4f);
618     assertEquals(fs.getMinimumScore(false), -7f);
619     assertEquals(fs.getMaximumScore(false), 11f);
620
621     // delete one positional and one non-positional
622     // min-max should be recomputed
623     assertTrue(fs.delete(sf6));
624     assertTrue(fs.delete(sf3));
625     assertEquals(fs.getMinimumScore(true), 4f);
626     assertEquals(fs.getMaximumScore(true), 4f);
627     assertEquals(fs.getMinimumScore(false), 11f);
628     assertEquals(fs.getMaximumScore(false), 11f);
629
630     // delete remaining features with score
631     assertTrue(fs.delete(sf4));
632     assertTrue(fs.delete(sf5));
633     assertEquals(fs.getMinimumScore(true), Float.NaN);
634     assertEquals(fs.getMaximumScore(true), Float.NaN);
635     assertEquals(fs.getMinimumScore(false), Float.NaN);
636     assertEquals(fs.getMaximumScore(false), Float.NaN);
637
638     // delete all features
639     assertTrue(fs.delete(sf1));
640     assertTrue(fs.delete(sf2));
641     assertTrue(fs.isEmpty());
642     assertEquals(fs.getMinimumScore(true), Float.NaN);
643     assertEquals(fs.getMaximumScore(true), Float.NaN);
644     assertEquals(fs.getMinimumScore(false), Float.NaN);
645     assertEquals(fs.getMaximumScore(false), Float.NaN);
646   }
647
648   @Test(groups = "Functional")
649   public void testListContains()
650   {
651     FeatureStore featureStore = newFeatureStore();
652     assertFalse(featureStore.listContains(null, null));
653     List<SequenceFeature> features = new ArrayList<>();
654     assertFalse(featureStore.listContains(features, null));
655
656     SequenceFeature sf1 = new SequenceFeature("type1", "desc1", 20, 30, 3f,
657             "group1");
658     assertFalse(featureStore.listContains(null, sf1));
659     assertFalse(featureStore.listContains(features, sf1));
660
661     features.add(sf1);
662     SequenceFeature sf2 = new SequenceFeature("type1", "desc1", 20, 30, 3f,
663             "group1");
664     SequenceFeature sf3 = new SequenceFeature("type1", "desc1", 20, 40, 3f,
665             "group1");
666
667     // sf2.equals(sf1) so contains should return true
668     assertTrue(featureStore.listContains(features, sf2));
669     assertFalse(featureStore.listContains(features, sf3));
670   }
671
672   @Test(groups = "Functional")
673   public void testGetFeaturesForGroup()
674   {
675     FeatureStore fs = newFeatureStore();
676
677     /*
678      * with no features
679      */
680     assertTrue(fs.getFeaturesForGroup(true, null).isEmpty());
681     assertTrue(fs.getFeaturesForGroup(false, null).isEmpty());
682     assertTrue(fs.getFeaturesForGroup(true, "uniprot").isEmpty());
683     assertTrue(fs.getFeaturesForGroup(false, "uniprot").isEmpty());
684
685     /*
686      * sf1: positional feature in the null group
687      */
688     SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 4, 10, 0f,
689             null);
690     fs.addFeature(sf1);
691     assertTrue(fs.getFeaturesForGroup(true, "uniprot").isEmpty());
692     assertTrue(fs.getFeaturesForGroup(false, "uniprot").isEmpty());
693     assertTrue(fs.getFeaturesForGroup(false, null).isEmpty());
694     List<SequenceFeature> features = fs.getFeaturesForGroup(true, null);
695     assertEquals(features.size(), 1);
696     assertTrue(features.contains(sf1));
697
698     /*
699      * sf2: non-positional feature in the null group
700      * sf3: positional feature in a non-null group
701      * sf4: non-positional feature in a non-null group
702      */
703     SequenceFeature sf2 = new SequenceFeature("Pfam", "desc", 0, 0, 0f,
704             null);
705     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 4, 10, 0f,
706             "Uniprot");
707     SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 0, 0, 0f,
708             "Rfam");
709     fs.addFeature(sf2);
710     fs.addFeature(sf3);
711     fs.addFeature(sf4);
712
713     features = fs.getFeaturesForGroup(true, null);
714     assertEquals(features.size(), 1);
715     assertTrue(features.contains(sf1));
716
717     features = fs.getFeaturesForGroup(false, null);
718     assertEquals(features.size(), 1);
719     assertTrue(features.contains(sf2));
720
721     features = fs.getFeaturesForGroup(true, "Uniprot");
722     assertEquals(features.size(), 1);
723     assertTrue(features.contains(sf3));
724
725     features = fs.getFeaturesForGroup(false, "Rfam");
726     assertEquals(features.size(), 1);
727     assertTrue(features.contains(sf4));
728   }
729
730   @Test(groups = "Functional")
731   public void testShiftFeatures()
732   {
733     FeatureStore fs = newFeatureStore();
734     assertFalse(fs.shiftFeatures(0, 1)); // nothing to do
735
736     SequenceFeature sf1 = new SequenceFeature("Cath", "", 2, 5, 0f, null);
737     fs.addFeature(sf1);
738     // nested feature:
739     SequenceFeature sf2 = new SequenceFeature("Cath", "", 8, 14, 0f, null);
740     fs.addFeature(sf2);
741     // contact feature:
742     SequenceFeature sf3 = new SequenceFeature("Disulfide bond", "", 23, 32,
743             0f, null);
744     fs.addFeature(sf3);
745     // non-positional feature:
746     SequenceFeature sf4 = new SequenceFeature("Cath", "", 0, 0, 0f, null);
747     fs.addFeature(sf4);
748
749     /*
750      * shift all features right by 5
751      */
752     assertTrue(fs.shiftFeatures(0, 5));
753
754     // non-positional features untouched:
755     List<SequenceFeature> nonPos = fs.getNonPositionalFeatures();
756     assertEquals(nonPos.size(), 1);
757     assertTrue(nonPos.contains(sf4));
758
759     // positional features are replaced
760     List<SequenceFeature> pos = fs.getPositionalFeatures();
761     assertEquals(pos.size(), 3);
762     assertFalse(pos.contains(sf1));
763     assertFalse(pos.contains(sf2));
764     assertFalse(pos.contains(sf3));
765     SequenceFeatures.sortFeatures(pos, true); // ascending start pos
766     assertEquals(pos.get(0).getBegin(), 7);
767     assertEquals(pos.get(0).getEnd(), 10);
768     assertEquals(pos.get(1).getBegin(), 13);
769     assertEquals(pos.get(1).getEnd(), 19);
770     assertEquals(pos.get(2).getBegin(), 28);
771     assertEquals(pos.get(2).getEnd(), 37);
772
773     /*
774      * now shift left by 15
775      * feature at [7-10] should be removed
776      * feature at [13-19] should become [1-4] 
777      */
778     assertTrue(fs.shiftFeatures(0, -15));
779     pos = fs.getPositionalFeatures();
780     assertEquals(pos.size(), 2);
781     SequenceFeatures.sortFeatures(pos, true);
782     assertEquals(pos.get(0).getBegin(), 1);
783     assertEquals(pos.get(0).getEnd(), 4);
784     assertEquals(pos.get(1).getBegin(), 13);
785     assertEquals(pos.get(1).getEnd(), 22);
786
787     /*
788      * shift right by 4 from position 2 onwards
789      * feature at [1-4] unchanged, feature at [13-22] shifts
790      */
791     assertTrue(fs.shiftFeatures(2, 4));
792     pos = fs.getPositionalFeatures();
793     assertEquals(pos.size(), 2);
794     SequenceFeatures.sortFeatures(pos, true);
795     assertEquals(pos.get(0).getBegin(), 1);
796     assertEquals(pos.get(0).getEnd(), 4);
797     assertEquals(pos.get(1).getBegin(), 17);
798     assertEquals(pos.get(1).getEnd(), 26);
799
800     /*
801      * shift right by 4 from position 18 onwards
802      * should be no change
803      */
804     SequenceFeature f1 = pos.get(0);
805     SequenceFeature f2 = pos.get(1);
806     assertFalse(fs.shiftFeatures(18, 4)); // no update
807     pos = fs.getPositionalFeatures();
808     assertEquals(pos.size(), 2);
809     SequenceFeatures.sortFeatures(pos, true);
810     assertSame(pos.get(0), f1);
811     assertSame(pos.get(1), f2);
812   }
813
814   @Test(groups = "Functional")
815   public void testDelete_readd()
816   {
817     /*
818      * add a feature and a nested feature
819      */
820     FeatureStore store = newFeatureStore();
821     SequenceFeature sf1 = addFeature(store, 10, 20);
822     // sf2 is nested in sf1 so will be stored in nestedFeatures
823     SequenceFeature sf2 = addFeature(store, 12, 14);
824     List<SequenceFeature> features = store.getPositionalFeatures();
825     assertEquals(features.size(), 2);
826     assertTrue(features.contains(sf1));
827     assertTrue(features.contains(sf2));
828     Collection<SequenceFeature> intervalStore = (Collection<SequenceFeature>) PA
829             .getValue(store, "features");
830     assertTrue(intervalStore.contains(sf1));
831     assertTrue(intervalStore.contains(sf2));
832   
833     /*
834      * delete the first feature
835      */
836     assertTrue(store.delete(sf1));
837     features = store.getPositionalFeatures();
838     assertFalse(features.contains(sf1));
839     assertTrue(features.contains(sf2));
840
841     /*
842      * re-add the 'nested' feature; is it now duplicated?
843      */
844     store.addFeature(sf2);
845     features = store.getPositionalFeatures();
846     assertEquals(features.size(), 1);
847     assertTrue(features.contains(sf2));
848   }
849
850   @Test(groups = "Functional")
851   public void testContains()
852   {
853     FeatureStore fs = newFeatureStore();
854     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
855             Float.NaN, "group1");
856     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 20,
857             Float.NaN, "group2");
858     SequenceFeature sf3 = new SequenceFeature("Cath", "", 0, 0, Float.NaN,
859             "group1");
860     SequenceFeature sf4 = new SequenceFeature("Cath", "", 0, 0, 0f,
861             "group1");
862     SequenceFeature sf5 = new SequenceFeature("Disulphide Bond", "", 5, 15,
863             Float.NaN, "group1");
864     SequenceFeature sf6 = new SequenceFeature("Disulphide Bond", "", 5, 15,
865             Float.NaN, "group2");
866
867     fs.addFeature(sf1);
868     fs.addFeature(sf3);
869     fs.addFeature(sf5);
870     assertTrue(fs.contains(sf1)); // positional feature
871     assertTrue(fs.contains(new SequenceFeature(sf1))); // identical feature
872     assertFalse(fs.contains(sf2)); // different group
873     assertTrue(fs.contains(sf3)); // non-positional
874     assertTrue(fs.contains(new SequenceFeature(sf3)));
875     assertFalse(fs.contains(sf4)); // different score
876     assertTrue(fs.contains(sf5)); // contact feature
877     assertTrue(fs.contains(new SequenceFeature(sf5)));
878     assertFalse(fs.contains(sf6)); // different group
879
880     /*
881      * add a nested feature
882      */
883     SequenceFeature sf7 = new SequenceFeature("Cath", "", 12, 16,
884             Float.NaN, "group1");
885     fs.addFeature(sf7);
886     assertTrue(fs.contains(sf7));
887     assertTrue(fs.contains(new SequenceFeature(sf7)));
888
889     /*
890      * delete the outer (enclosing, non-nested) feature
891      */
892     fs.delete(sf1);
893     assertFalse(fs.contains(sf1));
894     assertTrue(fs.contains(sf7));
895   }
896
897   @Test(groups = "Functional")
898   public void testFindFeatures_withResultList()
899   {
900     FeatureStore fs = newFeatureStore();
901     SequenceFeature sf1 = addFeature(fs, 10, 50);
902     SequenceFeature sf2 = addFeature(fs, 150, 250);
903   
904     List<SequenceFeature> overlaps = new ArrayList<>();
905     List<SequenceFeature> result = fs.findOverlappingFeatures(200, 200,
906             overlaps);
907     assertSame(result, overlaps);
908     assertEquals(result.size(), 1);
909     assertTrue(result.contains(sf2));
910     assertFalse(result.contains(sf1));
911
912     /*
913      * if no list supplied, method creates one
914      */
915     result = fs.findOverlappingFeatures(200, 200, null);
916     assertEquals(result.size(), 1);
917     assertTrue(result.contains(sf2));
918     assertFalse(result.contains(sf1));
919   }
920 }