aefcbc00ce4af8cb69ed324be61056d843feabec
[jalview.git] / test / jalview / datamodel / ColumnSelectionTest.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.datamodel;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertFalse;
25 import static org.testng.AssertJUnit.assertTrue;
26 import static org.testng.AssertJUnit.fail;
27
28 import jalview.analysis.AlignmentGenerator;
29 import jalview.gui.JvOptionPane;
30 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter;
31 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter.SearchableAnnotationField;
32 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter.ThresholdType;
33
34 import java.util.Arrays;
35 import java.util.BitSet;
36 import java.util.Collections;
37 import java.util.ConcurrentModificationException;
38 import java.util.Iterator;
39 import java.util.List;
40
41 import org.testng.annotations.BeforeClass;
42 import org.testng.annotations.Test;
43
44 public class ColumnSelectionTest
45 {
46
47   @BeforeClass(alwaysRun = true)
48   public void setUpJvOptionPane()
49   {
50     JvOptionPane.setInteractiveMode(false);
51     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
52   }
53
54   @Test(groups = { "Functional" })
55   public void testAddElement()
56   {
57     ColumnSelection cs = new ColumnSelection();
58     cs.addElement(2);
59     cs.addElement(5);
60     cs.addElement(3);
61     cs.addElement(5); // ignored
62     List<Integer> sel = cs.getSelected();
63     assertEquals("[2, 5, 3]", sel.toString());
64   }
65
66   @Test(groups = { "Functional" })
67   public void testSetElementsFrom()
68   {
69     ColumnSelection fromcs = new ColumnSelection();
70     ColumnSelection tocs = new ColumnSelection();
71     HiddenColumns hidden = new HiddenColumns();
72
73     fromcs.addElement(2);
74     fromcs.addElement(3);
75     fromcs.addElement(5);
76
77     tocs.setElementsFrom(fromcs, hidden);
78     assertTrue(tocs.equals(fromcs));
79
80     hidden.hideColumns(4, 6);
81     tocs.setElementsFrom(fromcs, hidden);
82
83     // expect cols 2 and 3 to be selected but not 5
84     ColumnSelection expectcs = new ColumnSelection();
85     expectcs.addElement(2);
86     expectcs.addElement(3);
87     assertTrue(tocs.equals(expectcs));
88   }
89
90   /**
91    * Test the remove method - in particular to verify that remove(int i) removes
92    * the element whose value is i, _NOT_ the i'th element.
93    */
94   @Test(groups = { "Functional" })
95   public void testRemoveElement()
96   {
97     ColumnSelection cs = new ColumnSelection();
98     cs.addElement(2);
99     cs.addElement(5);
100
101     // removing elements not in the list has no effect
102     cs.removeElement(0);
103     cs.removeElement(1);
104     List<Integer> sel = cs.getSelected();
105     assertEquals(2, sel.size());
106     assertEquals(Integer.valueOf(2), sel.get(0));
107     assertEquals(Integer.valueOf(5), sel.get(1));
108
109     // removing an element in the list removes it
110     cs.removeElement(2);
111     // ...and also from the read-only view
112     assertEquals(1, sel.size());
113     sel = cs.getSelected();
114     assertEquals(1, sel.size());
115     assertEquals(Integer.valueOf(5), sel.get(0));
116   }
117
118   /**
119    * Test the method that hides a specified column including any adjacent
120    * selected columns. This is a convenience method for the case where multiple
121    * column regions are selected and then hidden using menu option View | Hide |
122    * Selected Columns.
123    */
124   @Test(groups = { "Functional" })
125   public void testHideColumns_withSelection()
126   {
127     // create random alignment
128     AlignmentGenerator gen = new AlignmentGenerator(false);
129     AlignmentI al = gen.generate(50, 20, 123, 5, 5);
130
131     ColumnSelection cs = new ColumnSelection();
132     // select columns 4-6
133     cs.addElement(4);
134     cs.addElement(5);
135     cs.addElement(6);
136     // hide column 5 (and adjacent):
137     cs.hideSelectedColumns(5, al.getHiddenColumns());
138     // 4,5,6 now hidden:
139     Iterator<int[]> regions = al.getHiddenColumns().iterator();
140     assertEquals(1, al.getHiddenColumns().getNumberOfRegions());
141     assertEquals("[4, 6]", Arrays.toString(regions.next()));
142     // none now selected:
143     assertTrue(cs.getSelected().isEmpty());
144
145     // repeat, hiding column 4 (5 and 6)
146     al = gen.generate(50, 20, 123, 5, 5);
147     cs = new ColumnSelection();
148     cs.addElement(4);
149     cs.addElement(5);
150     cs.addElement(6);
151     cs.hideSelectedColumns(4, al.getHiddenColumns());
152     regions = al.getHiddenColumns().iterator();
153     assertEquals(1, al.getHiddenColumns().getNumberOfRegions());
154     assertEquals("[4, 6]", Arrays.toString(regions.next()));
155     assertTrue(cs.getSelected().isEmpty());
156
157     // repeat, hiding column (4, 5 and) 6
158     al = gen.generate(50, 20, 123, 5, 5);
159     cs = new ColumnSelection();
160     cs.addElement(4);
161     cs.addElement(5);
162     cs.addElement(6);
163     cs.hideSelectedColumns(6, al.getHiddenColumns());
164     regions = al.getHiddenColumns().iterator();
165     assertEquals(1, al.getHiddenColumns().getNumberOfRegions());
166     assertEquals("[4, 6]", Arrays.toString(regions.next()));
167     assertTrue(cs.getSelected().isEmpty());
168
169     // repeat, with _only_ adjacent columns selected
170     al = gen.generate(50, 20, 123, 5, 5);
171     cs = new ColumnSelection();
172     cs.addElement(4);
173     cs.addElement(6);
174     cs.hideSelectedColumns(5, al.getHiddenColumns());
175     regions = al.getHiddenColumns().iterator();
176     assertEquals(1, al.getHiddenColumns().getNumberOfRegions());
177     assertEquals("[4, 6]", Arrays.toString(regions.next()));
178     assertTrue(cs.getSelected().isEmpty());
179   }
180
181   /**
182    * Test the method that hides all (possibly disjoint) selected column ranges
183    */
184   @Test(groups = { "Functional" })
185   public void testHideSelectedColumns()
186   {
187     // create random alignment
188     AlignmentGenerator gen = new AlignmentGenerator(false);
189     AlignmentI al = gen.generate(50, 20, 123, 5, 5);
190
191     ColumnSelection cs = new ColumnSelection();
192     int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
193     for (int col : sel)
194     {
195       cs.addElement(col);
196     }
197
198     HiddenColumns cols = al.getHiddenColumns();
199     cols.hideColumns(15, 18);
200
201     cs.hideSelectedColumns(al);
202     assertTrue(cs.getSelected().isEmpty());
203     Iterator<int[]> regions = cols.iterator();
204     assertEquals(4, cols.getNumberOfRegions());
205     assertEquals("[2, 4]", Arrays.toString(regions.next()));
206     assertEquals("[7, 9]", Arrays.toString(regions.next()));
207     assertEquals("[15, 18]", Arrays.toString(regions.next()));
208     assertEquals("[20, 22]", Arrays.toString(regions.next()));
209   }
210
211   /**
212    * Test the method that gets runs of selected columns ordered by column. If
213    * this fails, HideSelectedColumns may also fail
214    */
215   @Test(groups = { "Functional" })
216   public void testGetSelectedRanges()
217   {
218     /*
219      * getSelectedRanges returns ordered columns regardless
220      * of the order in which they are added
221      */
222     ColumnSelection cs = new ColumnSelection();
223     int[] sel = { 4, 3, 7, 21, 9, 20, 8, 22, 2 };
224     for (int col : sel)
225     {
226       cs.addElement(col);
227     }
228     List<int[]> range;
229     range = cs.getSelectedRanges();
230     assertEquals(3, range.size());
231     assertEquals("[2, 4]", Arrays.toString(range.get(0)));
232     assertEquals("[7, 9]", Arrays.toString(range.get(1)));
233     assertEquals("[20, 22]", Arrays.toString(range.get(2)));
234     cs.addElement(0);
235     cs.addElement(1);
236     range = cs.getSelectedRanges();
237     assertEquals(3, range.size());
238     assertEquals("[0, 4]", Arrays.toString(range.get(0)));
239   }
240
241   @Test(groups = { "Functional" })
242   public void testInvertColumnSelection()
243   {
244     // create random alignment
245     AlignmentGenerator gen = new AlignmentGenerator(false);
246     AlignmentI al = gen.generate(50, 20, 123, 5, 5);
247
248     ColumnSelection cs = new ColumnSelection();
249     cs.addElement(4);
250     cs.addElement(6);
251     cs.addElement(8);
252
253     HiddenColumns cols = al.getHiddenColumns();
254     cols.hideColumns(3, 3);
255     cols.hideColumns(6, 6);
256
257     // invert selection from start (inclusive) to end (exclusive)
258     cs.invertColumnSelection(2, 9, al);
259     assertEquals("[2, 5, 7]", cs.getSelected().toString());
260
261     cs.invertColumnSelection(1, 9, al);
262     assertEquals("[1, 4, 8]", cs.getSelected().toString());
263   }
264
265   @Test(groups = { "Functional" })
266   public void testMaxColumnSelection()
267   {
268     ColumnSelection cs = new ColumnSelection();
269     cs.addElement(0);
270     cs.addElement(513);
271     cs.addElement(1);
272     assertEquals(513, cs.getMax());
273     cs.removeElement(513);
274     assertEquals(1, cs.getMax());
275     cs.removeElement(1);
276     assertEquals(0, cs.getMax());
277     cs.addElement(512);
278     cs.addElement(513);
279     assertEquals(513, cs.getMax());
280
281   }
282
283   @Test(groups = { "Functional" })
284   public void testMinColumnSelection()
285   {
286     ColumnSelection cs = new ColumnSelection();
287     cs.addElement(0);
288     cs.addElement(513);
289     cs.addElement(1);
290     assertEquals(0, cs.getMin());
291     cs.removeElement(0);
292     assertEquals(1, cs.getMin());
293     cs.addElement(0);
294     assertEquals(0, cs.getMin());
295   }
296
297   @Test(groups = { "Functional" })
298   public void testEquals()
299   {
300     ColumnSelection cs = new ColumnSelection();
301     cs.addElement(0);
302     cs.addElement(513);
303     cs.addElement(1);
304
305     // same selections added in a different order
306     ColumnSelection cs2 = new ColumnSelection();
307     cs2.addElement(1);
308     cs2.addElement(513);
309     cs2.addElement(0);
310
311     assertTrue(cs.equals(cs2));
312     assertTrue(cs.equals(cs));
313     assertTrue(cs2.equals(cs));
314     assertTrue(cs2.equals(cs2));
315
316     cs2.addElement(12);
317     assertFalse(cs.equals(cs2));
318     assertFalse(cs2.equals(cs));
319
320     cs2.removeElement(12);
321     assertTrue(cs.equals(cs2));
322   }
323
324   /*
325       cs2.hideSelectedColumns(88);
326       assertFalse(cs.equals(cs2));
327       /*
328        * unhiding a column adds it to selection!
329        */
330   /*    cs2.revealHiddenColumns(88);
331       assertFalse(cs.equals(cs2));
332       cs.addElement(88);
333       assertTrue(cs.equals(cs2));
334     */
335
336   /**
337    * Test the method that returns selected columns, in the order in which they
338    * were added
339    */
340   @Test(groups = { "Functional" })
341   public void testGetSelected()
342   {
343     ColumnSelection cs = new ColumnSelection();
344     int[] sel = { 4, 3, 7, 21 };
345     for (int col : sel)
346     {
347       cs.addElement(col);
348     }
349
350     List<Integer> selected = cs.getSelected();
351     assertEquals(4, selected.size());
352     assertEquals("[4, 3, 7, 21]", selected.toString());
353
354     /*
355      * getSelected returns a read-only view of the list
356      * verify the view follows any changes in it
357      */
358     cs.removeElement(7);
359     cs.addElement(1);
360     cs.removeElement(4);
361     assertEquals("[3, 21, 1]", selected.toString());
362   }
363
364   /**
365    * Test to verify that the list returned by getSelection cannot be modified
366    */
367   @Test(groups = { "Functional" })
368   public void testGetSelected_isReadOnly()
369   {
370     ColumnSelection cs = new ColumnSelection();
371     cs.addElement(3);
372
373     List<Integer> selected = cs.getSelected();
374     try
375     {
376       selected.clear();
377       fail("expected exception");
378     } catch (UnsupportedOperationException e)
379     {
380       // expected
381     }
382     try
383     {
384       selected.add(1);
385       fail("expected exception");
386     } catch (UnsupportedOperationException e)
387     {
388       // expected
389     }
390     try
391     {
392       selected.remove(3);
393       fail("expected exception");
394     } catch (UnsupportedOperationException e)
395     {
396       // expected
397     }
398     try
399     {
400       Collections.sort(selected);
401       fail("expected exception");
402     } catch (UnsupportedOperationException e)
403     {
404       // expected
405     }
406   }
407
408   /**
409    * Test that demonstrates a ConcurrentModificationException is thrown if you
410    * change the selection while iterating over it
411    */
412   @Test(
413     groups = "Functional",
414     expectedExceptions = { ConcurrentModificationException.class })
415   public void testGetSelected_concurrentModification()
416   {
417     ColumnSelection cs = new ColumnSelection();
418     cs.addElement(0);
419     cs.addElement(1);
420     cs.addElement(2);
421
422     /*
423      * simulate changing the list under us (e.g. in a separate
424      * thread) while iterating over it -> ConcurrentModificationException
425      */
426     List<Integer> selected = cs.getSelected();
427     for (Integer col : selected)
428     {
429       if (col.intValue() == 0)
430       {
431         cs.removeElement(1);
432       }
433     }
434   }
435
436   @Test(groups = "Functional")
437   public void testMarkColumns()
438   {
439     ColumnSelection cs = new ColumnSelection();
440     cs.addElement(5); // this will be cleared
441     BitSet toMark = new BitSet();
442     toMark.set(1);
443     toMark.set(3);
444     toMark.set(6);
445     toMark.set(9);
446
447     assertTrue(cs.markColumns(toMark, 3, 8, false, false, false));
448     List<Integer> selected = cs.getSelected();
449     assertEquals(2, selected.size());
450     assertTrue(selected.contains(3));
451     assertTrue(selected.contains(6));
452   }
453
454   @Test(groups = "Functional")
455   public void testMarkColumns_extend()
456   {
457     ColumnSelection cs = new ColumnSelection();
458     cs.addElement(1);
459     cs.addElement(5);
460     BitSet toMark = new BitSet();
461     toMark.set(1);
462     toMark.set(3);
463     toMark.set(6);
464     toMark.set(9);
465
466     /*
467      * extending selection of {3, 6} should leave {1, 3, 5, 6} selected
468      */
469     assertTrue(cs.markColumns(toMark, 3, 8, false, true, false));
470     List<Integer> selected = cs.getSelected();
471     assertEquals(4, selected.size());
472     assertTrue(selected.contains(1));
473     assertTrue(selected.contains(3));
474     assertTrue(selected.contains(5));
475     assertTrue(selected.contains(6));
476   }
477
478   @Test(groups = "Functional")
479   public void testMarkColumns_invert()
480   {
481     ColumnSelection cs = new ColumnSelection();
482     cs.addElement(5); // this will be cleared
483     BitSet toMark = new BitSet();
484     toMark.set(1);
485     toMark.set(3);
486     toMark.set(6);
487     toMark.set(9);
488
489     /*
490      * inverted selection of {3, 6} should select {4, 5, 7, 8}
491      */
492     assertTrue(cs.markColumns(toMark, 3, 8, true, false, false));
493     List<Integer> selected = cs.getSelected();
494     assertEquals(4, selected.size());
495     assertTrue(selected.contains(4));
496     assertTrue(selected.contains(5));
497     assertTrue(selected.contains(7));
498     assertTrue(selected.contains(8));
499   }
500
501   @Test(groups = "Functional")
502   public void testMarkColumns_toggle()
503   {
504     ColumnSelection cs = new ColumnSelection();
505     cs.addElement(1); // outside change range
506     cs.addElement(3);
507     cs.addElement(4);
508     cs.addElement(10); // outside change range
509     BitSet toMark = new BitSet();
510     toMark.set(1);
511     toMark.set(3);
512     toMark.set(6);
513     toMark.set(9);
514
515     /*
516      * toggling state of {3, 6} should leave {1, 4, 6, 10} selected
517      */
518     assertTrue(cs.markColumns(toMark, 3, 8, false, false, true));
519     List<Integer> selected = cs.getSelected();
520     assertEquals(4, selected.size());
521     assertTrue(selected.contains(1));
522     assertTrue(selected.contains(4));
523     assertTrue(selected.contains(6));
524     assertTrue(selected.contains(10));
525   }
526
527   @Test(groups = "Functional")
528   public void testCopyConstructor()
529   {
530     ColumnSelection cs = new ColumnSelection();
531     cs.addElement(3);
532     cs.addElement(1);
533
534     ColumnSelection cs2 = new ColumnSelection(cs);
535     assertTrue(cs2.hasSelectedColumns());
536
537     // order of column selection is preserved
538     assertEquals("[3, 1]", cs2.getSelected().toString());
539   }
540
541
542   @Test(groups = { "Functional" })
543   public void testStretchGroup_expand()
544   {
545     /*
546      * test that emulates clicking column 4 (selected)
547      * and dragging right to column 5 (all base 0)
548      */
549     ColumnSelection cs = new ColumnSelection();
550     cs.addElement(4);
551     SequenceGroup sg = new SequenceGroup();
552     sg.setStartRes(4);
553     sg.setEndRes(4);
554     cs.stretchGroup(5, sg, 4, 4);
555     assertEquals(cs.getSelected().size(), 2);
556     assertTrue(cs.contains(4));
557     assertTrue(cs.contains(5));
558     assertEquals(sg.getStartRes(), 4);
559     assertEquals(sg.getEndRes(), 5);
560
561     /*
562      * emulate drag right with columns 10-20 already selected
563      */
564     cs.clear();
565     for (int i = 10; i <= 20; i++)
566     {
567       cs.addElement(i);
568     }
569     assertEquals(cs.getSelected().size(), 11);
570     sg = new SequenceGroup();
571     sg.setStartRes(10);
572     sg.setEndRes(20);
573     cs.stretchGroup(21, sg, 10, 20);
574     assertEquals(cs.getSelected().size(), 12);
575     assertTrue(cs.contains(10));
576     assertTrue(cs.contains(21));
577     assertEquals(sg.getStartRes(), 10);
578     assertEquals(sg.getEndRes(), 21);
579   }
580
581   @Test(groups = { "Functional" })
582   public void testStretchGroup_shrink()
583   {
584     /*
585      * emulate drag left to 19 with columns 10-20 already selected
586      */
587     ColumnSelection cs = new ColumnSelection();
588     for (int i = 10; i <= 20; i++)
589     {
590       cs.addElement(i);
591     }
592     assertEquals(cs.getSelected().size(), 11);
593     SequenceGroup sg = new SequenceGroup();
594     sg.setStartRes(10);
595     sg.setEndRes(20);
596     cs.stretchGroup(19, sg, 10, 20);
597     assertEquals(cs.getSelected().size(), 10);
598     assertTrue(cs.contains(10));
599     assertTrue(cs.contains(19));
600     assertFalse(cs.contains(20));
601     assertEquals(sg.getStartRes(), 10);
602     assertEquals(sg.getEndRes(), 19);
603   }
604
605   @Test(groups = { "Functional" })
606   public void testFilterAnnotations()
607   {
608     ColumnSelection cs = new ColumnSelection();
609
610     /*
611      * filter with no conditions clears the selection
612      */
613     Annotation[] anns = new Annotation[] { null };
614     AnnotationFilterParameter filter = new AnnotationFilterParameter();
615     cs.addElement(3);
616     int added = cs.filterAnnotations(anns, filter);
617     assertEquals(0, added);
618     assertTrue(cs.isEmpty());
619
620     /*
621      * select on description (regex)
622      */
623     filter.setRegexString("w.rld");
624     filter.addRegexSearchField(SearchableAnnotationField.DESCRIPTION);
625     Annotation helix = new Annotation("(", "hello", '<', 2f);
626     Annotation sheet = new Annotation("(", "world", '<', 2f);
627     added = cs.filterAnnotations(new Annotation[] { null, helix, sheet },
628             filter);
629     assertEquals(1, added);
630     assertTrue(cs.contains(2));
631
632     /*
633      * select on label (invalid regex, exact match)
634      */
635     filter = new AnnotationFilterParameter();
636     filter.setRegexString("(");
637     filter.addRegexSearchField(SearchableAnnotationField.DISPLAY_STRING);
638     added = cs.filterAnnotations(new Annotation[] { null, helix, sheet },
639             filter);
640     assertEquals(2, added);
641     assertTrue(cs.contains(1));
642     assertTrue(cs.contains(2));
643
644     /*
645      * select Helix (secondary structure symbol H)
646      */
647     filter = new AnnotationFilterParameter();
648     filter.setFilterAlphaHelix(true);
649     helix = new Annotation("x", "desc", 'H', 0f);
650     sheet = new Annotation("x", "desc", 'E', 1f);
651     Annotation turn = new Annotation("x", "desc", 'S', 2f);
652     Annotation ann4 = new Annotation("x", "desc", 'Y', 3f);
653     added = cs
654             .filterAnnotations(new Annotation[]
655             { null, helix, sheet, turn, ann4 },
656             filter);
657     assertEquals(1, added);
658     assertTrue(cs.contains(1));
659
660     /*
661      * select Helix and Sheet (E)
662      */
663     filter.setFilterBetaSheet(true);
664     added = cs
665             .filterAnnotations(new Annotation[]
666             { null, helix, sheet, turn, ann4 }, filter);
667     assertEquals(2, added);
668     assertTrue(cs.contains(1));
669     assertTrue(cs.contains(2));
670
671     /*
672      * select Sheet and Turn (S)
673      */
674     filter.setFilterAlphaHelix(false);
675     filter.setFilterTurn(true);
676     added = cs
677             .filterAnnotations(new Annotation[]
678             { null, helix, sheet, turn, ann4 }, filter);
679     assertEquals(2, added);
680     assertTrue(cs.contains(2));
681     assertTrue(cs.contains(3));
682
683     /*
684      * select value < 2f (ann1, ann2)
685      */
686     filter = new AnnotationFilterParameter();
687     filter.setThresholdType(ThresholdType.BELOW_THRESHOLD);
688     filter.setThresholdValue(2f);
689     added = cs
690             .filterAnnotations(new Annotation[]
691             { null, helix, sheet, turn, ann4 }, filter);
692     assertEquals(2, added);
693     assertTrue(cs.contains(1));
694     assertTrue(cs.contains(2));
695
696     /*
697      * select value > 2f (ann4 only)
698      */
699     filter.setThresholdType(ThresholdType.ABOVE_THRESHOLD);
700     added = cs
701             .filterAnnotations(new Annotation[]
702             { null, helix, sheet, turn, ann4 }, filter);
703     assertEquals(1, added);
704     assertTrue(cs.contains(4));
705
706     /*
707      * select >2f or Helix
708      */
709     filter.setFilterAlphaHelix(true);
710     added = cs
711             .filterAnnotations(new Annotation[]
712             { null, helix, sheet, turn, ann4 }, filter);
713     assertEquals(2, added);
714     assertTrue(cs.contains(1));
715     assertTrue(cs.contains(4));
716
717     /*
718      * select < 1f or Helix; one annotation matches both
719      * return value should only count it once
720      */
721     filter.setThresholdType(ThresholdType.BELOW_THRESHOLD);
722     filter.setThresholdValue(1f);
723     added = cs
724             .filterAnnotations(new Annotation[]
725             { null, helix, sheet, turn, ann4 }, filter);
726     assertEquals(1, added);
727     assertTrue(cs.contains(1));
728   }
729 }