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