66cd892210488fc49cb1cfc5ade22586f91cd300
[jalview.git] / test / jalview / datamodel / features / FeatureStoreNCListBufferTest.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 FeatureStoreNCListBufferTest
17 {
18
19   private FeatureStore newFeatureStore()
20   {
21     return new FeatureStore(
22             FeatureStore.INTERVAL_STORE_NCARRAY);
23   }
24
25   @Test(groups = "Functional")
26   public void testFindFeatures_nonNested()
27   {
28     FeatureStore fs = newFeatureStore();
29     fs.addFeature(new SequenceFeature("", "", 10, 20, Float.NaN,
30             null));
31     // same range different description
32     fs.addFeature(new SequenceFeature("", "desc", 10, 20, Float.NaN, null));
33     fs.addFeature(new SequenceFeature("", "", 15, 25, Float.NaN, null));
34     fs.addFeature(new SequenceFeature("", "", 20, 35, Float.NaN, null));
35
36     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
37     assertTrue(overlaps.isEmpty());
38
39     overlaps = fs.findOverlappingFeatures(8, 10);
40     assertEquals(overlaps.size(), 2);
41     assertEquals(overlaps.get(0).getEnd(), 20);
42     assertEquals(overlaps.get(1).getEnd(), 20);
43
44     overlaps = fs.findOverlappingFeatures(12, 16);
45     assertEquals(overlaps.size(), 3);
46     assertEquals(overlaps.get(0).getEnd(), 20);
47     assertEquals(overlaps.get(1).getEnd(), 20);
48     assertEquals(overlaps.get(2).getEnd(), 25);
49
50     overlaps = fs.findOverlappingFeatures(33, 33);
51     assertEquals(overlaps.size(), 1);
52     assertEquals(overlaps.get(0).getEnd(), 35);
53   }
54
55   @Test(groups = "Functional")
56   public void testFindFeatures_nested()
57   {
58     FeatureStore 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     FeatureStore 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(FeatureStore 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     FeatureStore 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     FeatureStore 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     FeatureStore 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
284     // SequenceFeature sf4 = addFeature(store, 20, 30);
285     //// SequenceFeature sf5 = addFeature(store, 22, 26);
286     ////// SequenceFeature sf6 = addFeature(store, 23, 24); // child of sf5
287     //////// SequenceFeature sf9 = addFeature(store, 23, 23); // child of sf6
288     //////// SequenceFeature sf8 = addFeature(store, 24, 24); // child of sf6
289     ////// SequenceFeature sf7 = addFeature(store, 25, 25); // child of sf5
290     //
291     assertEquals(store.getPositionalFeatures().size(), 6);
292
293     // delete a node with children - they take its place
294     assertTrue(store.delete(sf6)); // sf8, sf9 should become children of sf5
295     assertEquals(store.getPositionalFeatures().size(), 5);
296     assertFalse(store.getPositionalFeatures().contains(sf6));
297
298     // delete a node with no children
299     assertTrue(store.delete(sf7));
300     assertEquals(store.getPositionalFeatures().size(), 4);
301     assertFalse(store.getPositionalFeatures().contains(sf7));
302
303     // delete root of NCList
304     assertTrue(store.delete(sf5));
305     assertEquals(store.getPositionalFeatures().size(), 3);
306     assertFalse(store.getPositionalFeatures().contains(sf5));
307
308     // continue the killing fields
309     assertTrue(store.delete(sf4));
310     assertEquals(store.getPositionalFeatures().size(), 2);
311     assertFalse(store.getPositionalFeatures().contains(sf4));
312
313     assertTrue(store.delete(sf9));
314     assertEquals(store.getPositionalFeatures().size(), 1);
315     assertFalse(store.getPositionalFeatures().contains(sf9));
316
317     assertTrue(store.delete(sf8));
318     assertTrue(store.getPositionalFeatures().isEmpty());
319   }
320
321   @Test(groups = "Functional")
322   public void testAddFeature()
323   {
324     FeatureStore fs = newFeatureStore();
325
326     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
327             Float.NaN, null);
328     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 20,
329             Float.NaN, null);
330
331     assertTrue(fs.addFeature(sf1));
332     assertEquals(fs.getFeatureCount(true), 1); // positional
333     assertEquals(fs.getFeatureCount(false), 0); // non-positional
334
335     /*
336      * re-adding the same or an identical feature should fail
337      */
338     assertFalse(fs.addFeature(sf1));
339     assertEquals(fs.getFeatureCount(true), 1);
340     assertFalse(fs.addFeature(sf2));
341     assertEquals(fs.getFeatureCount(true), 1);
342
343     /*
344      * add non-positional
345      */
346     SequenceFeature sf3 = new SequenceFeature("Cath", "", 0, 0, Float.NaN,
347             null);
348     assertTrue(fs.addFeature(sf3));
349     assertEquals(fs.getFeatureCount(true), 1); // positional
350     assertEquals(fs.getFeatureCount(false), 1); // non-positional
351     SequenceFeature sf4 = new SequenceFeature("Cath", "", 0, 0, Float.NaN,
352             null);
353     assertFalse(fs.addFeature(sf4)); // already stored
354     assertEquals(fs.getFeatureCount(true), 1); // positional
355     assertEquals(fs.getFeatureCount(false), 1); // non-positional
356
357     /*
358      * add contact
359      */
360     SequenceFeature sf5 = new SequenceFeature("Disulfide bond", "", 10, 20,
361             Float.NaN, null);
362     assertTrue(fs.addFeature(sf5));
363     assertEquals(fs.getFeatureCount(true), 2); // positional - add 1 for contact
364     assertEquals(fs.getFeatureCount(false), 1); // non-positional
365     SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "", 10, 20,
366             Float.NaN, null);
367     assertFalse(fs.addFeature(sf6)); // already stored
368     assertEquals(fs.getFeatureCount(true), 2); // no change
369     assertEquals(fs.getFeatureCount(false), 1); // no change
370   }
371
372   @Test(groups = "Functional")
373   public void testIsEmpty()
374   {
375     FeatureStore fs = newFeatureStore();
376     assertTrue(fs.isEmpty());
377     assertEquals(fs.getFeatureCount(true), 0);
378
379     /*
380      * non-nested feature
381      */
382     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
383             Float.NaN, null);
384     fs.addFeature(sf1);
385     assertFalse(fs.isEmpty());
386     assertEquals(fs.getFeatureCount(true), 1);
387     fs.delete(sf1);
388     assertTrue(fs.isEmpty());
389     assertEquals(fs.getFeatureCount(true), 0);
390
391     /*
392      * non-positional feature
393      */
394     sf1 = new SequenceFeature("Cath", "", 0, 0, Float.NaN, null);
395     fs.addFeature(sf1);
396     assertFalse(fs.isEmpty());
397     assertEquals(fs.getFeatureCount(false), 1); // non-positional
398     assertEquals(fs.getFeatureCount(true), 0); // positional
399     fs.delete(sf1);
400     assertTrue(fs.isEmpty());
401     assertEquals(fs.getFeatureCount(false), 0);
402
403     /*
404      * contact feature
405      */
406     sf1 = new SequenceFeature("Disulfide bond", "", 19, 49, Float.NaN, null);
407     fs.addFeature(sf1);
408     assertFalse(fs.isEmpty());
409     assertEquals(fs.getFeatureCount(true), 1);
410     fs.delete(sf1);
411     assertTrue(fs.isEmpty());
412     assertEquals(fs.getFeatureCount(true), 0);
413
414     /*
415      * sf2, sf3 added as nested features
416      */
417     sf1 = new SequenceFeature("Cath", "", 19, 49, Float.NaN, null);
418     SequenceFeature sf2 = new SequenceFeature("Cath", "", 20, 40,
419             Float.NaN, null);
420     SequenceFeature sf3 = new SequenceFeature("Cath", "", 25, 35,
421             Float.NaN, null);
422     fs.addFeature(sf1);
423     fs.addFeature(sf2);
424     fs.addFeature(sf3);
425     assertEquals(fs.getFeatureCount(true), 3);
426     assertTrue(fs.delete(sf1));
427     assertEquals(fs.getFeatureCount(true), 2);
428     assertEquals(fs.getFeatures().size(), 2);
429     assertFalse(fs.isEmpty());
430     assertTrue(fs.delete(sf2));
431     assertEquals(fs.getFeatureCount(true), 1);
432     assertFalse(fs.isEmpty());
433     assertTrue(fs.delete(sf3));
434     assertEquals(fs.getFeatureCount(true), 0);
435     assertTrue(fs.isEmpty()); // all gone
436   }
437
438   @Test(groups = "Functional")
439   public void testGetFeatureGroups()
440   {
441     FeatureStore fs = newFeatureStore();
442     assertTrue(fs.getFeatureGroups(true).isEmpty());
443     assertTrue(fs.getFeatureGroups(false).isEmpty());
444
445     SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 10, 20, 1f, "group1");
446     fs.addFeature(sf1);
447     Set<String> groups = fs.getFeatureGroups(true);
448     assertEquals(groups.size(), 1);
449     assertTrue(groups.contains("group1"));
450
451     /*
452      * add another feature of the same group, delete one, delete both
453      */
454     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group1");
455     fs.addFeature(sf2);
456     groups = fs.getFeatureGroups(true);
457     assertEquals(groups.size(), 1);
458     assertTrue(groups.contains("group1"));
459     fs.delete(sf2);
460     groups = fs.getFeatureGroups(true);
461     assertEquals(groups.size(), 1);
462     assertTrue(groups.contains("group1"));
463     fs.delete(sf1);
464     groups = fs.getFeatureGroups(true);
465     assertTrue(fs.getFeatureGroups(true).isEmpty());
466
467     SequenceFeature sf3 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group2");
468     fs.addFeature(sf3);
469     SequenceFeature sf4 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "Group2");
470     fs.addFeature(sf4);
471     SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 20, 30, 1f, null);
472     fs.addFeature(sf5);
473     groups = fs.getFeatureGroups(true);
474     assertEquals(groups.size(), 3);
475     assertTrue(groups.contains("group2"));
476     assertTrue(groups.contains("Group2")); // case sensitive
477     assertTrue(groups.contains(null)); // null allowed
478     assertTrue(fs.getFeatureGroups(false).isEmpty()); // non-positional
479
480     fs.delete(sf3);
481     groups = fs.getFeatureGroups(true);
482     assertEquals(groups.size(), 2);
483     assertFalse(groups.contains("group2"));
484     fs.delete(sf4);
485     groups = fs.getFeatureGroups(true);
486     assertEquals(groups.size(), 1);
487     assertFalse(groups.contains("Group2"));
488     fs.delete(sf5);
489     groups = fs.getFeatureGroups(true);
490     assertTrue(groups.isEmpty());
491
492     /*
493      * add non-positional feature
494      */
495     SequenceFeature sf6 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
496             "CathGroup");
497     fs.addFeature(sf6);
498     groups = fs.getFeatureGroups(false);
499     assertEquals(groups.size(), 1);
500     assertTrue(groups.contains("CathGroup"));
501     assertTrue(fs.delete(sf6));
502     assertTrue(fs.getFeatureGroups(false).isEmpty());
503   }
504
505   @Test(groups = "Functional")
506   public void testGetTotalFeatureLength()
507   {
508     FeatureStore fs = newFeatureStore();
509     assertEquals(fs.getTotalFeatureLength(), 0);
510
511     addFeature(fs, 10, 20); // 11
512     assertEquals(fs.getTotalFeatureLength(), 11);
513     addFeature(fs, 17, 37); // 21
514     SequenceFeature sf1 = addFeature(fs, 14, 74); // 61
515     assertEquals(fs.getTotalFeatureLength(), 93);
516
517     // non-positional features don't count
518     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
519             "group1");
520     fs.addFeature(sf2);
521     assertEquals(fs.getTotalFeatureLength(), 93);
522
523     // contact features count 1
524     SequenceFeature sf3 = new SequenceFeature("disulphide bond", "desc",
525             15, 35, 1f, "group1");
526     fs.addFeature(sf3);
527     assertEquals(fs.getTotalFeatureLength(), 94);
528
529     assertTrue(fs.delete(sf1));
530     assertEquals(fs.getTotalFeatureLength(), 33);
531     assertFalse(fs.delete(sf1));
532     assertEquals(fs.getTotalFeatureLength(), 33);
533     assertTrue(fs.delete(sf2));
534     assertEquals(fs.getTotalFeatureLength(), 33);
535     assertTrue(fs.delete(sf3));
536     assertEquals(fs.getTotalFeatureLength(), 32);
537   }
538
539   @Test(groups = "Functional")
540   public void testGetFeatureLength()
541   {
542     /*
543      * positional feature
544      */
545     SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 10, 20, 1f, "group1");
546     assertEquals(FeatureStore.getFeatureLength(sf1), 11);
547   
548     /*
549      * non-positional feature
550      */
551     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
552             "CathGroup");
553     assertEquals(FeatureStore.getFeatureLength(sf2), 0);
554
555     /*
556      * contact feature counts 1
557      */
558     SequenceFeature sf3 = new SequenceFeature("Disulphide Bond", "desc",
559             14, 28, 1f, "AGroup");
560     assertEquals(FeatureStore.getFeatureLength(sf3), 1);
561   }
562
563   @Test(groups = "Functional")
564   public void testMin()
565   {
566     assertEquals(FeatureStore.min(Float.NaN, Float.NaN), Float.NaN);
567     assertEquals(FeatureStore.min(Float.NaN, 2f), 2f);
568     assertEquals(FeatureStore.min(-2f, Float.NaN), -2f);
569     assertEquals(FeatureStore.min(2f, -3f), -3f);
570   }
571
572   @Test(groups = "Functional")
573   public void testMax()
574   {
575     assertEquals(FeatureStore.max(Float.NaN, Float.NaN), Float.NaN);
576     assertEquals(FeatureStore.max(Float.NaN, 2f), 2f);
577     assertEquals(FeatureStore.max(-2f, Float.NaN), -2f);
578     assertEquals(FeatureStore.max(2f, -3f), 2f);
579   }
580
581   @Test(groups = "Functional")
582   public void testGetMinimumScore_getMaximumScore()
583   {
584     FeatureStore fs = newFeatureStore();
585     assertEquals(fs.getMinimumScore(true), Float.NaN); // positional
586     assertEquals(fs.getMaximumScore(true), Float.NaN);
587     assertEquals(fs.getMinimumScore(false), Float.NaN); // non-positional
588     assertEquals(fs.getMaximumScore(false), Float.NaN);
589
590     // add features with no score
591     SequenceFeature sf1 = new SequenceFeature("type", "desc", 0, 0,
592             Float.NaN, "group");
593     fs.addFeature(sf1);
594     SequenceFeature sf2 = new SequenceFeature("type", "desc", 10, 20,
595             Float.NaN, "group");
596     fs.addFeature(sf2);
597     assertEquals(fs.getMinimumScore(true), Float.NaN);
598     assertEquals(fs.getMaximumScore(true), Float.NaN);
599     assertEquals(fs.getMinimumScore(false), Float.NaN);
600     assertEquals(fs.getMaximumScore(false), Float.NaN);
601
602     // add positional features with score
603     SequenceFeature sf3 = new SequenceFeature("type", "desc", 10, 20, 1f,
604             "group");
605     fs.addFeature(sf3);
606     SequenceFeature sf4 = new SequenceFeature("type", "desc", 12, 16, 4f,
607             "group");
608     fs.addFeature(sf4);
609     assertEquals(fs.getMinimumScore(true), 1f);
610     assertEquals(fs.getMaximumScore(true), 4f);
611     assertEquals(fs.getMinimumScore(false), Float.NaN);
612     assertEquals(fs.getMaximumScore(false), Float.NaN);
613
614     // add non-positional features with score
615     SequenceFeature sf5 = new SequenceFeature("type", "desc", 0, 0, 11f,
616             "group");
617     fs.addFeature(sf5);
618     SequenceFeature sf6 = new SequenceFeature("type", "desc", 0, 0, -7f,
619             "group");
620     fs.addFeature(sf6);
621     assertEquals(fs.getMinimumScore(true), 1f);
622     assertEquals(fs.getMaximumScore(true), 4f);
623     assertEquals(fs.getMinimumScore(false), -7f);
624     assertEquals(fs.getMaximumScore(false), 11f);
625
626     // delete one positional and one non-positional
627     // min-max should be recomputed
628     assertTrue(fs.delete(sf6));
629     assertTrue(fs.delete(sf3));
630     assertEquals(fs.getMinimumScore(true), 4f);
631     assertEquals(fs.getMaximumScore(true), 4f);
632     assertEquals(fs.getMinimumScore(false), 11f);
633     assertEquals(fs.getMaximumScore(false), 11f);
634
635     // delete remaining features with score
636     assertTrue(fs.delete(sf4));
637     assertTrue(fs.delete(sf5));
638     assertEquals(fs.getMinimumScore(true), Float.NaN);
639     assertEquals(fs.getMaximumScore(true), Float.NaN);
640     assertEquals(fs.getMinimumScore(false), Float.NaN);
641     assertEquals(fs.getMaximumScore(false), Float.NaN);
642
643     // delete all features
644     assertTrue(fs.delete(sf1));
645     assertTrue(fs.delete(sf2));
646     assertTrue(fs.isEmpty());
647     assertEquals(fs.getMinimumScore(true), Float.NaN);
648     assertEquals(fs.getMaximumScore(true), Float.NaN);
649     assertEquals(fs.getMinimumScore(false), Float.NaN);
650     assertEquals(fs.getMaximumScore(false), Float.NaN);
651   }
652
653   @Test(groups = "Functional")
654   public void testListContains()
655   {
656     FeatureStore featureStore = newFeatureStore();
657     assertFalse(featureStore.listContains(null, null));
658     List<SequenceFeature> features = new ArrayList<>();
659     assertFalse(featureStore.listContains(features, null));
660
661     SequenceFeature sf1 = new SequenceFeature("type1", "desc1", 20, 30, 3f,
662             "group1");
663     assertFalse(featureStore.listContains(null, sf1));
664     assertFalse(featureStore.listContains(features, sf1));
665
666     features.add(sf1);
667     SequenceFeature sf2 = new SequenceFeature("type1", "desc1", 20, 30, 3f,
668             "group1");
669     SequenceFeature sf3 = new SequenceFeature("type1", "desc1", 20, 40, 3f,
670             "group1");
671
672     // sf2.equals(sf1) so contains should return true
673     assertTrue(featureStore.listContains(features, sf2));
674     assertFalse(featureStore.listContains(features, sf3));
675   }
676
677   @Test(groups = "Functional")
678   public void testGetFeaturesForGroup()
679   {
680     FeatureStore fs = newFeatureStore();
681
682     /*
683      * with no features
684      */
685     assertTrue(fs.getFeaturesForGroup(true, null).isEmpty());
686     assertTrue(fs.getFeaturesForGroup(false, null).isEmpty());
687     assertTrue(fs.getFeaturesForGroup(true, "uniprot").isEmpty());
688     assertTrue(fs.getFeaturesForGroup(false, "uniprot").isEmpty());
689
690     /*
691      * sf1: positional feature in the null group
692      */
693     SequenceFeature sf1 = new SequenceFeature("Pfam", "desc", 4, 10, 0f,
694             null);
695     fs.addFeature(sf1);
696     assertTrue(fs.getFeaturesForGroup(true, "uniprot").isEmpty());
697     assertTrue(fs.getFeaturesForGroup(false, "uniprot").isEmpty());
698     assertTrue(fs.getFeaturesForGroup(false, null).isEmpty());
699     List<SequenceFeature> features = fs.getFeaturesForGroup(true, null);
700     assertEquals(features.size(), 1);
701     assertTrue(features.contains(sf1));
702
703     /*
704      * sf2: non-positional feature in the null group
705      * sf3: positional feature in a non-null group
706      * sf4: non-positional feature in a non-null group
707      */
708     SequenceFeature sf2 = new SequenceFeature("Pfam", "desc", 0, 0, 0f,
709             null);
710     SequenceFeature sf3 = new SequenceFeature("Pfam", "desc", 4, 10, 0f,
711             "Uniprot");
712     SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 0, 0, 0f,
713             "Rfam");
714     fs.addFeature(sf2);
715     fs.addFeature(sf3);
716     fs.addFeature(sf4);
717
718     features = fs.getFeaturesForGroup(true, null);
719     assertEquals(features.size(), 1);
720     assertTrue(features.contains(sf1));
721
722     features = fs.getFeaturesForGroup(false, null);
723     assertEquals(features.size(), 1);
724     assertTrue(features.contains(sf2));
725
726     features = fs.getFeaturesForGroup(true, "Uniprot");
727     assertEquals(features.size(), 1);
728     assertTrue(features.contains(sf3));
729
730     features = fs.getFeaturesForGroup(false, "Rfam");
731     assertEquals(features.size(), 1);
732     assertTrue(features.contains(sf4));
733   }
734
735   @Test(groups = "Functional")
736   public void testShiftFeatures()
737   {
738     FeatureStore fs = newFeatureStore();
739     assertFalse(fs.shiftFeatures(0, 1)); // nothing to do
740
741     SequenceFeature sf1 = new SequenceFeature("Cath", "", 2, 5, 0f, null);
742     fs.addFeature(sf1);
743     // nested feature:
744     SequenceFeature sf2 = new SequenceFeature("Cath", "", 8, 14, 0f, null);
745     fs.addFeature(sf2);
746     // contact feature:
747     SequenceFeature sf3 = new SequenceFeature("Disulfide bond", "", 23, 32,
748             0f, null);
749     fs.addFeature(sf3);
750     // non-positional feature:
751     SequenceFeature sf4 = new SequenceFeature("Cath", "", 0, 0, 0f, null);
752     fs.addFeature(sf4);
753
754     /*
755      * shift all features right by 5
756      */
757     assertTrue(fs.shiftFeatures(0, 5));
758
759     // non-positional features untouched:
760     List<SequenceFeature> nonPos = fs.getNonPositionalFeatures();
761     assertEquals(nonPos.size(), 1);
762     assertTrue(nonPos.contains(sf4));
763
764     // positional features are replaced
765     List<SequenceFeature> pos = fs.getPositionalFeatures();
766     assertEquals(pos.size(), 3);
767     assertFalse(pos.contains(sf1));
768     assertFalse(pos.contains(sf2));
769     assertFalse(pos.contains(sf3));
770     SequenceFeatures.sortFeatures(pos, true); // ascending start pos
771     assertEquals(pos.get(0).getBegin(), 7);
772     assertEquals(pos.get(0).getEnd(), 10);
773     assertEquals(pos.get(1).getBegin(), 13);
774     assertEquals(pos.get(1).getEnd(), 19);
775     assertEquals(pos.get(2).getBegin(), 28);
776     assertEquals(pos.get(2).getEnd(), 37);
777
778     /*
779      * now shift left by 15
780      * feature at [7-10] should be removed
781      * feature at [13-19] should become [1-4] 
782      */
783     assertTrue(fs.shiftFeatures(0, -15));
784     pos = fs.getPositionalFeatures();
785     assertEquals(pos.size(), 2);
786     SequenceFeatures.sortFeatures(pos, true);
787     assertEquals(pos.get(0).getBegin(), 1);
788     assertEquals(pos.get(0).getEnd(), 4);
789     assertEquals(pos.get(1).getBegin(), 13);
790     assertEquals(pos.get(1).getEnd(), 22);
791
792     /*
793      * shift right by 4 from position 2 onwards
794      * feature at [1-4] unchanged, feature at [13-22] shifts
795      */
796     assertTrue(fs.shiftFeatures(2, 4));
797     pos = fs.getPositionalFeatures();
798     assertEquals(pos.size(), 2);
799     SequenceFeatures.sortFeatures(pos, true);
800     assertEquals(pos.get(0).getBegin(), 1);
801     assertEquals(pos.get(0).getEnd(), 4);
802     assertEquals(pos.get(1).getBegin(), 17);
803     assertEquals(pos.get(1).getEnd(), 26);
804
805     /*
806      * shift right by 4 from position 18 onwards
807      * should be no change
808      */
809     SequenceFeature f1 = pos.get(0);
810     SequenceFeature f2 = pos.get(1);
811     assertFalse(fs.shiftFeatures(18, 4)); // no update
812     pos = fs.getPositionalFeatures();
813     assertEquals(pos.size(), 2);
814     SequenceFeatures.sortFeatures(pos, true);
815     assertSame(pos.get(0), f1);
816     assertSame(pos.get(1), f2);
817   }
818
819   @Test(groups = "Functional")
820   public void testDelete_readd()
821   {
822     /*
823      * add a feature and a nested feature
824      */
825     FeatureStore store = newFeatureStore();
826     SequenceFeature sf1 = addFeature(store, 10, 20);
827     // sf2 is nested in sf1 so will be stored in nestedFeatures
828     SequenceFeature sf2 = addFeature(store, 12, 14);
829     List<SequenceFeature> features = store.getPositionalFeatures();
830     assertEquals(features.size(), 2);
831     assertTrue(features.contains(sf1));
832     assertTrue(features.contains(sf2));
833     assertTrue(store.getFeatures().contains(sf1));
834     assertTrue(store.getFeatures().contains(sf2));
835   
836     /*
837      * delete the first feature
838      */
839     assertTrue(store.delete(sf1));
840     features = store.getPositionalFeatures();
841     assertFalse(features.contains(sf1));
842     assertTrue(features.contains(sf2));
843
844     /*
845      * re-add the 'nested' feature; is it now duplicated?
846      */
847     store.addFeature(sf2);
848     features = store.getPositionalFeatures();
849     assertEquals(features.size(), 1);
850     assertTrue(features.contains(sf2));
851   }
852
853   @Test(groups = "Functional")
854   public void testContains()
855   {
856     FeatureStore fs = newFeatureStore();
857     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
858             Float.NaN, "group1");
859     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 20,
860             Float.NaN, "group2");
861     SequenceFeature sf3 = new SequenceFeature("Cath", "", 0, 0, Float.NaN,
862             "group1");
863     SequenceFeature sf4 = new SequenceFeature("Cath", "", 0, 0, 0f,
864             "group1");
865     SequenceFeature sf5 = new SequenceFeature("Disulphide Bond", "", 5, 15,
866             Float.NaN, "group1");
867     SequenceFeature sf6 = new SequenceFeature("Disulphide Bond", "", 5, 15,
868             Float.NaN, "group2");
869
870     fs.addFeature(sf1);
871     fs.addFeature(sf3);
872     fs.addFeature(sf5);
873     assertTrue(fs.contains(sf1)); // positional feature
874     assertTrue(fs.contains(new SequenceFeature(sf1))); // identical feature
875     assertFalse(fs.contains(sf2)); // different group
876     assertTrue(fs.contains(sf3)); // non-positional
877     assertTrue(fs.contains(new SequenceFeature(sf3)));
878     assertFalse(fs.contains(sf4)); // different score
879     assertTrue(fs.contains(sf5)); // contact feature
880     assertTrue(fs.contains(new SequenceFeature(sf5)));
881     assertFalse(fs.contains(sf6)); // different group
882
883     /*
884      * add a nested feature
885      */
886     SequenceFeature sf7 = new SequenceFeature("Cath", "", 12, 16,
887             Float.NaN, "group1");
888     fs.addFeature(sf7);
889     assertTrue(fs.contains(sf7));
890     assertTrue(fs.contains(new SequenceFeature(sf7)));
891
892     /*
893      * delete the outer (enclosing, non-nested) feature
894      */
895     fs.delete(sf1);
896     assertFalse(fs.contains(sf1));
897     assertTrue(fs.contains(sf7));
898   }
899 }