JAL-2446 FeatureStore.isEmpty() added
[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
11 import org.testng.annotations.Test;
12
13 public class FeatureStoreTest
14 {
15
16   @Test(groups = "Functional")
17   public void testFindFeatures_nonNested()
18   {
19     FeatureStore fs = new FeatureStore();
20     fs.addFeature(new SequenceFeature("", "", 10, 20, Float.NaN,
21             null));
22     // same range different description
23     fs.addFeature(new SequenceFeature("", "desc", 10, 20, Float.NaN, null));
24     fs.addFeature(new SequenceFeature("", "", 15, 25, Float.NaN, null));
25     fs.addFeature(new SequenceFeature("", "", 20, 35, Float.NaN, null));
26
27     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
28     assertTrue(overlaps.isEmpty());
29
30     overlaps = fs.findOverlappingFeatures(8, 10);
31     assertEquals(overlaps.size(), 2);
32     assertEquals(overlaps.get(0).getEnd(), 20);
33     assertEquals(overlaps.get(1).getEnd(), 20);
34
35     overlaps = fs.findOverlappingFeatures(12, 16);
36     assertEquals(overlaps.size(), 3);
37     assertEquals(overlaps.get(0).getEnd(), 20);
38     assertEquals(overlaps.get(1).getEnd(), 20);
39     assertEquals(overlaps.get(2).getEnd(), 25);
40
41     overlaps = fs.findOverlappingFeatures(33, 33);
42     assertEquals(overlaps.size(), 1);
43     assertEquals(overlaps.get(0).getEnd(), 35);
44   }
45
46   @Test(groups = "Functional")
47   public void testFindFeatures_nested()
48   {
49     FeatureStore fs = new FeatureStore();
50     SequenceFeature sf1 = addFeature(fs, 10, 50);
51     SequenceFeature sf2 = addFeature(fs, 10, 40);
52     SequenceFeature sf3 = addFeature(fs, 20, 30);
53     // fudge feature at same location but different group (so is added)
54     SequenceFeature sf4 = new SequenceFeature("", "", 20, 30, Float.NaN,
55             "different group");
56     fs.addFeature(sf4);
57     SequenceFeature sf5 = addFeature(fs, 35, 36);
58
59     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
60     assertTrue(overlaps.isEmpty());
61
62     overlaps = fs.findOverlappingFeatures(10, 15);
63     assertEquals(overlaps.size(), 2);
64     assertTrue(overlaps.contains(sf1));
65     assertTrue(overlaps.contains(sf2));
66
67     overlaps = fs.findOverlappingFeatures(45, 60);
68     assertEquals(overlaps.size(), 1);
69     assertTrue(overlaps.contains(sf1));
70
71     overlaps = fs.findOverlappingFeatures(32, 38);
72     assertEquals(overlaps.size(), 3);
73     assertTrue(overlaps.contains(sf1));
74     assertTrue(overlaps.contains(sf2));
75     assertTrue(overlaps.contains(sf5));
76
77     overlaps = fs.findOverlappingFeatures(15, 25);
78     assertEquals(overlaps.size(), 4);
79     assertTrue(overlaps.contains(sf1));
80     assertTrue(overlaps.contains(sf2));
81     assertTrue(overlaps.contains(sf3));
82     assertTrue(overlaps.contains(sf4));
83   }
84
85   @Test(groups = "Functional")
86   public void testFindFeatures_mixed()
87   {
88     FeatureStore fs = new FeatureStore();
89     SequenceFeature sf1 = addFeature(fs, 10, 50);
90     SequenceFeature sf2 = addFeature(fs, 1, 15);
91     SequenceFeature sf3 = addFeature(fs, 20, 30);
92     SequenceFeature sf4 = addFeature(fs, 40, 100);
93     SequenceFeature sf5 = addFeature(fs, 60, 100);
94     SequenceFeature sf6 = addFeature(fs, 70, 70);
95
96     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(200, 200);
97     assertTrue(overlaps.isEmpty());
98
99     overlaps = fs.findOverlappingFeatures(1, 9);
100     assertEquals(overlaps.size(), 1);
101     assertTrue(overlaps.contains(sf2));
102
103     overlaps = fs.findOverlappingFeatures(5, 18);
104     assertEquals(overlaps.size(), 2);
105     assertTrue(overlaps.contains(sf1));
106     assertTrue(overlaps.contains(sf2));
107
108     overlaps = fs.findOverlappingFeatures(30, 40);
109     assertEquals(overlaps.size(), 3);
110     assertTrue(overlaps.contains(sf1));
111     assertTrue(overlaps.contains(sf3));
112     assertTrue(overlaps.contains(sf4));
113
114     overlaps = fs.findOverlappingFeatures(80, 90);
115     assertEquals(overlaps.size(), 2);
116     assertTrue(overlaps.contains(sf4));
117     assertTrue(overlaps.contains(sf5));
118
119     overlaps = fs.findOverlappingFeatures(68, 70);
120     assertEquals(overlaps.size(), 3);
121     assertTrue(overlaps.contains(sf4));
122     assertTrue(overlaps.contains(sf5));
123     assertTrue(overlaps.contains(sf6));
124   }
125
126   /**
127    * Helper method to add a feature of no particular type
128    * 
129    * @param fs
130    * @param from
131    * @param to
132    * @return
133    */
134   SequenceFeature addFeature(FeatureStore fs, int from, int to)
135   {
136     SequenceFeature sf1 = new SequenceFeature("", "", from, to, Float.NaN,
137             null);
138     fs.addFeature(sf1);
139     return sf1;
140   }
141
142   @Test(groups = "Functional")
143   public void testFindFeatures_contactFeatures()
144   {
145     FeatureStore fs = new FeatureStore();
146
147     SequenceFeature sf = new SequenceFeature("disulphide bond", "bond", 10,
148             20, Float.NaN, null);
149     fs.addFeature(sf);
150
151     /*
152      * neither contact point in range
153      */
154     List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
155     assertTrue(overlaps.isEmpty());
156
157     /*
158      * neither contact point in range
159      */
160     overlaps = fs.findOverlappingFeatures(11, 19);
161     assertTrue(overlaps.isEmpty());
162
163     /*
164      * first contact point in range
165      */
166     overlaps = fs.findOverlappingFeatures(5, 15);
167     assertEquals(overlaps.size(), 1);
168     assertTrue(overlaps.contains(sf));
169
170     /*
171      * second contact point in range
172      */
173     overlaps = fs.findOverlappingFeatures(15, 25);
174     assertEquals(overlaps.size(), 1);
175     assertTrue(overlaps.contains(sf));
176
177     /*
178      * both contact points in range
179      */
180     overlaps = fs.findOverlappingFeatures(5, 25);
181     assertEquals(overlaps.size(), 1);
182     assertTrue(overlaps.contains(sf));
183   }
184
185   /**
186    * Tests for the method that returns false for an attempt to add a feature
187    * that would enclose, or be enclosed by, another feature
188    */
189   @Test(groups = "Functional")
190   public void testAddNonNestedFeature()
191   {
192     FeatureStore fs = new FeatureStore();
193
194     String type = "Domain";
195     SequenceFeature sf1 = new SequenceFeature(type, type, 10, 20,
196             Float.NaN, null);
197     assertTrue(fs.addNonNestedFeature(sf1));
198
199     // co-located feature is ok
200     SequenceFeature sf2 = new SequenceFeature(type, type, 10, 20,
201             Float.NaN, null);
202     assertTrue(fs.addNonNestedFeature(sf2));
203
204     // overlap left is ok
205     SequenceFeature sf3 = new SequenceFeature(type, type, 5, 15, Float.NaN,
206             null);
207     assertTrue(fs.addNonNestedFeature(sf3));
208
209     // overlap right is ok
210     SequenceFeature sf4 = new SequenceFeature(type, type, 15, 25,
211             Float.NaN, null);
212     assertTrue(fs.addNonNestedFeature(sf4));
213
214     // add enclosing feature is not ok
215     SequenceFeature sf5 = new SequenceFeature(type, type, 10, 21,
216             Float.NaN, null);
217     assertFalse(fs.addNonNestedFeature(sf5));
218     SequenceFeature sf6 = new SequenceFeature(type, type, 4, 15, Float.NaN,
219             null);
220     assertFalse(fs.addNonNestedFeature(sf6));
221     SequenceFeature sf7 = new SequenceFeature(type, type, 1, 50, Float.NaN,
222             null);
223     assertFalse(fs.addNonNestedFeature(sf7));
224
225     // add enclosed feature is not ok
226     SequenceFeature sf8 = new SequenceFeature(type, type, 10, 19,
227             Float.NaN, null);
228     assertFalse(fs.addNonNestedFeature(sf8));
229     SequenceFeature sf9 = new SequenceFeature(type, type, 16, 25,
230             Float.NaN, null);
231     assertFalse(fs.addNonNestedFeature(sf9));
232     SequenceFeature sf10 = new SequenceFeature(type, type, 7, 7, Float.NaN,
233             null);
234     assertFalse(fs.addNonNestedFeature(sf10));
235   }
236
237   @Test(groups = "Functional")
238   public void testGetFeatures()
239   {
240     FeatureStore store = new FeatureStore();
241     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
242             Float.NaN, null);
243     store.addFeature(sf1);
244     // same range, different description
245     SequenceFeature sf2 = new SequenceFeature("Metal", "desc2", 10, 20,
246             Float.NaN, null);
247     store.addFeature(sf2);
248     // discontiguous range
249     SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 30, 40,
250             Float.NaN, null);
251     store.addFeature(sf3);
252     // overlapping range
253     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 15, 35,
254             Float.NaN, null);
255     store.addFeature(sf4);
256     // enclosing range
257     SequenceFeature sf5 = new SequenceFeature("Metal", "desc", 5, 50,
258             Float.NaN, null);
259     store.addFeature(sf5);
260     // non-positional feature
261     SequenceFeature sf6 = new SequenceFeature("Metal", "desc", 0, 0,
262             Float.NaN, null);
263     store.addFeature(sf6);
264     // contact feature
265     SequenceFeature sf7 = new SequenceFeature("Disulphide bond", "desc",
266             18, 45, Float.NaN, null);
267     store.addFeature(sf7);
268
269     List<SequenceFeature> features = store.getFeatures();
270     assertEquals(features.size(), 7);
271     assertTrue(features.contains(sf1));
272     assertTrue(features.contains(sf2));
273     assertTrue(features.contains(sf3));
274     assertTrue(features.contains(sf4));
275     assertTrue(features.contains(sf5));
276     assertTrue(features.contains(sf6));
277     assertTrue(features.contains(sf7));
278   }
279
280   @Test(groups = "Functional")
281   public void testDelete()
282   {
283     FeatureStore store = new FeatureStore();
284     SequenceFeature sf1 = addFeature(store, 10, 20);
285     assertTrue(store.getFeatures().contains(sf1));
286
287     /*
288      * simple deletion
289      */
290     assertTrue(store.delete(sf1));
291     assertTrue(store.getFeatures().isEmpty());
292
293     /*
294      * non-positional feature deletion
295      */
296     SequenceFeature sf2 = addFeature(store, 0, 0);
297     assertTrue(store.getFeatures().contains(sf2));
298     assertTrue(store.delete(sf2));
299     assertTrue(store.getFeatures().isEmpty());
300
301     /*
302      * contact feature deletion
303      */
304     SequenceFeature sf3 = new SequenceFeature("", "Disulphide Bond", 11,
305             23, Float.NaN, null);
306     store.addFeature(sf3);
307     assertEquals(store.getFeatures().size(), 1);
308     assertTrue(store.getFeatures().contains(sf3));
309     assertTrue(store.delete(sf3));
310     assertTrue(store.getFeatures().isEmpty());
311
312     /*
313      * nested feature deletion
314      */
315     SequenceFeature sf4 = addFeature(store, 20, 30);
316     SequenceFeature sf5 = addFeature(store, 22, 26); // to NCList
317     SequenceFeature sf6 = addFeature(store, 23, 24); // child of sf5
318     SequenceFeature sf7 = addFeature(store, 25, 25); // sibling of sf6
319     SequenceFeature sf8 = addFeature(store, 24, 24); // child of sf6
320     SequenceFeature sf9 = addFeature(store, 23, 23); // child of sf6
321     assertEquals(store.getFeatures().size(), 6);
322
323     // delete a node with children - they take its place
324     assertTrue(store.delete(sf6)); // sf8, sf9 should become children of sf5
325     assertEquals(store.getFeatures().size(), 5);
326     assertFalse(store.getFeatures().contains(sf6));
327
328     // delete a node with no children
329     assertTrue(store.delete(sf7));
330     assertEquals(store.getFeatures().size(), 4);
331     assertFalse(store.getFeatures().contains(sf7));
332
333     // delete root of NCList
334     assertTrue(store.delete(sf5));
335     assertEquals(store.getFeatures().size(), 3);
336     assertFalse(store.getFeatures().contains(sf5));
337
338     // continue the killing fields
339     assertTrue(store.delete(sf4));
340     assertEquals(store.getFeatures().size(), 2);
341     assertFalse(store.getFeatures().contains(sf4));
342
343     assertTrue(store.delete(sf9));
344     assertEquals(store.getFeatures().size(), 1);
345     assertFalse(store.getFeatures().contains(sf9));
346
347     assertTrue(store.delete(sf8));
348     assertTrue(store.getFeatures().isEmpty());
349   }
350
351   @Test(groups = "Functional")
352   public void testAddFeature()
353   {
354     FeatureStore fs = new FeatureStore();
355
356     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
357             Float.NaN, null);
358     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 20,
359             Float.NaN, null);
360
361     assertTrue(fs.addFeature(sf1));
362
363     /*
364      * re-adding the same or an identical feature should fail
365      */
366     assertFalse(fs.addFeature(sf1));
367     assertFalse(fs.addFeature(sf2));
368   }
369
370   @Test(groups = "Functional")
371   public void testIsEmpty()
372   {
373     FeatureStore fs = new FeatureStore();
374     assertTrue(fs.isEmpty());
375
376     /*
377      * non-nested feature
378      */
379     SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
380             Float.NaN, null);
381     fs.addFeature(sf1);
382     assertFalse(fs.isEmpty());
383     fs.delete(sf1);
384     assertTrue(fs.isEmpty());
385
386     /*
387      * non-positional feature
388      */
389     sf1 = new SequenceFeature("Cath", "", 0, 0, Float.NaN, null);
390     fs.addFeature(sf1);
391     assertFalse(fs.isEmpty());
392     fs.delete(sf1);
393     assertTrue(fs.isEmpty());
394
395     /*
396      * contact feature
397      */
398     sf1 = new SequenceFeature("Disulfide bond", "", 19, 49, Float.NaN, null);
399     fs.addFeature(sf1);
400     assertFalse(fs.isEmpty());
401     fs.delete(sf1);
402     assertTrue(fs.isEmpty());
403
404     /*
405      * sf2, sf3 added as nested features
406      */
407     sf1 = new SequenceFeature("Cath", "", 19, 49, Float.NaN, null);
408     SequenceFeature sf2 = new SequenceFeature("Cath", "", 20, 40,
409             Float.NaN, null);
410     SequenceFeature sf3 = new SequenceFeature("Cath", "", 25, 35,
411             Float.NaN, null);
412     fs.addFeature(sf1);
413     fs.addFeature(sf2);
414     fs.addFeature(sf3);
415     assertTrue(fs.delete(sf1));
416     // FeatureStore should now only contain features in the NCList
417     assertEquals(fs.nonNestedFeatures.size(), 0);
418     assertEquals(fs.nestedFeatures.size(), 2);
419     assertFalse(fs.isEmpty());
420     assertTrue(fs.delete(sf2));
421     assertFalse(fs.isEmpty());
422     assertTrue(fs.delete(sf3));
423     assertTrue(fs.isEmpty()); // all gone
424   }
425 }