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