JAL-2446 added getFeatureGroups
[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 testGetFeatures()
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.getFeatures();
271     assertEquals(features.size(), 7);
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     assertTrue(features.contains(sf6));
278     assertTrue(features.contains(sf7));
279   }
280
281   @Test(groups = "Functional")
282   public void testDelete()
283   {
284     FeatureStore store = new FeatureStore();
285     SequenceFeature sf1 = addFeature(store, 10, 20);
286     assertTrue(store.getFeatures().contains(sf1));
287
288     /*
289      * simple deletion
290      */
291     assertTrue(store.delete(sf1));
292     assertTrue(store.getFeatures().isEmpty());
293
294     /*
295      * non-positional feature deletion
296      */
297     SequenceFeature sf2 = addFeature(store, 0, 0);
298     assertTrue(store.getFeatures().contains(sf2));
299     assertTrue(store.delete(sf2));
300     assertTrue(store.getFeatures().isEmpty());
301
302     /*
303      * contact feature deletion
304      */
305     SequenceFeature sf3 = new SequenceFeature("", "Disulphide Bond", 11,
306             23, Float.NaN, null);
307     store.addFeature(sf3);
308     assertEquals(store.getFeatures().size(), 1);
309     assertTrue(store.getFeatures().contains(sf3));
310     assertTrue(store.delete(sf3));
311     assertTrue(store.getFeatures().isEmpty());
312
313     /*
314      * nested feature deletion
315      */
316     SequenceFeature sf4 = addFeature(store, 20, 30);
317     SequenceFeature sf5 = addFeature(store, 22, 26); // to NCList
318     SequenceFeature sf6 = addFeature(store, 23, 24); // child of sf5
319     SequenceFeature sf7 = addFeature(store, 25, 25); // sibling of sf6
320     SequenceFeature sf8 = addFeature(store, 24, 24); // child of sf6
321     SequenceFeature sf9 = addFeature(store, 23, 23); // child of sf6
322     assertEquals(store.getFeatures().size(), 6);
323
324     // delete a node with children - they take its place
325     assertTrue(store.delete(sf6)); // sf8, sf9 should become children of sf5
326     assertEquals(store.getFeatures().size(), 5);
327     assertFalse(store.getFeatures().contains(sf6));
328
329     // delete a node with no children
330     assertTrue(store.delete(sf7));
331     assertEquals(store.getFeatures().size(), 4);
332     assertFalse(store.getFeatures().contains(sf7));
333
334     // delete root of NCList
335     assertTrue(store.delete(sf5));
336     assertEquals(store.getFeatures().size(), 3);
337     assertFalse(store.getFeatures().contains(sf5));
338
339     // continue the killing fields
340     assertTrue(store.delete(sf4));
341     assertEquals(store.getFeatures().size(), 2);
342     assertFalse(store.getFeatures().contains(sf4));
343
344     assertTrue(store.delete(sf9));
345     assertEquals(store.getFeatures().size(), 1);
346     assertFalse(store.getFeatures().contains(sf9));
347
348     assertTrue(store.delete(sf8));
349     assertTrue(store.getFeatures().isEmpty());
350   }
351
352   @Test(groups = "Functional")
353   public void testAddFeature()
354   {
355     FeatureStore fs = new FeatureStore();
356
357     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
358             Float.NaN, null);
359     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 20,
360             Float.NaN, null);
361
362     assertTrue(fs.addFeature(sf1));
363
364     /*
365      * re-adding the same or an identical feature should fail
366      */
367     assertFalse(fs.addFeature(sf1));
368     assertFalse(fs.addFeature(sf2));
369   }
370
371   @Test(groups = "Functional")
372   public void testIsEmpty()
373   {
374     FeatureStore fs = new FeatureStore();
375     assertTrue(fs.isEmpty());
376
377     /*
378      * non-nested feature
379      */
380     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
381             Float.NaN, null);
382     fs.addFeature(sf1);
383     assertFalse(fs.isEmpty());
384     fs.delete(sf1);
385     assertTrue(fs.isEmpty());
386
387     /*
388      * non-positional feature
389      */
390     sf1 = new SequenceFeature("Cath", "", 0, 0, Float.NaN, null);
391     fs.addFeature(sf1);
392     assertFalse(fs.isEmpty());
393     fs.delete(sf1);
394     assertTrue(fs.isEmpty());
395
396     /*
397      * contact feature
398      */
399     sf1 = new SequenceFeature("Disulfide bond", "", 19, 49, Float.NaN, null);
400     fs.addFeature(sf1);
401     assertFalse(fs.isEmpty());
402     fs.delete(sf1);
403     assertTrue(fs.isEmpty());
404
405     /*
406      * sf2, sf3 added as nested features
407      */
408     sf1 = new SequenceFeature("Cath", "", 19, 49, Float.NaN, null);
409     SequenceFeature sf2 = new SequenceFeature("Cath", "", 20, 40,
410             Float.NaN, null);
411     SequenceFeature sf3 = new SequenceFeature("Cath", "", 25, 35,
412             Float.NaN, null);
413     fs.addFeature(sf1);
414     fs.addFeature(sf2);
415     fs.addFeature(sf3);
416     assertTrue(fs.delete(sf1));
417     // FeatureStore should now only contain features in the NCList
418     assertEquals(fs.nonNestedFeatures.size(), 0);
419     assertEquals(fs.nestedFeatures.size(), 2);
420     assertFalse(fs.isEmpty());
421     assertTrue(fs.delete(sf2));
422     assertFalse(fs.isEmpty());
423     assertTrue(fs.delete(sf3));
424     assertTrue(fs.isEmpty()); // all gone
425   }
426
427   @Test(groups = "Functional")
428   public void testGetFeatureGroups()
429   {
430     FeatureStore fs = new FeatureStore();
431     assertTrue(fs.getFeatureGroups().isEmpty());
432
433     SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 10, 20, 1f, "group1");
434     fs.addFeature(sf1);
435     Set<String> groups = fs.getFeatureGroups();
436     assertEquals(groups.size(), 1);
437     assertTrue(groups.contains("group1"));
438
439     /*
440      * add another feature of the same group, delete one, delete both
441      */
442     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group1");
443     fs.addFeature(sf2);
444     groups = fs.getFeatureGroups();
445     assertEquals(groups.size(), 1);
446     assertTrue(groups.contains("group1"));
447     fs.delete(sf2);
448     groups = fs.getFeatureGroups();
449     assertEquals(groups.size(), 1);
450     assertTrue(groups.contains("group1"));
451     fs.delete(sf1);
452     groups = fs.getFeatureGroups();
453     assertTrue(fs.getFeatureGroups().isEmpty());
454
455     SequenceFeature sf3 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group2");
456     fs.addFeature(sf3);
457     SequenceFeature sf4 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "Group2");
458     fs.addFeature(sf4);
459     SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 20, 30, 1f, null);
460     fs.addFeature(sf5);
461     groups = fs.getFeatureGroups();
462     assertEquals(groups.size(), 3);
463     assertTrue(groups.contains("group2"));
464     assertTrue(groups.contains("Group2")); // case sensitive
465     assertTrue(groups.contains(null)); // null allowed
466
467     fs.delete(sf3);
468     groups = fs.getFeatureGroups();
469     assertEquals(groups.size(), 2);
470     assertFalse(groups.contains("group2"));
471     fs.delete(sf4);
472     groups = fs.getFeatureGroups();
473     assertEquals(groups.size(), 1);
474     assertFalse(groups.contains("Group2"));
475     fs.delete(sf5);
476     groups = fs.getFeatureGroups();
477     assertTrue(groups.isEmpty());
478   }
479 }