1 package jalview.datamodel.features;
3 import static org.testng.Assert.assertEquals;
4 import static org.testng.Assert.assertFalse;
5 import static org.testng.Assert.assertTrue;
7 import jalview.datamodel.SequenceFeature;
12 import org.testng.annotations.Test;
14 public class FeatureStoreTest
17 @Test(groups = "Functional")
18 public void testFindFeatures_nonNested()
20 FeatureStore fs = new FeatureStore();
21 fs.addFeature(new SequenceFeature("", "", 10, 20, Float.NaN,
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));
28 List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
29 assertTrue(overlaps.isEmpty());
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);
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);
42 overlaps = fs.findOverlappingFeatures(33, 33);
43 assertEquals(overlaps.size(), 1);
44 assertEquals(overlaps.get(0).getEnd(), 35);
47 @Test(groups = "Functional")
48 public void testFindFeatures_nested()
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,
58 SequenceFeature sf5 = addFeature(fs, 35, 36);
60 List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
61 assertTrue(overlaps.isEmpty());
63 overlaps = fs.findOverlappingFeatures(10, 15);
64 assertEquals(overlaps.size(), 2);
65 assertTrue(overlaps.contains(sf1));
66 assertTrue(overlaps.contains(sf2));
68 overlaps = fs.findOverlappingFeatures(45, 60);
69 assertEquals(overlaps.size(), 1);
70 assertTrue(overlaps.contains(sf1));
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));
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));
86 @Test(groups = "Functional")
87 public void testFindFeatures_mixed()
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);
97 List<SequenceFeature> overlaps = fs.findOverlappingFeatures(200, 200);
98 assertTrue(overlaps.isEmpty());
100 overlaps = fs.findOverlappingFeatures(1, 9);
101 assertEquals(overlaps.size(), 1);
102 assertTrue(overlaps.contains(sf2));
104 overlaps = fs.findOverlappingFeatures(5, 18);
105 assertEquals(overlaps.size(), 2);
106 assertTrue(overlaps.contains(sf1));
107 assertTrue(overlaps.contains(sf2));
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));
115 overlaps = fs.findOverlappingFeatures(80, 90);
116 assertEquals(overlaps.size(), 2);
117 assertTrue(overlaps.contains(sf4));
118 assertTrue(overlaps.contains(sf5));
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));
128 * Helper method to add a feature of no particular type
135 SequenceFeature addFeature(FeatureStore fs, int from, int to)
137 SequenceFeature sf1 = new SequenceFeature("", "", from, to, Float.NaN,
143 @Test(groups = "Functional")
144 public void testFindFeatures_contactFeatures()
146 FeatureStore fs = new FeatureStore();
148 SequenceFeature sf = new SequenceFeature("disulphide bond", "bond", 10,
149 20, Float.NaN, null);
153 * neither contact point in range
155 List<SequenceFeature> overlaps = fs.findOverlappingFeatures(1, 9);
156 assertTrue(overlaps.isEmpty());
159 * neither contact point in range
161 overlaps = fs.findOverlappingFeatures(11, 19);
162 assertTrue(overlaps.isEmpty());
165 * first contact point in range
167 overlaps = fs.findOverlappingFeatures(5, 15);
168 assertEquals(overlaps.size(), 1);
169 assertTrue(overlaps.contains(sf));
172 * second contact point in range
174 overlaps = fs.findOverlappingFeatures(15, 25);
175 assertEquals(overlaps.size(), 1);
176 assertTrue(overlaps.contains(sf));
179 * both contact points in range
181 overlaps = fs.findOverlappingFeatures(5, 25);
182 assertEquals(overlaps.size(), 1);
183 assertTrue(overlaps.contains(sf));
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
190 @Test(groups = "Functional")
191 public void testAddNonNestedFeature()
193 FeatureStore fs = new FeatureStore();
195 String type = "Domain";
196 SequenceFeature sf1 = new SequenceFeature(type, type, 10, 20,
198 assertTrue(fs.addNonNestedFeature(sf1));
200 // co-located feature is ok
201 SequenceFeature sf2 = new SequenceFeature(type, type, 10, 20,
203 assertTrue(fs.addNonNestedFeature(sf2));
205 // overlap left is ok
206 SequenceFeature sf3 = new SequenceFeature(type, type, 5, 15, Float.NaN,
208 assertTrue(fs.addNonNestedFeature(sf3));
210 // overlap right is ok
211 SequenceFeature sf4 = new SequenceFeature(type, type, 15, 25,
213 assertTrue(fs.addNonNestedFeature(sf4));
215 // add enclosing feature is not ok
216 SequenceFeature sf5 = new SequenceFeature(type, type, 10, 21,
218 assertFalse(fs.addNonNestedFeature(sf5));
219 SequenceFeature sf6 = new SequenceFeature(type, type, 4, 15, Float.NaN,
221 assertFalse(fs.addNonNestedFeature(sf6));
222 SequenceFeature sf7 = new SequenceFeature(type, type, 1, 50, Float.NaN,
224 assertFalse(fs.addNonNestedFeature(sf7));
226 // add enclosed feature is not ok
227 SequenceFeature sf8 = new SequenceFeature(type, type, 10, 19,
229 assertFalse(fs.addNonNestedFeature(sf8));
230 SequenceFeature sf9 = new SequenceFeature(type, type, 16, 25,
232 assertFalse(fs.addNonNestedFeature(sf9));
233 SequenceFeature sf10 = new SequenceFeature(type, type, 7, 7, Float.NaN,
235 assertFalse(fs.addNonNestedFeature(sf10));
238 @Test(groups = "Functional")
239 public void testGetPositionalFeatures()
241 FeatureStore store = new FeatureStore();
242 SequenceFeature sf1 = new SequenceFeature("Metal", "desc", 10, 20,
244 store.addFeature(sf1);
245 // same range, different description
246 SequenceFeature sf2 = new SequenceFeature("Metal", "desc2", 10, 20,
248 store.addFeature(sf2);
249 // discontiguous range
250 SequenceFeature sf3 = new SequenceFeature("Metal", "desc", 30, 40,
252 store.addFeature(sf3);
254 SequenceFeature sf4 = new SequenceFeature("Metal", "desc", 15, 35,
256 store.addFeature(sf4);
258 SequenceFeature sf5 = new SequenceFeature("Metal", "desc", 5, 50,
260 store.addFeature(sf5);
261 // non-positional feature
262 SequenceFeature sf6 = new SequenceFeature("Metal", "desc", 0, 0,
264 store.addFeature(sf6);
266 SequenceFeature sf7 = new SequenceFeature("Disulphide bond", "desc",
267 18, 45, Float.NaN, null);
268 store.addFeature(sf7);
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));
280 features = store.getNonPositionalFeatures();
281 assertEquals(features.size(), 1);
282 assertTrue(features.contains(sf6));
285 @Test(groups = "Functional")
286 public void testDelete()
288 FeatureStore store = new FeatureStore();
289 SequenceFeature sf1 = addFeature(store, 10, 20);
290 assertTrue(store.getPositionalFeatures().contains(sf1));
295 assertTrue(store.delete(sf1));
296 assertTrue(store.getPositionalFeatures().isEmpty());
299 * non-positional feature deletion
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());
308 * contact feature deletion
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());
319 * nested feature deletion
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);
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));
334 // delete a node with no children
335 assertTrue(store.delete(sf7));
336 assertEquals(store.getPositionalFeatures().size(), 4);
337 assertFalse(store.getPositionalFeatures().contains(sf7));
339 // delete root of NCList
340 assertTrue(store.delete(sf5));
341 assertEquals(store.getPositionalFeatures().size(), 3);
342 assertFalse(store.getPositionalFeatures().contains(sf5));
344 // continue the killing fields
345 assertTrue(store.delete(sf4));
346 assertEquals(store.getPositionalFeatures().size(), 2);
347 assertFalse(store.getPositionalFeatures().contains(sf4));
349 assertTrue(store.delete(sf9));
350 assertEquals(store.getPositionalFeatures().size(), 1);
351 assertFalse(store.getPositionalFeatures().contains(sf9));
353 assertTrue(store.delete(sf8));
354 assertTrue(store.getPositionalFeatures().isEmpty());
357 @Test(groups = "Functional")
358 public void testAddFeature()
360 FeatureStore fs = new FeatureStore();
362 SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
364 SequenceFeature sf2 = new SequenceFeature("Cath", "", 10, 20,
367 assertTrue(fs.addFeature(sf1));
368 assertEquals(fs.getFeatureCount(true), 1); // positional
369 assertEquals(fs.getFeatureCount(false), 0); // non-positional
372 * re-adding the same or an identical feature should fail
374 assertFalse(fs.addFeature(sf1));
375 assertEquals(fs.getFeatureCount(true), 1);
376 assertFalse(fs.addFeature(sf2));
377 assertEquals(fs.getFeatureCount(true), 1);
382 SequenceFeature sf3 = new SequenceFeature("Cath", "", 0, 0, Float.NaN,
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,
389 assertFalse(fs.addFeature(sf4)); // already stored
390 assertEquals(fs.getFeatureCount(true), 1); // positional
391 assertEquals(fs.getFeatureCount(false), 1); // non-positional
396 SequenceFeature sf5 = new SequenceFeature("Disulfide bond", "", 0, 0,
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,
403 assertFalse(fs.addFeature(sf6)); // already stored
404 assertEquals(fs.getFeatureCount(true), 2); // no change
405 assertEquals(fs.getFeatureCount(false), 1); // no change
408 @Test(groups = "Functional")
409 public void testIsEmpty()
411 FeatureStore fs = new FeatureStore();
412 assertTrue(fs.isEmpty());
413 assertEquals(fs.getFeatureCount(true), 0);
418 SequenceFeature sf1 = new SequenceFeature("Cath", "", 10, 20,
421 assertFalse(fs.isEmpty());
422 assertEquals(fs.getFeatureCount(true), 1);
424 assertTrue(fs.isEmpty());
425 assertEquals(fs.getFeatureCount(true), 0);
428 * non-positional feature
430 sf1 = new SequenceFeature("Cath", "", 0, 0, Float.NaN, null);
432 assertFalse(fs.isEmpty());
433 assertEquals(fs.getFeatureCount(false), 1); // non-positional
434 assertEquals(fs.getFeatureCount(true), 0); // positional
436 assertTrue(fs.isEmpty());
437 assertEquals(fs.getFeatureCount(false), 0);
442 sf1 = new SequenceFeature("Disulfide bond", "", 19, 49, Float.NaN, null);
444 assertFalse(fs.isEmpty());
445 assertEquals(fs.getFeatureCount(true), 1);
447 assertTrue(fs.isEmpty());
448 assertEquals(fs.getFeatureCount(true), 0);
451 * sf2, sf3 added as nested features
453 sf1 = new SequenceFeature("Cath", "", 19, 49, Float.NaN, null);
454 SequenceFeature sf2 = new SequenceFeature("Cath", "", 20, 40,
456 SequenceFeature sf3 = new SequenceFeature("Cath", "", 25, 35,
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
476 @Test(groups = "Functional")
477 public void testGetFeatureGroups()
479 FeatureStore fs = new FeatureStore();
480 assertTrue(fs.getFeatureGroups(true).isEmpty());
481 assertTrue(fs.getFeatureGroups(false).isEmpty());
483 SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 10, 20, 1f, "group1");
485 Set<String> groups = fs.getFeatureGroups(true);
486 assertEquals(groups.size(), 1);
487 assertTrue(groups.contains("group1"));
490 * add another feature of the same group, delete one, delete both
492 SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group1");
494 groups = fs.getFeatureGroups(true);
495 assertEquals(groups.size(), 1);
496 assertTrue(groups.contains("group1"));
498 groups = fs.getFeatureGroups(true);
499 assertEquals(groups.size(), 1);
500 assertTrue(groups.contains("group1"));
502 groups = fs.getFeatureGroups(true);
503 assertTrue(fs.getFeatureGroups(true).isEmpty());
505 SequenceFeature sf3 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "group2");
507 SequenceFeature sf4 = new SequenceFeature("Cath", "desc", 20, 30, 1f, "Group2");
509 SequenceFeature sf5 = new SequenceFeature("Cath", "desc", 20, 30, 1f, null);
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
519 groups = fs.getFeatureGroups(true);
520 assertEquals(groups.size(), 2);
521 assertFalse(groups.contains("group2"));
523 groups = fs.getFeatureGroups(true);
524 assertEquals(groups.size(), 1);
525 assertFalse(groups.contains("Group2"));
527 groups = fs.getFeatureGroups(true);
528 assertTrue(groups.isEmpty());
531 * add non-positional feature
533 SequenceFeature sf6 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
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());
543 @Test(groups = "Functional")
544 public void testGetTotalFeatureLength()
546 FeatureStore fs = new FeatureStore();
547 assertEquals(fs.getTotalFeatureLength(), 0);
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);
555 // non-positional features don't count
556 SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 0, 0, 1f,
559 assertEquals(fs.getTotalFeatureLength(), 93);
561 // contact features count 1
562 SequenceFeature sf2 = new SequenceFeature("disulphide bond", "desc",
563 15, 35, 1f, "group1");
565 assertEquals(fs.getTotalFeatureLength(), 94);