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