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