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