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