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