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.assertSame;
26 import static org.testng.AssertJUnit.assertTrue;
27 import static org.testng.AssertJUnit.fail;
29 import jalview.gui.JvOptionPane;
31 import java.util.Arrays;
32 import java.util.BitSet;
33 import java.util.Collections;
34 import java.util.ConcurrentModificationException;
35 import java.util.List;
37 import org.testng.annotations.BeforeClass;
38 import org.testng.annotations.Test;
40 public class ColumnSelectionTest
43 @BeforeClass(alwaysRun = true)
44 public void setUpJvOptionPane()
46 JvOptionPane.setInteractiveMode(false);
47 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
50 @Test(groups = { "Functional" })
51 public void testAddElement()
53 ColumnSelection cs = new ColumnSelection();
57 cs.addElement(5); // ignored
58 List<Integer> sel = cs.getSelected();
59 assertEquals("[2, 5, 3]", sel.toString());
63 * Test the remove method - in particular to verify that remove(int i) removes
64 * the element whose value is i, _NOT_ the i'th element.
66 @Test(groups = { "Functional" })
67 public void testRemoveElement()
69 ColumnSelection cs = new ColumnSelection();
73 // removing elements not in the list has no effect
76 List<Integer> sel = cs.getSelected();
77 assertEquals(2, sel.size());
78 assertEquals(new Integer(2), sel.get(0));
79 assertEquals(new Integer(5), sel.get(1));
81 // removing an element in the list removes it
83 // ...and also from the read-only view
84 assertEquals(1, sel.size());
85 sel = cs.getSelected();
86 assertEquals(1, sel.size());
87 assertEquals(new Integer(5), sel.get(0));
91 * Test the method that finds the visible column position of an alignment
92 * column, allowing for hidden columns.
94 @Test(groups = { "Functional" })
95 public void testFindColumnPosition()
97 ColumnSelection cs = new ColumnSelection();
98 assertEquals(5, cs.findColumnPosition(5));
100 // hiding column 6 makes no difference
101 cs.hideColumns(6, 6);
102 assertEquals(5, cs.findColumnPosition(5));
104 // hiding column 4 moves column 5 to column 4
105 cs.hideColumns(4, 4);
106 assertEquals(4, cs.findColumnPosition(5));
108 // hiding columns 1 and 2 moves column 5 to column 2
109 cs.hideColumns(1, 2);
110 assertEquals(2, cs.findColumnPosition(5));
114 * Test the code used to locate the reference sequence ruler origin
116 @Test(groups = { "Functional" })
117 public void testLocateVisibleBoundsofSequence()
119 ColumnSelection cs = new ColumnSelection();
120 SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---");
121 assertEquals(2, seq.findIndex(seq.getStart()));
125 Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
126 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
127 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
128 seq.findIndex(seq.getEnd()) - 1 }),
129 Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
131 // hidden column on gap after end of sequence - should not affect bounds
134 Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
135 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
136 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
137 seq.findIndex(seq.getEnd()) - 1 }),
138 Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
140 cs.revealAllHiddenColumns();
141 // hidden column on gap before beginning of sequence - should vis bounds by
145 Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 2,
146 seq.findIndex(seq.getEnd()) - 2, seq.getStart(),
147 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
148 seq.findIndex(seq.getEnd()) - 1 }),
149 Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
151 cs.revealAllHiddenColumns();
152 // hide columns around most of sequence - leave one residue remaining
153 cs.hideColumns(1, 3);
154 cs.hideColumns(6, 11);
156 cs.getVisibleSequenceStrings(0, 5, new SequenceI[] { seq })[0]);
158 Arrays.toString(new int[] { 1, 1, 3, 3,
159 seq.findIndex(seq.getStart()) - 1,
160 seq.findIndex(seq.getEnd()) - 1 }),
161 Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
162 cs.revealAllHiddenColumns();
164 // hide whole sequence - should just get location of hidden region
165 // containing sequence
166 cs.hideColumns(1, 11);
168 Arrays.toString(new int[] { 0, 1, 0, 0,
169 seq.findIndex(seq.getStart()) - 1,
170 seq.findIndex(seq.getEnd()) - 1 }),
171 Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
175 @Test(groups = { "Functional" })
176 public void testLocateVisibleBoundsPathologicals()
178 // test some pathological cases we missed
179 AlignmentI al = new Alignment(new SequenceI[] { new Sequence(
180 "refseqGaptest", "KTDVTI----------NFI-----G----L") });
181 ColumnSelection cs = new ColumnSelection();
182 cs.hideInsertionsFor(al.getSequenceAt(0));
186 + al.getSequenceAt(0).getCharAt(
187 cs.adjustForHiddenColumns(9)));
191 @Test(groups = { "Functional" })
192 public void testHideColumns()
194 ColumnSelection cs = new ColumnSelection();
196 List<int[]> hidden = cs.getHiddenColumns();
197 assertEquals(1, hidden.size());
198 assertEquals("[5, 5]", Arrays.toString(hidden.get(0)));
201 assertEquals(2, hidden.size());
202 // two hidden ranges, in order:
203 assertSame(hidden, cs.getHiddenColumns());
204 assertEquals("[3, 3]", Arrays.toString(hidden.get(0)));
205 assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
207 // hiding column 4 expands [3, 3] to [3, 4]
208 // not fancy enough to coalesce this into [3, 5] though
210 hidden = cs.getHiddenColumns();
211 assertEquals(2, hidden.size());
212 assertEquals("[3, 4]", Arrays.toString(hidden.get(0)));
213 assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
215 // clear hidden columns (note they are added to selected)
216 cs.revealAllHiddenColumns();
217 // it is now actually null but getter returns an empty list
218 assertTrue(cs.getHiddenColumns().isEmpty());
220 cs.hideColumns(3, 6);
221 hidden = cs.getHiddenColumns();
222 int[] firstHiddenRange = hidden.get(0);
223 assertEquals("[3, 6]", Arrays.toString(firstHiddenRange));
225 // adding a subrange of already hidden should do nothing
226 cs.hideColumns(4, 5);
227 assertEquals(1, hidden.size());
228 assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
229 cs.hideColumns(3, 5);
230 assertEquals(1, hidden.size());
231 assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
232 cs.hideColumns(4, 6);
233 assertEquals(1, hidden.size());
234 assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
235 cs.hideColumns(3, 6);
236 assertEquals(1, hidden.size());
237 assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
239 cs.revealAllHiddenColumns();
240 cs.hideColumns(2, 4);
241 hidden = cs.getHiddenColumns();
242 assertEquals(1, hidden.size());
243 assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
245 // extend contiguous with 2 positions overlap
246 cs.hideColumns(3, 5);
247 assertEquals(1, hidden.size());
248 assertEquals("[2, 5]", Arrays.toString(hidden.get(0)));
250 // extend contiguous with 1 position overlap
251 cs.hideColumns(5, 6);
252 assertEquals(1, hidden.size());
253 assertEquals("[2, 6]", Arrays.toString(hidden.get(0)));
255 // extend contiguous with overlap both ends:
256 cs.hideColumns(1, 7);
257 assertEquals(1, hidden.size());
258 assertEquals("[1, 7]", Arrays.toString(hidden.get(0)));
262 * Test the method that hides a specified column including any adjacent
263 * selected columns. This is a convenience method for the case where multiple
264 * column regions are selected and then hidden using menu option View | Hide |
267 @Test(groups = { "Functional" })
268 public void testHideColumns_withSelection()
270 ColumnSelection cs = new ColumnSelection();
271 // select columns 4-6
275 // hide column 5 (and adjacent):
278 List<int[]> hidden = cs.getHiddenColumns();
279 assertEquals(1, hidden.size());
280 assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
281 // none now selected:
282 assertTrue(cs.getSelected().isEmpty());
284 // repeat, hiding column 4 (5 and 6)
285 cs = new ColumnSelection();
290 hidden = cs.getHiddenColumns();
291 assertEquals(1, hidden.size());
292 assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
293 assertTrue(cs.getSelected().isEmpty());
295 // repeat, hiding column (4, 5 and) 6
296 cs = new ColumnSelection();
301 hidden = cs.getHiddenColumns();
302 assertEquals(1, hidden.size());
303 assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
304 assertTrue(cs.getSelected().isEmpty());
306 // repeat, with _only_ adjacent columns selected
307 cs = new ColumnSelection();
311 hidden = cs.getHiddenColumns();
312 assertEquals(1, hidden.size());
313 assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
314 assertTrue(cs.getSelected().isEmpty());
318 * Test the method that hides all (possibly disjoint) selected column ranges
320 @Test(groups = { "Functional" })
321 public void testHideSelectedColumns()
323 ColumnSelection cs = new ColumnSelection();
324 int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
329 cs.hideColumns(15, 18);
331 cs.hideSelectedColumns();
332 assertTrue(cs.getSelected().isEmpty());
333 List<int[]> hidden = cs.getHiddenColumns();
334 assertEquals(4, hidden.size());
335 assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
336 assertEquals("[7, 9]", Arrays.toString(hidden.get(1)));
337 assertEquals("[15, 18]", Arrays.toString(hidden.get(2)));
338 assertEquals("[20, 22]", Arrays.toString(hidden.get(3)));
342 * Test the method that gets runs of selected columns ordered by column. If
343 * this fails, HideSelectedColumns may also fail
345 @Test(groups = { "Functional" })
346 public void testGetSelectedRanges()
349 * getSelectedRanges returns ordered columns regardless
350 * of the order in which they are added
352 ColumnSelection cs = new ColumnSelection();
353 int[] sel = { 4, 3, 7, 21, 9, 20, 8, 22, 2 };
359 range = cs.getSelectedRanges();
360 assertEquals(3, range.size());
361 assertEquals("[2, 4]", Arrays.toString(range.get(0)));
362 assertEquals("[7, 9]", Arrays.toString(range.get(1)));
363 assertEquals("[20, 22]", Arrays.toString(range.get(2)));
366 range = cs.getSelectedRanges();
367 assertEquals(3, range.size());
368 assertEquals("[0, 4]", Arrays.toString(range.get(0)));
372 * Test the method that reveals a range of hidden columns given the start
373 * column of the range
375 @Test(groups = { "Functional" })
376 public void testRevealHiddenColumns()
378 ColumnSelection cs = new ColumnSelection();
379 cs.hideColumns(5, 8);
381 cs.revealHiddenColumns(5);
382 // hidden columns list now null but getter returns empty list:
383 assertTrue(cs.getHiddenColumns().isEmpty());
384 // revealed columns are marked as selected (added to selection):
385 assertEquals("[10, 5, 6, 7, 8]", cs.getSelected().toString());
387 // calling with a column other than the range start does nothing:
388 cs = new ColumnSelection();
389 cs.hideColumns(5, 8);
390 List<int[]> hidden = cs.getHiddenColumns();
391 cs.revealHiddenColumns(6);
392 assertSame(hidden, cs.getHiddenColumns());
393 assertTrue(cs.getSelected().isEmpty());
396 @Test(groups = { "Functional" })
397 public void testRevealAllHiddenColumns()
399 ColumnSelection cs = new ColumnSelection();
400 cs.hideColumns(5, 8);
401 cs.hideColumns(2, 3);
404 cs.revealAllHiddenColumns();
407 * revealing hidden columns adds them (in order) to the (unordered)
410 assertTrue(cs.getHiddenColumns().isEmpty());
411 assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", cs.getSelected().toString());
414 @Test(groups = { "Functional" })
415 public void testIsVisible()
417 ColumnSelection cs = new ColumnSelection();
418 cs.hideColumns(2, 4);
419 cs.hideColumns(6, 7);
420 assertTrue(cs.isVisible(0));
421 assertTrue(cs.isVisible(-99));
422 assertTrue(cs.isVisible(1));
423 assertFalse(cs.isVisible(2));
424 assertFalse(cs.isVisible(3));
425 assertFalse(cs.isVisible(4));
426 assertTrue(cs.isVisible(5));
427 assertFalse(cs.isVisible(6));
428 assertFalse(cs.isVisible(7));
431 @Test(groups = { "Functional" })
432 public void testGetVisibleContigs()
434 ColumnSelection cs = new ColumnSelection();
435 cs.hideColumns(3, 6);
436 cs.hideColumns(8, 9);
437 cs.hideColumns(12, 12);
439 // start position is inclusive, end position exclusive:
440 int[] visible = cs.getVisibleContigs(1, 13);
441 assertEquals("[1, 2, 7, 7, 10, 11]", Arrays.toString(visible));
443 visible = cs.getVisibleContigs(4, 14);
444 assertEquals("[7, 7, 10, 11, 13, 13]", Arrays.toString(visible));
446 visible = cs.getVisibleContigs(3, 10);
447 assertEquals("[7, 7]", Arrays.toString(visible));
449 visible = cs.getVisibleContigs(4, 6);
450 assertEquals("[]", Arrays.toString(visible));
453 @Test(groups = { "Functional" })
454 public void testInvertColumnSelection()
456 ColumnSelection cs = new ColumnSelection();
460 cs.hideColumns(3, 3);
461 cs.hideColumns(6, 6);
463 // invert selection from start (inclusive) to end (exclusive)
464 // hidden columns are _not_ changed
465 cs.invertColumnSelection(2, 9);
466 assertEquals("[2, 5, 7]", cs.getSelected().toString());
468 cs.invertColumnSelection(1, 9);
469 assertEquals("[1, 4, 8]", cs.getSelected().toString());
472 @Test(groups = { "Functional" })
473 public void testMaxColumnSelection()
475 ColumnSelection cs = new ColumnSelection();
479 assertEquals(513, cs.getMax());
480 cs.removeElement(513);
481 assertEquals(1, cs.getMax());
483 assertEquals(0, cs.getMax());
486 assertEquals(513, cs.getMax());
490 @Test(groups = { "Functional" })
491 public void testMinColumnSelection()
493 ColumnSelection cs = new ColumnSelection();
497 assertEquals(0, cs.getMin());
499 assertEquals(1, cs.getMin());
501 assertEquals(0, cs.getMin());
504 @Test(groups = { "Functional" })
505 public void testEquals()
507 ColumnSelection cs = new ColumnSelection();
513 cs.hideColumns(5, 9);
515 // same selections added in a different order
516 ColumnSelection cs2 = new ColumnSelection();
521 // with no hidden columns
522 assertFalse(cs.equals(cs2));
523 assertFalse(cs2.equals(cs));
525 // with hidden columns added in a different order
526 cs2.hideColumns(6, 9);
527 cs2.hideColumns(5, 8);
530 assertTrue(cs.equals(cs2));
531 assertTrue(cs.equals(cs));
532 assertTrue(cs2.equals(cs));
533 assertTrue(cs2.equals(cs2));
536 assertFalse(cs.equals(cs2));
537 assertFalse(cs2.equals(cs));
539 cs2.removeElement(12);
540 assertTrue(cs.equals(cs2));
543 assertFalse(cs.equals(cs2));
545 * unhiding a column adds it to selection!
547 cs2.revealHiddenColumns(88);
548 assertFalse(cs.equals(cs2));
550 assertTrue(cs.equals(cs2));
554 * Test the method that returns selected columns, in the order in which they
557 @Test(groups = { "Functional" })
558 public void testGetSelected()
560 ColumnSelection cs = new ColumnSelection();
561 int[] sel = { 4, 3, 7, 21 };
567 List<Integer> selected = cs.getSelected();
568 assertEquals(4, selected.size());
569 assertEquals("[4, 3, 7, 21]", selected.toString());
572 * getSelected returns a read-only view of the list
573 * verify the view follows any changes in it
578 assertEquals("[3, 21, 1]", selected.toString());
582 * Test to verify that the list returned by getSelection cannot be modified
584 @Test(groups = { "Functional" })
585 public void testGetSelected_isReadOnly()
587 ColumnSelection cs = new ColumnSelection();
590 List<Integer> selected = cs.getSelected();
594 fail("expected exception");
595 } catch (UnsupportedOperationException e)
602 fail("expected exception");
603 } catch (UnsupportedOperationException e)
610 fail("expected exception");
611 } catch (UnsupportedOperationException e)
617 Collections.sort(selected);
618 fail("expected exception");
619 } catch (UnsupportedOperationException e)
626 * Test that demonstrates a ConcurrentModificationException is thrown if you
627 * change the selection while iterating over it
630 groups = "Functional",
631 expectedExceptions = { ConcurrentModificationException.class })
632 public void testGetSelected_concurrentModification()
634 ColumnSelection cs = new ColumnSelection();
640 * simulate changing the list under us (e.g. in a separate
641 * thread) while iterating over it -> ConcurrentModificationException
643 List<Integer> selected = cs.getSelected();
644 for (Integer col : selected)
646 if (col.intValue() == 0)
653 @Test(groups = "Functional")
654 public void testMarkColumns()
656 ColumnSelection cs = new ColumnSelection();
657 cs.addElement(5); // this will be cleared
658 BitSet toMark = new BitSet();
664 assertTrue(cs.markColumns(toMark, 3, 8, false, false, false));
665 List<Integer> selected = cs.getSelected();
666 assertEquals(2, selected.size());
667 assertTrue(selected.contains(3));
668 assertTrue(selected.contains(6));
671 @Test(groups = "Functional")
672 public void testMarkColumns_extend()
674 ColumnSelection cs = new ColumnSelection();
677 BitSet toMark = new BitSet();
684 * extending selection of {3, 6} should leave {1, 3, 5, 6} selected
686 assertTrue(cs.markColumns(toMark, 3, 8, false, true, false));
687 List<Integer> selected = cs.getSelected();
688 assertEquals(4, selected.size());
689 assertTrue(selected.contains(1));
690 assertTrue(selected.contains(3));
691 assertTrue(selected.contains(5));
692 assertTrue(selected.contains(6));
695 @Test(groups = "Functional")
696 public void testMarkColumns_invert()
698 ColumnSelection cs = new ColumnSelection();
699 cs.addElement(5); // this will be cleared
700 BitSet toMark = new BitSet();
707 * inverted selection of {3, 6} should select {4, 5, 7, 8}
709 assertTrue(cs.markColumns(toMark, 3, 8, true, false, false));
710 List<Integer> selected = cs.getSelected();
711 assertEquals(4, selected.size());
712 assertTrue(selected.contains(4));
713 assertTrue(selected.contains(5));
714 assertTrue(selected.contains(7));
715 assertTrue(selected.contains(8));
718 @Test(groups = "Functional")
719 public void testMarkColumns_toggle()
721 ColumnSelection cs = new ColumnSelection();
722 cs.addElement(1); // outside change range
725 cs.addElement(10); // outside change range
726 BitSet toMark = new BitSet();
733 * toggling state of {3, 6} should leave {1, 4, 6, 10} selected
735 assertTrue(cs.markColumns(toMark, 3, 8, false, false, true));
736 List<Integer> selected = cs.getSelected();
737 assertEquals(4, selected.size());
738 assertTrue(selected.contains(1));
739 assertTrue(selected.contains(4));
740 assertTrue(selected.contains(6));
741 assertTrue(selected.contains(10));
744 @Test(groups = "Functional")
745 public void testCopyConstructor()
747 ColumnSelection cs = new ColumnSelection();
750 cs.hideColumns(10, 11);
751 cs.hideColumns(5, 7);
752 assertEquals("[5, 7]", Arrays.toString(cs.getHiddenColumns().get(0)));
754 ColumnSelection cs2 = new ColumnSelection(cs);
755 assertTrue(cs2.hasSelectedColumns());
756 assertTrue(cs2.hasHiddenColumns());
757 // order of column selection is preserved
758 assertEquals("[3, 1]", cs2.getSelected().toString());
759 assertEquals(2, cs2.getHiddenColumns().size());
760 // hidden columns are held in column order
761 assertEquals("[5, 7]", Arrays.toString(cs2.getHiddenColumns().get(0)));
762 assertEquals("[10, 11]", Arrays.toString(cs2.getHiddenColumns().get(1)));