JAL-2480 SequenceFeaturesI encapsulates features api
[jalview.git] / test / jalview / datamodel / features / SequenceFeaturesTest.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 SequenceFeaturesTest
15 {
16   @Test(groups = "Functional")
17   public void testGetPositionalFeatures()
18   {
19     SequenceFeaturesI store = new SequenceFeatures();
20     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
21             Float.NaN, null);
22     store.add(sf1);
23     // same range, different description
24     SequenceFeature sf2 = new SequenceFeature("Metal", "desc2", 10, 20,
25             Float.NaN, null);
26     store.add(sf2);
27     // discontiguous range
28     SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 30, 40,
29             Float.NaN, null);
30     store.add(sf3);
31     // overlapping range
32     SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 15, 35,
33             Float.NaN, null);
34     store.add(sf4);
35     // enclosing range
36     SequenceFeature sf5 = new SequenceFeature("Metal", "desc", 5, 50,
37             Float.NaN, null);
38     store.add(sf5);
39     // non-positional feature
40     SequenceFeature sf6 = new SequenceFeature("Metal", "desc", 0, 0,
41             Float.NaN, null);
42     store.add(sf6);
43     // contact feature
44     SequenceFeature sf7 = new SequenceFeature("Disulphide bond", "desc",
45             18, 45, Float.NaN, null);
46     store.add(sf7);
47     // different feature type
48     SequenceFeature sf8 = new SequenceFeature("Pfam", "desc", 30, 40,
49             Float.NaN, null);
50     store.add(sf8);
51     SequenceFeature sf9 = new SequenceFeature("Pfam", "desc", 15, 35,
52             Float.NaN, null);
53     store.add(sf9);
54
55     /*
56      * get all positional features
57      */
58     List<SequenceFeature> features = store.getPositionalFeatures();
59     assertEquals(features.size(), 8);
60     assertTrue(features.contains(sf1));
61     assertTrue(features.contains(sf2));
62     assertTrue(features.contains(sf3));
63     assertTrue(features.contains(sf4));
64     assertTrue(features.contains(sf5));
65     assertFalse(features.contains(sf6)); // non-positional
66     assertTrue(features.contains(sf7));
67     assertTrue(features.contains(sf8));
68     assertTrue(features.contains(sf9));
69
70     /*
71      * get features by type
72      */
73     assertTrue(store.getPositionalFeatures((String) null).isEmpty());
74     assertTrue(store.getPositionalFeatures("Cath").isEmpty());
75     assertTrue(store.getPositionalFeatures("METAL").isEmpty());
76
77     features = store.getPositionalFeatures("Metal");
78     assertEquals(features.size(), 5);
79     assertTrue(features.contains(sf1));
80     assertTrue(features.contains(sf2));
81     assertTrue(features.contains(sf3));
82     assertTrue(features.contains(sf4));
83     assertTrue(features.contains(sf5));
84     assertFalse(features.contains(sf6));
85
86     features = store.getPositionalFeatures("Disulphide bond");
87     assertEquals(features.size(), 1);
88     assertTrue(features.contains(sf7));
89
90     features = store.getPositionalFeatures("Pfam");
91     assertEquals(features.size(), 2);
92     assertTrue(features.contains(sf8));
93     assertTrue(features.contains(sf9));
94   }
95
96   @Test(groups = "Functional")
97   public void testGetContactFeatures()
98   {
99     SequenceFeaturesI store = new SequenceFeatures();
100     // non-contact
101     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
102             Float.NaN, null);
103     store.add(sf1);
104     // non-positional
105     SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 0, 0,
106             Float.NaN, null);
107     store.add(sf2);
108     // contact feature
109     SequenceFeature sf3 = new SequenceFeature("Disulphide bond", "desc",
110             18, 45, Float.NaN, null);
111     store.add(sf3);
112     // repeat for different feature type
113     SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 10, 20,
114             Float.NaN, null);
115     store.add(sf4);
116     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 0, 0,
117             Float.NaN, null);
118     store.add(sf5);
119     SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "desc", 18,
120             45, Float.NaN, null);
121     store.add(sf6);
122   
123     /*
124      * get all contact features
125      */
126     List<SequenceFeature> features = store.getContactFeatures();
127     assertEquals(features.size(), 2);
128     assertTrue(features.contains(sf3));
129     assertTrue(features.contains(sf6));
130   
131     /*
132      * get contact features by type
133      */
134     assertTrue(store.getContactFeatures((String) null).isEmpty());
135     assertTrue(store.getContactFeatures("Cath").isEmpty());
136     assertTrue(store.getContactFeatures("Pfam").isEmpty());
137     assertTrue(store.getContactFeatures("DISULPHIDE BOND").isEmpty());
138   
139     features = store.getContactFeatures("Disulphide bond");
140     assertEquals(features.size(), 1);
141     assertTrue(features.contains(sf3));
142   
143     features = store.getContactFeatures("Disulfide bond");
144     assertEquals(features.size(), 1);
145     assertTrue(features.contains(sf6));
146   }
147
148   @Test(groups = "Functional")
149   public void testGetNonPositionalFeatures()
150   {
151     SequenceFeaturesI store = new SequenceFeatures();
152     // positional
153     SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
154             Float.NaN, null);
155     store.add(sf1);
156     // non-positional
157     SequenceFeature sf2 = new SequenceFeature("Metal", "desc", 0, 0,
158             Float.NaN, null);
159     store.add(sf2);
160     // contact feature
161     SequenceFeature sf3 = new SequenceFeature("Disulphide bond", "desc",
162             18, 45, Float.NaN, null);
163     store.add(sf3);
164     // repeat for different feature type
165     SequenceFeature sf4 = new SequenceFeature("Pfam", "desc", 10, 20,
166             Float.NaN, null);
167     store.add(sf4);
168     SequenceFeature sf5 = new SequenceFeature("Pfam", "desc", 0, 0,
169             Float.NaN, null);
170     store.add(sf5);
171     SequenceFeature sf6 = new SequenceFeature("Disulfide bond", "desc", 18,
172             45, Float.NaN, null);
173     store.add(sf6);
174     // one more non-positional, different description
175     SequenceFeature sf7 = new SequenceFeature("Pfam", "desc2", 0, 0,
176             Float.NaN, null);
177     store.add(sf7);
178   
179     /*
180      * get all non-positional features
181      */
182     List<SequenceFeature> features = store.getNonPositionalFeatures();
183     assertEquals(features.size(), 3);
184     assertTrue(features.contains(sf2));
185     assertTrue(features.contains(sf5));
186     assertTrue(features.contains(sf7));
187   
188     /*
189      * get non-positional features by type
190      */
191     assertTrue(store.getNonPositionalFeatures((String) null).isEmpty());
192     assertTrue(store.getNonPositionalFeatures("Cath").isEmpty());
193     assertTrue(store.getNonPositionalFeatures("PFAM").isEmpty());
194   
195     features = store.getNonPositionalFeatures("Metal");
196     assertEquals(features.size(), 1);
197     assertTrue(features.contains(sf2));
198   
199     features = store.getNonPositionalFeatures("Pfam");
200     assertEquals(features.size(), 2);
201     assertTrue(features.contains(sf5));
202     assertTrue(features.contains(sf7));
203   }
204
205   /**
206    * Helper method to add a feature of no particular type
207    * 
208    * @param sf
209    * @param type
210    * @param from
211    * @param to
212    * @return
213    */
214   SequenceFeature addFeature(SequenceFeaturesI sf, String type, int from,
215           int to)
216   {
217     SequenceFeature sf1 = new SequenceFeature(type, "", from, to,
218             Float.NaN,
219             null);
220     sf.add(sf1);
221     return sf1;
222   }
223
224   @Test(groups = "Functional")
225   public void testFindFeatures()
226   {
227     SequenceFeaturesI sf = new SequenceFeatures();
228     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
229     SequenceFeature sf2 = addFeature(sf, "Pfam", 1, 15);
230     SequenceFeature sf3 = addFeature(sf, "Pfam", 20, 30);
231     SequenceFeature sf4 = addFeature(sf, "Pfam", 40, 100);
232     SequenceFeature sf5 = addFeature(sf, "Pfam", 60, 100);
233     SequenceFeature sf6 = addFeature(sf, "Pfam", 70, 70);
234     SequenceFeature sf7 = addFeature(sf, "Cath", 10, 50);
235     SequenceFeature sf8 = addFeature(sf, "Cath", 1, 15);
236     SequenceFeature sf9 = addFeature(sf, "Cath", 20, 30);
237     SequenceFeature sf10 = addFeature(sf, "Cath", 40, 100);
238     SequenceFeature sf11 = addFeature(sf, "Cath", 60, 100);
239     SequenceFeature sf12 = addFeature(sf, "Cath", 70, 70);
240     // null type is weird but possible:
241     SequenceFeature sf13 = addFeature(sf, null, 5, 12);
242   
243     List<SequenceFeature> overlaps = sf.findFeatures(200, 200, "Pfam");
244     assertTrue(overlaps.isEmpty());
245   
246     overlaps = sf.findFeatures( 1, 9, "Pfam");
247     assertEquals(overlaps.size(), 1);
248     assertTrue(overlaps.contains(sf2));
249   
250     overlaps = sf.findFeatures( 5, 18, "Pfam");
251     assertEquals(overlaps.size(), 2);
252     assertTrue(overlaps.contains(sf1));
253     assertTrue(overlaps.contains(sf2));
254   
255     overlaps = sf.findFeatures(30, 40, "Pfam");
256     assertEquals(overlaps.size(), 3);
257     assertTrue(overlaps.contains(sf1));
258     assertTrue(overlaps.contains(sf3));
259     assertTrue(overlaps.contains(sf4));
260   
261     overlaps = sf.findFeatures( 80, 90, "Pfam");
262     assertEquals(overlaps.size(), 2);
263     assertTrue(overlaps.contains(sf4));
264     assertTrue(overlaps.contains(sf5));
265   
266     overlaps = sf.findFeatures( 68, 70, "Pfam");
267     assertEquals(overlaps.size(), 3);
268     assertTrue(overlaps.contains(sf4));
269     assertTrue(overlaps.contains(sf5));
270     assertTrue(overlaps.contains(sf6));
271
272     overlaps = sf.findFeatures(16, 69, "Cath");
273     assertEquals(overlaps.size(), 4);
274     assertTrue(overlaps.contains(sf7));
275     assertFalse(overlaps.contains(sf8));
276     assertTrue(overlaps.contains(sf9));
277     assertTrue(overlaps.contains(sf10));
278     assertTrue(overlaps.contains(sf11));
279     assertFalse(overlaps.contains(sf12));
280
281     assertTrue(sf.findFeatures(0, 1000, "Metal").isEmpty());
282
283     overlaps = sf.findFeatures(7, 7, (String) null);
284     assertEquals(overlaps.size(), 1);
285     assertTrue(overlaps.contains(sf13));
286   }
287
288   @Test(groups = "Functional")
289   public void testDelete()
290   {
291     SequenceFeaturesI sf = new SequenceFeatures();
292     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
293     assertTrue(sf.getPositionalFeatures().contains(sf1));
294
295     assertFalse(sf.delete(null));
296     SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 15, 0f, null);
297     assertFalse(sf.delete(sf2)); // not added, can't delete it
298     assertTrue(sf.delete(sf1));
299     assertTrue(sf.getPositionalFeatures().isEmpty());
300   }
301
302   @Test(groups = "Functional")
303   public void testHasFeatures()
304   {
305     SequenceFeaturesI sf = new SequenceFeatures();
306     assertFalse(sf.hasFeatures());
307
308     SequenceFeature sf1 = addFeature(sf, "Pfam", 10, 50);
309     assertTrue(sf.hasFeatures());
310
311     sf.delete(sf1);
312     assertFalse(sf.hasFeatures());
313   }
314
315   /**
316    * Tests for the method that gets feature groups for positional or
317    * non-positional features
318    */
319   @Test(groups = "Functional")
320   public void testGetFeatureGroups()
321   {
322     SequenceFeaturesI sf = new SequenceFeatures();
323     assertTrue(sf.getFeatureGroups(true).isEmpty());
324     assertTrue(sf.getFeatureGroups(false).isEmpty());
325
326     /*
327      * add a non-positional feature (begin/end = 0/0)
328      */
329     SequenceFeature sfx = new SequenceFeature("AType", "Desc", 0, 0, 0f,
330             "AGroup");
331     sf.add(sfx);
332     Set<String> groups = sf.getFeatureGroups(true); // for positional
333     assertTrue(groups.isEmpty());
334     groups = sf.getFeatureGroups(false); // for non-positional
335     assertEquals(groups.size(), 1);
336     assertTrue(groups.contains("AGroup"));
337
338     /*
339      * add, then delete, more non-positional features of different types
340      */
341     SequenceFeature sfy = new SequenceFeature("AnotherType", "Desc", 0, 0,
342             0f,
343             "AnotherGroup");
344     sf.add(sfy);
345     SequenceFeature sfz = new SequenceFeature("AThirdType", "Desc", 0, 0,
346             0f,
347             null);
348     sf.add(sfz);
349     groups = sf.getFeatureGroups(false);
350     assertEquals(groups.size(), 3);
351     assertTrue(groups.contains("AGroup"));
352     assertTrue(groups.contains("AnotherGroup"));
353     assertTrue(groups.contains(null)); // null is a possible group
354     sf.delete(sfz);
355     sf.delete(sfy);
356     groups = sf.getFeatureGroups(false);
357     assertEquals(groups.size(), 1);
358     assertTrue(groups.contains("AGroup"));
359
360     /*
361      * add positional features
362      */
363     SequenceFeature sf1 = new SequenceFeature("Pfam", "Desc", 10, 50, 0f,
364             "PfamGroup");
365     sf.add(sf1);
366     groups = sf.getFeatureGroups(true);
367     assertEquals(groups.size(), 1);
368     assertTrue(groups.contains("PfamGroup"));
369     groups = sf.getFeatureGroups(false); // non-positional unchanged
370     assertEquals(groups.size(), 1);
371     assertTrue(groups.contains("AGroup"));
372
373     SequenceFeature sf2 = new SequenceFeature("Cath", "Desc", 10, 50, 0f,
374             null);
375     sf.add(sf2);
376     groups = sf.getFeatureGroups(true);
377     assertEquals(groups.size(), 2);
378     assertTrue(groups.contains("PfamGroup"));
379     assertTrue(groups.contains(null));
380
381     sf.delete(sf1);
382     sf.delete(sf2);
383     assertTrue(sf.getFeatureGroups(true).isEmpty());
384
385     SequenceFeature sf3 = new SequenceFeature("CDS", "", 10, 50, 0f,
386             "Ensembl");
387     sf.add(sf3);
388     SequenceFeature sf4 = new SequenceFeature("exon", "", 10, 50, 0f,
389             "Ensembl");
390     sf.add(sf4);
391     groups = sf.getFeatureGroups(true);
392     assertEquals(groups.size(), 1);
393     assertTrue(groups.contains("Ensembl"));
394
395     /*
396      * delete last Ensembl group feature from CDS features
397      * but still have one in exon features
398      */
399     sf.delete(sf3);
400     groups = sf.getFeatureGroups(true);
401     assertEquals(groups.size(), 1);
402     assertTrue(groups.contains("Ensembl"));
403
404     /*
405      * delete the last non-positional feature
406      */
407     sf.delete(sfx);
408     groups = sf.getFeatureGroups(false);
409     assertTrue(groups.isEmpty());
410   }
411
412   @Test(groups = "Functional")
413   public void testGetFeatureTypesForGroups()
414   {
415     SequenceFeaturesI sf = new SequenceFeatures();
416     assertTrue(sf.getFeatureTypesForGroups(true, (String) null).isEmpty());
417   
418     /*
419      * add feature with group = "Uniprot", type = "helix"
420      */
421     String groupUniprot = "Uniprot";
422     SequenceFeature sf1 = new SequenceFeature("helix", "Desc", 10, 50, 0f,
423             groupUniprot);
424     sf.add(sf1);
425     Set<String> groups = sf.getFeatureTypesForGroups(true, groupUniprot);
426     assertEquals(groups.size(), 1);
427     assertTrue(groups.contains("helix"));
428     assertTrue(sf.getFeatureTypesForGroups(true, (String) null).isEmpty());
429   
430     /*
431      * add feature with group = "Uniprot", type = "strand"
432      */
433     SequenceFeature sf2 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
434             groupUniprot);
435     sf.add(sf2);
436     groups = sf.getFeatureTypesForGroups(true, groupUniprot);
437     assertEquals(groups.size(), 2);
438     assertTrue(groups.contains("helix"));
439     assertTrue(groups.contains("strand"));
440
441     /*
442      * delete the "strand" Uniprot feature - still have "helix"
443      */
444     sf.delete(sf2);
445     groups = sf.getFeatureTypesForGroups(true, groupUniprot);
446     assertEquals(groups.size(), 1);
447     assertTrue(groups.contains("helix"));
448
449     /*
450      * delete the "helix" Uniprot feature - none left
451      */
452     sf.delete(sf1);
453     assertTrue(sf.getFeatureTypesForGroups(true, groupUniprot).isEmpty());
454
455     /*
456      * add some null group features
457      */
458     SequenceFeature sf3 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
459             null);
460     sf.add(sf3);
461     SequenceFeature sf4 = new SequenceFeature("turn", "Desc", 10, 50, 0f,
462             null);
463     sf.add(sf4);
464     groups = sf.getFeatureTypesForGroups(true, (String) null);
465     assertEquals(groups.size(), 2);
466     assertTrue(groups.contains("strand"));
467     assertTrue(groups.contains("turn"));
468
469     /*
470      * add strand/Cath  and turn/Scop and query for one or both groups
471      * (find feature types for groups selected in Feature Settings)
472      */
473     SequenceFeature sf5 = new SequenceFeature("strand", "Desc", 10, 50, 0f,
474             "Cath");
475     sf.add(sf5);
476     SequenceFeature sf6 = new SequenceFeature("turn", "Desc", 10, 50, 0f,
477             "Scop");
478     sf.add(sf6);
479     groups = sf.getFeatureTypesForGroups(true, "Cath");
480     assertEquals(groups.size(), 1);
481     assertTrue(groups.contains("strand"));
482     groups = sf.getFeatureTypesForGroups(true, "Scop");
483     assertEquals(groups.size(), 1);
484     assertTrue(groups.contains("turn"));
485     groups = sf.getFeatureTypesForGroups(true, "Cath", "Scop");
486     assertEquals(groups.size(), 2);
487     assertTrue(groups.contains("turn"));
488     assertTrue(groups.contains("strand"));
489     // alternative vararg syntax
490     groups = sf.getFeatureTypesForGroups(true, new String[] { "Cath",
491         "Scop" });
492     assertEquals(groups.size(), 2);
493     assertTrue(groups.contains("turn"));
494     assertTrue(groups.contains("strand"));
495   }
496 }