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