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