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