JAL-3429 JAL-3253-applet
[jalview.git] / test / jalview / datamodel / features / FeatureStoreJavaTest.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.List;
12 import java.util.Set;
13
14 import org.testng.annotations.Test;
15
16 public class FeatureStoreJavaTest
17 {
18
19   private int intervalStoreOption = FeatureStore.intervalStoreJavaOption;
20
21   private FeatureStoreI newFeatureStore()
22   {
23     return new FeatureStoreImpl(intervalStoreOption);
24   }
25
26   @Test(groups = "Functional")
27   public void testFindFeatures_nonNested()
28   {
29     FeatureStoreI 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     FeatureStoreI 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     FeatureStoreI 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(FeatureStoreI 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     FeatureStoreI 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     FeatureStoreI 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     FeatureStoreI 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     FeatureStoreI 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     FeatureStoreI 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     assertEquals(fs.getFeatures().size(), 2);
422     assertFalse(fs.isEmpty());
423     assertTrue(fs.delete(sf2));
424     assertEquals(fs.getFeatureCount(true), 1);
425     assertFalse(fs.isEmpty());
426     assertTrue(fs.delete(sf3));
427     assertEquals(fs.getFeatureCount(true), 0);
428     assertTrue(fs.isEmpty()); // all gone
429   }
430
431   @Test(groups = "Functional")
432   public void testGetFeatureGroups()
433   {
434     FeatureStoreI fs = newFeatureStore();
435     assertTrue(fs.getFeatureGroups(true).isEmpty());
436     assertTrue(fs.getFeatureGroups(false).isEmpty());
437
438     SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 10, 20, 1f, "group1");
439     fs.addFeature(sf1);
440     Set<String> groups = fs.getFeatureGroups(true);
441     assertEquals(groups.size(), 1);
442     assertTrue(groups.contains("group1"));
443
444     /*
445      * add another feature of the same group, delete one, delete both
446      */
447     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group1");
448     fs.addFeature(sf2);
449     groups = fs.getFeatureGroups(true);
450     assertEquals(groups.size(), 1);
451     assertTrue(groups.contains("group1"));
452     fs.delete(sf2);
453     groups = fs.getFeatureGroups(true);
454     assertEquals(groups.size(), 1);
455     assertTrue(groups.contains("group1"));
456     fs.delete(sf1);
457     groups = fs.getFeatureGroups(true);
458     assertTrue(fs.getFeatureGroups(true).isEmpty());
459
460     SequenceFeature sf3 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group2");
461     fs.addFeature(sf3);
462     SequenceFeature sf4 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "Group2");
463     fs.addFeature(sf4);
464     SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 20, 30, 1f, null);
465     fs.addFeature(sf5);
466     groups = fs.getFeatureGroups(true);
467     assertEquals(groups.size(), 3);
468     assertTrue(groups.contains("group2"));
469     assertTrue(groups.contains("Group2")); // case sensitive
470     assertTrue(groups.contains(null)); // null allowed
471     assertTrue(fs.getFeatureGroups(false).isEmpty()); // non-positional
472
473     fs.delete(sf3);
474     groups = fs.getFeatureGroups(true);
475     assertEquals(groups.size(), 2);
476     assertFalse(groups.contains("group2"));
477     fs.delete(sf4);
478     groups = fs.getFeatureGroups(true);
479     assertEquals(groups.size(), 1);
480     assertFalse(groups.contains("Group2"));
481     fs.delete(sf5);
482     groups = fs.getFeatureGroups(true);
483     assertTrue(groups.isEmpty());
484
485     /*
486      * add non-positional feature
487      */
488     SequenceFeature sf6 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
489             "CathGroup");
490     fs.addFeature(sf6);
491     groups = fs.getFeatureGroups(false);
492     assertEquals(groups.size(), 1);
493     assertTrue(groups.contains("CathGroup"));
494     assertTrue(fs.delete(sf6));
495     assertTrue(fs.getFeatureGroups(false).isEmpty());
496   }
497
498   @Test(groups = "Functional")
499   public void testGetTotalFeatureLength()
500   {
501     FeatureStoreI fs = newFeatureStore();
502     assertEquals(fs.getTotalFeatureLength(), 0);
503
504     addFeature(fs, 10, 20); // 11
505     assertEquals(fs.getTotalFeatureLength(), 11);
506     addFeature(fs, 17, 37); // 21
507     SequenceFeature sf1 = addFeature(fs, 14, 74); // 61
508     assertEquals(fs.getTotalFeatureLength(), 93);
509
510     // non-positional features don't count
511     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
512             "group1");
513     fs.addFeature(sf2);
514     assertEquals(fs.getTotalFeatureLength(), 93);
515
516     // contact features count 1
517     SequenceFeature sf3 = new SequenceFeature("disulphide bond", "desc",
518             15, 35, 1f, "group1");
519     fs.addFeature(sf3);
520     assertEquals(fs.getTotalFeatureLength(), 94);
521
522     assertTrue(fs.delete(sf1));
523     assertEquals(fs.getTotalFeatureLength(), 33);
524     assertFalse(fs.delete(sf1));
525     assertEquals(fs.getTotalFeatureLength(), 33);
526     assertTrue(fs.delete(sf2));
527     assertEquals(fs.getTotalFeatureLength(), 33);
528     assertTrue(fs.delete(sf3));
529     assertEquals(fs.getTotalFeatureLength(), 32);
530   }
531
532   @Test(groups = "Functional")
533   public void testGetFeatureLength()
534   {
535     /*
536      * positional feature
537      */
538     SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 10, 20, 1f, "group1");
539     assertEquals(FeatureStore.getFeatureLength(sf1), 11);
540   
541     /*
542      * non-positional feature
543      */
544     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
545             "CathGroup");
546     assertEquals(FeatureStore.getFeatureLength(sf2), 0);
547
548     /*
549      * contact feature counts 1
550      */
551     SequenceFeature sf3 = new SequenceFeature("Disulphide Bond", "desc",
552             14, 28, 1f, "AGroup");
553     assertEquals(FeatureStore.getFeatureLength(sf3), 1);
554   }
555
556   @Test(groups = "Functional")
557   public void testMin()
558   {
559     assertEquals(FeatureStore.min(Float.NaN, Float.NaN), Float.NaN);
560     assertEquals(FeatureStore.min(Float.NaN, 2f), 2f);
561     assertEquals(FeatureStore.min(-2f, Float.NaN), -2f);
562     assertEquals(FeatureStore.min(2f, -3f), -3f);
563   }
564
565   @Test(groups = "Functional")
566   public void testMax()
567   {
568     assertEquals(FeatureStore.max(Float.NaN, Float.NaN), Float.NaN);
569     assertEquals(FeatureStore.max(Float.NaN, 2f), 2f);
570     assertEquals(FeatureStore.max(-2f, Float.NaN), -2f);
571     assertEquals(FeatureStore.max(2f, -3f), 2f);
572   }
573
574   @Test(groups = "Functional")
575   public void testGetMinimumScore_getMaximumScore()
576   {
577     FeatureStoreI fs = newFeatureStore();
578     assertEquals(fs.getMinimumScore(true), Float.NaN); // positional
579     assertEquals(fs.getMaximumScore(true), Float.NaN);
580     assertEquals(fs.getMinimumScore(false), Float.NaN); // non-positional
581     assertEquals(fs.getMaximumScore(false), Float.NaN);
582
583     // add features with no score
584     SequenceFeature sf1 = new SequenceFeature("type", "desc", 0, 0,
585             Float.NaN, "group");
586     fs.addFeature(sf1);
587     SequenceFeature sf2 = new SequenceFeature("type", "desc", 10, 20,
588             Float.NaN, "group");
589     fs.addFeature(sf2);
590     assertEquals(fs.getMinimumScore(true), Float.NaN);
591     assertEquals(fs.getMaximumScore(true), Float.NaN);
592     assertEquals(fs.getMinimumScore(false), Float.NaN);
593     assertEquals(fs.getMaximumScore(false), Float.NaN);
594
595     // add positional features with score
596     SequenceFeature sf3 = new SequenceFeature("type", "desc", 10, 20, 1f,
597             "group");
598     fs.addFeature(sf3);
599     SequenceFeature sf4 = new SequenceFeature("type", "desc", 12, 16, 4f,
600             "group");
601     fs.addFeature(sf4);
602     assertEquals(fs.getMinimumScore(true), 1f);
603     assertEquals(fs.getMaximumScore(true), 4f);
604     assertEquals(fs.getMinimumScore(false), Float.NaN);
605     assertEquals(fs.getMaximumScore(false), Float.NaN);
606
607     // add non-positional features with score
608     SequenceFeature sf5 = new SequenceFeature("type", "desc", 0, 0, 11f,
609             "group");
610     fs.addFeature(sf5);
611     SequenceFeature sf6 = new SequenceFeature("type", "desc", 0, 0, -7f,
612             "group");
613     fs.addFeature(sf6);
614     assertEquals(fs.getMinimumScore(true), 1f);
615     assertEquals(fs.getMaximumScore(true), 4f);
616     assertEquals(fs.getMinimumScore(false), -7f);
617     assertEquals(fs.getMaximumScore(false), 11f);
618
619     // delete one positional and one non-positional
620     // min-max should be recomputed
621     assertTrue(fs.delete(sf6));
622     assertTrue(fs.delete(sf3));
623     assertEquals(fs.getMinimumScore(true), 4f);
624     assertEquals(fs.getMaximumScore(true), 4f);
625     assertEquals(fs.getMinimumScore(false), 11f);
626     assertEquals(fs.getMaximumScore(false), 11f);
627
628     // delete remaining features with score
629     assertTrue(fs.delete(sf4));
630     assertTrue(fs.delete(sf5));
631     assertEquals(fs.getMinimumScore(true), Float.NaN);
632     assertEquals(fs.getMaximumScore(true), Float.NaN);
633     assertEquals(fs.getMinimumScore(false), Float.NaN);
634     assertEquals(fs.getMaximumScore(false), Float.NaN);
635
636     // delete all features
637     assertTrue(fs.delete(sf1));
638     assertTrue(fs.delete(sf2));
639     assertTrue(fs.isEmpty());
640     assertEquals(fs.getMinimumScore(true), Float.NaN);
641     assertEquals(fs.getMaximumScore(true), Float.NaN);
642     assertEquals(fs.getMinimumScore(false), Float.NaN);
643     assertEquals(fs.getMaximumScore(false), Float.NaN);
644   }
645
646   @Test(groups = "Functional")
647   public void testListContains()
648   {
649     FeatureStoreI featureStore = newFeatureStore();
650     assertFalse(featureStore.listContains(null, null));
651     List<SequenceFeature> features = new ArrayList<>();
652     assertFalse(featureStore.listContains(features, null));
653
654     SequenceFeature sf1 = new SequenceFeature("type1", "desc1", 20, 30, 3f,
655             "group1");
656     assertFalse(featureStore.listContains(null, sf1));
657     assertFalse(featureStore.listContains(features, sf1));
658
659     features.add(sf1);
660     SequenceFeature sf2 = new SequenceFeature("type1", "desc1", 20, 30, 3f,
661             "group1");
662     SequenceFeature sf3 = new SequenceFeature("type1", "desc1", 20, 40, 3f,
663             "group1");
664
665     // sf2.equals(sf1) so contains should return true
666     assertTrue(featureStore.listContains(features, sf2));
667     assertFalse(featureStore.listContains(features, sf3));
668   }
669
670   @Test(groups = "Functional")
671   public void testGetFeaturesForGroup()
672   {
673     FeatureStoreI fs = newFeatureStore();
674
675     /*
676      * with no features
677      */
678     assertTrue(fs.getFeaturesForGroup(true, null).isEmpty());
679     assertTrue(fs.getFeaturesForGroup(false, null).isEmpty());
680     assertTrue(fs.getFeaturesForGroup(true, "uniprot").isEmpty());
681     assertTrue(fs.getFeaturesForGroup(false, "uniprot").isEmpty());
682
683     /*
684      * sf1: positional feature in the null group
685      */
686     SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 4, 10, 0f,
687             null);
688     fs.addFeature(sf1);
689     assertTrue(fs.getFeaturesForGroup(true, "uniprot").isEmpty());
690     assertTrue(fs.getFeaturesForGroup(false, "uniprot").isEmpty());
691     assertTrue(fs.getFeaturesForGroup(false, null).isEmpty());
692     List<SequenceFeature> features = fs.getFeaturesForGroup(true, null);
693     assertEquals(features.size(), 1);
694     assertTrue(features.contains(sf1));
695
696     /*
697      * sf2: non-positional feature in the null group
698      * sf3: positional feature in a non-null group
699      * sf4: non-positional feature in a non-null group
700      */
701     SequenceFeature sf2 = new SequenceFeature("Pfam", "desc", 0, 0, 0f,
702             null);
703     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 4, 10, 0f,
704             "Uniprot");
705     SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 0, 0, 0f,
706             "Rfam");
707     fs.addFeature(sf2);
708     fs.addFeature(sf3);
709     fs.addFeature(sf4);
710
711     features = fs.getFeaturesForGroup(true, null);
712     assertEquals(features.size(), 1);
713     assertTrue(features.contains(sf1));
714
715     features = fs.getFeaturesForGroup(false, null);
716     assertEquals(features.size(), 1);
717     assertTrue(features.contains(sf2));
718
719     features = fs.getFeaturesForGroup(true, "Uniprot");
720     assertEquals(features.size(), 1);
721     assertTrue(features.contains(sf3));
722
723     features = fs.getFeaturesForGroup(false, "Rfam");
724     assertEquals(features.size(), 1);
725     assertTrue(features.contains(sf4));
726   }
727
728   @Test(groups = "Functional")
729   public void testShiftFeatures()
730   {
731     FeatureStoreI fs = newFeatureStore();
732     assertFalse(fs.shiftFeatures(0, 1)); // nothing to do
733
734     SequenceFeature sf1 = new SequenceFeature("Cath", "", 2, 5, 0f, null);
735     fs.addFeature(sf1);
736     // nested feature:
737     SequenceFeature sf2 = new SequenceFeature("Cath", "", 8, 14, 0f, null);
738     fs.addFeature(sf2);
739     // contact feature:
740     SequenceFeature sf3 = new SequenceFeature("Disulfide bond", "", 23, 32,
741             0f, null);
742     fs.addFeature(sf3);
743     // non-positional feature:
744     SequenceFeature sf4 = new SequenceFeature("Cath", "", 0, 0, 0f, null);
745     fs.addFeature(sf4);
746
747     /*
748      * shift all features right by 5
749      */
750     assertTrue(fs.shiftFeatures(0, 5));
751
752     // non-positional features untouched:
753     List<SequenceFeature> nonPos = fs.getNonPositionalFeatures();
754     assertEquals(nonPos.size(), 1);
755     assertTrue(nonPos.contains(sf4));
756
757     // positional features are replaced
758     List<SequenceFeature> pos = fs.getPositionalFeatures();
759     assertEquals(pos.size(), 3);
760     assertFalse(pos.contains(sf1));
761     assertFalse(pos.contains(sf2));
762     assertFalse(pos.contains(sf3));
763     SequenceFeatures.sortFeatures(pos, true); // ascending start pos
764     assertEquals(pos.get(0).getBegin(), 7);
765     assertEquals(pos.get(0).getEnd(), 10);
766     assertEquals(pos.get(1).getBegin(), 13);
767     assertEquals(pos.get(1).getEnd(), 19);
768     assertEquals(pos.get(2).getBegin(), 28);
769     assertEquals(pos.get(2).getEnd(), 37);
770
771     /*
772      * now shift left by 15
773      * feature at [7-10] should be removed
774      * feature at [13-19] should become [1-4] 
775      */
776     assertTrue(fs.shiftFeatures(0, -15));
777     pos = fs.getPositionalFeatures();
778     assertEquals(pos.size(), 2);
779     SequenceFeatures.sortFeatures(pos, true);
780     assertEquals(pos.get(0).getBegin(), 1);
781     assertEquals(pos.get(0).getEnd(), 4);
782     assertEquals(pos.get(1).getBegin(), 13);
783     assertEquals(pos.get(1).getEnd(), 22);
784
785     /*
786      * shift right by 4 from position 2 onwards
787      * feature at [1-4] unchanged, feature at [13-22] shifts
788      */
789     assertTrue(fs.shiftFeatures(2, 4));
790     pos = fs.getPositionalFeatures();
791     assertEquals(pos.size(), 2);
792     SequenceFeatures.sortFeatures(pos, true);
793     assertEquals(pos.get(0).getBegin(), 1);
794     assertEquals(pos.get(0).getEnd(), 4);
795     assertEquals(pos.get(1).getBegin(), 17);
796     assertEquals(pos.get(1).getEnd(), 26);
797
798     /*
799      * shift right by 4 from position 18 onwards
800      * should be no change
801      */
802     SequenceFeature f1 = pos.get(0);
803     SequenceFeature f2 = pos.get(1);
804     assertFalse(fs.shiftFeatures(18, 4)); // no update
805     pos = fs.getPositionalFeatures();
806     assertEquals(pos.size(), 2);
807     SequenceFeatures.sortFeatures(pos, true);
808     assertSame(pos.get(0), f1);
809     assertSame(pos.get(1), f2);
810   }
811
812   @Test(groups = "Functional")
813   public void testDelete_readd()
814   {
815     /*
816      * add a feature and a nested feature
817      */
818     FeatureStoreI store = newFeatureStore();
819     SequenceFeature sf1 = addFeature(store, 10, 20);
820     // sf2 is nested in sf1 so will be stored in nestedFeatures
821     SequenceFeature sf2 = addFeature(store, 12, 14);
822     List<SequenceFeature> features = store.getPositionalFeatures();
823     assertEquals(features.size(), 2);
824     assertTrue(features.contains(sf1));
825     assertTrue(features.contains(sf2));
826     assertTrue(store.getFeatures().contains(sf1));
827     assertTrue(store.getFeatures().contains(sf2));
828   
829     /*
830      * delete the first feature
831      */
832     assertTrue(store.delete(sf1));
833     features = store.getPositionalFeatures();
834     assertFalse(features.contains(sf1));
835     assertTrue(features.contains(sf2));
836
837     /*
838      * re-add the 'nested' feature; is it now duplicated?
839      */
840     store.addFeature(sf2);
841     features = store.getPositionalFeatures();
842     assertEquals(features.size(), 1);
843     assertTrue(features.contains(sf2));
844   }
845
846   @Test(groups = "Functional")
847   public void testContains()
848   {
849     FeatureStoreI fs = newFeatureStore();
850     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
851             Float.NaN, "group1");
852     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 20,
853             Float.NaN, "group2");
854     SequenceFeature sf3 = new SequenceFeature("Cath", "", 0, 0, Float.NaN,
855             "group1");
856     SequenceFeature sf4 = new SequenceFeature("Cath", "", 0, 0, 0f,
857             "group1");
858     SequenceFeature sf5 = new SequenceFeature("Disulphide Bond", "", 5, 15,
859             Float.NaN, "group1");
860     SequenceFeature sf6 = new SequenceFeature("Disulphide Bond", "", 5, 15,
861             Float.NaN, "group2");
862
863     fs.addFeature(sf1);
864     fs.addFeature(sf3);
865     fs.addFeature(sf5);
866     assertTrue(fs.contains(sf1)); // positional feature
867     assertTrue(fs.contains(new SequenceFeature(sf1))); // identical feature
868     assertFalse(fs.contains(sf2)); // different group
869     assertTrue(fs.contains(sf3)); // non-positional
870     assertTrue(fs.contains(new SequenceFeature(sf3)));
871     assertFalse(fs.contains(sf4)); // different score
872     assertTrue(fs.contains(sf5)); // contact feature
873     assertTrue(fs.contains(new SequenceFeature(sf5)));
874     assertFalse(fs.contains(sf6)); // different group
875
876     /*
877      * add a nested feature
878      */
879     SequenceFeature sf7 = new SequenceFeature("Cath", "", 12, 16,
880             Float.NaN, "group1");
881     fs.addFeature(sf7);
882     assertTrue(fs.contains(sf7));
883     assertTrue(fs.contains(new SequenceFeature(sf7)));
884
885     /*
886      * delete the outer (enclosing, non-nested) feature
887      */
888     fs.delete(sf1);
889     assertFalse(fs.contains(sf1));
890     assertTrue(fs.contains(sf7));
891   }
892 }