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