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