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