JAL-2480 more test coverage
[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.List;
10 import java.util.Set;
11
12 import org.testng.annotations.Test;
13
14 public class FeatureStoreTest
15 {
16
17   @Test(groups = "Functional")
18   public void testFindFeatures_nonNested()
19   {
20     FeatureStore fs = new FeatureStore();
21     fs.addFeature(new SequenceFeature("", "", 10, 20, Float.NaN,
22             null));
23     // same range different description
24     fs.addFeature(new SequenceFeature("", "desc", 10, 20, Float.NaN, null));
25     fs.addFeature(new SequenceFeature("", "", 15, 25, Float.NaN, null));
26     fs.addFeature(new SequenceFeature("", "", 20, 35, Float.NaN, null));
27
28     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
29     assertTrue(overlaps.isEmpty());
30
31     overlaps = fs.findOverlappingFeatures(8, 10);
32     assertEquals(overlaps.size(), 2);
33     assertEquals(overlaps.get(0).getEnd(), 20);
34     assertEquals(overlaps.get(1).getEnd(), 20);
35
36     overlaps = fs.findOverlappingFeatures(12, 16);
37     assertEquals(overlaps.size(), 3);
38     assertEquals(overlaps.get(0).getEnd(), 20);
39     assertEquals(overlaps.get(1).getEnd(), 20);
40     assertEquals(overlaps.get(2).getEnd(), 25);
41
42     overlaps = fs.findOverlappingFeatures(33, 33);
43     assertEquals(overlaps.size(), 1);
44     assertEquals(overlaps.get(0).getEnd(), 35);
45   }
46
47   @Test(groups = "Functional")
48   public void testFindFeatures_nested()
49   {
50     FeatureStore fs = new FeatureStore();
51     SequenceFeature sf1 = addFeature(fs, 10, 50);
52     SequenceFeature sf2 = addFeature(fs, 10, 40);
53     SequenceFeature sf3 = addFeature(fs, 20, 30);
54     // fudge feature at same location but different group (so is added)
55     SequenceFeature sf4 = new SequenceFeature("", "", 20, 30, Float.NaN,
56             "different group");
57     fs.addFeature(sf4);
58     SequenceFeature sf5 = addFeature(fs, 35, 36);
59
60     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
61     assertTrue(overlaps.isEmpty());
62
63     overlaps = fs.findOverlappingFeatures(10, 15);
64     assertEquals(overlaps.size(), 2);
65     assertTrue(overlaps.contains(sf1));
66     assertTrue(overlaps.contains(sf2));
67
68     overlaps = fs.findOverlappingFeatures(45, 60);
69     assertEquals(overlaps.size(), 1);
70     assertTrue(overlaps.contains(sf1));
71
72     overlaps = fs.findOverlappingFeatures(32, 38);
73     assertEquals(overlaps.size(), 3);
74     assertTrue(overlaps.contains(sf1));
75     assertTrue(overlaps.contains(sf2));
76     assertTrue(overlaps.contains(sf5));
77
78     overlaps = fs.findOverlappingFeatures(15, 25);
79     assertEquals(overlaps.size(), 4);
80     assertTrue(overlaps.contains(sf1));
81     assertTrue(overlaps.contains(sf2));
82     assertTrue(overlaps.contains(sf3));
83     assertTrue(overlaps.contains(sf4));
84   }
85
86   @Test(groups = "Functional")
87   public void testFindFeatures_mixed()
88   {
89     FeatureStore fs = new FeatureStore();
90     SequenceFeature sf1 = addFeature(fs, 10, 50);
91     SequenceFeature sf2 = addFeature(fs, 1, 15);
92     SequenceFeature sf3 = addFeature(fs, 20, 30);
93     SequenceFeature sf4 = addFeature(fs, 40, 100);
94     SequenceFeature sf5 = addFeature(fs, 60, 100);
95     SequenceFeature sf6 = addFeature(fs, 70, 70);
96
97     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(200, 200);
98     assertTrue(overlaps.isEmpty());
99
100     overlaps = fs.findOverlappingFeatures(1, 9);
101     assertEquals(overlaps.size(), 1);
102     assertTrue(overlaps.contains(sf2));
103
104     overlaps = fs.findOverlappingFeatures(5, 18);
105     assertEquals(overlaps.size(), 2);
106     assertTrue(overlaps.contains(sf1));
107     assertTrue(overlaps.contains(sf2));
108
109     overlaps = fs.findOverlappingFeatures(30, 40);
110     assertEquals(overlaps.size(), 3);
111     assertTrue(overlaps.contains(sf1));
112     assertTrue(overlaps.contains(sf3));
113     assertTrue(overlaps.contains(sf4));
114
115     overlaps = fs.findOverlappingFeatures(80, 90);
116     assertEquals(overlaps.size(), 2);
117     assertTrue(overlaps.contains(sf4));
118     assertTrue(overlaps.contains(sf5));
119
120     overlaps = fs.findOverlappingFeatures(68, 70);
121     assertEquals(overlaps.size(), 3);
122     assertTrue(overlaps.contains(sf4));
123     assertTrue(overlaps.contains(sf5));
124     assertTrue(overlaps.contains(sf6));
125   }
126
127   /**
128    * Helper method to add a feature of no particular type
129    * 
130    * @param fs
131    * @param from
132    * @param to
133    * @return
134    */
135   SequenceFeature addFeature(FeatureStore fs, int from, int to)
136   {
137     SequenceFeature sf1 = new SequenceFeature("", "", from, to, Float.NaN,
138             null);
139     fs.addFeature(sf1);
140     return sf1;
141   }
142
143   @Test(groups = "Functional")
144   public void testFindFeatures_contactFeatures()
145   {
146     FeatureStore fs = new FeatureStore();
147
148     SequenceFeature sf = new SequenceFeature("disulphide bond", "bond", 10,
149             20, Float.NaN, null);
150     fs.addFeature(sf);
151
152     /*
153      * neither contact point in range
154      */
155     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
156     assertTrue(overlaps.isEmpty());
157
158     /*
159      * neither contact point in range
160      */
161     overlaps = fs.findOverlappingFeatures(11, 19);
162     assertTrue(overlaps.isEmpty());
163
164     /*
165      * first contact point in range
166      */
167     overlaps = fs.findOverlappingFeatures(5, 15);
168     assertEquals(overlaps.size(), 1);
169     assertTrue(overlaps.contains(sf));
170
171     /*
172      * second contact point in range
173      */
174     overlaps = fs.findOverlappingFeatures(15, 25);
175     assertEquals(overlaps.size(), 1);
176     assertTrue(overlaps.contains(sf));
177
178     /*
179      * both contact points in range
180      */
181     overlaps = fs.findOverlappingFeatures(5, 25);
182     assertEquals(overlaps.size(), 1);
183     assertTrue(overlaps.contains(sf));
184   }
185
186   /**
187    * Tests for the method that returns false for an attempt to add a feature
188    * that would enclose, or be enclosed by, another feature
189    */
190   @Test(groups = "Functional")
191   public void testAddNonNestedFeature()
192   {
193     FeatureStore fs = new FeatureStore();
194
195     String type = "Domain";
196     SequenceFeature sf1 = new SequenceFeature(type, type, 10, 20,
197             Float.NaN, null);
198     assertTrue(fs.addNonNestedFeature(sf1));
199
200     // co-located feature is ok
201     SequenceFeature sf2 = new SequenceFeature(type, type, 10, 20,
202             Float.NaN, null);
203     assertTrue(fs.addNonNestedFeature(sf2));
204
205     // overlap left is ok
206     SequenceFeature sf3 = new SequenceFeature(type, type, 5, 15, Float.NaN,
207             null);
208     assertTrue(fs.addNonNestedFeature(sf3));
209
210     // overlap right is ok
211     SequenceFeature sf4 = new SequenceFeature(type, type, 15, 25,
212             Float.NaN, null);
213     assertTrue(fs.addNonNestedFeature(sf4));
214
215     // add enclosing feature is not ok
216     SequenceFeature sf5 = new SequenceFeature(type, type, 10, 21,
217             Float.NaN, null);
218     assertFalse(fs.addNonNestedFeature(sf5));
219     SequenceFeature sf6 = new SequenceFeature(type, type, 4, 15, Float.NaN,
220             null);
221     assertFalse(fs.addNonNestedFeature(sf6));
222     SequenceFeature sf7 = new SequenceFeature(type, type, 1, 50, Float.NaN,
223             null);
224     assertFalse(fs.addNonNestedFeature(sf7));
225
226     // add enclosed feature is not ok
227     SequenceFeature sf8 = new SequenceFeature(type, type, 10, 19,
228             Float.NaN, null);
229     assertFalse(fs.addNonNestedFeature(sf8));
230     SequenceFeature sf9 = new SequenceFeature(type, type, 16, 25,
231             Float.NaN, null);
232     assertFalse(fs.addNonNestedFeature(sf9));
233     SequenceFeature sf10 = new SequenceFeature(type, type, 7, 7, Float.NaN,
234             null);
235     assertFalse(fs.addNonNestedFeature(sf10));
236   }
237
238   @Test(groups = "Functional")
239   public void testGetPositionalFeatures()
240   {
241     FeatureStore store = new FeatureStore();
242     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
243             Float.NaN, null);
244     store.addFeature(sf1);
245     // same range, different description
246     SequenceFeature sf2 = new SequenceFeature("Metal", "desc2", 10, 20,
247             Float.NaN, null);
248     store.addFeature(sf2);
249     // discontiguous range
250     SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 30, 40,
251             Float.NaN, null);
252     store.addFeature(sf3);
253     // overlapping range
254     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 15, 35,
255             Float.NaN, null);
256     store.addFeature(sf4);
257     // enclosing range
258     SequenceFeature sf5 = new SequenceFeature("Metal", "desc", 5, 50,
259             Float.NaN, null);
260     store.addFeature(sf5);
261     // non-positional feature
262     SequenceFeature sf6 = new SequenceFeature("Metal", "desc", 0, 0,
263             Float.NaN, null);
264     store.addFeature(sf6);
265     // contact feature
266     SequenceFeature sf7 = new SequenceFeature("Disulphide bond", "desc",
267             18, 45, Float.NaN, null);
268     store.addFeature(sf7);
269
270     List<SequenceFeature> features = store.getPositionalFeatures();
271     assertEquals(features.size(), 6);
272     assertTrue(features.contains(sf1));
273     assertTrue(features.contains(sf2));
274     assertTrue(features.contains(sf3));
275     assertTrue(features.contains(sf4));
276     assertTrue(features.contains(sf5));
277     assertFalse(features.contains(sf6));
278     assertTrue(features.contains(sf7));
279
280     features = store.getNonPositionalFeatures();
281     assertEquals(features.size(), 1);
282     assertTrue(features.contains(sf6));
283   }
284
285   @Test(groups = "Functional")
286   public void testDelete()
287   {
288     FeatureStore store = new FeatureStore();
289     SequenceFeature sf1 = addFeature(store, 10, 20);
290     assertTrue(store.getPositionalFeatures().contains(sf1));
291
292     /*
293      * simple deletion
294      */
295     assertTrue(store.delete(sf1));
296     assertTrue(store.getPositionalFeatures().isEmpty());
297
298     /*
299      * non-positional feature deletion
300      */
301     SequenceFeature sf2 = addFeature(store, 0, 0);
302     assertFalse(store.getPositionalFeatures().contains(sf2));
303     assertTrue(store.getNonPositionalFeatures().contains(sf2));
304     assertTrue(store.delete(sf2));
305     assertTrue(store.getNonPositionalFeatures().isEmpty());
306
307     /*
308      * contact feature deletion
309      */
310     SequenceFeature sf3 = new SequenceFeature("", "Disulphide Bond", 11,
311             23, Float.NaN, null);
312     store.addFeature(sf3);
313     assertEquals(store.getPositionalFeatures().size(), 1);
314     assertTrue(store.getPositionalFeatures().contains(sf3));
315     assertTrue(store.delete(sf3));
316     assertTrue(store.getPositionalFeatures().isEmpty());
317
318     /*
319      * nested feature deletion
320      */
321     SequenceFeature sf4 = addFeature(store, 20, 30);
322     SequenceFeature sf5 = addFeature(store, 22, 26); // to NCList
323     SequenceFeature sf6 = addFeature(store, 23, 24); // child of sf5
324     SequenceFeature sf7 = addFeature(store, 25, 25); // sibling of sf6
325     SequenceFeature sf8 = addFeature(store, 24, 24); // child of sf6
326     SequenceFeature sf9 = addFeature(store, 23, 23); // child of sf6
327     assertEquals(store.getPositionalFeatures().size(), 6);
328
329     // delete a node with children - they take its place
330     assertTrue(store.delete(sf6)); // sf8, sf9 should become children of sf5
331     assertEquals(store.getPositionalFeatures().size(), 5);
332     assertFalse(store.getPositionalFeatures().contains(sf6));
333
334     // delete a node with no children
335     assertTrue(store.delete(sf7));
336     assertEquals(store.getPositionalFeatures().size(), 4);
337     assertFalse(store.getPositionalFeatures().contains(sf7));
338
339     // delete root of NCList
340     assertTrue(store.delete(sf5));
341     assertEquals(store.getPositionalFeatures().size(), 3);
342     assertFalse(store.getPositionalFeatures().contains(sf5));
343
344     // continue the killing fields
345     assertTrue(store.delete(sf4));
346     assertEquals(store.getPositionalFeatures().size(), 2);
347     assertFalse(store.getPositionalFeatures().contains(sf4));
348
349     assertTrue(store.delete(sf9));
350     assertEquals(store.getPositionalFeatures().size(), 1);
351     assertFalse(store.getPositionalFeatures().contains(sf9));
352
353     assertTrue(store.delete(sf8));
354     assertTrue(store.getPositionalFeatures().isEmpty());
355   }
356
357   @Test(groups = "Functional")
358   public void testAddFeature()
359   {
360     FeatureStore fs = new FeatureStore();
361
362     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
363             Float.NaN, null);
364     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 20,
365             Float.NaN, null);
366
367     assertTrue(fs.addFeature(sf1));
368     assertEquals(fs.getFeatureCount(true), 1); // positional
369     assertEquals(fs.getFeatureCount(false), 0); // non-positional
370
371     /*
372      * re-adding the same or an identical feature should fail
373      */
374     assertFalse(fs.addFeature(sf1));
375     assertEquals(fs.getFeatureCount(true), 1);
376     assertFalse(fs.addFeature(sf2));
377     assertEquals(fs.getFeatureCount(true), 1);
378
379     /*
380      * add non-positional
381      */
382     SequenceFeature sf3 = new SequenceFeature("Cath", "", 0, 0, Float.NaN,
383             null);
384     assertTrue(fs.addFeature(sf3));
385     assertEquals(fs.getFeatureCount(true), 1); // positional
386     assertEquals(fs.getFeatureCount(false), 1); // non-positional
387     SequenceFeature sf4 = new SequenceFeature("Cath", "", 0, 0, Float.NaN,
388             null);
389     assertFalse(fs.addFeature(sf4)); // already stored
390     assertEquals(fs.getFeatureCount(true), 1); // positional
391     assertEquals(fs.getFeatureCount(false), 1); // non-positional
392
393     /*
394      * add contact
395      */
396     SequenceFeature sf5 = new SequenceFeature("Disulfide bond", "", 0, 0,
397             Float.NaN, null);
398     assertTrue(fs.addFeature(sf5));
399     assertEquals(fs.getFeatureCount(true), 2); // positional - add 1 for contact
400     assertEquals(fs.getFeatureCount(false), 1); // non-positional
401     SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "", 0, 0,
402             Float.NaN, null);
403     assertFalse(fs.addFeature(sf6)); // already stored
404     assertEquals(fs.getFeatureCount(true), 2); // no change
405     assertEquals(fs.getFeatureCount(false), 1); // no change
406   }
407
408   @Test(groups = "Functional")
409   public void testIsEmpty()
410   {
411     FeatureStore fs = new FeatureStore();
412     assertTrue(fs.isEmpty());
413     assertEquals(fs.getFeatureCount(true), 0);
414
415     /*
416      * non-nested feature
417      */
418     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
419             Float.NaN, null);
420     fs.addFeature(sf1);
421     assertFalse(fs.isEmpty());
422     assertEquals(fs.getFeatureCount(true), 1);
423     fs.delete(sf1);
424     assertTrue(fs.isEmpty());
425     assertEquals(fs.getFeatureCount(true), 0);
426
427     /*
428      * non-positional feature
429      */
430     sf1 = new SequenceFeature("Cath", "", 0, 0, Float.NaN, null);
431     fs.addFeature(sf1);
432     assertFalse(fs.isEmpty());
433     assertEquals(fs.getFeatureCount(false), 1); // non-positional
434     assertEquals(fs.getFeatureCount(true), 0); // positional
435     fs.delete(sf1);
436     assertTrue(fs.isEmpty());
437     assertEquals(fs.getFeatureCount(false), 0);
438
439     /*
440      * contact feature
441      */
442     sf1 = new SequenceFeature("Disulfide bond", "", 19, 49, Float.NaN, null);
443     fs.addFeature(sf1);
444     assertFalse(fs.isEmpty());
445     assertEquals(fs.getFeatureCount(true), 1);
446     fs.delete(sf1);
447     assertTrue(fs.isEmpty());
448     assertEquals(fs.getFeatureCount(true), 0);
449
450     /*
451      * sf2, sf3 added as nested features
452      */
453     sf1 = new SequenceFeature("Cath", "", 19, 49, Float.NaN, null);
454     SequenceFeature sf2 = new SequenceFeature("Cath", "", 20, 40,
455             Float.NaN, null);
456     SequenceFeature sf3 = new SequenceFeature("Cath", "", 25, 35,
457             Float.NaN, null);
458     fs.addFeature(sf1);
459     fs.addFeature(sf2);
460     fs.addFeature(sf3);
461     assertEquals(fs.getFeatureCount(true), 3);
462     assertTrue(fs.delete(sf1));
463     assertEquals(fs.getFeatureCount(true), 2);
464     // FeatureStore should now only contain features in the NCList
465     assertTrue(fs.nonNestedFeatures.isEmpty());
466     assertEquals(fs.nestedFeatures.size(), 2);
467     assertFalse(fs.isEmpty());
468     assertTrue(fs.delete(sf2));
469     assertEquals(fs.getFeatureCount(true), 1);
470     assertFalse(fs.isEmpty());
471     assertTrue(fs.delete(sf3));
472     assertEquals(fs.getFeatureCount(true), 0);
473     assertTrue(fs.isEmpty()); // all gone
474   }
475
476   @Test(groups = "Functional")
477   public void testGetFeatureGroups()
478   {
479     FeatureStore fs = new FeatureStore();
480     assertTrue(fs.getFeatureGroups(true).isEmpty());
481     assertTrue(fs.getFeatureGroups(false).isEmpty());
482
483     SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 10, 20, 1f, "group1");
484     fs.addFeature(sf1);
485     Set<String> groups = fs.getFeatureGroups(true);
486     assertEquals(groups.size(), 1);
487     assertTrue(groups.contains("group1"));
488
489     /*
490      * add another feature of the same group, delete one, delete both
491      */
492     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group1");
493     fs.addFeature(sf2);
494     groups = fs.getFeatureGroups(true);
495     assertEquals(groups.size(), 1);
496     assertTrue(groups.contains("group1"));
497     fs.delete(sf2);
498     groups = fs.getFeatureGroups(true);
499     assertEquals(groups.size(), 1);
500     assertTrue(groups.contains("group1"));
501     fs.delete(sf1);
502     groups = fs.getFeatureGroups(true);
503     assertTrue(fs.getFeatureGroups(true).isEmpty());
504
505     SequenceFeature sf3 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group2");
506     fs.addFeature(sf3);
507     SequenceFeature sf4 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "Group2");
508     fs.addFeature(sf4);
509     SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 20, 30, 1f, null);
510     fs.addFeature(sf5);
511     groups = fs.getFeatureGroups(true);
512     assertEquals(groups.size(), 3);
513     assertTrue(groups.contains("group2"));
514     assertTrue(groups.contains("Group2")); // case sensitive
515     assertTrue(groups.contains(null)); // null allowed
516     assertTrue(fs.getFeatureGroups(false).isEmpty()); // non-positional
517
518     fs.delete(sf3);
519     groups = fs.getFeatureGroups(true);
520     assertEquals(groups.size(), 2);
521     assertFalse(groups.contains("group2"));
522     fs.delete(sf4);
523     groups = fs.getFeatureGroups(true);
524     assertEquals(groups.size(), 1);
525     assertFalse(groups.contains("Group2"));
526     fs.delete(sf5);
527     groups = fs.getFeatureGroups(true);
528     assertTrue(groups.isEmpty());
529
530     /*
531      * add non-positional feature
532      */
533     SequenceFeature sf6 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
534             "CathGroup");
535     fs.addFeature(sf6);
536     groups = fs.getFeatureGroups(false);
537     assertEquals(groups.size(), 1);
538     assertTrue(groups.contains("CathGroup"));
539     assertTrue(fs.delete(sf6));
540     assertTrue(fs.getFeatureGroups(false).isEmpty());
541   }
542
543   @Test(groups = "Functional")
544   public void testGetTotalFeatureLength()
545   {
546     FeatureStore fs = new FeatureStore();
547     assertEquals(fs.getTotalFeatureLength(), 0);
548
549     addFeature(fs, 10, 20); // 11
550     assertEquals(fs.getTotalFeatureLength(), 11);
551     addFeature(fs, 17, 37); // 21
552     addFeature(fs, 14, 74); // 61
553     assertEquals(fs.getTotalFeatureLength(), 93);
554
555     // non-positional features don't count
556     SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
557             "group1");
558     fs.addFeature(sf1);
559     assertEquals(fs.getTotalFeatureLength(), 93);
560
561     // contact features count 1
562     SequenceFeature sf2 = new SequenceFeature("disulphide bond", "desc",
563             15, 35, 1f, "group1");
564     fs.addFeature(sf2);
565     assertEquals(fs.getTotalFeatureLength(), 94);
566   }
567 }