2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.datamodel;
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;
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;
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;
41 import org.testng.annotations.BeforeClass;
42 import org.testng.annotations.Test;
44 public class ColumnSelectionTest
47 @BeforeClass(alwaysRun = true)
48 public void setUpJvOptionPane()
50 JvOptionPane.setInteractiveMode(false);
51 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
54 @Test(groups = { "Functional" })
55 public void testAddElement()
57 ColumnSelection cs = new ColumnSelection();
61 cs.addElement(5); // ignored
62 List<Integer> sel = cs.getSelected();
63 assertEquals("[2, 5, 3]", sel.toString());
66 @Test(groups = { "Functional" })
67 public void testSetElementsFrom()
69 ColumnSelection fromcs = new ColumnSelection();
70 ColumnSelection tocs = new ColumnSelection();
71 HiddenColumns hidden = new HiddenColumns();
77 tocs.setElementsFrom(fromcs, hidden);
78 assertTrue(tocs.equals(fromcs));
80 hidden.hideColumns(4, 6);
81 tocs.setElementsFrom(fromcs, hidden);
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));
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.
94 @Test(groups = { "Functional" })
95 public void testRemoveElement()
97 ColumnSelection cs = new ColumnSelection();
101 // removing elements not in the list has no effect
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));
109 // removing an element in the list removes it
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));
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 |
124 @Test(groups = { "Functional" })
125 public void testHideColumns_withSelection()
127 // create random alignment
128 AlignmentGenerator gen = new AlignmentGenerator(false);
129 AlignmentI al = gen.generate(50, 20, 123, 5, 5);
131 ColumnSelection cs = new ColumnSelection();
132 // select columns 4-6
136 // hide column 5 (and adjacent):
137 cs.hideSelectedColumns(5, al.getHiddenColumns());
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());
145 // repeat, hiding column 4 (5 and 6)
146 al = gen.generate(50, 20, 123, 5, 5);
147 cs = new ColumnSelection();
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());
157 // repeat, hiding column (4, 5 and) 6
158 al = gen.generate(50, 20, 123, 5, 5);
159 cs = new ColumnSelection();
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());
169 // repeat, with _only_ adjacent columns selected
170 al = gen.generate(50, 20, 123, 5, 5);
171 cs = new ColumnSelection();
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());
182 * Test the method that hides all (possibly disjoint) selected column ranges
184 @Test(groups = { "Functional" })
185 public void testHideSelectedColumns()
187 // create random alignment
188 AlignmentGenerator gen = new AlignmentGenerator(false);
189 AlignmentI al = gen.generate(50, 20, 123, 5, 5);
191 ColumnSelection cs = new ColumnSelection();
192 int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
198 HiddenColumns cols = al.getHiddenColumns();
199 cols.hideColumns(15, 18);
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()));
212 * Test the method that gets runs of selected columns ordered by column. If
213 * this fails, HideSelectedColumns may also fail
215 @Test(groups = { "Functional" })
216 public void testGetSelectedRanges()
219 * getSelectedRanges returns ordered columns regardless
220 * of the order in which they are added
222 ColumnSelection cs = new ColumnSelection();
223 int[] sel = { 4, 3, 7, 21, 9, 20, 8, 22, 2 };
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)));
236 range = cs.getSelectedRanges();
237 assertEquals(3, range.size());
238 assertEquals("[0, 4]", Arrays.toString(range.get(0)));
241 @Test(groups = { "Functional" })
242 public void testInvertColumnSelection()
244 // create random alignment
245 AlignmentGenerator gen = new AlignmentGenerator(false);
246 AlignmentI al = gen.generate(50, 20, 123, 5, 5);
248 ColumnSelection cs = new ColumnSelection();
253 HiddenColumns cols = al.getHiddenColumns();
254 cols.hideColumns(3, 3);
255 cols.hideColumns(6, 6);
257 // invert selection from start (inclusive) to end (exclusive)
258 cs.invertColumnSelection(2, 9, al);
259 assertEquals("[2, 5, 7]", cs.getSelected().toString());
261 cs.invertColumnSelection(1, 9, al);
262 assertEquals("[1, 4, 8]", cs.getSelected().toString());
265 @Test(groups = { "Functional" })
266 public void testMaxColumnSelection()
268 ColumnSelection cs = new ColumnSelection();
272 assertEquals(513, cs.getMax());
273 cs.removeElement(513);
274 assertEquals(1, cs.getMax());
276 assertEquals(0, cs.getMax());
279 assertEquals(513, cs.getMax());
283 @Test(groups = { "Functional" })
284 public void testMinColumnSelection()
286 ColumnSelection cs = new ColumnSelection();
290 assertEquals(0, cs.getMin());
292 assertEquals(1, cs.getMin());
294 assertEquals(0, cs.getMin());
297 @Test(groups = { "Functional" })
298 public void testEquals()
300 ColumnSelection cs = new ColumnSelection();
305 // same selections added in a different order
306 ColumnSelection cs2 = new ColumnSelection();
311 assertTrue(cs.equals(cs2));
312 assertTrue(cs.equals(cs));
313 assertTrue(cs2.equals(cs));
314 assertTrue(cs2.equals(cs2));
317 assertFalse(cs.equals(cs2));
318 assertFalse(cs2.equals(cs));
320 cs2.removeElement(12);
321 assertTrue(cs.equals(cs2));
325 cs2.hideSelectedColumns(88);
326 assertFalse(cs.equals(cs2));
328 * unhiding a column adds it to selection!
330 /* cs2.revealHiddenColumns(88);
331 assertFalse(cs.equals(cs2));
333 assertTrue(cs.equals(cs2));
337 * Test the method that returns selected columns, in the order in which they
340 @Test(groups = { "Functional" })
341 public void testGetSelected()
343 ColumnSelection cs = new ColumnSelection();
344 int[] sel = { 4, 3, 7, 21 };
350 List<Integer> selected = cs.getSelected();
351 assertEquals(4, selected.size());
352 assertEquals("[4, 3, 7, 21]", selected.toString());
355 * getSelected returns a read-only view of the list
356 * verify the view follows any changes in it
361 assertEquals("[3, 21, 1]", selected.toString());
365 * Test to verify that the list returned by getSelection cannot be modified
367 @Test(groups = { "Functional" })
368 public void testGetSelected_isReadOnly()
370 ColumnSelection cs = new ColumnSelection();
373 List<Integer> selected = cs.getSelected();
377 fail("expected exception");
378 } catch (UnsupportedOperationException e)
385 fail("expected exception");
386 } catch (UnsupportedOperationException e)
393 fail("expected exception");
394 } catch (UnsupportedOperationException e)
400 Collections.sort(selected);
401 fail("expected exception");
402 } catch (UnsupportedOperationException e)
409 * Test that demonstrates a ConcurrentModificationException is thrown if you
410 * change the selection while iterating over it
413 groups = "Functional",
414 expectedExceptions = { ConcurrentModificationException.class })
415 public void testGetSelected_concurrentModification()
417 ColumnSelection cs = new ColumnSelection();
423 * simulate changing the list under us (e.g. in a separate
424 * thread) while iterating over it -> ConcurrentModificationException
426 List<Integer> selected = cs.getSelected();
427 for (Integer col : selected)
429 if (col.intValue() == 0)
436 @Test(groups = "Functional")
437 public void testMarkColumns()
439 ColumnSelection cs = new ColumnSelection();
440 cs.addElement(5); // this will be cleared
441 BitSet toMark = new BitSet();
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));
454 @Test(groups = "Functional")
455 public void testMarkColumns_extend()
457 ColumnSelection cs = new ColumnSelection();
460 BitSet toMark = new BitSet();
467 * extending selection of {3, 6} should leave {1, 3, 5, 6} selected
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));
478 @Test(groups = "Functional")
479 public void testMarkColumns_invert()
481 ColumnSelection cs = new ColumnSelection();
482 cs.addElement(5); // this will be cleared
483 BitSet toMark = new BitSet();
490 * inverted selection of {3, 6} should select {4, 5, 7, 8}
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));
501 @Test(groups = "Functional")
502 public void testMarkColumns_toggle()
504 ColumnSelection cs = new ColumnSelection();
505 cs.addElement(1); // outside change range
508 cs.addElement(10); // outside change range
509 BitSet toMark = new BitSet();
516 * toggling state of {3, 6} should leave {1, 4, 6, 10} selected
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));
527 @Test(groups = "Functional")
528 public void testCopyConstructor()
530 ColumnSelection cs = new ColumnSelection();
534 ColumnSelection cs2 = new ColumnSelection(cs);
535 assertTrue(cs2.hasSelectedColumns());
537 // order of column selection is preserved
538 assertEquals("[3, 1]", cs2.getSelected().toString());
542 @Test(groups = { "Functional" })
543 public void testStretchGroup_expand()
546 * test that emulates clicking column 4 (selected)
547 * and dragging right to column 5 (all base 0)
549 ColumnSelection cs = new ColumnSelection();
551 SequenceGroup sg = new SequenceGroup();
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);
562 * emulate drag right with columns 10-20 already selected
565 for (int i = 10; i <= 20; i++)
569 assertEquals(cs.getSelected().size(), 11);
570 sg = new SequenceGroup();
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);
581 @Test(groups = { "Functional" })
582 public void testStretchGroup_shrink()
585 * emulate drag left to 19 with columns 10-20 already selected
587 ColumnSelection cs = new ColumnSelection();
588 for (int i = 10; i <= 20; i++)
592 assertEquals(cs.getSelected().size(), 11);
593 SequenceGroup sg = new SequenceGroup();
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);
605 @Test(groups = { "Functional" })
606 public void testFilterAnnotations()
608 ColumnSelection cs = new ColumnSelection();
611 * filter with no conditions clears the selection
613 Annotation[] anns = new Annotation[] { null };
614 AnnotationFilterParameter filter = new AnnotationFilterParameter();
616 int added = cs.filterAnnotations(anns, filter);
617 assertEquals(0, added);
618 assertTrue(cs.isEmpty());
621 * select on description (regex)
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 },
629 assertEquals(1, added);
630 assertTrue(cs.contains(2));
633 * select on label (invalid regex, exact match)
635 filter = new AnnotationFilterParameter();
636 filter.setRegexString("(");
637 filter.addRegexSearchField(SearchableAnnotationField.DISPLAY_STRING);
638 added = cs.filterAnnotations(new Annotation[] { null, helix, sheet },
640 assertEquals(2, added);
641 assertTrue(cs.contains(1));
642 assertTrue(cs.contains(2));
645 * select Helix (secondary structure symbol H)
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);
654 .filterAnnotations(new Annotation[]
655 { null, helix, sheet, turn, ann4 },
657 assertEquals(1, added);
658 assertTrue(cs.contains(1));
661 * select Helix and Sheet (E)
663 filter.setFilterBetaSheet(true);
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));
672 * select Sheet and Turn (S)
674 filter.setFilterAlphaHelix(false);
675 filter.setFilterTurn(true);
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));
684 * select value < 2f (ann1, ann2)
686 filter = new AnnotationFilterParameter();
687 filter.setThresholdType(ThresholdType.BELOW_THRESHOLD);
688 filter.setThresholdValue(2f);
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));
697 * select value > 2f (ann4 only)
699 filter.setThresholdType(ThresholdType.ABOVE_THRESHOLD);
701 .filterAnnotations(new Annotation[]
702 { null, helix, sheet, turn, ann4 }, filter);
703 assertEquals(1, added);
704 assertTrue(cs.contains(4));
707 * select >2f or Helix
709 filter.setFilterAlphaHelix(true);
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));
718 * select < 1f or Helix; one annotation matches both
719 * return value should only count it once
721 filter.setThresholdType(ThresholdType.BELOW_THRESHOLD);
722 filter.setThresholdValue(1f);
724 .filterAnnotations(new Annotation[]
725 { null, helix, sheet, turn, ann4 }, filter);
726 assertEquals(1, added);
727 assertTrue(cs.contains(1));